考虑下面的例子:

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


当前回答

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

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

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

其他回答

左边的第二个比较很简单,在把y赋值给x之后(在左边),你就比较3 == 3。在第一个例子中,你比较x = 1和新赋值x = 3。似乎总有一个当前状态从左到右读取x的语句。

我不确定Java语言规范中是否有规定加载变量的前一个值…

有。下次当你不清楚说明书上写了什么时,请先阅读说明书,如果不清楚就提出问题。

... 右边(x = y),根据括号所示的顺序,应该先计算。

这种说法是错误的。括号并不表示求值的顺序。在Java中,求值的顺序是从左到右,不管括号是什么。括号决定子表达式的边界,而不是求值的顺序。

为什么第一个表达式的值为假,而第二个表达式的值为真?

==运算符的规则是:左边求值产生一个值,右边求值产生一个值,比较两个值,比较的就是表达式的值。

换句话说,expr1 == expr2的含义始终与您编写的temp1 = expr1;Temp2 = expr2;然后求temp1 == temp2。

对于左侧有局部变量的=运算符的规则是:对左侧求值产生一个变量,对右侧求值产生一个值,执行赋值,结果就是被赋值的值。

所以把它们放在一起:

x == (x = y)

我们有一个比较算子。对左边求值,得到x的当前值。对右边求值,得到变量x,对右边求值,得到y的当前值,赋值给x,结果就是赋值。然后我们将x的原始值与赋值进行比较。

你可以把(x = y) == x作为练习。记住,所有求左边值的规则都先于求右边值的规则。

我希望(x = y)首先被求值,然后它将x与自身(3)进行比较并返回true。

您的期望是基于对Java规则的一组不正确的信念。希望你现在有正确的信念,将来会期待真实的事物。

这个问题不同于“Java表达式中子表达式的求值顺序”。

这种说法是错误的。那个问题完全相关。

X在这里肯定不是“子表达式”。

这种说法也是错误的。在每个示例中它都是两次子表达式。

它需要被加载来进行比较,而不是被“评估”。

我不知道这是什么意思。

显然你还有很多错误的信念。我的建议是阅读说明书,直到你的错误信念被正确信念所取代。

这个问题是java特有的,表达式x == (x = y)与通常为棘手的面试问题而设计的牵强的不切实际的构造不同,它来自一个真实的项目。

这个表达的出处与问题无关。规范中清楚地描述了这些表达式的规则;读它!

它应该是比较-替换习惯用法的一行替换

由于这一行替换给您(代码的读者)造成了很大的困惑,所以我认为这是一个糟糕的选择。让代码更简洁但更难理解并不是一种胜利。它不太可能使代码变得更快。

顺便说一句,c#有compare和replace作为一个库方法,它可以被简化为一条机器指令。我相信Java没有这样的方法,因为它不能在Java类型系统中表示。

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

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

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

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

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

这里的问题是算术运算符/关系运算符在两个运算符中的优先顺序= vs =占主导地位的是==(关系运算符占主导地位),因为它在=赋值运算符之前。 不管优先级,求值的顺序是LTR(从左到右)优先级在求值顺序之后。 因此,不考虑任何约束条件,求值就是LTR。