【问题标题】:Java assignment operator executionJava赋值运算符执行
【发布时间】:2018-11-30 21:00:27
【问题描述】:

在 Java 中,我知道赋值计算为正确操作数的值,因此像 x == (y = x) 这样的语句计算为 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

这里发生了什么?

【问题讨论】:

  • 我想你想看到x.equals( y = x )的结果
  • 编译器能否内联xy
  • 您是否假设在评估左侧的x之前执行右侧的赋值x = y
  • @khelwood 是的,这是我的假设。它不能
  • @nits.kk 我不这么认为。 OP 已经说过他们理解 x == (y = x) 的计算结果为真。你建议的行为就会很明显......

标签: java string equals assignment-operator


【解决方案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  

【讨论】:

  • 你真的运行过这个示例代码吗?正如问题所述,System.out.println(x.equals(x = y)); 返回 false,这与您的答案所声称的相反。
【解决方案2】:

我在 Eclipse 中尝试过你的问题,你的两个表达都是正确的。 1) x == (y = x) 评估为真 这是真的,因为 x 的值分配给 y 是 'hello' 然后 x 和 y 比较它们会 一样,所以结果是正确的

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

    • 这是不真实的,这不是评估作业的方式
    【解决方案4】:

    我用外行术语将问题视为"hello".equals("goodbye")。所以它返回 false。

    【讨论】:

      【解决方案5】:

      好问题! JLS 给出了答案……

      §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 之前。

      因此,在将局部变量 x 更改为引用字符串 goodbye 之前,将记住对字符串 hello 的引用作为目标引用。结果,目标对象hello调用了equals方法,参数goodbye,所以调用的结果是false

      【讨论】:

        【解决方案6】:

        Reimus 给出了正确答案,但我想详细说明一下。

        在 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"
        

        现在看来,它应该以这种方式运行似乎很明显,但也很容易准确地看到每一行中发生了什么,这是你应该努力争取的。

        【讨论】:

        • Re: "always": 有太多明确的说法:你会建议System.out.println("Bytes: "+1024*k); 写成三个陈述吗?
        • @DavisHerring 这是在调试的上下文中。如果您正在尝试调试该语句,那么是的,我绝对建议将该语句拆分为它的组件。一个语句用于乘法,另一个语句用于打印。它允许最大的灵活性。作为一般的经验法则,您希望每行只做一件事。它使代码更具可读性和更易于调试。
        【解决方案7】:

        首先:这是一个有趣的问题,但永远不应该出现在“真实代码”中,因为即使你知道它是如何工作的,你在同一行中调用的变量也会令人困惑。

        这里发生的是这三个步骤:

        1. 找出在哪个对象上调用该方法(即评估第一个x,这将导致对字符串“hello”的引用)
        2. 找出参数(即评估x = y,这将更改x 以指向字符串“goodbye”并返回对该字符串的引用)
        3. 在#1 的结果上调用equals 方法,使用#2 的结果作为参数(将分别引用字符串“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 行对第 9 行计算的结果和第 10-12 行的结果调用 equals

        【讨论】:

        • TL;DR: x.equals(x = y) => "hello".equals(x = y) => "hello".equals(x = "goodbye") => "hello".equals("goodbye") => false.
        • 需要注意的重要一点是. has a higher precedence than =.
        • 这更多的是关于评估顺序而不是优先级。无论如何,括号使优先级无关紧要。
        【解决方案8】:

        请务必记住,java 中的 String 是一个对象,因此是一个引用。当你打电话时

        x.equals(...)
        

        它正在检查 x 当前引用的位置的值是否等于您传入的值。在内部,您正在更改 x 的值 引用,但是您仍在使用 original 引用(对“hello”的引用)调用equals。因此,现在您的代码正在比较“hello”是否等于“goodbye”,但显然不是。此后,如果再次使用x,将导致引用与y相同的值。

        【讨论】:

        • 如果我理解你,这个:"hello".equals((x = y)) 应该返回true ?
        • 否,因为 (x = y) 将返回 y 的值,即“再见”。所以如果你做了“goodbye”.equals(x=y),那将返回 true
        【解决方案9】:

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

        【讨论】:

        • 这并没有解释为什么会发生这种情况,也没有提供对其他人有帮助的额外细节。
        • 现在我大声读出来了,我确实倾向于同意你的观点。其他答案非常冗长,所以请编辑它。
        猜你喜欢
        • 2011-11-16
        • 2015-05-02
        • 2015-03-19
        • 2015-12-13
        • 2013-02-14
        • 2014-04-17
        • 1970-01-01
        • 2014-11-08
        • 2011-08-02
        相关资源
        最近更新 更多