在Java中比较Integer包装器时,为什么128 == 128为假,而127 == 127为真?


172
class D {
    public static void main(String args[]) {
        Integer b2=128;
        Integer b3=128;
        System.out.println(b2==b3);
    }
}

输出:

false

class D {
    public static void main(String args[]) {
        Integer b2=127;
        Integer b3=127;
        System.out.println(b2==b3);
    }
}

输出:

true

注意:-128至127之间的数字为真。



1
您是如何提出这个问题的?它确实很有趣,但是从来没有遇到过“在现实世界中”这样的事情……还是?
无限母马2012年

Answers:


217

当您使用Java编译数字文字并将其分配给Integer(大写I)时,编译器将发出:

Integer b2 =Integer.valueOf(127)

当您使用自动装箱时,也会生成此行代码。

valueOf 实现了“合并”某些数字,对于小于128的值,它将返回相同的实例。

从Java 1.6源代码的第621行:

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

high可以使用system属性将的值配置为另一个值。

-Djava.lang.Integer.IntegerCache.high = 999

如果使用该系统属性运行程序,它将输出true!

显而易见的结论是:永远不要依赖两个相同的引用,始终将它们与.equals()方法进行比较。

因此,b2.equals(b3)对于b2,b3的所有逻辑相等值,将输出true。

请注意,Integer出于性能原因不存在缓存,而是为了符合JLS,第5.1.7节;必须为-128至127(含)之间的值指定对象标识。

Integer#valueOf(int)也记录此行为:

通过缓存经常请求的值,此方法可能会产生明显更好的空间和时间性能。此方法将始终缓存-128至127(包括)范围内的值,并且可能缓存该范围之外的其他值。


1
请注意,小于127的值将被java忽略,而大于Integer.MAX_VALUE-128的值将被封顶。
Andreas Petersson,2009年

在Java 5及更高版本中,为字节值缓存了整数,从而使new Integer(1)== new Integer(1)。但是,在Java 1.4或更低版本中不是这种情况,因此请注意,如果最终必须降级到该环境。
MetroidFan2002

11
不,这是错误的。新的Integer(1)==新的Integer(1)为false,与jvm无关。AFAIK没有编译器会欺骗“ new”关键字。它必须始终实例化一个新对象。
安德烈亚斯·彼得森

1
@霍尔格有趣的一点。但是,这在技术上是可能的自定义实现了一套以取代从JDK Integer类......(不要问为什么有人会是疯狂) -那么它可能有副作用,不允许优化掉
安德烈亚斯彼得森

1
@AndreasPetersson当然。“编译器”是指JIT编译器,它确实知道实际的实现类,并且只有在构造函数没有副作用的情况下才可以进行优化。或优化表达式以仅重现副作用,然后使用false。实际上,这可能已作为应用逃逸分析和标量替换的副作用在今天已经发生。
Holger

24

自动装箱会缓存-128到127。这在JLS(5.1.7)中指定。

如果要装箱的值 p 是true,false,一个字节,\ u0000到\ u007f范围内的char或-128和127之间的int或short数字,则令r1和r2为任何两次装箱转换的结果的p。r1 == r2总是这样。

在处理对象时要记住的一个简单规则是- .equals如果要检查两个对象是否“相等”,请使用==;要查看它们是否指向同一实例时,请使用此规则。


1
注意:JLS在Java 9中已更改。现在仅对编译时常量表达式提供保证;查看已接受答案的更新。
Stephen C

9

在两种情况下,使用原始数据类型ints都会产生期望的输出。

但是,由于使用的是Integer对象,所以==运算符具有不同的含义。

在对象的上下文中,==检查变量是否引用相同的对象引用。

要比较对象的值,应使用equals()方法,例如

 b2.equals(b1)

这将指示b2小于,大于还是等于b1(有关详细信息,请查看API)


7

它与Java中的内存优化有关。

为了节省内存,Java“重用”了其值在以下范围内的所有包装对象:

所有布尔值(真和假)

所有字节值

从\ u0000到\ u007f的所有字符值(即0到127,十进制)

从-128到127的所有Short和Integer值。


3

看一看在Integer.java,如果值是-128到127之间,它将使用缓存池,因此(Integer) 1 == (Integer) 1(Integer) 222 != (Integer) 222

 /**
 * Returns an {@code Integer} instance representing the specified
 * {@code int} value.  If a new {@code Integer} instance is not
 * required, this method should generally be used in preference to
 * the constructor {@link #Integer(int)}, as this method is likely
 * to yield significantly better space and time performance by
 * caching frequently requested values.
 *
 * This method will always cache values in the range -128 to 127,
 * inclusive, and may cache other values outside of this range.
 *
 * @param  i an {@code int} value.
 * @return an {@code Integer} instance representing {@code i}.
 * @since  1.5
 */
public static Integer valueOf(int i) {
    assert IntegerCache.high >= 127;
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}       

0

其他答案描述了为什么可以观察到所观察到的效果,但这实际上与程序员的意义无关(当然,很有趣,但是在编写实际代码时应该忘记所有这些)。

要比较Integer对象是否相等,请使用equals方法。

不要尝试使用标识运算符来比较Integer对象是否相等==

某些相等的值可能是相同的对象,但是通常不应该依赖此值。


-4

我写了以下内容,因为此问题不仅限于Integer。我的结论是,如果不正确地使用API​​,通常会看到错误的行为。正确使用它,您应该看到正确的行为:

public static void main (String[] args) {
    Byte b1=127;
    Byte b2=127;

    Short s1=127; //incorrect should use Byte
    Short s2=127; //incorrect should use Byte
    Short s3=128;
    Short s4=128;

    Integer i1=127; //incorrect should use Byte
    Integer i2=127; //incorrect should use Byte
    Integer i3=128;
    Integer i4=128;

    Integer i5=32767; //incorrect should use Short
    Integer i6=32767; //incorrect should use Short

    Long l1=127L;           //incorrect should use Byte
    Long l2=127L;           //incorrect should use Byte
    Long l3=13267L;         //incorrect should use Short
    Long l4=32767L;         //incorrect should use Short
    Long l5=2147483647L;    //incorrect should use Integer 
    Long l6=2147483647L;    //incorrect should use Integer
    Long l7=2147483648L;
    Long l8=2147483648L;

    System.out.print(b1==b2); //true  (incorrect) Used API correctly
    System.out.print(s1==s2); //true  (incorrect) Used API incorrectly
    System.out.print(i1==i2); //true  (incorrect) Used API incorrectly
    System.out.print(l1==l2); //true  (incorrect) Used API incorrectly

    System.out.print(s3==s4); //false (correct) Used API correctly
    System.out.print(i3==i4); //false (correct) Used API correctly
    System.out.print(i5==i6); //false (correct) Used API correctly
    System.out.print(l3==l4); //false (correct) Used API correctly
    System.out.print(l7==l8); //false (correct) Used API correctly
    System.out.print(l5==l6); //false (correct) Used API incorrectly

}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.