2013-03-08 4 views
8

В следующем примере, что на самом деле происходит?Назначение переменной, что на самом деле происходит, Java

int a = 1; 
a += (a = 2); 

Выходной сигнал 3, однако я хотел знать, что на самом деле происходит под крышками. Например, я знаю, что круглые скобки имеют более высокий приоритет до +, поэтому сначала происходит (a = 2) выражение должно быть a = 2 + 2. Во время выполнения сначала должно выполняться выражение в круглых скобках, а затем a равно 2. Кажется, что первый a слева от + получает «загружен» до (a = 2), и это последнее выражение, похоже, не отменяет предыдущую загрузку. Другими словами, я совершенно смущен тем, что именно происходит за кулисами.

Если кто знает, спасибо заблаговременно.

+1

Связанный: http://stackoverflow.com/questions/11324850/why-swapping-integer-variable-by-xor-doesnt-work-in-a-single-line/11325458 – nhahtdh

+0

То же, что 'Int А = 1; int tmpvar = (a = 2); a + = tmpvar; ' –

+0

Он переводится в' a = a + (a = 2); ', а операнды оцениваются слева направо. –

ответ

2

См. Ссылочный пример 15.7.1-2 от http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.7.1, который почти идентичен приведенному вами примеру. В частности:

Если оператор является оператором составного присваивания (§15.26.2), а затем оценки левого операнда включает в себя как запоминании переменных, что левый операнд обозначает и извлекая и сохраняя значение , которое используется для использования в двоичной операции с неявным использованием.

Из-за этого приоритета сначала оценивается левая рука + =.

Это может ввести в заблуждении вас из-за скобки, но обратите внимание на раздел оценки скобки: http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.7.3, и в частности:

Язык программирования Java уважает порядок оценки , указанный в явном виде скобки и неявно с помощью оператора приоритет.

В этом случае неявный приоритет, заданный оператором + =, указывает, что левый операнд будет запоминаться в спецификации. Хотя верно, что операторы присваивания, в том числе «+ =» имеют наименьший приоритет, спецификация для + = указывает, что левый операнд будет запоминаться в 15.26.2.

+1

Фактически приоритет + = является наименьшим и должен быть оценен последним – Rollerball

+0

Прочтите текст примера: В следующей программе два оператора присваивания извлекают и запоминают значение левого операнда, которое равно 9, до того, как будет оценен правый операнд оператора сложения, после чего переменная будет установлена ​​на 3. – Kirby

+0

Этот пример такой же, как у вас, но с разными номерами. – Kirby

4

От JLS section §15.26.2 Compound Assignment Operators:

Выражение присваивания соединение вида E1 оп = E2 эквивалентно Е1 = (T) ((Е1) оп (E2)), где Т представляет собой тип от E1, за исключением того, что E1 оценивается только один раз.

Так для примера мы имеем:

a = (a) + (a = 2) 

С вычисленного выражения слева направо. Следовательно, выход 3

+0

В заявлении, которое вы цитируете, ничего не говорится о порядке оценки. – nhahtdh

+0

@ rich.okelly ok, так что в основном левый (a) неявно помещается в круглые скобки? – Rollerball

+0

@nhahtdh Выведено - порядок приоритета гарантируется заказом в заявлении с помощью скобок, формулирующих неоднозначность. –

2

Давайте посмотрим на байткод следующей программе:

package A; 

public class Test 
{ 
    public static void main(String[] args) 
    { 
     int a = 1; 
     a += (a = 2); 
    } 
} 

Нам просто нужно запустить эту команду:

javap -c Test.class 

получить следующий байт-код:

public class A.Test { 
    public A.Test(); 
    Code: 
     0: aload_0 
     1: invokespecial #1   // Method java/lang/Object."<init>":()V 
     4: return 

    public static void main(java.lang.String[]); 
    Code: 
     0: iconst_1 
     1: istore_1 
     2: iload_1 
     3: iconst_2 
     4: dup 
     5: istore_1 
     6: iadd 
     7: istore_1 
     8: return 
} 

Объяснение:

Мы сосредоточимся только на двух линиях внутри основного метода:

int a = 1; 
a += (a = 2); 

[int a = 1; начинается здесь]

0: iconst_1 
  • Толчки Int 1 на стек ,
------------- 
|   | 
------------- 
|   | 
------------- 
|  1  | 
------------- 
    STACK 

1: istore_1 
  • Попс INT значение из стека variable 1 (variable 1 представляет a)
------------- 
|   |    variable 1 
-------------   -------------- 
|   |   |  1  | 
-------------   -------------- 
|   | 
------------- 
    STACK 

[int a = 1; заканчивается здесь]


[a += (a = 2); начинается здесь]

2: iload_1 
  • Загружает целочисленное значение из локального variable 1 и помещает его в стек.
------------- 
|   |    variable 1 
-------------   -------------- 
|   |   |   | 
-------------   -------------- 
|  1  | 
------------- 
    STACK 

3: iconst_2 
  • Толчки INT 2 в стек.
------------- 
|   |    variable 1 
-------------   -------------- 
|  2  |   |   | 
-------------   -------------- 
|  1  | 
------------- 
    STACK 

4: dup 
  • дублировать значение на вершине стека.
------------- 
|  2  |    variable 1 
-------------   -------------- 
|  2  |   |   | 
-------------   -------------- 
|  1  | 
------------- 
    STACK 

5: istore_1 
  • Pops INT значение из стека в variable 1.
------------- 
|   |    variable 1 
-------------   -------------- 
|  2  |   |  2  | 
-------------   -------------- 
|  1  | 
------------- 
    STACK 

6: iadd 
  • добавляет два значения вместе.
------------- 
|   |    variable 1 
-------------   -------------- 
|   |   |  2  | 
-------------   -------------- 
|  3  | 
------------- 
    STACK 

7: istore_1 
  • Pops INT значение из стека в variable 1.
------------- 
|   |    variable 1 
-------------   -------------- 
|   |   |  3  | 
-------------   -------------- 
|   | 
------------- 
    STACK 

[a += (a = 2); заканчивается здесь]


8: return 
  • Основной метод возвращает значение.

Вывод:

a = a + (a = 2) сделано несколько операций. 2: iload_1 выполняется как первая команда a += (a = 2);, которая считывает первый операнд уравнения a = a + (a = 2) и толкает его в стек.

Далее выполняются 3: iconst_2 и 4: dup, которые в основном нажимают int 2 два раза на стек; один для загрузки его в a, а второй в качестве второго операнда. После этого выполняется 5: istore_1, которое загружается 2 в a (a = 2).

Наконец, 6: iadd и 7: istore_1 выполняются где 6: iadd добавляет первый операнд и второй операнд и помещает результат в стек, и 7: istore_1 выскакивает результат и загружает его в a.


Для простоты, давайте иметь быстрый взгляд на этот код:

int a = 1; 
int b = 3; 
a += b; 

и вот его байткодом:

public class A.Test { 
    public A.Test(); 
    Code: 
     0: aload_0 
     1: invokespecial #1   // Method java/lang/Object."<init>":()V 
     4: return 

    public static void main(java.lang.String[]); 
    Code: 
     0: iconst_1 
     1: istore_1 
     2: iconst_3 
     3: istore_2 
     4: iload_1 
     5: iload_2 
     6: iadd 
     7: istore_1 
     8: return 
} 

Как вы можете видеть, это просто делает следующее:

  • Грузы int 1 в a.
  • Грузы int 3 в b.
  • Толкает a, затем b в стек.
  • Выполняет добавление на них и толкает результат на стек.
  • Восстанавливает результат из стека и сохраняет его в a.
Смежные вопросы