考虑下面的例子:

class Quirky {
    public static void main(String[] args) {
        int x = 1;
        int y = 3;

        System.out.println(x == (x = y)); // false
        x = 1; // reset
        System.out.println((x = y) == x); // true
     }
}

我不确定Java语言规范中是否有一项规定加载变量的前一个值,以便与右边(x = y)进行比较,根据括号所暗示的顺序,应该首先计算右边的值。

为什么第一个表达式的值为假,而第二个表达式的值为真?我希望(x = y)首先被求值,然后它将x与自身(3)进行比较并返回true。


这个问题与Java表达式中子表达式的求值顺序不同,因为x在这里肯定不是“子表达式”。它需要被加载来进行比较,而不是被“评估”。这个问题是java特有的,表达式x == (x = y)与通常为棘手的面试问题而设计的牵强的不切实际的构造不同,它来自一个真实的项目。它应该是比较-替换习惯用法的一行替换

int oldX = x;
x = y;
return oldX == y;

它比x86的CMPXCHG指令更简单,在Java中应该有更短的表达式。


当前回答

==是一个二进制相等运算符。

二进制操作符的左操作数似乎在右操作数的任何部分被求值之前就已经完全求值了。 Java 11规范>求值顺序>首先求左操作数

其他回答

它与操作符优先级以及如何计算操作符有关。

括号'()'优先级更高,具有从左到右的结合性。 等式'=='在这个问题中是下一个,从左到右具有结合律。 赋值'='排在最后,具有从右到左的结合性。

系统使用堆栈计算表达式。表达式从左向右求值。

现在回到最初的问题:

int x = 1;
int y = 3;
System.out.println(x == (x = y)); // false

首先x(1)将被压入堆栈。 然后inner (x = y)将被计算并推入值为x(3)的堆栈。 现在x(1)将与x(3)进行比较,因此结果为假。

x = 1; // reset
System.out.println((x = y) == x); // true

在这里, (x = y)将被计算,现在x值变为3,x(3)将被推入堆栈。 现在x(3)的值改变后的等式将被推入堆栈。 现在表达式将被求值,两者将相同,因此结果为真。

考虑另一个更简单的例子:

int x = 1;
System.out.println(x == ++x); // false
x = 1; // reset
System.out.println(++x == x); // true

在这里,必须在进行比较之前应用++x中的前递增操作符-就像在您的示例中(x = y)必须在进行比较之前计算一样。

但是,表达式求值仍然从左→→右,所以第一个比较实际上是1 == 2,而第二个比较是2 == 2。 同样的事情也发生在你的例子中。

在第一个测试中,您检查的是1 == 3。

在第二个测试中,你的检查是3 == 3。

(x = y)赋值并测试该值。在前面的例子中,x先= 1,然后x被赋值为3。1 = 3吗?

在后者中,x被赋值为3,显然它仍然是3。3 == 3吗?

表达式从左到右求值。在这种情况下:

int x = 1;
int y = 3;

x == (x = y)) // false
x ==    t

- left x = 1
- let t = (x = y) => x = 3
- x == (x = y)
  x == t
  1 == 3 //false

(x = y) == x); // true
   t    == x

- left (x = y) => x = 3
           t    =      3 
-  (x = y) == x
-     t    == x
-     3    == 3 //true

这是不一样的。左边总是在右边之前求值,括号没有指定执行顺序,而是指定命令的分组。

:

      x == (x = y)

你所做的基本上是一样的:

      x == y

x在比较后会得到y的值。

而与:

      (x = y) == x

你所做的基本上是一样的:

      x == x

在x取y的值之后。它总是返回true。