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(); } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端