Выполнение оператора присваивания Java

в 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 С x и y должен делиться теми же ссылками теперь, но вместо этого, я получаю false.

Screenshot showing the source and that the output is "false"

что здесь происходит?

9 ответов


прежде всего: это интересный вопрос, но он никогда не должен возникать в "реальном коде", так как назначение переменной, которую вы вызываете в той же строке, сбивает с толку, даже если вы знаете, как это работает.

что происходит, вот эти 3 шага:

  1. выяснить, какой объект для вызова метода (т. е. оценить первый x, это приведет к ссылке на строку "hello")
  2. выяснить параметры (т. е. оценить x = y, которым изменится x указать на строку "goodbye", а также вернуть ссылку на эту строку)
  3. вызов метода equals о результате #1, используя результат #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 вызывает equals на результат, вычисленный в строке #9 и результат строк #10-12.


хороший вопрос! И у 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.

поэтому ссылка на строку hello запоминается как целевая ссылка перед локальной переменной x изменяется для ссылки на строку goodbye. В результате equals метод вызывается для объекта hello С аргументом goodbye, таким образом, результатом вызова является false.


важно помнить, что a String в java является объектом и, следовательно, ссылкой. Когда вы звоните

x.equals(...)

это проверка, если значение в этом месте имеются ссылки x равно тому,что вы передаете. Внутри, вы меняете значение x и ссылка, но вы все еще звоните equals С оригинал ссылка (ссылка на "привет"). Итак, прямо сейчас ваш код сравнивает, чтобы увидеть, если "привет" равнозначно "до свидания", чего явно нет. После этого пункта, если вы используете x опять же, это приведет ссылку на то же значение, что и y.


x=y в скобках означает, что выражение (x=y) теперь goodbye, в то время как внешний x в x.equals имеет значение hello


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"

теперь кажется очевидным, что он должен вести себя таким образом, но также очень легко увидеть, что именно происходит в каждой строке, к чему вы должны стремиться.


Я пробовал ваш вопрос в eclipse, ваше оба выражения верны. 1) x = = (y = x) оценить в true это верно, потому что значение x присваивается y, которое является "привет", тогда x и y сравнивают они будут то же самое так результат будет true

2) x.равно (x = y) это ложь поскольку значение y присваивается x, которое прощается, то x и X сравнивают их значение будет отличаться, поэтому результат будет false


Я вижу вопрос в терминах непрофессионала как "hello".equals("goodbye"). Поэтому он возвращает false.


в 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  

Он видит, если x.equals (присвоить x y, возвращает true всегда) так что в основном x.равно (true)