温馨提醒:本篇文章内容过于底层,不太熟悉的同学可以先学习 操作数栈
本篇文章从字节码的角度分析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 // 将第四个本地变量增加 119: istore 4 // 将栈顶 int 型数值存入第五个本地变量 (注意:此时第四个本地变量为 11,栈顶的值仍为 10,所以第五个本地变量为 10,即 i4 = 10)21: bipush 10 // 将单字节的常量值 10 推送至栈顶23: istore 5 // 将栈顶 int 型数值存入第六个本地变量25: iinc 5, 1 // 将第六个本地变量增加 128: 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 // 将第八个本地变量增加 141: istore 7 // 将栈顶 int 型数值存入第八个本地变量 (注意:此时第八个本地变量为 11,栈顶的值为 10,所以第八个本地变量最终为 10,即 i7 = 10)43: bipush 10 // 将单字节的常量值 10 推送至栈顶45: istore 8 // 将栈顶 int 型数值存入第九个本地变量47: iinc 8, 1 // 将第九个本地变量增加 150: 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 // 将第十个本地变量增加 163: iinc 9, 1 // 将第十个本地变量增加 166: 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 之后的值