ChatGPT解决这个技术问题 Extra ChatGPT

为什么 == 与 Integer.valueOf(String) 的比较对 127 和 128 给出不同的结果?

我不知道为什么这些代码行返回不同的值:

System.out.println(Integer.valueOf("127")==Integer.valueOf("127"));
System.out.println(Integer.valueOf("128")==Integer.valueOf("128"));
System.out.println(Integer.parseInt("128")==Integer.valueOf("128"));

输出是:

true
false
true

为什么第一个返回 true 而第二个返回 false127128 之间有什么我不知道的不同之处吗? (我当然知道 127 < 128。)

另外,为什么第三个返回 true

我已阅读答案 of this question,但我仍然不明白它如何返回 true,以及为什么第二行中的代码返回 false

整数是一个对象;如果要比较是否相等,请使用 .equals(),否则所有赌注都将关闭。
@KarlDamgaardAsmussen 实际上在这里我真的很想测试它们是否是对同一个对象的引用,起初我不明白为什么 127 128 返回不同的结果。
@DnR 如果 Java 是一种具有标准化规范的语言,我认为它会让这些事情由实现甚至强制未定义的行为决定。
@jszumski:不过,这个问题不仅仅是缓存部分。此外,链接的答案充其量是不完整的 - 它并没有详细说明缓存的内容和原因。
有关此讨论的进一步跟进,请参阅this meta post

M
Makoto

这里有一个显着的区别。

valueOf 正在返回一个 Integer 对象,它的值可能缓存在 -128 和 127 之间。这就是为什么第一个值返回 true - 它已缓存 - 而第二个值返回 false - 128 不是一个缓存值,因此您将获得两个单独的 Integer 实例。

请务必注意,您正在将引用与 Integer#valueOf 进行比较,如果您正在比较的值大于缓存支持的值,则它不会评估为true,即使解析的值是等价的(例如:Integer.valueOf(128) == Integer.valueOf(128))。您必须改用 equals()

parseInt 正在返回一个原始 int。这就是为什么第三个值返回 true - 128 == 128 被评估,当然是 true

现在,恰好使第三个结果 true

对于您正在使用的等价运算符和您拥有的数据类型(即 int 和 Integer),会发生拆箱转换。当然,您从右侧的 valueOf 获得一个 Integer 。

转换后,您将比较两个原始 int 值。比较的发生与您对基元的预期一样,因此您最终比较了 128 和 128。


@user3152527:有相当大的区别 - 一个被视为对象,这意味着您可以调用方法并在抽象数据结构中与之交互,例如 List。另一个是原始值,它只是一个原始值。
@user3152527 你问了一个很好的问题(在最坏的情况下也不是一个愚蠢的问题)。但是您已将其修复为使用 .equals,对吗?
啊,看来提问者没有理解Java中的一个基本事实:当使用“==”比较两个对象时,您正在测试它们是否是对同一对象的引用。使用“equals()”时,您正在测试它们是否具有相同的值。您不能使用“等于”来比较基元。
@Jay 不,我明白这一点。但一开始让我感到困惑的是为什么第一个返回 true 而第二个返回 false 使用相同的比较方法 == 。无论如何,它现在清楚了。
Nit:不仅仅是整数“可能”被缓存在 -128 和 127 之间。根据 JLS 5.1.7,它必须。它可能被缓存在该范围之外,但不一定(而且通常不是)。
D
Dawood ibn Kareem

Integer 类有一个静态缓存,它存储 256 个特殊的 Integer 对象 - 一个对应于 -128 和 127 之间的每个值。考虑到这一点,考虑这三个之间的区别。

new Integer(123);

这(显然)创建了一个全新的 Integer 对象。

Integer.parseInt("123");

这会在解析 String 后返回一个 int 原始值。

Integer.valueOf("123");

这比其他的更复杂。它从解析 String 开始。然后,如果该值介于 -128 和 127 之间,则从静态缓存中返回相应的对象。如果该值超出此范围,则它调用 new Integer() 并传入该值,以便您获得一个新对象。

现在,考虑问题中的三个表达式。

Integer.valueOf("127")==Integer.valueOf("127");

这将返回 true,因为值为 127 的 Integer 从静态缓存中检索了两次,并与自身进行了比较。只涉及一个 Integer 对象,因此返回 true

Integer.valueOf("128")==Integer.valueOf("128");

这将返回 false,因为 128 不在静态缓存中。因此,为等式的每一边都创建了一个新的 Integer。由于有两个不同的 Integer 对象,并且对象的 == 仅在双方都是完全相同的对象时才返回 true,这将是 false

Integer.parseInt("128")==Integer.valueOf("128");

这是将左侧的原始 int 值 128 与右侧新创建的 Integer 对象进行比较。但是因为比较 intInteger 没有意义,Java 会在比较之前自动拆箱 Integer;因此您最终会将 intint 进行比较。由于原语 128 等于其自身,因此返回 true


最好的解释在这里。
P
Philzen

注意从这些方法返回值。 valueOf 方法返回一个 Integer 实例:

public static Integer valueOf(int i)

parseInt 方法返回整数值(原始类型):

public static int parseInt(String s) throws NumberFormatException

比较说明:

为了节省内存,包装对象的两个实例在它们的原始值相同时将始终为 ==:布尔字节字符从 \u0000 到 \u007f(7f 是十进制的 127) Short 和 Integer 从 -128 到 127当 == 用于比较原始与包装器时,包装器将被解包并且比较将是原始到原始的。

在您的情况下(根据上述规则):

Integer.valueOf("127")==Integer.valueOf("127")

此表达式比较对同一对象的引用,因为它包含介于 -128 和 127 之间的整数值,因此它返回 true

Integer.valueOf("128")==Integer.valueOf("128")

此表达式比较对不同对象的引用,因为它们包含不在 <-128, 127> 中的整数值。所以它返回 false

Integer.parseInt("128")==Integer.valueOf("128")

此表达式比较原始值(左侧)和对对象的引用(右侧),因此右侧将被展开,其原始类型将与左侧进行比较,因此返回 true


能否提供报价来源的网址?
N
Nambi

整数对象缓存在 -128 和 127 之间,共 256 个整数

您不应将对象引用与 == 或 != 进行比较。您应该使用 .equals(..) 代替,或者更好 - 使用原始 int 而不是 Integer。

parseInt:将字符串参数解析为有符号十进制整数。字符串中的字符必须都是十进制数字,除了第一个字符可以是 ASCII 减号“-”(“\u002D”)表示负值。返回结果整数值,就好像参数和基数 10 作为参数提供给 parseInt(java.lang.String, int) 方法一样。

valueOf 返回一个 Integer 对象,当使用第二个参数给出的基数进行解析时,该对象包含从指定 String 中提取的值。第一个参数被解释为表示由第二个参数指定的基数中的有符号整数,就像将参数提供给 parseInt(java.lang.String, int) 方法一样。结果是一个表示字符串指定的整数值的 Integer 对象。

相当于

new Integer(Integer.parseInt(s, radix))

radix - 用于解释 s 的基数

因此,如果您之间的整数等于 Integer.valueOf()

-128 到 127 它在您的情况下返回 true

对于 lesser than -128 和 greater than 127 它给出 false


C
Community

为了补充给定的答案,还请注意以下几点:

public class Test { 
    public static void main(String... args) { 
        Integer a = new Integer(129);
        Integer b = new Integer(129);
        System.out.println(a == b);
    }
}

此代码还将打印:false

正如用户 Jay 在评论中声明的已接受答案,在对象上使用运算符 == 时必须小心,在这里您要检查两个引用是否相同,这不是,因为它们是不同的对象,尽管它们代表相同的值。要比较对象,您应该改用 equals 方法:

Integer a = new Integer(128);
Integer b = new Integer(128);
System.out.println(a.equals(b));

这将打印:true

您可能会问,但是为什么第一行打印的是 true。查看 Integer.valueOf 方法的源代码,您可以看到以下内容:

public static Integer valueOf(String s) throws NumberFormatException {
    return Integer.valueOf(parseInt(s, 10));
}

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

如果参数是 IntegerCache.low(默认为 -128)和 IntegerCache.high(在运行时以最小值 127 计算)之间的整数,则返回预分配(缓存)对象。因此,当您使用 127 作为参数时,您将获得对同一缓存对象的两个引用,并在比较引用时获得 true