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

2022-09-02 04:16:06

在Java中搜索逻辑等式如何工作的解释时,答案总是大致如下:==

  • 对于基元,它返回基元是否具有相同的值(这包括将基元与其 WrapperObject 进行比较,因为 WrapperObject 会自动将其解装为基元)。
  • 对于对象,它返回它们是否表示堆上的相同对象。

但这些解释似乎都暗示着这是两个不同的东西,它们的行为会有所不同,这取决于你是否比较了对象和基元。在我看来,它们实际上一定是完全相同的东西:从堆栈中获取两个变量并比较它们的值。==

改变的不是 的行为,而是它所比较的值所代表的东西。如果您要比较的内容是基元,则堆栈上的值是基元本身的值。如果要比较对象,则堆栈上的值是引用的值(因此是堆上对象的地址)。==

我是否误解了某些东西,或者实际上在所有情况下都表现得一样?奖励点,如果你能指出我关于这如何在幕后真正工作的文档。==


答案 1

正如其他答案/注释所说,在Java语言级别,运算符语义(在JLS 15.21中)以独立于实现的方式指定。严格来说,您无法从 JLS 文本中推断出“幕后”实现细节。您只能说,任何符合要求的实现都必须以某种方式运行====

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

在字节码级别,有许多不同的字节码指令实现取决于类型(或引用)的逻辑。但是,比较的语义是相似的。一旦字节码被验证为类型安全,就可以对整数和地址进行相同的处理,以便在硬件级别进行比较。==intlong==

在硬件(机器指令)级别,对于基元积分类型和非基元值,其工作原理相同。在这两种情况下,它都将执行一个机器指令,该指令比较从寄存器或内存(堆或堆栈)中获取的两个“字”。==


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

这有不同的字节码,在硬件级别,使用的指令与整数和参考情况下使用的指令不同。(特殊值的处理通常在浮动硬件中处理。


JLS 指定了 for 、 的语义,并在比较值之前将这些值提升为另一种类型 (、 、 或 )。如果操作数具有不同(未装箱)的类型,则其他情况也会发生升级。==booleanbyteshortcharintlongfloatdouble

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


总结以上...

我是否误解了某些内容,或者==在所有情况下实际上都表现得一样?

不,如果您包括浮点类型,以及基元加宽和取消装箱的注意事项,则不会。

奖励点,如果你能指出我关于这如何在幕后真正工作的文档。

没有官方(Oracle)公共文档。JLS 和 JVM 规范没有规定实现策略。


答案 2

我理解你的解释,考虑到某些术语的定义,这是正确的。但它不符合Java谈论对象和基元的方式。

在Java中对对象进行“引用”的想法被严重淡化了;我认为有可能成为一名“Java程序员”,却不真正理解什么是引用。你可以记住它有什么不同的地方的规则 - '=='运算符,参数传递给方法 - 而不了解它是如何实现的,或者可以实现的。

因此,我认为对于许多用Java编程的人来说,说==“在所有情况下都表现得一样”会令人困惑,因为这涉及太多的“幕后”知识。Java并不鼓励你(或要求你)在这种程度上“在幕后”寻找。