源码:
public class test {
public static void main(String[] args) {
int i=0;
int x=0;
while(i<10){
x=x++;
i++;
}
System.out.println(x);//0
}
}
字节码:
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: iconst_0
1: istore_1
2: iconst_0
3: istore_2
4: iload_1
5: bipush 10
7: if_icmpge 21
10: iload_2
11: iinc 2, 1
14: istore_2
15: iinc 1, 1
18: goto 4
21: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
24: iload_2
25: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
28: return
LineNumberTable:
line 3: 0
line 4: 2
line 5: 4
line 6: 10
line 7: 15
line 9: 21
line 10: 28
LocalVariableTable:
Start Length Slot Name Signature
0 29 0 args [Ljava/lang/String;
2 27 1 i I
4 25 2 x I
StackMapTable: number_of_entries = 2
此处我就不详细讲解指令,若不懂指令意思的请查看我上一篇博客,并且上一篇博客有图解,理解起来也更加容易:从字节码角度分析a++相关问题
1)iconst_0:
- 将常量0入栈
- JVM中 iconst 是一个入栈指令,其作用是用来将 int 类型的数字。
- 取值在 -1 到 5 之间的整数是使用 iconst_xx 来表示的。
- 当取值等于 -1 时,采用 iconst_m1 指令,当取值在 0 到 5 之间时,分别对应 iconst_0、iconst_1、iconst_2、iconst_3、iconst_4、iconst_5 这几个指令。
2)istore_1:
- 将操作数栈顶数据弹出,存入局部变量表的 slot 1,即 i 变量中:i=0;
3)iconst_0: 同上
4)istore_2:
- 将操作数栈顶数据弹出,存入局部变量表的 slot 2,即 x 变量中: x=0;
5)iload_1:
- 将局部变量表的slot 1中的数据读取到操作数栈中,即读取 i 变量的值
6)bipush 10:
- 将 10 压入操作数栈
7)if_icmpge 21:
- 栈顶下一个元素的值是否大于等于栈顶元素值,即 i 是否>=10
- 若 i>=10 为真,则跳转到21号语句执行。
- 若 i>=10 为假,则继续执行下一条语句。
8)iload_2:
- 将局部变量表的slot 2中的数据读取到操作数栈中,即将变量 x 的值0读入栈中。
9)iinc 2,1:
- 将局部变量表中的solt 2中的数据值加1,即变量 x 的值加1:x=1;
- 注意:操作数栈中存储的只是一个值而已,不是变量 x 的引用;所以,局部变量表中
x的值加1,不会影响到操作数栈中的值,即操作数栈中的值不会加1。定义:iinc 指令是直接在局部变量 slot 上进行运算。
10) istore_2:
- 将操作数栈顶数据弹出,存入局部变量表的 slot 2。
- 注意:此时操作数栈顶的元素是第8号语句读入的 x 变量值,即此时栈顶元素的值为0; 但此时局部变量表中的slot 2 的值在第9号语句加1了,即此时 x 的值为1; 将栈顶元素存入局部变量表 slot 2 中,就相当于将 x变量的值重新赋值为0了,即 x=0;
11) iinc 1,1:
- 将局部变量表中的solt 1中的数据值加1,即变量 i 的值加1:i=1;
12) goto 4:
- 跳转到4号语句执行。
综上所述: 只要在第9号语句执行了自增(iinc 2,1),都会在第10号语句将 x 的重新赋值为0。
原理: iinc 指令是直接在局部变量 slot 上进行运算;不会影响到操作数栈中的值。