在以下示例中,实际发生了什么?
int a = 1;
a += (a = 2);
输出为 3,但我想知道幕后实际发生了什么。例如,我知道括号具有更高的优先级,+
因此首先发生(a = 2)表达式应该变为a = 2 + 2
. 在运行时,首先应该执行括号内的表达式,然后 a 变为 2。似乎a
左边的第一个+
在 of 之前被“加载”,(a = 2)
最后一个表达式似乎没有覆盖之前的加载。换句话说,我对幕后究竟发生了什么感到很困惑。
如果有人知道,提前非常感谢。
在以下示例中,实际发生了什么?
int a = 1;
a += (a = 2);
输出为 3,但我想知道幕后实际发生了什么。例如,我知道括号具有更高的优先级,+
因此首先发生(a = 2)表达式应该变为a = 2 + 2
. 在运行时,首先应该执行括号内的表达式,然后 a 变为 2。似乎a
左边的第一个+
在 of 之前被“加载”,(a = 2)
最后一个表达式似乎没有覆盖之前的加载。换句话说,我对幕后究竟发生了什么感到很困惑。
如果有人知道,提前非常感谢。
从 JLS部分 §15.26.2 复合赋值运算符:
E1 op= E2 形式的复合赋值表达式等价于 E1 = (T)((E1) op (E2)),其中 T 是 E1 的类型,除了 E1 只计算一次。
因此,对于您的示例,我们有:
a = (a) + (a = 2)
表达式从左到右求值。因此输出 3
请参阅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 中被记住。
我们来看看下面程序的字节码:
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
1
推入堆栈。-------------
| |
-------------
| |
-------------
| 1 |
-------------
STACK
1: istore_1
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
2
推入堆栈。-------------
| | variable 1
------------- --------------
| 2 | | |
------------- --------------
| 1 |
-------------
STACK
4: dup
-------------
| 2 | variable 1
------------- --------------
| 2 | | |
------------- --------------
| 1 |
-------------
STACK
5: istore_1
variable 1
.-------------
| | variable 1
------------- --------------
| 2 | | 2 |
------------- --------------
| 1 |
-------------
STACK
6: iadd
-------------
| | variable 1
------------- --------------
| | | 2 |
------------- --------------
| 3 |
-------------
STACK
7: istore_1
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
and4: dup
被执行,基本上将 int2
两次压入堆栈;一个用于加载它,a
另一个作为第二个操作数。之后,5: istore_1
执行加载2
到a
( a = 2
) 中。
最后,6: iadd
and7: 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
}
如您所见,它只是执行以下操作:
1
到a
.3
到b
.a
然后推b
入堆栈。a
.