ByteCode篇:i++ 与++i 的简单描述
首先看一段代码,使用 i 进行自增:
int i = 0;
i++; //1
++i; //2
int j = i++; //3
int z = ++i; //4
int x = ++i + i++; //5,6
System.out.println(j); //j=2
System.out.println(z); //z=4
System.out.println(x); //x=10
System.out.println(i); //i=6
i 一共自增6次,最后结果为6,以上使用数字1-6分别区分每一次的自增。
以下为javap得出的字节码:
0: iconst_0
1: istore_1
2: iinc 1, 1
5: iinc 1, 1
8: iload_1
9: iinc 1, 1
12: istore_2
13: iinc 1, 1
16: iload_1
17: istore_3
18: iinc 1, 1
21: iload_1
22: iload_1
23: iinc 1, 1
26: iadd
27: istore 4
首先,调用方法会产生栈帧,栈帧中有操作数栈和局部变量表,为便于理解,贴上局部变量表:
LocalVariableTable:
Start Length Slot Name Signature
0 59 0 args [Ljava/lang/String;
2 57 1 i I
13 46 2 j I
18 41 3 z I
29 30 4 x I
局部变量表中slot为1的即为i存储的位置
0: iconst_0
1: istore_1
上图操作将常量0写入到局部变量表1中,即初始赋值i=0;
2: iinc 1, 1
5: iinc 1, 1
上图对应源代码的1和2,可以得出i++ 和 ++i 内部实现都是使用了
iinc slot 1 ,slot代表局部变量表的位置,所以innc实际就是对局部变量表的对应值加1
8: iload_1
9: iinc 1, 1
12: istore_2
13: iinc 1, 1
16: iload_1
17: istore_3
上图8-12对应3,13-17对应4,即:
- i++是先将局部变量表的值加载到操作数栈,再对局部变量表的变量进行自增;
- 而++i是先局部变量表自增,再加载到操作数栈。
i++ 就类似使用了一个temp变量存储i自增前的值;
再看最后的x是如何生成的:
18: iinc 1, 1
21: iload_1
22: iload_1
23: iinc 1, 1
26: iadd
27: istore 4
先++i,此时局部变量表中i为5,操作数栈压栈5
后i++,操作数栈又压栈5,局部变量表i再加1为6
之后iadd操作数栈的两个5,将10写入到slot=4,即x的位置
最后x=10,i=6,结果正确
总结
变量自增的操作只是在局部变量表上进行处理,但若出现赋值操作(需要压栈弹栈)就为以下两种处理:
- i++是先压栈i,再局部变量表i加1
- ++i是先局部变量表i加1,再压栈i(此时i已经增加1)
若只是纯粹对i自增,使用++i和i++就随意了
遗留问题
int i = 0;
int y = i++ + i++;
System.out.println(y);
请分析y=1