在比较 Java 中的两个原语与两个对象时,== 实际上是相同还是不同?

Posted

技术标签:

【中文标题】在比较 Java 中的两个原语与两个对象时,== 实际上是相同还是不同?【英文标题】:Does == actually work the same or different when comparing two primitives vs two Objects in Java? 【发布时间】:2019-12-23 18:10:09 【问题描述】:

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

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

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

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

我是否误解了某些内容,或者== 在所有情况下的行为实际上都是一样的?如果您可以向我指出有关这在幕后如何真正起作用的文档,则可以加分。

【问题讨论】:

跳过中间人,直接进入权威来源:Java Language Reference § 15.21, Equality Operators。但我认为你的想法是正确的:无论你是在处理数字、布尔值还是引用的比较,它们都归结为的比较。 从计算机的角度来看,这并不意味着它在做两件不同的事情——这种解释是从人类的角度来看的。 如果你从概念上将Java中的对象引用想象成C中的指针,那么对象引用的内容、值就是一个数字:内存中我们可以找到对象内容的地方。所以,是的,objectX == objectY 类似于比较两个原始整数,从这个意义上说,== 在比较原始值和比较对象引用方面确实表现相同。希望这能让您放心……但正如 Stephen C 的正确答案所解释的那样,真正重要的是 Java 规范定义的行为。 【参考方案1】:

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

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

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

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


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

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


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

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


总结以上...

我是否误解了某些内容,或者 == 实际上在所有情况下的行为都相同?

不,如果您包含浮点类型,以及原始扩展和取消装箱的考虑因素,则不会。

如果您可以向我指出有关这在幕后如何真正起作用的文档,则可以加分。

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

【讨论】:

【参考方案2】:

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

Java 中“引用”对象的想法被严重低估了;我认为有可能成为一个“Java 程序员”并没有真正理解什么是引用。你可以记住它产生影响的规则——'==' 运算符,将参数传递给方法——但不明白它是如何实现的,或者可能是如何实现的。

所以我认为许多使用 Java 编程的人说 == '在所有情况下都表现相同' 会让人感到困惑,因为这涉及到太多的“幕后”知识。 Java 不鼓励您(或要求您)在这种程度上“隐藏”。

【讨论】:

以上是关于在比较 Java 中的两个原语与两个对象时,== 实际上是相同还是不同?的主要内容,如果未能解决你的问题,请参考以下文章

Java中怎么比较两个字符串是不是相等?

JAVA比较两个字符串是不是相等用啥

两个多头之间的比较没有按预期工作[重复]

比较两个对象的不同属性集

Java 判断两个对象是否相等

Java equals()方法与“==”的区别