在Java中奇怪的整数装箱

我刚刚看到类似这样的代码:

public class Scratch { public static void main(String[] args) { Integer a = 1000, b = 1000; System.out.println(a == b); Integer c = 100, d = 100; System.out.println(c == d); } } 

运行时,这块代码将打印出来:

 false true 

我明白为什么第一个是false :因为这两个对象是单独的对象,所以==比较引用。 但是我不明白,为什么第二条语句返回true ? 当Integer的值在一定范围内时,是否有一些奇怪的自动装箱规则? 这里发生了什么?

true线路实际上是由语言规范保证的。 从第5.1.7节 :

如果p的值为真,false,一个字节,范围在\ u0000到\ u00f之间的一个字符,或者在-128到127之间的一个int或者一个短数字,那么让r1和r2是任意两个装箱转换的结果p。 r1 == r2总是如此。

讨论继续,这表明尽pipe你的第二行产出是有保证的,但第一行不是(见下面引用的最后一段):

理想情况下,装箱一个给定的原始值p,总会产生一个相同的参考。 实际上,使用现有的实现技术可能是不可行的。 上面的规则是一个务实的妥协。 上面的最后一个条款要求某些常用值总是被装入不可区分的对象中。 实现可能会caching这些,懒惰或热切。

对于其他的值,这个公式不允许对程序员的盒装值的身份进行任何假设。 这将允许(但不要求)分享这些引用的一部分或全部。

这确保了在大多数情况下,行为将是所期望的,而不施加不适当的性能损失,特别是在小型设备上。 例如,内存限制较less的实现可能会caching所有字符和短语,以及在-32K – + 32K范围内的整数和长整数。

 public class Scratch { public static void main(String[] args) { Integer a = 1000, b = 1000; //1 System.out.println(a == b); Integer c = 100, d = 100; //2 System.out.println(c == d); } } 

输出:

 false true 

是的,第一个输出产生比较参考; 'a'和'b' – 这是两个不同的参考。 在第一点,实际上创build了两个类似于 –

 Integer a = new Integer(1000); Integer b = new Integer(1000); 

第二个输出是由于JVM尝试保存内存,当Integer落在一个范围内(从-128到127)而产生的。 在第2点,没有为“d”创build新的Integertypes的引用。 整数types引用variables“d”不是创build新的对象,而是只分配了先前创build的由“c”引用的对象。 所有这些都是由JVM完成的。

这些内存保存规则不仅适用于Integer。 为了节省内存的目的,下面的包装对象的两个实例(虽然通过装箱创build),总是==它们的原始值是相同的 –

  • 布尔
  • 字节
  • \ u0000\u007f字符(7f是十进制的127)
  • 短和整数从-128127

在一些范围内的整数对象(我想可能是从-128到127)得到caching和重用。 整个范围之外的整数每次都会得到一个新的对象。

我的猜测是,Java保留了一个已经“装箱”的小整数的caching,因为它们非常普遍,并且节省了大量的时间来重新使用现有的对象,而不是创build一个新的对象。

是的,当数值在一定范围内时,有一个奇怪的自动装箱规则。 当给一个Objectvariables赋一个常量时,语言定义中没有任何东西需要创build一个新的对象。 它可以重用caching中的现有对象。

实际上,JVM通常会为此存储一个小整数的caching,以及诸如Boolean.TRUE和Boolean.FALSE的值。

在Java中,拳击的整数范围在-128到127之间。 当你在这个范围内使用数字时,你可以将它与==运算符进行比较。 对于范围之外的Integer对象,您必须使用equals。

这是一个有趣的观点。 有效的Javabuild议总是为自己的类重写equals。 另外,为了检查java类的两个对象实例的相等性,总是使用equals方法。

 public class Scratch { public static void main(String[] args) { Integer a = 1000, b = 1000; System.out.println(a.equals(b)); Integer c = 100, d = 100; System.out.println(c.equals(d)); } } 

收益:

 true true 

在Java 5中,引入了一个新特性来保存内存并提高Integertypes对象处理的性能。 整数对象在内部caching,并通过相同的引用对象重用。

  1. 这适用于范围在-127到+127之间的整数值(最大整数值)。

  2. 整数caching仅适用于自动装箱。 整数对象在使用构造函数构build时不会被caching。

欲了解更多详情,请通过下面链接:

整数caching的详细信息

如果我们检查Integer obeject的源代码,我们会findvalueOf方法的来源,就像这样:

 public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); } 

这可以解释为什么在自动装箱过程中Integer对象(在-128( Integer.low )到127( Integer.high )范围内)是相同的被引用对象。 我们可以看到有一个IntegerCache类负责Integercaching数组,这是Integer类的一个私有的静态内部类。

还有一个有趣的例子可以帮助我们理解这个奇怪的情况:

 public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException { Class cache = Integer.class.getDeclaredClasses()[0]; Field myCache = cache.getDeclaredField("cache"); myCache.setAccessible(true); Integer[] newCache = (Integer[]) myCache.get(cache); newCache[132] = newCache[133]; Integer a = 2; Integer b = a + a; System.out.printf("%d + %d = %d", a, a, b); //The output is: 2 + 2 = 5 }