代码
生成字节码时,去掉了 @Test 注解
package bytecode;
import org.junit.Test;
public class AutoIncrement {
@Test
public void oneAtuoIncrementEnd() {
int i = 0;
int j = i++;
System.out.println("i = " + i + ", j = " + j ); // i = 1, j = 0
}
@Test
public void oneAtuoIncrementPre() {
int i = 0;
int j = ++i;
System.out.println("i = " + i + ", j = " + j ); // i = 1, j = 1
}
@Test
public void twoAtuoIncrementEnd() {
int i = 0;
int j = i++ + i++;
System.out.println("i = " + i + ", j = " + j ); // i = 2, j = 1
}
@Test
public void twoAtuoIncrementPre() {
int i = 0;
int j = ++i + ++i;
System.out.println("i = " + i + ", j = " + j ); // i = 2, j = 1
}
@Test
public void threeAtuoIncrementEnd() {
int i = 0;
int j = i++ + i++ + i++;
System.out.println("i = " + i + ", j = " + j ); // i = 3, j = 3
}
@Test
public void threeAtuoIncrementPre() {
int i = 0;
int j = ++i + ++i + ++i;
System.out.println("i = " + i + ", j = " + j ); // i = 3, j = 6
}
}
复制代码
运行结果
j = i++ -> i = 1, j = 0
j = ++i -> i = 1, j = 1
j = i++ + i++ -> i = 2, j = 1
j = ++i + ++i -> i = 2, j = 3
j = i++ + i++ + i++ -> i = 3, j = 3
j = ++i + ++i + ++i -> i = 3, j = 6
复制代码
字节码分析
j = i++
0: iconst_0 // 将 int 类型数值 0 推送至栈顶
1: istore_1 // 将栈顶 int 型数值 0 存入本地变量表 Slot 1 位置,即存入代码中 i 变量的初始值 0
2: iload_1 // 将本地变量表 Slot 1 位置的 0 推送至栈顶
3: iinc 1, 1 // 将局部变量表的 Slot 1 位置的变量 +1,即 i 加 1,其中,iinc 1(Slot 位置),1 (增加的数值)
6: istore_2 // 将栈顶 int 型数值 0 存入本地变量表 Slot 2 位置,即存入 j 的值为栈顶的 0
j = i++ 的字节码:
先执行 iload_1,将本地变量表 Slot 1 位置的 0 推送至栈顶,此时栈顶为 0
再执行 iinc 1, 1,将局部变量表的 Slot 1 位置的变量 +1,即 i 加 1,Slot 1 的 i 为 1
然后执行 istore_2,将栈顶 int 型数值 0 存入本地变量表 Slot 2 位置,即存入 j 的值为栈顶的 0
综上,j = i++,i 先把当前的值(0)入栈,再执行++操作(iinc,只会修改本地变量表 Slot 1 位置的数据,由 0 变为 1),j 取的是栈顶的数据为未自增之前的数值 0
j = ++i
0: iconst_0 // 将 int 类型数值 0 推送至栈顶
1: istore_1 // 将栈顶 int 型数值 0 存入本地变量表 Slot 1 位置,即存入代码中 i 变量的初始值 0
2: iinc 1, 1 // 将局部变量表的 Slot 1 位置的变量 +1,即 i 加 1,其中,iinc 1(Slot 位置),1(增加的数值)
5: iload_1 // 将本地变量表 Slot 1 位置的 1 推送至栈顶
6: istore_2 // 将栈顶 int 型数值 1 存入本地变量表 Slot 2 位置,即存入 i 变量的初始值 1
j = ++i 的字节码:
先执行 iinc 1, 1,将局部变量表的 Slot 1 位置的变量 +1,即 i 加 1,Slot 1 的 i 为 1
再执行 iload_1,将本地变量表 Slot 1 位置的 1 推送至栈顶,此时栈顶为 1
然后执行 istore_2,将栈顶 int 型数值 1 存入本地变量表 Slot 2 位置,即存入 j 的值为栈顶的 1
综上,j =++ i,先执行++操作(iinc,只会修改本地变量表 Slot 1 位置的数据,由 0 变为 1),再把当前的值(1)入栈,j 取的是栈顶的数据为自增之后的数值 1
j = i++ + i++
0: iconst_0 // 将 int 类型数值 0 推送至栈顶
1: istore_1 // 将栈顶 int 型数值 0 存入本地变量表 Slot 1 位置,即存入代码中 i 变量的初始值 0
2: iload_1 // 将本地变量表 Slot 1 位置的 0 推送至栈顶
3: iinc 1, 1 // 将局部变量表的 Slot 1 位置的变量 +1,即 i 加 1,i 为 1,其中,iinc 1(Slot 位置),1(增加的数值)
6: iload_1 // 将本地变量表 Slot 1 位置的 1 推送至栈顶
7: iinc 1, 1 // 将局部变量表的 Slot 1 位置的变量 +1,即 i 加 1,i 为 2
10: iadd // 将栈顶两 int 类型数相加,结果入栈,此时栈顶的两个数值为 1 和 0,相加结果为 1
j = i++ + i++ 的字节码:
先计算第一个 i++ 的结果,计算完成后,此时 i 的值为 1,栈顶的值为 0
再计算第二个 i++ 的结果,计算完成后,此时 i 的值为 2,栈顶的值为 1
把栈顶两个数值相加,得到 j 的结果 1(0 + 1)
j = ++i + ++i
0: iconst_0 // 将 int 类型数值 0 推送至栈顶
1: istore_1 // 将栈顶 int 型数值 0 存入本地变量表 Slot 1 位置,即存入代码中 i 变量的初始值 0
2: iinc 1, 1 // 将局部变量表的 Slot 1 位置的变量 +1,即 i 加 1,i 为 1
5: iload_1 // 将本地变量表 Slot 1 位置的 1 推送至栈顶,栈顶为 1
6: iinc 1, 1 // 将局部变量表的 Slot 1 位置的变量 +1,即 i 加 1,i 为 2
9: iload_1 // 将本地变量表 Slot 1 位置的 2 推送至栈顶,栈顶为 2
10: iadd // 将栈顶两 int 类型数相加,结果入栈,此时栈顶的两个数值为 2 和 1,相加结果为 3
j = ++i + ++i 的字节码:
先计算第一个 ++i 的结果,计算完成后,此时 i 的值为 1,栈顶的值为 1
再计算第二个 ++i 的结果,计算完成后,此时 i 的值为 2,栈顶的值为 2
把栈顶两个数值相加,得到 j 的结果 3(1 + 2)
同理,可知:
j = i++ + i++ + i++ 的字节码:
先计算第一个 i++ 的结果,计算完成后,此时 i 的值为 1,栈顶的值为 0
再计算第二个 i++ 的结果,计算完成后,此时 i 的值为 2,栈顶的值为 1
再计算第三个 i++ 的结果,计算完成后,此时 i 的值为 3,栈顶的值为 2
把栈顶两个数值相加,再把栈顶两个数值相加,得到 j 的结果 3(0 + 1 + 2)
j = ++i + ++i + ++i 的字节码:
先计算第一个 ++i 的结果,计算完成后,此时 i 的值为 1,栈顶的值为 1
再计算第二个 ++i 的结果,计算完成后,此时 i 的值为 2,栈顶的值为 2
再计算第三个 ++i 的结果,计算完成后,此时 i 的值为 3,栈顶的值为 3
把栈顶两个数值相加,再把栈顶两个数值相加,得到 j 的结果 6(1 + 2 + 3)
完整字节码
Classfile /D:/Study/Code/Tools/src/main/java/bytecode/AutoIncrement.class
Last modified 2021-2-24; size 1579 bytes
MD5 checksum 838cf613c197694d24dfc6fe96035d9a
Compiled from "AutoIncrement.java"
public class bytecode.AutoIncrement
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #12.#31 // java/lang/Object."<init>":()V
#2 = Fieldref #32.#33 // java/lang/System.out:Ljava/io/PrintStream;
#3 = Class #34 // java/lang/StringBuilder
#4 = Methodref #3.#31 // java/lang/StringBuilder."<init>":()V
#5 = String #35 // i =
#6 = Methodref #3.#36 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#7 = Methodref #3.#37 // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
#8 = String #38 // , j =
#9 = Methodref #3.#39 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#10 = Methodref #40.#41 // java/io/PrintStream.println:(Ljava/lang/String;)V
#11 = Class #42 // bytecode/AutoIncrement
#12 = Class #43 // java/lang/Object
#13 = Utf8 <init>
#14 = Utf8 ()V
#15 = Utf8 Code
#16 = Utf8 LineNumberTable
#17 = Utf8 LocalVariableTable
#18 = Utf8 this
#19 = Utf8 Lbytecode/AutoIncrement;
#20 = Utf8 oneAtuoIncrementEnd
#21 = Utf8 i
#22 = Utf8 I
#23 = Utf8 j
#24 = Utf8 oneAtuoIncrementPre
#25 = Utf8 twoAtuoIncrementEnd
#26 = Utf8 twoAtuoIncrementPre
#27 = Utf8 threeAtuoIncrementEnd
#28 = Utf8 threeAtuoIncrementPre
#29 = Utf8 SourceFile
#30 = Utf8 AutoIncrement.java
#31 = NameAndType #13:#14 // "<init>":()V
#32 = Class #44 // java/lang/System
#33 = NameAndType #45:#46 // out:Ljava/io/PrintStream;
#34 = Utf8 java/lang/StringBuilder
#35 = Utf8 i =
#36 = NameAndType #47:#48 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#37 = NameAndType #47:#49 // append:(I)Ljava/lang/StringBuilder;
#38 = Utf8 , j =
#39 = NameAndType #50:#51 // toString:()Ljava/lang/String;
#40 = Class #52 // java/io/PrintStream
#41 = NameAndType #53:#54 // println:(Ljava/lang/String;)V
#42 = Utf8 bytecode/AutoIncrement
#43 = Utf8 java/lang/Object
#44 = Utf8 java/lang/System
#45 = Utf8 out
#46 = Utf8 Ljava/io/PrintStream;
#47 = Utf8 append
#48 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#49 = Utf8 (I)Ljava/lang/StringBuilder;
#50 = Utf8 toString
#51 = Utf8 ()Ljava/lang/String;
#52 = Utf8 java/io/PrintStream
#53 = Utf8 println
#54 = Utf8 (Ljava/lang/String;)V
{
public bytecode.AutoIncrement();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 6: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lbytecode/AutoIncrement;
public void oneAtuoIncrementEnd();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=3, locals=3, args_size=1
0: iconst_0 // 将int类型数值 0 推送至栈顶
1: istore_1 // 将栈顶 int 型数值 0 存入本地变量表 Slot 1 位置,即存入代码中i变量的初始值0
2: iload_1 // 将本地变量表 Slot 1 位置的 0 推送至栈顶
3: iinc 1, 1 // 将局部变量表的 Slot 1 位置的变量 +1,即 i加1,其中,iinc 1(Slot位置),1(增加的数值)
6: istore_2 // 将栈顶 int 型数值 0 存入本地变量表 Slot 2 位置,即存入i变量的初始值0
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
// 常量池中的常量值(int, float, string reference, object reference)入栈,此处入栈字符串 "i = "
17: ldc #5 // String i =
19: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: iload_1 // 将本地变量表 Slot 1 位置的 0 推送至栈顶
23: invokevirtual #7 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
26: ldc #8 // String , j =
28: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
31: iload_2 // 将本地变量表 Slot 2 位置的 1 推送至栈顶
32: invokevirtual #7 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
35: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
38: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
41: return
LineNumberTable:
line 9: 0
line 10: 2
line 11: 7
line 12: 41
LocalVariableTable:
Start Length Slot Name Signature
0 42 0 this Lbytecode/AutoIncrement;
2 40 1 i I
7 35 2 j I
public void oneAtuoIncrementPre();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=3, locals=3, args_size=1
0: iconst_0 // 将int类型数值 0 推送至栈顶
1: istore_1 // 将栈顶 int 型数值 0 存入本地变量表 Slot 1 位置,即存入代码中i变量的初始值0
2: iinc 1, 1 // 将局部变量表的 Slot 1 位置的变量 +1,即 i加1,其中,iinc 1(Slot位置),1(增加的数值)
5: iload_1 // 将本地变量表 Slot 1 位置的 1 推送至栈顶
6: istore_2 // 将栈顶 int 型数值 1 存入本地变量表 Slot 2 位置,即存入i变量的初始值1
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: ldc #8 // String , j =
28: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
31: iload_2
32: invokevirtual #7 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
35: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
38: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
41: return
LineNumberTable:
line 16: 0
line 17: 2
line 18: 7
line 19: 41
LocalVariableTable:
Start Length Slot Name Signature
0 42 0 this Lbytecode/AutoIncrement;
2 40 1 i I
7 35 2 j I
public void twoAtuoIncrementEnd();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=3, locals=3, args_size=1
0: iconst_0 // 将int类型数值 0 推送至栈顶
1: istore_1 // 将栈顶 int 型数值 0 存入本地变量表 Slot 1 位置,即存入代码中i变量的初始值0
2: iload_1 // 将本地变量表 Slot 1 位置的 0 推送至栈顶
3: iinc 1, 1 // 将局部变量表的 Slot 1 位置的变量 +1,即 i加1,i为1,其中,iinc 1(Slot位置),1(增加的数值)
6: iload_1 // 将本地变量表 Slot 1 位置的 1 推送至栈顶
7: iinc 1, 1 // 将局部变量表的 Slot 1 位置的变量 +1,即 i加1,i为2
10: iadd // 将栈顶两int类型数相加,结果入栈,此时栈顶的两个数值为1和0,相加结果为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: ldc #8 // String , j =
33: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36: iload_2
37: invokevirtual #7 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
40: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
43: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
46: return
LineNumberTable:
line 23: 0
line 24: 2
line 25: 12
line 26: 46
LocalVariableTable:
Start Length Slot Name Signature
0 47 0 this Lbytecode/AutoIncrement;
2 45 1 i I
12 35 2 j I
public void twoAtuoIncrementPre();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=3, locals=3, args_size=1
0: iconst_0 // 将int类型数值 0 推送至栈顶
1: istore_1 // 将栈顶 int 型数值 0 存入本地变量表 Slot 1 位置,即存入代码中i变量的初始值0
2: iinc 1, 1 // 将局部变量表的 Slot 1 位置的变量 +1,即 i加1,i为1
5: iload_1 // 将本地变量表 Slot 1 位置的 1 推送至栈顶,栈顶为1
6: iinc 1, 1 // 将局部变量表的 Slot 1 位置的变量 +1,即 i加1,i为2
9: iload_1 // 将本地变量表 Slot 1 位置的 2 推送至栈顶,栈顶为2
10: iadd // 将栈顶两int类型数相加,结果入栈,此时栈顶的两个数值为2和1,相加结果为3
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: ldc #8 // String , j =
33: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36: iload_2
37: invokevirtual #7 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
40: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
43: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
46: return
LineNumberTable:
line 30: 0
line 31: 2
line 32: 12
line 33: 46
LocalVariableTable:
Start Length Slot Name Signature
0 47 0 this Lbytecode/AutoIncrement;
2 45 1 i I
12 35 2 j I
public void threeAtuoIncrementEnd();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=3, locals=3, args_size=1
0: iconst_0
1: istore_1
2: iload_1
3: iinc 1, 1
6: iload_1
7: iinc 1, 1
10: iadd
11: iload_1
12: iinc 1, 1
15: iadd
16: istore_2
17: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
20: new #3 // class java/lang/StringBuilder
23: dup
24: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
27: ldc #5 // String i =
29: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
32: iload_1
33: invokevirtual #7 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
36: ldc #8 // String , j =
38: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
41: iload_2
42: invokevirtual #7 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
45: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
48: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
51: return
LineNumberTable:
line 37: 0
line 38: 2
line 39: 17
line 40: 51
LocalVariableTable:
Start Length Slot Name Signature
0 52 0 this Lbytecode/AutoIncrement;
2 50 1 i I
17 35 2 j I
public void threeAtuoIncrementPre();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=3, locals=3, args_size=1
0: iconst_0
1: istore_1
2: iinc 1, 1
5: iload_1
6: iinc 1, 1
9: iload_1
10: iadd
11: iinc 1, 1
14: iload_1
15: iadd
16: istore_2
17: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
20: new #3 // class java/lang/StringBuilder
23: dup
24: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
27: ldc #5 // String i =
29: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
32: iload_1
33: invokevirtual #7 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
36: ldc #8 // String , j =
38: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
41: iload_2
42: invokevirtual #7 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
45: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
48: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
51: return
LineNumberTable:
line 44: 0
line 45: 2
line 46: 17
line 47: 51
LocalVariableTable:
Start Length Slot Name Signature
0 52 0 this Lbytecode/AutoIncrement;
2 50 1 i I
17 35 2 j I
}
SourceFile: "AutoIncrement.java"
复制代码
相关链接
用java字节码解释i++和++i
从字节码的角度分析i++和++i的本质区别
Java字节码指令收集大全
评论