【问题标题】:Why does the behavior of the Integer constant pool change at 127?为什么整数常量池的行为在 127 处发生变化?
【发布时间】:2012-10-17 09:08:57
【问题描述】:

我无法理解 Java 整数常量池的工作原理。

我了解字符串的行为,因此能够证明自己的情况与整数常量相同。

所以,对于整数

Integer i1 = 127;
Integer i2 = 127;
System.out.println(i1==i2); // True

&

Integer i1 = new Integer(127);
Integer i2 = new Integer(127);
System.out.println(i1==i2); // False

到此为止,一切都在我脑海中浮现。

我无法理解的是,当我从 127 增加整数时,它的行为会有所不同。这种行为在 127 之后会发生变化,下面是代码 sn-p

Integer i1 = 128;
Integer i2 = 128;
System.out.println(i1==i2); // False. WHY?????

谁能帮我理解这个?

【问题讨论】:

标签: java integer constants


【解决方案1】:

Java维护从-128127的整数池

像下面这样声明整数

Integer i1 = 127;

结果为

Integer i1 = Integer.valueOf(127);

所以第一种情况实际发生的是

Integer i1 = 127;<---Integer.valueOf(127);
Integer i2 = 127;<---Integer.valueOf(127);<---Same reference as first

来自IntegervalueOf方法的源代码

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

因此,如果值在 -128127 之间,并且您调用 valueOf,则您将获得相同的引用,否则它只会返回 new Integer(i)

由于引用相同,您的 == 运算符适用于 valueOf 在此范围之间返回的整数。

【讨论】:

  • +1 用于提及 Integer.valueOf()。其他答案似乎认为这只是魔术。
  • 最佳解释,这应该是被接受的答案
【解决方案2】:

Java 缓存-128 to 127 范围内的整数对象。因此,当您尝试将此范围内的值分配给 wrapper 对象时,boxing 操作将调用 Integer.valueOf 方法,然后它将分配对池中已有对象的引用。

另一方面,如果您将该范围之外的值分配给wrapper 引用类型,Integer.valueOf 将为该值创建一个新的Integer 对象。因此,将 reference 与具有超出此范围的值的 Integer 对象进行比较将为您提供 false

所以,

Integer i = 127;  --> // Equivalent to `Integer.valueOf(127)`
Integer i2 = 127;

// Equivalent to `Integer.valueOf(128)`
// returns `new Integer(128)` for value outside the `Range - [-128, 127]`
Integer i3 = 128; 
Integer i4 = 128;

System.out.println(i == i2); // true, reference pointing to same literal
System.out.println(i3 == i4); // false, reference pointing to different objects

但是,当您使用new 运算符创建整数实例时,将在堆上创建一个新对象。 所以,

Integer i = new Integer(127);
Integer i2 = new Integer(127);

System.out.println(i == i2); // false

【讨论】:

  • 您的 i3 与 i4 比较不正确,因为您使用的是 int 而不是 Integer
  • @JonSkeet。哦!对不起。我应该在所有四个地方都有Integer。将编辑它。谢谢指点:)
  • 答案基本不正确。 Java 不会“缓存整数文字”。 Java 缓存 Integer 对象 从 -128 到 127如果您调用 Integer.valueOf() 来获取它们, 这确实发生在自动装箱中,但不会发生在 OP 的代码中,这这就是为什么它没有给出预期的答案。
  • @EJP。您现在可以删除您的反对票吗?当然,如果你投了反对票。
  • 当然,当您的答案正确时。不是以前。我已经提供了足够的信息供您更正它。你还没有完全吸收它。
【解决方案3】:

不,数字常量池的工作方式与字符串不同。对于字符串,只保留编译时常量 - 而对于整数类型的包装器类型,任何装箱操作将始终使用适用于该值的池。比如:

int x = 10;
int y = x + 1;
Integer z = y; // Not a compile-time constant!
Integer constant = 11;
System.out.println(z == constant); // true; reference comparison

JLS 保证小范围的池化值,但如果他们愿意,实现可以使用更大的范围。

请注意,虽然不能保证,但我查看的每个实现都使用 Integer.valueOf 来执行装箱操作 - 因此您可以在没有语言帮助的情况下获得相同的效果:

Integer x = Integer.valueOf(100);
Integer y = Integer.valueOf(100);
System.out.println(x == y); // true

来自section 5.1.7 of the JLS

如果被装箱的值 p 是真、假、一个字节或 \u0000 到 \u007f 范围内的一个字符,或者一个介于 -128 和 127(包括)之间的 int 或短数字,则令 r1 和 r2 为p 的任意两次装箱转换的结果。 r1 == r2 总是如此。

理想情况下,装箱一个给定的原始值 p,总是会产生一个相同的引用。在实践中,使用现有的实现技术这可能是不可行的。上述规则是一种务实的妥协。上面的最后一个子句要求某些公共值总是被装箱到无法区分的对象中。实现可能会懒惰地或急切地缓存这些。对于其他值,此公式不允许程序员对装箱值的身份进行任何假设。这将允许(但不要求)共享部分或全部这些引用。

这可确保在最常见的情况下,行为将是期望的行为,而不会造成过度的性能损失,尤其是在小型设备上。例如,内存限制较少的实现可能会缓存所有 char 和 short 值,以及 -32K 到 +32K 范围内的 int 和 long 值。

【讨论】:

    【解决方案4】:

    简而言之,Java 缓存整数的较新版本在 -128 到 127 范围内(256 个值)。 看这里

    What exactly does comparing Integers with == do?

    【讨论】:

      猜你喜欢
      • 2016-10-18
      • 2021-12-18
      • 2016-01-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多