8

在以下示例中,实际发生了什么?

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

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

如果有人知道,提前非常感谢。

4

3 回答 3

4

从 JLS部分 §15.26.2 复合赋值运算符

E1 op= E2 形式的复合赋值表达式等价于 E1 = (T)((E1) op (E2)),其中 T 是 E1 的类型,除了 E1 只计算一次。

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

a = (a) + (a = 2)

表达式从左到右求值。因此输出 3

于 2013-03-08T21:12:14.663 回答
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 中被记住。

于 2013-03-08T21:21:56.877 回答
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
  • 将int1推入堆栈。
-------------
|           |
-------------
|           |
-------------
|     1     |
-------------
    STACK

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

[int a = 1;到此结束]


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

2: iload_1
  • 从本地加载一个 int 值variable 1并将其推入堆栈。
-------------
|           |             variable 1
-------------           --------------
|           |           |            |
-------------           --------------
|     1     |
-------------
    STACK

3: iconst_2
  • 将int2推入堆栈。
-------------
|           |             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_2and4: dup被执行,基本上将 int2两次压入堆栈;一个用于加载它,a另一个作为第二个操作数。之后,5: istore_1执行加载2a( a = 2) 中。

最后,6: iaddand7: istore_1执行 where6: 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 加载1a.
  • 将int 加载3b.
  • a然后推b入堆栈。
  • 对它们执行加法并将结果压入堆栈。
  • 从堆栈中弹出结果并将其存储到a.
于 2013-03-08T22:42:04.577 回答