Java 赋值运算符执行

2022-08-31 13:09:10

在Java中,我知道赋值的计算结果是正确的操作数的值,所以像 evaluate to 这样的语句。x == (y = x)true

但是,此代码输出 。false

public static void main(String[]args){
    String x = "hello";
    String y = "goodbye";
    System.out.println(x.equals(x = y));
}

这是为什么呢?在我的理解中,它首先计算 ,它分配 的值,然后返回 的值。然后进行评估,这应该是因为现在并且应该共享相同的引用,但是相反,我得到了.(x = y)xyyx.equals(y)truexyfalse

Screenshot showing the source and that the output is "false"

这是怎么回事?


答案 1

首先:这是一个有趣的问题,但永远不应该出现在“真实代码”中,因为即使你知道它是如何工作的,在同一行中分配给你调用的变量也是令人困惑的。

这里发生的事情是以下3个步骤:

  1. 弄清楚要调用哪个对象的方法(即计算第一个对象,这将导致对字符串“hello”的引用)x
  2. 找出参数(即 evaluate ,它将更改为指向字符串“再见”,并返回对该字符串的引用)x = yx
  3. 使用 #2 的结果作为参数(这将分别引用字符串“hello”和“goodbye”)来调用 #1 结果的方法。equals

查看为该方法生成的字节码可以清楚地说明(假设您精通Java字节码):

     0: ldc           #2                  // String hello
     2: astore_1
     3: ldc           #3                  // String goodbye
     5: astore_2
     6: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
     9: aload_1
    10: aload_2
    11: dup
    12: astore_1
    13: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
    16: invokevirtual #6                  // Method java/io/PrintStream.println:(Z)V
    19: return

第 9 行是上面的步骤 1(即计算并记住值)。x

第 10-12 行是步骤 2。它加载 ,复制它(一次用于赋值,一次用于赋值表达式的返回值)并将其赋值给 。yx

第 13 行调用在第 9 行中计算的结果和第 10-12 行的结果。equals


答案 2

问得好!JLS有答案...

§15.12.4.1(例 15.12.4.1-2)。方法调用期间的评估顺序:

作为实例方法调用的一部分,有一个表达式表示要调用的对象。在计算方法调用的任何参数表达式的任何部分之前,此表达式似乎已完全计算完毕。

所以,在:

String x = "hello";
String y = "goodbye";
System.out.println(x.equals(x = y));

首先计算之前出现的 之前,在参数表达式 之前。x.equalsx = y

因此,在将局部变量更改为引用字符串之前,将对字符串的引用记住为目标引用。因此,该方法被调用为具有参数的目标对象,因此调用的结果是 。helloxgoodbyeequalshellogoodbyefalse