46

我无法理解整数的 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 之后会发生变化,下面是代码片段

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

有人可以帮我理解这一点吗?

4

4 回答 4

47

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

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 是真、假、一个字节或 \u0000 到 \u007f 范围内的一个字符,或者一个介于 -128 和 127(含)之间的 int 或短数字,则令 r1 和 r2 为p 的任意两个拳击转换。r1 == r2 总是如此。

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

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

于 2012-10-27T07:24:13.227 回答
29

Java维护Integer pool from -128to127

像下面这样声明整数

Integer i1 = 127;

结果到

Integer i1 = Integer.valueOf(127);

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

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

来自Integerfor类 valueOf 方法的源代码

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

因此,如果值介于-128to之间,127您将获得相同的参考,valueOf否则它只会返回new Integer(i)

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

于 2012-10-27T07:22:47.023 回答
9

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

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

所以,

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
于 2012-10-27T07:23:38.350 回答
2

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

将整数与 == 进行比较究竟有什么作用?

于 2012-10-27T07:25:15.223 回答