下面是常见的字节码指令
1)iconst_0到iconst_5—将int常量0,1,2,3,4,5分别压入到操作数栈顶
(2)iload_0到iload_3—将第0,1,2,3个本地变量的值分别压入到操作数栈顶
(3)istore_0到iload_3—将操作数栈顶的值存入第0,1,2,3个本地变量中
(4)iadd—取出操作数栈顶的两个int型数值相加,并将结果压入栈顶
isub—取出操作数栈顶的两个int型数值相减,并将结果压入栈顶
imul—取出操作数栈顶的两个int型数值相乘,并将结果压入栈顶
idiv—取出操作数栈顶的两个int型数值相除,并将结果压入栈顶
普通的自增或自减不容易出现错误,但有一种特殊的情况:
int a = 1;
a = ++a;
System.out.println(a);
结果为2
int a = 1;
a = ++a;
System.out.println(a);
结果为1
如果按照一般的理解结果应该都为2,那么为什么会出现两种不同的结果呢?这是因为在底层字节码的编写中出现了差异。
Code:
0: iconst_1
1: istore_1
2: iinc 1, 1
5: iload_1
6: istore_1
7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
10: iload_1
11: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
14: return
这是++a的字节码,0:将常量1压入栈顶
1:将栈顶的常量1取出存入本地变量
2:将int型的本地变量自增
5:将本地变量的值压入栈顶
6:将栈顶的数值存入本地变量中
总的来说自增发生在存入栈之前
Code:
0: iconst_2
1: istore_1
2: iload_1
3: iinc 1, 1
6: istore_1
7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
10: iload_1
11: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
14: return
这是a++的字节码,可以明显看出iinc自增发生在存入栈之后,这也就造成了之后将栈顶的数字拿出来给变量时,仍然使用的时自增前的数字。
简单的说就是在对变量进行操作前,会将变量的值交给栈,在操作完成后再将栈中的数据赋给变量。
++a存入栈中的数据为2,而a++存入栈中的数据为1,之后虽然对变量进行了自增,但最后变量获取的值是有栈顶的数据所给予的。而栈顶的数据为1,所以结果也就为1。
再补充一个例题:
int a=2;
a+=a-=a*a;
System.out.println(a);
结果为0
反编译的结果
Code:
0: iconst_2
1: istore_1
2: iload_1
3: iload_1
4: iload_1
5: iload_1
6: imul
7: isub
8: dup
9: istore_1
10: iadd
11: istore_1
12: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
15: iload_1
16: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
19: return
可以看出在进行运算前将所有的变量的值(4个a)压入栈中,之后再进行运算。在运算时a一直为2,所以结果为0.
由此可以看出,在遇到相关的表达式之前,应考虑压入栈中的变量值为多少,因为结果是由操作前栈顶中的数值决定的。