一、Java的i = i++深入JVM指令讲解 图文并茂 看了必懂
先看一段简单的代码
package com.zhou.jvm.runtimedataAreainstructionset; /** * @author zhouyanxiang * @create 2020-08-2020/8/5-19:55 */ public class TestPlusPlus { public static void main(String[] args) { int i = 8; i = i++; System.out.println(i); } }
最后程序打印的结果是8不是9
那么这个是为什么呢?我们从JVM指令来看
编译这个类之后我们用jclasslib插件show bytecode with jclasslib,可以看到如下
我把里面的Bytecode单独拿出来,粘在这里
0 bipush 8 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 #3 <java/io/PrintStream.println> 15 return
接下来一条一条指令的解析
1. bipush 8
第一步,bipush指令具体的含义就是把一个int类型的数据压入操作数栈。具体的可以参照官网链接,如下图所示把8压入了操作数栈
2. istore_1
第二步,istore_1就是将操作数栈的数字弹出栈并写入局部变量表当中建立的一个index值为1的索引的变量,这里程序的i变量此时就为8,完成了int i = 8的操作。
3. iload_1
第三步,iload_1指令就是将局部变量表中的值再次压入操作数栈当中去。所以此时操作数栈里面的值此时为8
4. iinc 1 by 1
第四步,iinc 1 by 1就是将局部变量表中索引值为1的变量加一,就是完成了i++这一步操作,此时局部变量表中的i为9
5. istore_1
第五步,istore_1就是将操作数栈的数字弹出栈并写入局部变量表当中建立的一个index值为1的索引的变量,这里程序的i变量此时就为8,完成了int i = i++的操作。
6. iload_1
因此最后输出的就是8
二、换成i=++i的JVM指令研究
package com.zhou.jvm.runtimedataAreainstructionset; /** * @author zhouyanxiang * @create 2020-08-2020/8/5-19:55 */ public class TestPlusPlus { public static void main(String[] args) { int i = 8; i = ++i; System.out.println(i); } }
这个类的Bytecode
i=++i的bytecode
0 bipush 8 2 istore_1 3 iinc 1 by 1 6 iload_1 7 istore_1 8 getstatic #2 <java/lang/System.out> 11 iload_1 12 invokevirtual #3 <java/io/PrintStream.println> 15 return
把i = i++的bytecode拿出来对比
0 bipush 8 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 #3 <java/io/PrintStream.println> 15 return
可以看出两者之间的区别在于 iload_1 和 iinc 1 by 1的顺序问题
i=++i的是先 iinc 1 by 1再 iload_1,就是说先对局部变量表的变量进行自增再把这个自增以后的值压入操作数栈,自然这个操作数栈的数据已经+1了
i=i++的是先iload_1 再iinc 1 by 1,就是说先把局部变量表中的变量的值压入操作数栈,再进行自增操作,所以这个对于后续的istore_1操作有影响,使得这个最终的局部变量表中的数据还是之前没有加1的那个数值。