温馨提醒:本篇文章内容过于底层,不太熟悉的同学可以先学习 操作数栈
本篇文章从字节码的角度分析i++
和++j
背后的逻辑!!
先给出四个可能会让你很晕~的例子,相信如果搞清楚i++
和++j
原理的人可以很快知道每个变量最终的结果
public void add() {
// problem 1
int i1 = 10;
i1++;
int i2 = 10;
++i2;
// problem 2
int i3 = 10;
int i4 = i3++;
int i5 = 10;
int i6 = ++i5;
// problem 3
int i7 = 10;
i7 = i7++;
int i8 = 10;
i8 = ++i8;
// problem 4
int i9 = 10;
i9 = i9++ + ++i9;
}
下面再给出通过反编译后,该方法对应的字节码,这也是本篇文章分析的重点
如果不知道每个字节码指令对应什么意思,可以去查《虚拟机字节码指令表》
// ------------------------- problem 1 -------------------------
0: bipush 10 // 将单字节的常量值 10 推送至栈顶
2: istore_1 // 将栈顶 int 型数值存入第二个本地变量 (注意:下标从 0 开始,所以第二个对应下标是 1。下面同理!!)
3: iinc 1, 1 // 将指定 int 型变量增加指定值 (此处为将第二个本地变量增加 1;若为 i += 2,那么对应的字节码就为 iinc 1, 2)
6: bipush 10 // 将单字节的常量值 10 推送至栈顶
8: istore_2 // 将栈顶 int 型数值存入第三个本地变量
9: iinc 2, 1 // 将第三个本地变量增加 1
// ------------------------- problem 2 -------------------------
12: bipush 10 // 将单字节的常量值 10 推送至栈顶
14: istore_3 // 将栈顶 int 型数值存入第四个本地变量
15: iload_3 // 将第四个 int 型本地变量推送至栈顶
16: iinc 3, 1 // 将第四个本地变量增加 1
19: istore 4 // 将栈顶 int 型数值存入第五个本地变量 (注意:此时第四个本地变量为 11,栈顶的值仍为 10,所以第五个本地变量为 10,即 i4 = 10)
21: bipush 10 // 将单字节的常量值 10 推送至栈顶
23: istore 5 // 将栈顶 int 型数值存入第六个本地变量
25: iinc 5, 1 // 将第六个本地变量增加 1
28: iload 5 // 将第六个 int 型本地变量推送至栈顶
30: istore 6 // 将栈顶 int 型数值存入第七个本地变量 (注意:此时第六个本地变量为 11,栈顶的值为 11,所以第七个本地变量为 11,即 i6 = 11)
// ------------------------- problem 3 -------------------------
32: bipush 10 // 将单字节的常量值 10 推送至栈顶
34: istore 7 // 将栈顶 int 型数值存入第八个本地变量
36: iload 7 // 将第八个 int 型本地变量推送至栈顶
38: iinc 7, 1 // 将第八个本地变量增加 1
41: istore 7 // 将栈顶 int 型数值存入第八个本地变量 (注意:此时第八个本地变量为 11,栈顶的值为 10,所以第八个本地变量最终为 10,即 i7 = 10)
43: bipush 10 // 将单字节的常量值 10 推送至栈顶
45: istore 8 // 将栈顶 int 型数值存入第九个本地变量
47: iinc 8, 1 // 将第九个本地变量增加 1
50: iload 8 // 将第九个 int 型本地变量推送至栈顶
52: istore 8 // 将栈顶 int 型数值存入第九个本地变量 (注意:此时第九个本地变量为 11,栈顶的值为 11,所以第九个本地变量最终为 11,即 i8 = 10)
// ------------------------- problem 4 -------------------------
54: bipush 10 // 将单字节的常量值 10 推送至栈顶
56: istore 9 // 将栈顶 int 型数值存入第十个本地变量
58: iload 9 // 将第十个 int 型本地变量推送至栈顶
60: iinc 9, 1 // 将第十个本地变量增加 1
63: iinc 9, 1 // 将第十个本地变量增加 1
66: iload 9 // 将第十个 int 型本地变量推送至栈顶
68: iadd // 将栈顶两 int 型数值相加并将结果压入栈顶 (注意:此时栈顶两个值分别为「+1 前的值」和「+1 两次后的值」;最终栈顶的值为 10 + 12 = 22)
69: istore 9 // 将栈顶 int 型数值存入第十个本地变量 (注意:此时第十个本地变量为 12,栈顶的值为 22,所以第十个本地变量最终为 22,即 i9 = 22)
71: return // 从当前方法返回 void
总结:
首先i++
和++i
对于变量 i 的效果是等价的,都是使 i 增加 1
不同的在于这两个表达式的值:
i++
是先将局部变量表中的 i 推送至操作数栈顶,然后将局部变量表中的 i + 1,最后将操作数栈顶的值赋给表达式,所以表达式的值为 i + 1 之前的值
++i
是先将局部变量表中的 i + 1,然后将局部变量表中的 i 推送至操作数栈顶,最后将操作数栈顶的值赋给表达式,所以表达式的值为 i + 1 之后的值