在比较 Java 中的两个基元和两个对象时,== 实际上工作相同还是不同?
Does == actually work the same or different when comparing two primitives vs two Objects in Java?
当搜索有关逻辑等于 ==
在 Java 中如何工作的解释时,答案总是类似于:
- 对于基元,returns基元是否具有相同的值(这包括将基元与其 WrapperObject 进行比较,因为 WrapperObject 会自动拆箱为基元)。
- 对于对象,returns它们是否代表堆上的同一个对象。
但这些解释似乎都暗示这些是 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.
在字节码级别,有许多不同的字节码指令根据类型(int
、long
或参考)。但是,比较的语义是相似的。一旦字节码被验证为类型安全,整数和地址 可以 处理相同的目的,以便在硬件级别进行 ==
比较。
在硬件(机器指令)级别 ==
对于原始整数类型和非原始值的工作方式相同。在这两种情况下,它将执行一条机器指令,该指令比较从寄存器或内存(堆或堆栈)中获取的两个 "words"。
JLS 为 float
和 double
指定的 ==
语义有点不同,因为 特殊值 (无穷大和非- a-number 值)需要特殊处理。例如:NaN == NaN 是 false
。另请参阅 IEEE 754 浮点标准。
为此有不同的字节码,并且在硬件级别使用的指令与整数和参考案例中使用的指令不同。 (特殊值的处理通常在浮动硬件中处理。)
JLS 为 boolean
、byte
、short
和 char
指定的 ==
语义是将值提升为另一种类型(int
、long
、float
或 double
),然后再比较它们。如果操作数具有不同的(未装箱的)类型,则提升也会发生在其他情况下。
此外,如果一个(但不是两个!)操作数被装箱,则会发生拆箱。如果两个操作数都被装箱,那么 ==
是参考比较。
总结以上...
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 规范没有规定实现策略。
当搜索有关逻辑等于 ==
在 Java 中如何工作的解释时,答案总是类似于:
- 对于基元,returns基元是否具有相同的值(这包括将基元与其 WrapperObject 进行比较,因为 WrapperObject 会自动拆箱为基元)。
- 对于对象,returns它们是否代表堆上的同一个对象。
但这些解释似乎都暗示这些是 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.
在字节码级别,有许多不同的字节码指令根据类型(int
、long
或参考)。但是,比较的语义是相似的。一旦字节码被验证为类型安全,整数和地址 可以 处理相同的目的,以便在硬件级别进行 ==
比较。
在硬件(机器指令)级别 ==
对于原始整数类型和非原始值的工作方式相同。在这两种情况下,它将执行一条机器指令,该指令比较从寄存器或内存(堆或堆栈)中获取的两个 "words"。
JLS 为 float
和 double
指定的 ==
语义有点不同,因为 特殊值 (无穷大和非- a-number 值)需要特殊处理。例如:NaN == NaN 是 false
。另请参阅 IEEE 754 浮点标准。
为此有不同的字节码,并且在硬件级别使用的指令与整数和参考案例中使用的指令不同。 (特殊值的处理通常在浮动硬件中处理。)
JLS 为 boolean
、byte
、short
和 char
指定的 ==
语义是将值提升为另一种类型(int
、long
、float
或 double
),然后再比较它们。如果操作数具有不同的(未装箱的)类型,则提升也会发生在其他情况下。
此外,如果一个(但不是两个!)操作数被装箱,则会发生拆箱。如果两个操作数都被装箱,那么 ==
是参考比较。
总结以上...
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 规范没有规定实现策略。