Java 为什么x==(x=y)与(x=y)=x不一样?

Java 为什么x==(x=y)与(x=y)=x不一样?,java,variable-assignment,equality,operator-precedence,jls,Java,Variable Assignment,Equality,Operator Precedence,Jls,考虑以下示例: 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

考虑以下示例:

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)

为什么第一个表达式的计算结果为
false
,而第二个表达式的计算结果为
true
?我希望首先计算
(x=y)
,然后它将
x
与其自身(
3
)进行比较,并返回
true


这个问题不同于这里的
x
绝对不是一个“子表达式”。它需要加载进行比较,而不是“评估”。这个问题是特定于Java的,表达式
x==(x=y)
不同于通常为棘手的面试问题精心设计的牵强的不切实际的结构,它来自一个真实的项目。它被认为是比较替换成语的一行替换

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

它甚至比x86 CMPXCHG指令更简单,应该在Java中使用一个更短的表达式。

正如LouisWasserman所说,表达式是从左到右计算的。java并不关心“evaluate”实际做什么,它只关心生成一个(非易失性的、最终的)值来处理

//the example values
x = 1;
y = 3;
因此,要计算
System.out.println()的第一个输出,需要执行以下操作:

x == (x = y)
1 == (x = y)
1 == (x = 3) //assign 3 to x, returns 3
1 == 3
false
并计算第二个:

(x = y) == x
(x = 3) == x //assign 3 to x, returns 3
3 == x
3 == 3
true

请注意,第二个值的计算结果将始终为true,而与
x
y
的初始值无关,因为您正在有效地将一个值的赋值与它所赋值的变量进行比较,并且
a=b
b
将按该顺序进行计算,定义始终相同。

==
是二进制文件

在计算右操作数的任何部分之前,二元运算符的左操作数似乎已被完全计算


在第一个测试中,您正在检查dos1==3

在第二个测试中,您的检查为3==3

(x=y)指定值并测试该值。在前一个示例中,先将x=1,然后将x指定为3。1=3吗

在后者中,x被指定为3,显然它仍然是3。3=3吗

按照括号中的顺序,应首先计算

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

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

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

注意,并非所有语言都是如此;例如,在C++中,除非你使用的是一个短路运算符,如<代码> & & <代码>或<代码> <代码>,操作数的评价顺序通常是未指定的,你不应该依赖它。
教师需要停止使用误导性短语解释运算符优先级,如“这使加法首先发生”。给出一个表达式
x*y+z
,正确的解释是“运算符优先级使加法发生在
x*y
z
之间,而不是
y
z
”,没有提及任何“顺序”。

它与运算符优先级和运算符的计算方式有关

括号“()”具有更高的优先级,并具有从左到右的关联性。 在这个问题中,等式“==”是下一个等式,它具有从左到右的关联性。 赋值“=”排在最后,并且具有从右到左的关联性

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

x == (x = y);
现在是原始问题:

int x = 1;
int y = 3;
System.out.println(x == (x = y)); // false
第一个x(1)将被推送到堆栈。 然后,内部(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==y
比较后,x的值为y

在以下情况下:

您所做的基本上与以下操作相同:

x==x

x之后取y的值。它将始终返回true

基本上第一条语句x的值为1 所以Java将1==与新的x变量进行比较,这两个变量将不同


在第二个例子中,你说的是x=y,这意味着x的值发生了变化,因此当你再次调用它时,它将是相同的值,这就是为什么它是真的,并且x==x

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

int x = 1;
System.out.println(x == ++x); // false
x = 1; // reset
System.out.println(++x == x); // true
这里,在进行比较之前必须应用
++x
中的预增量运算符-就像示例中的
(x=y)
必须在比较之前计算

但是,表达式求值仍然发生在左侧→ T
int x = 1;
System.out.println(x == ++x); // false
x = 1; // reset
System.out.println(++x == x); // true
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 == (x = y);
(x = y) == x;
    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);