为什么整数常量池的行为在 127 处发生变化?

2022-08-31 23:58:51

我无法理解 Java Constant Pool for Integer 是如何工作的。

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

因此,对于整数

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 之后更改,下面是代码片段

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

有人能帮我理解这一点吗?


答案 1

否,数字的常量池的工作方式与字符串不同。对于字符串,仅会插入编译时常量 - 而对于整数类型的包装器类型,如果任何装箱操作适用于该值,则将始终使用池。例如:

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

JLS 的第 5.1.7 节中可以看出:

如果装箱的值 p 是 true、false、一个字节或介于 \u0000 和 \u007f 范围内的字符,或者是介于 -128 和 127 之间的整数或短数字(含),则让 r1 和 r2 成为 p 的任意两个装箱转换的结果。总是 r1 == r2。

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

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


答案 2

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

从 for 类方法的源代码IntegervalueOf

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

因此,如果值介于 to 之间,则得到相同的引用,并且您调用 else 它只是返回-128127valueOfnew Integer(i)

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