Java小细节

Java小细节,把遇到的小问题汇集到这里,不定期持续更新~~

 

①下面的程序打印结果是什么?

复制代码
public class TestMain1 {
    public static void main(String[] args) {
        Integer i1 = 127;
        Integer i2 = 127;
        System.out.println(i1==i2);
        
        Integer i3 = 128;
        Integer i4 = 128;
        System.out.println(i3==i4);
        
        Integer i5 = 127;
        Integer i6 = new Integer(127);
        System.out.println(i5==i6);
        
        Integer i7 = 128;
        Integer i8 = new Integer(128);
        System.out.println(i7==i8);
    }
}
复制代码

运行一下,得到的结果是:true  false  false   false

第3、4打印出false比较好理解,因为是new的不同对象。但是第1、2打印的结果为什么不一样呢?

这是因为:Integer的缓存做了处理。看看Integer的valueOf方法就明白了:

public static Integer valueOf(int i) {
    final int offset = 128;
    if (i >= -128 && i <= 127) { // must cache
        return IntegerCache.cache[i + offset];
    }
    return new Integer(i);
}

就是说-128~127之间的数据,返回的是同一个对象。所以i1==i2 i3!=i4

Java的解释是:....this method is likely to yield significantly better space and time performance by caching frequently requested values...

 

②下面这段程序能正常运行吗?

复制代码
import java.util.Arrays;
import java.util.List;


public class TestMain2 {
    public static void main(String[] args) {
        String[] array= new String[5];
        for (int i = 0; i < array.length; i++) {
            array[i] = String.valueOf(i);
        }
        
        List<String> list = Arrays.asList(array); 
        
        list.add("test");
    }

}
复制代码

答案是不能,会抛出java.lang.UnsupportedOperationException异常

解析:Arrays.asList方法得到的list是一个静态的内部类

 

private static class ArrayList<E> extends AbstractList<E> implements RandomAccess, java.io.Serializable

 

而这个ArrayList并没有覆写父类的add方法,所以上面的代码会调用AbstractList中的add方法。

而AbstractList中的add方法是这个样子的:

public boolean add(E o) {
    add(size(), o);
    return true;
}

public void add(int index, E element) {
    throw new UnsupportedOperationException();
}

所以,执行到list.add("test");就会抛出异常。

 

③遍历集合的同时删除元素

 昨天看同事写的代码中出现的代码,是在是匪夷所思,一个多年Java经验的人写出来的啊~~

 除开具体的业务逻辑,大概是下面这个样子

复制代码
    
    public static void main(String[] args) {
        List<String> testList = new ArrayList<String>();
        testList.add("one");
        testList.add("two");
        testList.add("three");
for (String string : testList) { System.out.println(string); if (string.equals("one")) { testList.remove(string); } } }
复制代码

先说明一下,for-each循环会被编译器转换成Iterator来进行。上面的代码会出现什么问题呢?抛出java.util.ConcurrentModificationException,

看看源码即知:

复制代码
    public E next() {
            checkForComodification();
        try {
        E next = get(cursor);
        lastRet = cursor++;
        return next;
        } catch(IndexOutOfBoundsException e) {
        checkForComodification();
        throw new NoSuchElementException();
        }
    }
复制代码

在AbstractList中实现了next方法,在取得下一个之前进行检查,checkForComodification源码如下:

final void checkForComodification() {
        if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}
modCount:表示该集合对象结构被修改的次数,add或者remove,都会让modCount++
expectedModCount:表示期待的modCount值,用来判断在遍历过程中集合是否被修改过。

回到正题,上面的代码执行remove之后,modCount==4,而expectedModCount==3

在取得下一个next的时候,会抛出异常。(是的,你没看错,是remove之后,并不是remove的同时抛出的异常)

不过,这样的代码,并不总是抛出ConcurrentModificationException,看看下面的代码:

复制代码
    
    public static void main(String[] args) {
        List<String> testList = new ArrayList<String>();
        testList.add("one");
        testList.add("two");
        //testList.add("three");

        for (String string : testList) {
            System.out.println(string);
            if (string.equals("one")) {
                testList.remove(string);
            }
        }
    }
    
复制代码

这个代码和上面的只有一行只差,他不会抛出ConcurrentModificationException异常,但是不会打印出two,你知道为什么吗?

答案其实在上面也提到了,在取得下一个next的时候,会抛出异常。(是的,你没看错,是remove之后,并不是remove的同时抛出的异常)

删除“one”之后,size变成1,就不会有下一次遍历,即使modCount==3,而expectedModCount==2,异常也不会触发。

不管怎么说,上面的删除方式是错误的,那么正确的该怎么写呢?用Iterator的remove方法

复制代码
    
    public static void main(String[] args) {
        List<String> testList = new ArrayList<String>();
        testList.add("one");
        testList.add("two");
        testList.add("three");

        Iterator<String> it = testList.iterator();
        while (it.hasNext()) {
            String temp = it.next();
            System.out.println(temp);
            if (temp.equals("one")) {
                it.remove();
            }
        }
    }
    
复制代码

 

 

 

 

 

 

 

 

 

posted @   yejg1212  阅读(321)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端
点击右上角即可分享
微信分享提示