考虑下面的例子:

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中应该有更短的表达式。


当前回答

根据括号所示的顺序,哪个应该先计算

不。一个常见的误解是,括号对计算或求值顺序有任何(一般的)影响。它们只是将表达式的部分强制到特定的树中,将正确的操作数绑定到作业的正确操作。

(而且,如果不使用它们,这些信息来自操作符的“优先级”和结合性,这是语言语法树定义的结果。事实上,当你使用圆括号时,它仍然是这样工作的,但我们简化了,说我们不依赖于任何优先规则。)

一旦完成了这一点(即一旦你的代码被解析成一个程序),这些操作数仍然需要被求值,并且有关于如何做的单独的规则:所说的规则(正如Andrew向我们展示的那样)声明每个操作的LHS在Java中首先被求值。

注意,并非所有语言都是如此;例如,在c++中,除非使用&&或||这样的短路操作符,否则操作数的求值顺序通常是未指定的,无论如何都不应该依赖于它。

老师们需要停止使用像“这使得加法先发生”这样的误导性短语来解释运算符优先级。给定一个表达式x * y + z,正确的解释应该是“运算符优先级使加法发生在x * y和z之间,而不是在y和z之间”,没有提到任何“顺序”。

其他回答

==是一个比较相等运算符,它从左到右工作。

x == (x = y);

这里x的旧赋值与x的新赋值进行比较,(1==3)//false

(x = y) == x;

然而,在这里,x的新赋值与在比较之前赋给它的新持有值进行比较,(3==3)//true

现在考虑一下

    System.out.println((8 + (5 * 6)) * 9);
    System.out.println(8 + (5 * 6) * 9);
    System.out.println((8 + 5) * 6 * 9);
    System.out.println((8 + (5) * 6) * 9);
    System.out.println(8 + 5 * 6 * 9);

输出: 342 278 702 342 278

因此,括号在算术表达式中起主要作用,而不是在比较表达式中起主要作用。

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

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

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

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

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

现在回到最初的问题:

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)的值改变后的等式将被推入堆栈。 现在表达式将被求值,两者将相同,因此结果为真。

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

:

      x == (x = y)

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

      x == y

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

而与:

      (x = y) == x

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

      x == x

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

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

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