代码
生成字节码时,去掉了 @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_SUPERConstant 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字节码指令收集大全
评论