【Java】Java编程问题top100---基础语法系列(一)

解博明
2023-12-01


记得这个是当时刚入学那会尝试去看的一个github上的优秀内容,当时很多东西看得云里雾里,不太懂。
现在正好有空了,再来一次,会稍作修改再加上代码实践,有问题可以留言。
重温的同时,希望对你能有所帮助。
以下系列将会是对stackoverflow上Java相关、投票数TOP100的问答(精修版)。

一、Java += 操作符实质

问题:我之前以为:
i += j 等同于 i = i + j;
但假设有:
int i = 5; long j = 8;
这时 i = i + j 不能编译,但 i += j 却可以编译。这说明两者还是有差别的
这是否意味着,i += j,实际是等同于 i= (type of i) (i + j)呢?

// Java += 操作符实质
public class test01 {
    public static void main(String[] args) {
        int i = 5;
        long j = 8;
        // i = i + j;// 编译报错,需要的类型int,提供了long
        i += j; // 这行没有问题
        i = i + (int) j; // 这行也没问题
    }
}

回答
复合赋值表达式E1 op= E2来说, (诸如 i += j i -= j i*=j i/=j等等),其实是等同于 E1 = (T)((E1) op (E2))
其中,E1和E2表示两个操作数,T是E1这个元素的类型
举例来说,如下的代码
int i = 5;long j = 8;
i += j;等同于i = (int)(i + j); 也就是(i + j)先执行,然后(i + j)的结果强制类型转换为int类型
新人注意:表达式i = (int)(i + j); (i + j)这个是带括号的。
它也等价于i = i + (int) j;

二、将InputStream转换为String

首先项目名字那按右键选Add Framework Support或者选添加框架支持,然后选Maven,在pom.xml里写入下面这个

    <dependencies>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.4</version>
        </dependency>
    </dependencies>

使用IOUtils

最靠谱的方法,用Apache commons IOUtils
文档test02.txt

ABC a b ABc b
你好
//将InputStream转换为String
public class test02 {
    public static void main(String[] args) throws Exception {
        FileInputStream inputStream = new FileInputStream("D:\test02.txt");
        String encoding = "UTF-8";
        StringWriter writer = new StringWriter();
        IOUtils.copy(inputStream, writer, encoding);
        String theString = writer.toString();
        // String theString = IOUtils.toString(inputStream, encoding);
        // 这个方法其实封装了上面的方法,减少了一个参数
    }
}

或者

String theString = IOUtils.toString(inputStream, encoding);
//这个方法其实封装了上面的方法,减少了一个参数 

自己写轮子

如果不想引入Apache库,也可以这样做

//将InputStream转换为String
public class test02 {
    static String convertStreamToString(InputStream is) {
        Scanner s = new Scanner(is).useDelimiter("你好");//读取到你好这个词就会停止输入
        return s.hasNext() ? s.next() : "";
    }
    public static void main(String[] args) throws FileNotFoundException {
        FileInputStream fileInputStream = new FileInputStream("test.txt");
        System.out.println(convertStreamToString(fileInputStream));
    }
}

三、将数组转换为List

1、最简单的for循环遍历挨个赋值给List。
2、Arrays.asList(array);

// 将数组转换为List
public class test03 {
    public static void main(String[] args) {
        Integer[] array = {1, 2, 3, 4};
        List<Integer> arrayLists = Arrays.asList(array);// 这里
        System.out.println(arrayLists);
    }
}

但这样做会有坑:

  1. 生成的list,是固定长度的。也就是说,如果调用它的add()方法或者remove()方法,都会抛异常UnsupportedOperationException。
  2. 如果修改数组的值list中的对应值也会改变

因为Arrays.asList() 返回的是Arrays里的内部静态类,而不是Java.util.ArrayList这个类。
这个java.util.Arrays.ArrayList有set(),get(),contains()方法,但是没有任何add() 方法,所以它是固定大小的.

建议用这个
Collections.addAll(arrayLists, array);

// 将数组转换为List
public class test03 {
    public static void main(String[] args) {
        Integer[] array = {1, 2, 3, 4};
        List<Integer> arrayLists = new ArrayList<>();
        Collections.addAll(arrayLists, array);// 这里
    }
}

四、如何遍历map对象

在Java中有多种遍历HashMap的方法。
让我们回顾一下最常见的方法和它们各自的优缺点。
由于所有的Map都实现了Map接口,所以接下来方法适用于所有Map(如:HaspMap,TreeMap,LinkedMap,HashTable,ConcurrentHashMAp,……)

使用For-Each迭代entries(方法一)

这是最常见的方法,并在大多数情况下更可取的。当你在循环中需要使用Map的键和值时,就可以使用这个方法

public class test04 {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        // map = null;
        map.put("a", 1);
        testOne(map);
    }

    public static void testOne(Map<String, Integer> map) {
        if (map != null && !map.isEmpty()) {
            for (Map.Entry<String, Integer> entry : map.entrySet()) {
                System.out.println("key = " + entry.getKey() + ", value = " + entry.getValue());
            }
        }else {
            System.out.println(map);
        }
    }
}

注意:遍历之前你应该判断是否为空引用。如果遍历的map是null的话,For-Each循环会抛出NullPointerException异常。

使用For-Each迭代keys和values(方法二)

如果你只需要用到map的keys或values时,你可以遍历KeySet或者values代替entrySet

    public static void testTwo(Map<String, Integer> map) {
        if (map != null && !map.isEmpty()) {
            for (String key : map.keySet()) {
                System.out.println("Key = " + key);
            }
            for (Integer value : map.values()) {
                System.out.println("Value = " + value);
            }
        }
    }

这个方法比entrySet迭代具有轻微的性能优势(大约快10%)并且代码更简洁

使用Iterator迭代(方法三)

使用泛型

    public static void testThree(Map<String, Integer> map) {
        Iterator<Map.Entry<String, Integer>> entries = map.entrySet().iterator();
        while (entries.hasNext()) {
            Map.Entry<String, Integer> entry = entries.next();
            System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
        }
    }

不使用泛型

public static void testFour(Map<String, Integer> map) {
        if (map != null && !map.isEmpty()) {
            Iterator entries = map.entrySet().iterator();
            while (entries.hasNext()) {
                Map.Entry entry = (Map.Entry) entries.next();
                String key = (String) entry.getKey();
                Integer value = (Integer) entry.getValue();
                System.out.println("Key = " + key + ", Value = " + value);
            }
        } else {
            System.out.println(map);
        }
    }

你可以使用同样的技术迭代keyset或者values
首先,它是遍历java 5 以前版本的map的唯一方法。
第二是可以让你在迭代的时候从map中删除entries的(通过调用iterator.remover())唯一方法。
如果你试图在For-Each迭代的时候删除entries,你将会得到unpredictable resultes 异常。
从性能方面看,这个方法等价于使用For-Each迭代(方法二)

迭代keys并搜索values(低效的)(方法四)

    public static void testFive(Map<String, Integer> map) {
        if (map != null && !map.isEmpty()) {
            for (String key : map.keySet()) {
                Integer value = map.get(key);
                System.out.println("Key = " + key + ", Value = " + value);
            }
        } else {
            System.out.println(map);
        }
    }

比方法一更简洁,但是实际更慢更低效,通过key得到value值更耗时

总结

如果你只需要使用key或者value使用方法二使用For-Each迭代keys和values,如果你使用java 5 以前的版本或者打算在迭代的时候移除entries,使用方法三使用Iterator迭代其他情况请使用方法一For-Each迭代entries方法。避免使用迭代keys并搜索values(低效的)方法。

五、public,protected,private,不加修饰符。有什么区别呢?

如下表所示,Y表示能访问(可见性),N表示不能访问,例如第一行的第3个Y,表示类的变量/方法如果是用public修饰,它的子类能访问这个变量/方法

修饰符类内部同个包(package)子类其他范围
publicYYYY
protectedYYYN
无修饰符YYN or Y(见说明)N
privateYNNN

说明:
需要特别说明“无修饰符”这个情况,子类能否访问父类中无修饰符的变量/方法,取决于子类是否在同一个包中。如果子类和父类在同一个包中,那么子类可以访问父类中的无修饰符的变量/方法,否则不行。

Java编程问题top100—基础语法系列导航

Java编程问题top100—基础语法系列(一)
Java编程问题top100—基础语法系列(二)
Java编程问题top100—基础语法系列(三)
未完待续

如有错误,还请多多指教!
转载或者引用本文内容请注明来源及原作者:橘足轻重;

 类似资料: