蚂蚁链

蚂蚁链,链天下

Java学习篇之---小整数缓存机制

这是一个有意思的话题。如果你运行如下代码:
Integer a = 1000, b = 1000;    
System.out.println(a == b);//1  
Integer c = 100, d = 100;    
System.out.println(c == d);//2  

你将会得到:
false  
true  
基本知识:
正如我们所知,如果两个引用指向同一个对象,按照“==”来判断两者是相等的。如果两个引用指向两个不同的对象,那么按照"=="来判断两者是不等的,即使两者有相同的引用内容。

因此最后一条语句也应该是false。

这就是有趣之处了。如果你去看Integer.java类,你会发现有一个内部私有类IntegerCache.java,它缓存了从-128~127之间的所有整数对象。
所以事情就是,所有的小整数都被在内部进行缓存。例如当我们声明如下:
Integer c = 100;  
它实际上内部进行的操作是这样的:
Integer i = Integer.valueOf(100);  
如果我们去看valueOf()方法,我们可以看到:
public static Integer valueOf(int i) {  
      if (i >= IntegerCache.low && i  
          return IntegerCache.cache[i + (-IntegerCache.low)];  
      return new Integer(i);  
}  
如果值在-128~127之间,那么它就会从高速缓存中返回实例。
所以:
Integer c = 100, d = 100;  
指向了同一个对象。
这就是为什么当我们写如下代码后我们获得true的原因:
System.out.println(c == d);  
现在你可能会问,为什么这里需要缓存?
合理的解释是这样的:在此范围内的小整数比大整数使用率要高,所以为了减少潜在的内存占用,使用相同的底层对象是有价值的。


然而,通过反射API你会误用此功能。

运行起来,享受以下代码吧:
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {  
      Class cache = Integer.class.getDeclaredClasses()[0]; //1  
      Field myCache = cache.getDeclaredField("cache"); //2  
      myCache.setAccessible(true);//3  
      Integer[] newCache = (Integer[]) myCache.get(cache); //4  
      newCache[132] = newCache[133]; //5  
      int a = 2;  
      int b = a + a;  
      System.out.printf("%d + %d = %d", a, a, b); //  
    }  
运行结果:

对以上代码的解释:
正常情况下newCache[132]中存放的是4,newCache[133]中存放的是5,现在用newCache[133]中的值替换newCache[132]中的值。这样的话计算完毕之后发现结果是4,于是系统就会去默认应该缓存4的地址上取值,但是此时由于已经将原本是值4地址上的值换成了5,所以结果是5。

另外需要注意的是:

 System.out.printf("%d + %d = %d", a, a, b); //

System.out.println(a+" + "+a+" = "+b ); //

结果是不一样的。参考println的方法声明,作者对于不同的基本类型都提供了overwrite,而printf并没有为基本类型提供方法。

问题的本质在于:println(2+2)的结果仍是基本类型,并没有进行装箱,结果为4;System.out.println((Integer)2+2);的结果为5。

以上内容大部分翻译自:https://dzone.com/articles/why-1000-1000-returns-false-but-100-100-returns-tr

 

Go Back

评论

检索

文章类型

文章分类