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(); } } }