1.问题
Java 中运行以下代码 :
public static int test() {
int i = 0;
i = i++;
return i;
}
返回结果为:0
2.JVM执行的代码
直接在Windows命令窗口中使用 javap -c XXX 查看JVM执行的代码:
public static int test();
Code:
0: iconst_0
1: istore_0
2: iload_0
3: iinc 0, 1
6: istore_0
7: iload_0
8: ireturn
JVM调用执行该方法时,对应栈帧(Stack Frame) 在虚拟机中入栈;栈帧中包含:局部变量表、操作数栈、动态链接、方法返回地址等信息。
局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量,i就放在局部变量表中;而操作数栈是一个后入先出的栈,用于方法的计算。
3.现在来看看JVM的字节码指令
- 0: iconst_0 --> 将int类型的0入栈,即放到操作数栈的栈顶
- 1: istore_0 --> 将操作数栈顶元素 0 弹出,存入局部变量表的0号索引位上(这是个静态方法,第0位不用存放当前对象的引用)
- 2: iload_0 --> 将局部变量表的0号索引位的值的副本,压入操作数栈
- 3: iinc 0, 1 --> iinc是对int类型的值进行自增操作:0代表局部变量表索引;1代表要增的数值; —即,局部变量表中 0 号位置的数值+1 变为 1
- 6: istore_0 --> 将操作数栈顶元素 0 弹出,存储到局部变量表的索引 0 位;此时覆盖了上一步局部变量表的自增计算结果
- 7: iload_0 --> 将局部变量表的0号索引位的值的副本,压入操作数栈
- 8: ireturn --> 从当前方法返回int
4.结论
自增操作是对局部变量表中的值进行自增,而操作数栈中的值并不会改变。
当 i = i++ 进行赋值操作时,先会将局部变量表中的i值(0)押入操作数栈,然后自增(局部变量表中 -> 1)(操作数栈顶的值并没有发生变化),最后将操作数栈顶的元素弹出并覆盖局部变量表中的数值。
5.扩展
i = ++i:
public static int test() {
int i = 0;
i = ++i;
return i;
}
返回值:1
JVM的字节码指令:
public static int test();
Code:
0: iconst_0
1: istore_0
2: iinc 0, 1
5: iload_0
6: istore_0
7: iload_0
8: ireturn
区别:
第2、3步执行顺序不一样,也就是i=++i先自增,后入栈,前后值一致。
那 i 如果是类的静态变量呢?
//i = i++
static int i =0;
public static int test() {
i = i++;
return i;
}
//i = ++i
static int i =0;
public static int test() {
i = ++i;
return i;
}