Java赋值运算符执行


76

在Java中,我知道赋值的结果是正确的操作数的值,因此类似语句的结果x == (y = x)为to true

但是,此代码输出false

public static void main(String[]args){
    String x = "hello";
    String y = "goodbye";
    System.out.println(x.equals(x = y));
}

为什么是这样?以我的理解,它首先求值(x = y),它分配x的值y,然后返回的值y。然后x.equals(y)进行了评估,应该是true从那时起xy现在应该共享相同的引用,但是我得到了false

屏幕截图显示了源,并且输出为“ false”

这是怎么回事


13
我认为您想查看x.equals( y = x )
nits.kk的

1
可以在编译器内联xy
利诺(Lino)

3
你假设分配x = y执行右手边x左手侧进行评估?
khelwood

@khelwood是的,那是我的假设。它一定不能
山姆

1
@ nits.kk我不这么认为。OP已经说过他们了解x == (y = x)评估为正确。您所建议的行为将变得很明显……
Pedro

Answers:


76

首先:这是一个有趣的问题,但是永远不要出现在“真实代码”中,因为即使您知道它的工作原理,在同一行中分配给您所调用的变量也会造成混淆。

这是三个步骤:

  1. 找出要在哪个对象上调用该方法(即评估第一个对象x,这将导致对字符串“ hello”的引用)
  2. 找出参数(即评估x = y,它将更改x为指向字符串“再见”,并返回对该字符串的引用)
  3. equals使用#2的结果作为参数在#1的结果上调用方法(这将分别引用字符串“ hello”和“ goodbye”)。

查看为该方法生成的字节码可以很清楚地知道(假设您精通Java字节码):

     0: ldc           #2                  // String hello
     2: astore_1
     3: ldc           #3                  // String goodbye
     5: astore_2
     6: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
     9: aload_1
    10: aload_2
    11: dup
    12: astore_1
    13: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
    16: invokevirtual #6                  // Method java/io/PrintStream.println:(Z)V
    19: return

第9行是上面的第1步(即评估x并记住该值)。

第10-12行是步骤2。它加载y,复制它(一次分配,一次分配赋值表达式的返回值),然后将其分配给x

第13行调用equals第9行中计算的结果和第10-12行的结果。


36
TL; DR:x.equals(x = y)=> "hello".equals(x = y)=> "hello".equals(x = "goodbye")=> "hello".equals("goodbye")=> false
Bernhard Barker

8
需要注意的重要一点是,.它的优先级高于=
Gaurang Tandon

4
这更多是关于评估顺序,而不是优先顺序。括号使优先级始终无关紧要。
amalloy

38

好问题!捷豹路虎有答案...

§15.12.4.1(示例15.12.4.1-2)。方法调用期间的评估顺序:

作为实例方法调用的一部分,有一个表达式表示要调用的对象。在评估方法调用的任何参数表达式的任何部分之前,此表达式似乎已被完全评估。

因此,在:

String x = "hello";
String y = "goodbye";
System.out.println(x.equals(x = y));

发生x之前.equals首先计算,参数表达式之前x = y

因此,hello  在将局部变量x更改为引用string之前,要记住对该字符串的引用  作为目标引用goodbye。结果,该equals方法针对hello带有参数的目标对象被调用goodbye,因此调用的结果为false


28

重要的是要记住,Stringjava中的a是一个对象,因此是一个引用。你打电话时

x.equals(...)

这是检查是否在目前引用的位置的值x等于要传递什么。在里面,你正在改变该值x引用,但你仍然调用equals原来的引用(参考“你好”)。因此,现在您的代码正在进行比较,以查看“ hello”是否等于“再见”,而事实显然并非如此。此后,如果x再次使用,将导致引用与y相同的值。


如果我了解您,则:"hello".equals((x = y))应该返回true
Halayem Anis

3
不,因为(x = y)将返回y的值,即“再见”。因此,如果您执行了“再见” .equals(x = y),则返回true
Keveloper

5

x=y括号中的表示(x=y)现在是goodbye,而外部x中x.equals的值为hello


2
这确实不能解释为什么会发生这种情况,也不能提供对其他人有帮助的其他详细信息。
格雷

现在,我大声读出来,我倾向于同意you..the其他的答案是相当冗长寿所以力编辑它..
扯谈贾达夫CD

4

雷蒙斯给出了正确的答案,但我想详细说明。

在Java(和大多数语言)中,约定是变量在左侧,赋值在右侧。

让我们分解一下:

String x = "hello";
//x <- "hello"

String y = "goodbye";
//y <- "goodbye";

出于调试目的以及代码可读性的考虑,将行分开以使它们仅做一件事始终是一个好习惯。

System.out.println(x.equals(x = y)); //Compound statement

在此,x.equals(...)在对x的原始引用(或“ hello”)上调用,将其更新为第二个引用。

我将其写为(这将为您提供预期的答案):

x = y;
// x <- y = "goodbye"

boolean xEqualsX = x.equals(x);
// xEqualsX <- true

System.out.println(xEqualsX);
// "true"

现在看来应该以这种方式表现,但要弄清楚每行中到底发生了什么,这确实很容易,这是您应该争取的。


2
回复:“总是”:有太多的明确性:您是否建议将System.out.println("Bytes: "+1024*k);其写成三个语句?
戴维斯·鲱鱼

@DavisHerring这是在调试的上下文中。如果您要调试该语句,那么可以,我绝对建议您将此语句拆分为组件。一个语句用于乘法运算,另一个语句用于打印。它提供了最大的灵活性。作为一般经验法则,您希望每一行只做一件事。它使代码更具可读性,更易于调试。
随机杀手

2

我已经在日食中尝试过您的问题,您的两种表达都是正确的。1)x ==(y = x)评估为true,这是真的,因为x的值分配给y是'hello',则x和y比较它们将相同,因此结果为true

2)x.equal(x = y)这是错误的,因为y的值分配给了x,再见了,然后x和x比较它们的值将不同,因此结果将为false


1

我认为这个问题是通俗易懂的"hello".equals("goodbye")。因此它返回false。


1

在java中,String是一个类。

String x = "hello";
String y = "goodbye"; 

是两个不同的字符串,它们引用两个不同的值,这些值不相同,如果您比较

 System.out.println(x.equals(x = y)); 
//this compare value (hello and goodbye) return true

    System.out.println(x == (y = x)); 
// this compare reference of an object (x and y) return false  

1
您是否实际运行了此示例代码?如问题所述,System.out.println(x.equals(x = y));返回false,这与您的答案相反。
查理·哈丁

-4

它正在查看是否x.equals(将x分配给y,始终返回true),所以基本上是x.equals(true)


8
这是不正确的,这不是评估作业的方式
Sam
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.