i++与++i的区别
写在前面:欢迎来到「发奋的小张」的博客。我是小张,一名普通的在校大学生。在学习之余,用博客来记录我学习过程中的点点滴滴,也希望我的博客能够更给同样热爱学习热爱技术的你们带来收获!希望大家多多关照,我们一起成长一起进步。也希望大家多多支持我鸭,喜欢我就给我一个关注吧!
在说i++与++i的区别之前,我们不妨来看一下下面这几行代码:
这里有四个问题,分别输出的结果是什么?
/**
* 程序员面试过程中,常见的 i++ 与 ++i 的区别,这里需要从java的字节码文件进行分析
*/
public void add(){
//第一类问题
int i1 = 10;
i1++;
System.out.println(i1);
int i2 = 10;
++i2;
System.out.println(i2);
//第二类问题
int i3 = 10;
int i4 = i3++;
System.out.println(i4);
int i5 = 10;
int i6 = ++i5;
System.out.println(i6);
//第三类问题
int i7 = 10;
i7 = i7++;
System.out.println(i7);
int i8 = 10;
i8 = ++i8;
System.out.println(i8);
//第四类问题:重点,易错
int i9 = 10;
int i10 = i9++ + ++i9;
System.out.println(i10);
}
对于大多数人来说,前三个问题的输出都不成问题。关键在于最后一个问题,很多人可能没见过。
第一,二类问题分析
相信大家都知道,第一类问题的输出都是11,结果相同。因此很好理解。第二类问题也很好理解,输出的结果是10,11.
这里给大家解释一下,在运算中:
i++ :先引用后增加
++i :先增加后引用
即
i++ :先在i所在的表达式中使用i的当前值,后让i加1
++i :让i先加1,然后在i所在的表达式中使用i的新值
也就是说:
在 int i4 = i3++;这个表达式中,先执行 i4 = i3,把i3的值赋给i4,之后i3才会进行+1操作。
在 int i6 = ++i5;这个表达式中,先执行 i3+1操作,之后在进行把i3的值赋给i4的操作。
第三类问题分析
可能有的人在回答这个问题时,就会不小心掉坑里哦。我猜一下,有的人会说答案是11,11.
其实细心的人就会发现,第三类问题的答案和第二类的没区别,答案依旧是10,11.
我们来分析一下,为什么结果还是一样的呢。我们知道,i++与++i的区别就是一个先引用后增加,一个先增加后引用。如果你理解了这句话,这第三类问题就不攻自破了。他其实就是第二类问题的变形。
只不过不同的是,它把自身作为值的传递和存储对象了。会给我们造成一种错觉。
下面我从java字节码的角度带大家分析一下这个问题:(此处涉及jvm的知识,希望大家集中注意力)
首先,我将这个程序预编译成class类:
下面就是生成的class文件,此处被idea反编译回来了!
public void add() {
int i1 = 10;
int i1 = i1 + 1;
System.out.println(i1);
int i2 = 10;
int i2 = i2 + 1;
System.out.println(i2);
int i3 = 10;
int var12 = i3 + 1;
System.out.println(i3);
int i5 = 10;
int i5 = i5 + 1;
System.out.println(i5);
int i7 = 10;
int var14 = i7 + 1;
System.out.println(i7);
int i8 = 10;
int i8 = i8 + 1;
System.out.println(i8);
int i9 = 10;
int i9 = i9 + 1;
++i9;
int i10 = i9 + i9;
System.out.println(i10);
}
其实从这里,我们就可以看出一些头绪。
大家注意看这里:
此处就已经证实我之前说的,由于i++会先传值,在自增。所以i7会被先传值,而+1的值会被赋给一个新的临时变量存储起来。
而++i则会先自增,再传值,所以编译成class文件后,先自+1,后重新赋值回i8.
从而得到10,11这两个结果!
想要自己动手尝试的小伙伴,记得先将程序编译一下,快捷键是ctrl+shift+F9。然后再打开如下如所示位置即可查看被编译的class文件.
因此这里我就不用往下分析了。
第四类问题(重难点)
想必很多小伙伴都不明白这一题输出的答案为什么是22,为什么不是21?心里肯定在想出题还能这样出!
答案马上揭晓。
首先,我们需要了解一个知识点。大家都听说过java程序要想运行起来,必须得在特定的java环境中。那么,这个环境中就由一个叫JVM的虚拟机来进行java程序的一切操作和管理。
在jvm中,虚拟机栈会对方法中的变量和操作进行处理。在虚拟机栈中,有一个局部变量表和操作数栈,他们两个相互配合,完成一个方法的变量赋值,四则运算!
说到这里,估计有的人已经有点懵了。那我就先上结果把!
结论:在进行表达式运算时,都是自右向左依次压入栈中
因此,此处的 int i10 = i9++ + ++i9; 相当于先进行 ++i9,在进行 i10 = i9++的操作。
所以,此处的表达式就相当于
i9 = i9 + 1;
i10 = i9+i9;
i9 = i9 + 1;
也就是下图的class文件所表示的这样!
其实这里的分析只是取巧,真正的分析因该是对他的字节码进行分析。
先附上它的字节码:
0 bipush 10
2 istore_0
3 iinc 0 by 1
6 getstatic #3 <java/lang/System.out>
9 iload_0
10 invokevirtual #4 <java/io/PrintStream.println>
13 bipush 10
15 istore_1
16 iinc 1 by 1
19 getstatic #3 <java/lang/System.out>
22 iload_1
23 invokevirtual #4 <java/io/PrintStream.println>
26 bipush 10
28 istore_2
29 iload_2
30 iinc 2 by 1
33 istore_3
34 getstatic #3 <java/lang/System.out>
37 iload_3
38 invokevirtual #4 <java/io/PrintStream.println>
41 bipush 10
43 istore 4
45 iinc 4 by 1
48 iload 4
50 istore 5
52 getstatic #3 <java/lang/System.out>
55 iload 5
57 invokevirtual #4 <java/io/PrintStream.println>
60 bipush 10
62 istore 6
64 iload 6
66 iinc 6 by 1
69 istore 6
71 getstatic #3 <java/lang/System.out>
74 iload 6
76 invokevirtual #4 <java/io/PrintStream.println>
79 bipush 10
81 istore 7
83 iinc 7 by 1
86 iload 7
88 istore 7
90 getstatic #3 <java/lang/System.out>
93 iload 7
95 invokevirtual #4 <java/io/PrintStream.println>
98 bipush 10
100 istore 8
102 iload 8
104 iinc 8 by 1
107 iinc 8 by 1
110 iload 8
112 iadd
113 istore 9
115 getstatic #3 <java/lang/System.out>
118 iload 9
120 invokevirtual #4 <java/io/PrintStream.println>
123 return
上面就是此程序的字节码文件!
为了便于大家理解,我在这里引用尚硅谷的宋红康老师的一张图大致表示一下:(此处的图并不匹配本程序,只是让大家看一下结构)
先解释一下马上要用到的几个操作:
- bipush 10 :指将10压入操作数栈中
- istore 0 : 指将操作数栈中压入的10出栈,并存入局部变量表中的0号地址
- iinc 0 by 1 :指当前值+1操作
- iload_0 : 指将局部变量表中0号地址的值取出,存入栈中
下面我们要看得是问题四得那一部分指令:
98 bipush 10
100 istore 8
102 iload 8
104 iinc 8 by 1
107 iinc 8 by 1
110 iload 8
112 iadd
113 istore 9
115 getstatic #3 <java/lang/System.out>
118 iload 9
120 invokevirtual #4 <java/io/PrintStream.println>
下面是大致执行过程:
由于操作得指令比较多,所以博主偷了个懒,把有些指令合并成一张图表示了。希望大家见谅!
以上就是从字节码文件解释为啥输出结果是22,而不是21了。
如果对字节码不理解的,建议就记住一个结论:
表达式运算,先从右开始。因此从右自左入栈!i++ :先引用后增加
++i :先增加后引用。
博主后记:
希望看到此篇博文的小伙伴,如果发现有什么不对的地方,欢迎在下方留言指正!博主一定虚心接受并改正!大家一起共同进步。如果对你有所帮助,可以给博主一个赞👍。
转载:https://blog.csdn.net/qq_43431171/article/details/106689114