考虑下面的例子:
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之间”,没有提到任何“顺序”。
它与操作符优先级以及如何计算操作符有关。
括号'()'优先级更高,具有从左到右的结合性。
等式'=='在这个问题中是下一个,从左到右具有结合律。
赋值'='排在最后,具有从右到左的结合性。
系统使用堆栈计算表达式。表达式从左向右求值。
现在回到最初的问题:
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)的值改变后的等式将被推入堆栈。
现在表达式将被求值,两者将相同,因此结果为真。