写点什么

字节码角度分析 i++ 和 ++i 的区别

发布于: 2021 年 02 月 24 日

代码

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

发布于: 2021 年 02 月 24 日阅读数: 39
用户头像

因为只会扯淡,还扯不好淡,所有想扯点什么 2017.11.30 加入

还未添加个人简介

评论

发布
暂无评论
字节码角度分析i++和++i的区别