文章开头提到以下代码
1 | Integer a = new Integer( 2 ); |
2 | Integer b = new Integer( 2 ); |
3 | System.out.print(a == b); |
我记得以前在网上看到有人说new Integer的时候, 如果之前已经new过同样的数字, 再次new的时候不会创建新对象, 而是从缓存中去读取之前创建的对象, 然后把引用赋值给新创建的对象, 如果按照这个原理, 那么文章中提到的输出为true是对的。
但是经过测试后发现, 实际情况并非如此, 以下是我的测试代码和结果:
02 | public void testPlus() { |
04 | Integer a = new Integer( 2 ); |
05 | Integer b = new Integer( 2 ); |
06 | System.out.println(a==b); |
08 | Integer c= Integer.parseInt( "2" ); |
09 | System.out.println(a==c); |
13 | System.out.println(d==e); |
15 | Integer f = Integer.valueOf( 2 ); |
16 | Integer g = Integer.valueOf( 2 ); |
17 | System.out.println(e==f); |
18 | System.out.println(a==f); |
19 | System.out.println(f==g); |
可以发现, 无论是JDK6 还是7 以上理论都是错误的。
查看Integer对应的构造方法可以看到, 方法中只有一行:
1 | public Integer( int value) { |
这就意味着new Integer(int)的时候jdk并没有从缓存中去读取已存在的对象, 甚至压根就没有去缓存。
那什么时候JDK才会从缓存中读取对象来进行赋值呢?
我们看Integer.valueOf(int)方法发现
1 | public static Integer valueOf( int i) { |
2 | assert IntegerCache.high >= 127 ; |
3 | if (i >= IntegerCache.low && i <= IntegerCache.high) |
4 | return IntegerCache.cache[i + (-IntegerCache.low)]; |
而查看IntegerCache发现
01 | private static class IntegerCache { |
02 | private IntegerCache(){} |
04 | static final Integer cache[] = new Integer[-(- 128 ) + 127 + 1 ]; |
07 | for ( int i = 0 ; i < cache.length; i++) |
08 | cache[i] = new Integer(i - 128 ); |
也就是说即便是valueOf也是在IntegerCache第一次初始化的时候创建了一个256大小的缓存, 每次valueOf(int)的时候如果数字大小在-128到127之间就不会创建新对象, 而是从缓存读取。
而Integer类中除了valueOf使用了缓存以外, 其他地方并未使用。
而这里需要注意的是int自动装包调用的是valueOf方法, 所有才会存在d==e输出true。
同样, 查看Long(long) Short(short)发现也存在同样情况, 而Double则不存在。
另外该文章中提到一个可以让2+2=5的方法, 即修改IntegerCache数组指针, 通过这种方法2+2可以等于任意一个-128到127之间的数字! 实际项目中如果没有极特殊的需求, 应该禁止这样玩! 搞不好会让人吐血。
02 | public void testReflect(){ |
03 | Class cache = Integer. class .getDeclaredClasses()[ 0 ]; |
06 | c = cache.getDeclaredField( "cache" ); |
07 | c.setAccessible( true ); |
08 | Integer[] array = (Integer[]) c.get(cache); |
10 | array[ 132 ] = array[ 133 ]; |
11 | } catch (NoSuchFieldException e) { |
13 | } catch (SecurityException e) { |
15 | } catch (IllegalArgumentException e) { |
17 | } catch (IllegalAccessException e) { |
21 | System.out.printf( "%d" , 2 + 2 ); |