前言
很多时候我们遇到一些面试 i++ 和 ++i我们很容易入坑,一不小心就落入了陷阱,本文从字节码层面看i++和++i,了解底层的实现,知其然知其所以然
一、i++ 的 坑
我们先看个小例子,下面会输出什么:
public static void main(String[] args) {
int i = 0;
for(int j = 0; j < 50;j++){
i = i++;
}
System.out.println(i);
}
答案是0, 如果你答对了 恭喜你,答错了那我们来看下底层字节码:
public static void main(java.lang.String[]);
Code:
0: iconst_0 //0 加载到操作数栈顶
1: istore_1 //操作数栈顶 放入 局部变量表第一个位置 即给i 赋初值
2: iconst_0 // 0 加载到操作数栈顶
3: istore_2 // 操作数栈顶 放入局部变量表 第二个位置 即给j 赋初值
4: iload_2 //将 j 加载到栈顶
5: bipush 50 //将 50 加载到栈顶
7: if_icmpge 21 // 比较 j 和 50 如果 >= 50 则跳转至 21行 否则向下执行
10: iload_1 //加载i的值 到操作数栈顶 这时候还是0
11: iinc 1, 1 //i 的值+1,这里是直接发生在 局部变量表中的,并未到操作数栈,局部变量表值变更为2
14: istore_1 //将操作数栈顶放到局部变量表第一个位置,也就是i的值,这时候操作数栈还是0
15: iinc 2, 1 //j++
18: goto 4 //跳转至第4行 继续执行循环
21: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
24: iload_1
25: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
28: return
我们可以看到 i++ 只操作了局部变量表,并不会和操作数栈发生关系,如果i= i++ 的结果就是 先读了i 的值 到操作数栈,然后局部变量表操作自增,然后 操作数栈又写回了 局部变量表发生了覆盖,所以结果会是 0.
那么下面代码会输出什么呢?
public static void main(String[] args) {
int i = 0;
for(int j = 0; j < 50;j++){
i = ++i;
}
System.out.println(i);
}
public static void main(String[] args) {
int i = 0;
for(int j = 0; j < 50;j++){
++i;
}
System.out.println(i);
}
public static void main(String[] args) {
int i = 0;
for(int j = 0; j < 50;j++){
i++;
}
System.out.println(i);
}
这里结果都会是 50,第一种情况虽然发生了覆盖,但是 覆盖的值和 局部变量表中的值是一致的,所以是50,
第二和第三种都是操作的局部变量表,未发生覆盖,输出时才将局部变量表加载到栈顶,所以也是 50.
二、稍微复杂一点的案例
//java 代码
public static void main(String[] args) {
int i = 0;
i = i++ + i++ + i++ + ++i;
System.out.println(i);
}
字节码我就不贴了,我们来分析下这个流程:
首先我们把 “=” 后面 用 + 号隔开,出现了四部分 i++ 、i++、i++、++i
第一个:会采用初始值 0, 做加法之后会+1 变为1,存入局部变量表;
第二个:会取局部变量表 的1做加法,然后 + 1后变为2,放入局部变量表;
第三个:取局部变量表的值 2 做加法,然后+1 变为3,放入局部变量表;
第四个:会将局部变量表的值+1,变为4,然后从局部变量表中取出做加法
所以最终的结果是:0+1+2+4 = 7。感兴趣的可以分析下字节码