了解JVM 加载字节码的过程,有助于理解算术操作 i++ 和 ++i的区别
函数 | 字节码指令 | 说明 | |
private void method1(){ int i = 10; i++; } | 0 bipush 10 2 istore_1 3 iinc 1 by 1 6 return | 把10从常量池中 加载 到 操作数栈中; 操作数栈出栈, 即把10存储到局部变量表中下标为1的位置; 局部变量表中下标为1的 元素自加1; 返回 | |
private void method2(){ int i = 10; ++i; } | 0 bipush 10 2 istore_1 3 iinc 1 by 1 6 return | 同上 | |
private void method3(){ int j = 20; | 0 bipush 10 2 istore_1 3 iload_1 4 iinc 1 by 1 7 istore_2 8 bipush 20 10 istore_3 11 iinc 3 by 1 14 iload_3 15 istore 4 17 return | 把10从常量池中 加载 到 操作数栈中; 操作数栈出栈, 即把10存储到局部变量表中下标为1的位置; 把局部变量表中下标为1的数加载到操作数栈中; 局部变量表中下标为1的 元素自加1; 操作数栈出栈,即把10到局部变量表中下标为2的位置; 把20从常量池中 加载 到 操作数栈中; 操作数栈出栈, 即把20存储到局部变量表中下标为3的位置; 局部变量表中下标为3的 元素自加1; 把局部变量表中下标为3 的数加载到操作数栈中; 操作数栈出栈,即把21到局部变量表中下标为4的位置; 返回 | 注意红色部分的顺序 |
private void method4(){ int i = 10; i = i++; System.out.println(i); } | 0 bipush 10 2 istore_1 3 iload_1 4 iinc 1 by 1 7 istore_1 8 getstatic #2 <java/lang/System.out> 11 iload_1 12 invokevirtual #10 <java/io/PrintStream.println> 15 return | 把10从常量池中 加载 到 操作数栈中; 操作数栈出栈, 即把10存储到局部变量表中下标为1的位置; 把局部变量表中下标为1的数加载到操作数栈中; 局部变量表中下标为1的元素自加1; 操作数栈出栈,即把10存储到局部变量表中下标为1的位置; ....
| 注意红色部分的顺序 |
private void method5(){ int i = 10; i = i + 3; } | 0 bipush 10 2 istore_1 3 iload_1 4 iconst_3 5 iadd 6 istore_1 7 return | ||
private void method6(){ int i = 10; i +=3; } | 0 bipush 10 2 istore_1 3 iinc 1 by 3 6 return | 相比上一个少了入栈和出栈操作, 直接对局部变量表的元素自加3 |
由此可见,个人的理解是:
(1) 自加操作是针对局部变量表的元素
(2) 而赋值操作是需要经过操作数栈,即先从局部变量表或者常量池中取出来(入栈), 再存储到局部变量表中(出栈)。
因此,赋值后的结果,需要关注操作数栈!!