先看两个例子
示例一
public class Test {
public static void main(String[] args) {
int i = 0;
int j = i++;
System.out.println("i=" + i);
System.out.println("j=" + j);
}
}
示例二
public class Test {
public static void main(String[] args) {
int i = 0;
i = i++;
int j = i++;
System.out.println("i=" + i);
System.out.println("j=" + j);
}
}
结果都是
i=1
j=0
怎么解释?我猜你会说:“i先赋值给j,再自增”
额。。。可以这么理解,但代码在实际执行过程中不是这样的。因为在运算符优先级中,++优先于=,所以++会先运行,赋值后运行。
知识储备
这里简单说明,详细解释可网上查阅
1、运算符优先级:++优先于=,所以++会先运行,赋值后运行。
2、Java虚拟机的栈帧
局部变量表:用于存放临时变量,++是在这里面操作的
操作数栈:先进后出,用于数据操作,如加、减、乘与除的计算等等
动态链接:本文用不到,无视
方法返回地址:本文用不到,无视
3、Java虚拟机字节码指令
iconst_<i>:将数据<i>压入【操作数栈】,这里的i是数值
注:
iconst_<i>:i的取值范围是[-1,5]
bipush:[-128,127]
sipush:[-32768,32767]
ldc :[-2147483648,2147483647]
istore_<i>:将【操作数栈】栈顶的数据弹出(弹出后,栈里没有了),并存到【局部变量表】的<i>位置
注:静态变量i从0开始,非静态从1开始,所以后面代码里看到的是1开始的
iload_<i>:将【局部变量表】的<i>位置的数据,加载压入到【操作数栈】
iinc <i>, <j>:将【局部变量表】的<i>位置的数据,加上<j>
注:iinc时,【操作数栈】没有变化
下面进入正题
先用jdk自带的工具把【示例一代码】反编译成字节码。
获取class文件
javac Test.java
反汇编,得到字节码
javap -c Test.class
得到字节码代码
public class com.test.Test {
public com.test.Test();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_0
1: istore_1
2: iload_1
3: iinc 1, 1
6: istore_2
7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
10: new #3 // class java/lang/StringBuilder
13: dup
14: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
17: ldc #5 // String i=
19: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: iload_1
23: invokevirtual #7 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
26: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
29: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
32: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
35: new #3 // class java/lang/StringBuilder
38: dup
39: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
42: ldc #10 // String j=
44: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
47: iload_2
48: invokevirtual #7 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
51: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
54: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
57: return
}
解读前5行
0: iconst_0 》》常量0入【操作数栈】
1: istore_1 》》将【操作数栈】栈顶的数据弹出,并存到【局部变量表】1的位置,此时i=0
2: iload_1 》》将【局部变量表】1位置的数据,压入【操作数栈】,此时,栈顶数据是0
3: iinc 1, 1 》》将【局部变量表】1位置的数据,加1,此时i=1
6: istore_2 》》将【操作数栈】栈顶的数据弹出,并存到【局部变量表】2的位置,此时j=0
图解
示例二的字节码代码
public class com.test.Test {
public com.test.Test();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_0
1: istore_1
2: iload_1
3: iinc 1, 1
6: istore_1
7: iload_1
8: iinc 1, 1
11: istore_2
12: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
15: new #3 // class java/lang/StringBuilder
18: dup
19: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
22: ldc #5 // String i=
24: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
27: iload_1
28: invokevirtual #7 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
31: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
34: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
37: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
40: new #3 // class java/lang/StringBuilder
43: dup
44: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
47: ldc #10 // String j=
49: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
52: iload_2
53: invokevirtual #7 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
56: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
59: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
62: return
}
直接上图解
再来个++i的示例
源码
public class Test {
public static void main(String[] args) {
int i = 0;
int j = ++i;
System.out.println("i=" + i);
System.out.println("j=" + j);
}
}
字节码
public class com.test.Test {
public com.test.Test();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_0
1: istore_1
2: iinc 1, 1
5: iload_1
6: istore_2
7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
10: new #3 // class java/lang/StringBuilder
13: dup
14: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
17: ldc #5 // String i=
19: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: iload_1
23: invokevirtual #7 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
26: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
29: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
32: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
35: new #3 // class java/lang/StringBuilder
38: dup
39: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
42: ldc #10 // String j=
44: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
47: iload_2
48: invokevirtual #7 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
51: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
54: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
57: return
}
解读
0: iconst_0 》》常量0入【操作数栈】
1: istore_1 》》将【操作数栈】栈顶的数据弹出,并存到【局部变量表】1的位置,此时i=0
2: iinc 1, 1 》》将【局部变量表】1位置的数据,加1,此时i=1
5: iload_1 》》将【局部变量表】1位置的数据,压入【操作数栈】,此时,栈顶数据是1
6: istore_2 》》将【操作数栈】栈顶的数据弹出,并存到【局部变量表】2的位置,此时j=1
图解
运行结果
i=1
j=1
希望对你有所帮助!