Java 赋值运算符执行

Java assignment operator execution

在 Java 中,我理解赋值的计算结果为右操作数的值,因此 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),它将x分配给y的值,然后returns分配给y的值。然后评估 x.equals(y),它应该是 true,因为 xy 现在应该共享相同的引用,但是我得到 false

这里发生了什么?

首先:这是一个有趣的问题,但永远不应该出现在 "real code" 中,因为即使您知道它是如何工作的,但在同一行中为您调用的变量赋值也会造成混淆。

这里发生的是这 3 个步骤:

  1. 确定要在哪个对象上调用该方法(即计算第一个 x,这将导致对字符串 "hello" 的引用)
  2. 找出参数(即评估 x = y,这将更改 x 以指向字符串 "goodbye" 以及 return 对该字符串的引用)
  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 步。它加载 y,复制它(一次用于赋值,一次用于赋值表达式的 return 值)并将其赋值给 x.

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

括号中的

x=y 表示表达式 (x=y) 现在是 goodbye,而 x.equals 中的外部 x 的值是 hello

重要的是要记住 java 中的 String 是一个对象,因此是一个引用。当你打电话

x.equals(...)

它正在检查 x 当前引用的位置处的值是否等于您传入的值。在内部,您正在更改 x 的值引用 ,但您仍在调用 equals 并使用 原始 引用(对 "hello" 的引用)。所以,现在您的代码正在比较 "hello" 是否等于 "goodbye",显然不是。在此之后,如果您再次使用 x,将导致引用与 y.

相同的值

Reimus 给出了正确的答案,但我想详细说明一下。

在Java(和大多数语言)中,惯例是变量在左边,赋值在右边。

让我们分解一下:

String x = "hello";
//x <- "hello"

String y = "goodbye";
//y <- "goodbye";

出于调试目的以及代码可读性,最好将代码行分开,以便它们只做一件事。

System.out.println(x.equals(x = y)); //Compound statement

此处,x.equals(...) 是在对 x 的原始引用上调用的,或 "hello",它是为第二个引用而更新的。

我会这样写(这会给你预期的答案):

x = y;
// x <- y = "goodbye"

boolean xEqualsX = x.equals(x);
// xEqualsX <- true

System.out.println(xEqualsX);
// "true"

现在看起来很明显它应该以这种方式运行,但也很容易准确地看到每一行中发生了什么,这是您应该努力实现的目标。

好问题! JLS 有答案...

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

As part of an instance method invocation, there is an expression that denotes the object to be invoked. This expression appears to be fully evaluated before any part of any argument expression to the method invocation is evaluated.

所以,在:

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

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

之前

因此,在局部变量 x 更改为引用字符串 goodbye 之前,对字符串 hello 的引用被记住为目标引用。结果,为目标对象hello调用了equals方法,参数为goodbye,所以调用的结果是false.

我将外行人的问题视为 "hello".equals("goodbye")。所以它 returns 错误。

正在查看是否 x.equals(将 x 分配给 y,returns 始终为真) 所以基本上 x.equals(true)

我在eclipse中试过你的问题,你的两个表达式都是正确的。 1) x == (y = x) 计算结果为真 这是真的,因为 x 分配给 y 的值是 'hello' 然后 x 和 y 比较它们将 同样,结果为真

2) x.equal(x = y) 这是错误的 因为 y 的值分配给 x 这是再见然后 x 和 x 比较它们的值将不同所以结果将为 false

In java 字符串是一个 class.

String x = "hello";
String y = "goodbye"; 

是两个不同的字符串,指的是两个不相同的不同值 如果你比较

 System.out.println(x.equals(x = y)); 
//this compare value (hello and goodbye) return true

    System.out.println(x == (y = x)); 
// this compare reference of an object (x and y) return false