分配一个变量,实际发生了什么,Java

时间:2013-03-08 21:09:48

标签: java runtime variable-assignment

在下面的示例中实际发生了什么?

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

输出是3,但我想知道封面下究竟发生了什么。 例如,我知道括号的优先级高于+所以首先发生(a = 2)表达式应该变为a = 2 + 2。 在运行时首先应该执行括号内的表达式,然后a变为2.似乎a左边的第一个+(a = 2)之前得到“加载”并且最后一个表达式似乎没有覆盖以前的加载。 换句话说,我对幕后究竟发生了什么感到困惑。

如果有人知道,请提前多多感谢。

3 个答案:

答案 0 :(得分:4)

来自JLS section §15.26.2 Compound Assignment Operators

  

形式E1 op = E2的复合赋值表达式是等价的   到E1 =(T)((E1)op(E2)),其中T是E1的类型,除了E1   仅评估一次。

因此,对于您的示例,我们有:

a = (a) + (a = 2)

从左到右评估表达式。因此输出3

答案 1 :(得分:2)

请参阅http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.7.1中引用的示例 15.7.1-2 ,这与您提供的示例几乎完全相同。特别是:

  

如果运算符是复合赋值运算符(第15.26.2节),那么   左手操作数的评估包括记住   左侧操作数表示并获取和保存的变量   该隐含二进制操作中使用的变量值。

由于这个优先权,首先评估+ =的左手。

由于括号,您可能会感到困惑,但请注意括号评估部分:http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.7.3,特别是:

  

Java编程语言尊重评估顺序   由括号明确指出并由运算符隐式指示   优先级。

在这种情况下,+ =运算符设置的隐式优先级表示将根据规范记住左手操作数。虽然包含“+ =”的赋值运算符具有最低优先级,但+ =的规范表示将按15.26.2记住左侧操作数。

答案 2 :(得分: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
}

<强>

说明:

我们只关注main方法中的两行:

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

[int a = 1;从这里开始]

0: iconst_1
  • 将int 1推入堆栈。
-------------
|           |
-------------
|           |
-------------
|     1     |
-------------
    STACK

1: istore_1
  • 从堆栈弹出int值到variable 1variable 1表示a
-------------
|           |             variable 1
-------------           --------------
|           |           |     1      |
-------------           --------------
|           |
-------------
    STACK

[int a = 1;在这里结束]


[a += (a = 2);从这里开始]

2: iload_1
  • 从本地variable 1加载一个int值并将其推送到堆栈。
-------------
|           |             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
  • 将堆栈中的int值弹出到variable 1
-------------
|           |             variable 1
-------------           --------------
|     2     |           |      2     |
-------------           --------------
|     1     |
-------------
    STACK

6: iadd
  • 将前两个值加在一起。
-------------
|           |             variable 1
-------------           --------------
|           |           |      2     |
-------------           --------------
|     3     |
-------------
    STACK

7: istore_1
  • 将堆栈中的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_24: dup,基本上将int 2两次推入堆栈;一个用于将其加载到a,另一个用作第二个操作数。之后,执行5: istore_1,将2加载到aa = 2)。

最后,执行6: iadd7: 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
}

如您所见,它只是执行以下操作:

  • 1加载到a
  • 3加载到b
  • a然后b推入堆栈。
  • 对它们执行添加并将结果推送到堆栈。
  • 从堆栈中弹出结果并将其存储到a