为什么在Java中比较整数包装类型时,128==128是错误的,但127==127是正确的?

203
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之间的数字是真实的。

8个回答

253
当您在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的值配置为其他值。

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

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

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

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

请注意,Integer缓存不是为了提高性能,而是为了符合JLS,第5.1.7节;必须为值-128到127(含)之间给出对象标识。

Integer#valueOf(int)也记录了这种行为:

通过缓存经常请求的值,此方法很可能会产生明显的空间和时间性能优势。此方法将始终缓存范围在-128到127(含)之间的值,并且可能会缓存此范围之外的其他值。


1
请注意,Java将忽略小于127的值,并将大于Integer.MAX_VALUE-128的值限制在该范围内。 - Andreas Petersson
在Java 5及更高版本中,整数值被缓存为字节值,因此new Integer(1) == new Integer(1)。但是,在Java 1.4或更低版本中不是这种情况,因此如果您最终需要降级到该环境,请注意。 - MetroidFan2002
15
不,这是错误的。无论在哪个JVM上,"new Integer(1) == new Integer(1)"都是false。据我所知,没有任何编译器会在"new"关键字上作弊,它必须始终实例化一个新对象。 - Andreas Petersson
2
@Holger 有趣的观点。但是从技术上讲,可以用自定义实现替换JDK中的Integer类...(不要问为什么有人会那么疯狂)-然后它可能会产生不能优化的副作用。 - Andreas Petersson
2
@AndreasPetersson 确定。 "编译器" 指的是 JIT 编译器,它确切地知道实际的实现类,并且只有在构造函数没有副作用时才能进行优化。或者将表达式优化为仅重现副作用,然后使用 false。实际上,这可能已经发生了,因为应用逃逸分析和标量替换的副作用。 - Holger
显示剩余5条评论

28

自动装箱将-128到127缓存起来。这在JLS(5.1.7)中有规定。

如果被装箱的值p为true、false、一个byte、一个范围在\u0000到\u007f之间的char,或者是介于-128到127之间的int或short类型数字,则让r1和r2分别为对p进行任意两次装箱转换的结果。此时,总是成立r1==r2。

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


2
注意:Java 9中JLS已更改。现在仅对编译时常量表达式进行保证;请参见已接受答案的更新。 - Stephen C

11
使用原始数据类型int将在两种情况下产生true,这是预期的输出。 然而,由于您使用的是Integer对象,所以==运算符具有不同的含义。 在对象的上下文中,==检查变量是否引用相同的对象引用。 要比较对象的值,应该使用equals()方法。例如:
 b2.equals(b1)

这将指示b2是否小于b1,大于它,还是相等的(请查看API以获取详细信息)


8

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

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

所有布尔值(true和false)

所有字节值

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

所有短整型和整型值从-128到127。


4

请看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);
}       

2

其他答案描述了为什么可以观察到这些效果,但对于程序员来说,这实际上并不重要(当然很有趣,但在编写实际代码时应该完全忘记它)。

要比较整数对象的相等性,请使用equals方法。

不要试图使用身份运算符==比较整数对象的相等性。

有时一些相等的值是相同的对象,但这通常是不能依赖的。


1
如果值在-128和127之间,它将使用缓存池,这仅适用于自动装箱。因此,您将得到以下结果:
    public static void main(String[] args) {
        Integer a  = new Integer(100);
        Integer b = new Integer(100);
        System.out.println(a == b);         // false. == compare two instances, they are difference
        System.out.println(a.equals(b));    // true. equals compares the value

        Integer a2 = 100;
        Integer b2 = 100;
        System.out.println(a2 == b2);       // true. auto-boxing uses cached pool between -128/127
        System.out.println(a2.equals(b2));  // true. equals compares the value

        Integer a3 = 129;
        Integer b3 = 129;
        System.out.println(a3 == b3);       // false. not using cached pool
        System.out.println(a3.equals(b3));  // true. equals compares the value
    }
}

-4

我写下了以下内容,因为这个问题不仅仅适用于整数。我的结论是,如果你错误地使用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

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接