先看两行代码(i初始值都为0)
System.out.println(i++);
System.out.println(++i);
我们都知道输出的结果是分别是 0 和 1;
单从指令结果上来看,a++和++a都是使a的值加1;并且这里也可以看出前++是先增加再使用,而后++是先使用后++
但是当我们加上赋值语句时结果又会有不同了
public class Test{
public static void main(String args[]){
int i = 0;
i = i++;
int j = 0;
j = ++j;
System.out.println(i+" "+j);
}
}
输出: 0 1
我们先用javap命令查看一下这两条语句的字节码
0: iconst_0
1: istore_1
2: iload_1
3: iinc 1, 1
6: istore_1
7: iconst_0
8: istore_2
9: iinc 2, 1
12: iload_2
13: istore_2
这里可以看出编号0~6是执行int i = 0;i = i++;两行代码,而7~13代表的是前++操作;
第0:首先iconst的作用是将一个int类型常量加载到操作数栈;所以这里的意思就是加载i并将i结果0放入操作栈栈顶,这一行指令的_0即代表操作数栈的索引值;
第1:istore的作用是将一个int类型数值从操作数栈加载到局部变量表;所以这里的操作是将操作栈栈顶元素出栈并存入局部变量表索引为1的地方(0通常存放当前实例的this引用)
第2:iload作用是将局部变量表数据加载到操作数栈;这里执行的操作就是将局部变量表索引为1处的值的副本加载到操作数栈,而这个操作执行完以后局部变量表索引为1处的值和操作数栈栈顶值都为0
3:iinc作用是对int类型值进行自增操作;后面的两个值分别代表局部变量表中要修改的值的索引和要增加的数值;所以这时局部变量表索引1处值变为了1,因为在第二步时局部变量表索引值
第6:和第二步作用一样,将一个int类型数值从操作数栈弹出加载到局部变量表为1处,但是,在第2的操作之后局部变量表索引1处的值已经为0,所以也就将局部变量表索引为1处值的变为了0
所以代码看似是把i++赋给了i,但是实际上字节码运行情况却是把局部变量表改好了最后又把自己的0覆盖了局部变量表的数值,相当于没做事
i++和++j字节码我们可以看到只有后面三行不同,其余操作都一样,i++是
2: iload_1
3: iinc 1, 1
6: istore_1
而++j的是
9: iinc 2, 1
12: iload_2
13: istore_2
因为定义了两个变量,所以j在局部变量表中的索引是2;这里++j执行步骤为:
- 执行iinc执行自增操作,将局部变量表索引2处值变为了1
- 执行iload_2将局部变量表索引为2处值的副本入栈,栈顶变为1
- 执行istore_2,将操作数栈栈顶元素弹出并存入局部变量表索引为2处,所以此时操作局部变量表索引为2处的值为1(感觉两行代码有冗余的意思)
上面从字节码角度解释了一遍,感觉有点难懂的画看下这个代码就知道了,其实上面的执行步骤可以用代码来解释;
后++,即i=i++相当于
t= i;
i = i+1;
i = t;
而前++也就是j=++j则相当于
i = i+1;
t = i;
i = t;