我们平常工作中对某个数进行累加的时候,一般都是用i++,但是我看HashMap和其他一些jdk里面的代码,用到自增基本上用的都是 ++i,这两个有啥区别呢?我们都知道一个是i++先赋值再自增, 另一个是先自增再赋值。我们只知其然,不知其所以然。 今天我尝试给大家解释一下,希望我能表达清楚吧。
首先列出我们的测试代码,你可以想一下这个i会打印几。
我们通过测试可以知道 第一个main方法输出的是8,第二个输出是9,第二个我相信大家知道的不知道的都知道会输出9, 至于第一个为啥会输出8,肯定有点懵。先要了解一点,方法执行都是以一个栈帧的形式放在虚拟机栈里面,虚拟机栈里面存放的内容有,局部变量表,操作数栈,动态链接,以及方法的出口。执行其实都是执行一条条的指令出栈和入栈。下面我们通过工具来看main方法的指令集,这里我们需要借助一个idea的插件jclasslib Bytecode viewer
安装
在idea的plugins里面搜索jclasslib Bytecode viewer,然后点击安装
使用 鼠标光标放要查看二进制码的类名上,然后点击view,下拉框下面会有一个新的选项我们就可以看到这个类的二进制码文件啦,我们找到main方法这一块,我们可以看到这个方法里面所有的指令我们把这些指令都列出来,一个个来解释他们的意思
这是i++的main方法指令集
在解释之前我们还需要先看下局部变量表的内容
具体指令意思可以查看https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.istore_n的官网
bipush 8 :这个指令实际上是bipush代表的是我们平常说的压栈,把8压到栈里面istore_1 : 使用的指令istore_n 它从操作数堆栈中弹出,并且将局部变量中下标为1的值(从上面的局部变量表中可以知道这个值是我们的i)设置为value。iload_1 :iload_n 将局部变量表n位置的值进行压栈iinc 1 by 1 : iinc局部变量表1位置的值增加1(这一步是在局部变量表中执行,不会压栈)getstatic #2 <java/lang/System.out>:getstatic这个获取系统的一个静态变量invokevirtual #3 <java/io/PrintStream.println>:invokevirtual执行一个多态的方法15 return :返回用到的指令都解释了一下,现在我们统一来解释一下i++的指令执行流程
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.把8压到栈中 2.把8从栈中弹出来,赋值给i,这个时候i=8 3.把i = 8 又压到栈中 4.i在局部变量表中自增,此时局部变量表中的为9,这个过程不会压栈所以此时栈中还是8 5.把8弹出来赋值给i所以就从9变为了8我们再来看下++i的操作指令
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 1.把8压到栈中 2.把8从栈中弹出来,赋值给i,这个时候i=8 3.i自增为9,此时把9压到栈中 4.再把9出栈,赋值给i 所以i = 9因为i++和++i的指令集中,一个是先压栈,再执行i++的指令,而++操作不会压栈,所以栈中的i还是之前存进去的8,而++i是先执行自增指令,再去压栈,再从栈中弹出赋值给i。