在下面的示例中实际发生了什么?
int a = 1;
a += (a = 2);
输出是3,但我想知道封面下究竟发生了什么。
例如,我知道括号的优先级高于+
所以首先发生(a = 2)表达式应该变为a = 2 + 2
。
在运行时首先应该执行括号内的表达式,然后a变为2.似乎a
左边的第一个+
在(a = 2)
之前得到“加载”并且最后一个表达式似乎没有覆盖以前的加载。
换句话说,我对幕后究竟发生了什么感到困惑。
如果有人知道,请提前多多感谢。
答案 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
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
加载一个int值并将其推送到堆栈。-------------
| | 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
和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
}
如您所见,它只是执行以下操作:
1
加载到a
。3
加载到b
。a
然后b
推入堆栈。a
。