在比较 Java 中的两个基元和两个对象时,== 实际上工作相同还是不同?

Does == actually work the same or different when comparing two primitives vs two Objects in Java?

当搜索有关逻辑等于 == 在 Java 中如何工作的解释时,答案总是类似于:

但这些解释似乎都暗示这些是 2 种不同的东西== 的行为不同取决于你是在比较对象还是基元。在我看来,它们实际上必须是完全相同的东西:从堆栈中取出两个变量并比较它们的值。

改变的不是 == 的行为,而是它所比较的​​值所代表的内容。如果你比较的东西是原语,那么 Stack 上的值就是原语本身的值。如果您正在比较对象,那么堆栈上的值就是引用的值(因此是堆上对象的地址)。

我是不是误解了什么,或者 == 实际上在所有情况下都表现得一样吗?如果你能指出我在幕后如何真正工作的文档,奖励积分。

我理解你的解释,并且给出了某些术语的定义是正确的。但它不符合 Java 谈论对象和原语的方式。

'reference' 到 Java 中的对象的想法被严重低估了;我认为有可能是 'Java programmer' 并没有真正理解参考是什么。您可以记住它产生影响的规则——“==”运算符,将参数传递给方法——而不理解它是如何实现的,或者可能是如何实现的。

所以我认为很多在 Java 编程的人说 == 'behaves the same in all situations' 会感到困惑,因为这涉及太多 'under the covers' 知识。 Java 不鼓励您(或要求您)看 'under the covers' 到那种程度。

正如其他答案/评论所说,在 Java 语言级别 == 运算符语义是 指定的 (在 JLS 15.21 中)一种独立于实现的方式。严格来说,您不能从 JLS 文本中推断出 "under the hood" 实现细节。您只能说 == 的任何一致实现必须 以某种方式表现

我假设我们正在谈论传统的 JVM,其中引用的实际机器表示是机器地址。可以通过其他方式实现引用;例如使用某种间接寻址机制,例如 PIDLAM.

在字节码级别,有许多不同的字节码指令根据类型(intlong 或参考)。但是,比较的语义是相似的。一旦字节码被验证为类型安全,整数和地址 可以 处理相同的目的,以便在硬件级别进行 == 比较。

在硬件(机器指令)级别 == 对于原始整数类型和非原始值的工作方式相同。在这两种情况下,它将执行一条机器指令,该指令比较从寄存器或内存(堆或堆栈)中获取的两个 "words"。


JLS 为 floatdouble 指定的 == 语义有点不同,因为 特殊值 (无穷大和非- a-number 值)需要特殊处理。例如:NaN == NaN 是 false。另请参阅 IEEE 754 浮点标准。

为此有不同的字节码,并且在硬件级别使用的指令与整数和参考案例中使用的指令不同。 (特殊值的处理通常在浮动硬件中处理。)


JLS 为 booleanbyteshortchar 指定的 == 语义是将值提升为另一种类型(intlongfloatdouble),然后再比较它们。如果操作数具有不同的(未装箱的)类型,则提升也会发生在其他情况下。

此外,如果一个(但不是两个!)操作数被装箱,则会发生拆箱。如果两个操作数都被装箱,那么 == 是参考比较。


总结以上...

Have I mis-understood something, or does == actually behave the same in all situations?

不,如果您包括浮点类型,以及原始加宽和拆箱的注意事项。

Bonus points if you can point me to documentation on how this really works under the covers.

没有这方面的官方 (Oracle) public 文档。 JLS 和 JVM 规范没有规定实现策略。