JVM字节码
通过javap命令查看class文件的字节码内容
创建一个简单的测试类
public class Test1 { public static void main(String[] args) { int a = 2; int b = 5; int c = b-a; System.out.println(c); } }
cmd打开窗口,使用命令
javap ‐v Test01.class > Test01.txt
查看Texst1.xml文件
Classfile /E:/accp/Y2/进阶内容/JVM/jvmTest/JvmTest/JVM_Project1/target/classes/com/zn/Test1.class Last modified 2020-3-10; size 563 bytes MD5 checksum 227f9972c01499bef690d9371d0b0e14 Compiled from "Test1.java" public class com.zn.Test1 minor version: 0 major version: 51 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #5.#23 // java/lang/Object."<init>":()V #2 = Fieldref #24.#25 // java/lang/System.out:Ljava/io/PrintStream; #3 = Methodref #26.#27 // java/io/PrintStream.println:(I)V #4 = Class #28 // com/zn/Test1 #5 = Class #29 // java/lang/Object #6 = Utf8 <init> #7 = Utf8 ()V #8 = Utf8 Code #9 = Utf8 LineNumberTable #10 = Utf8 LocalVariableTable #11 = Utf8 this #12 = Utf8 Lcom/zn/Test1; #13 = Utf8 main #14 = Utf8 ([Ljava/lang/String;)V #15 = Utf8 args #16 = Utf8 [Ljava/lang/String; #17 = Utf8 a #18 = Utf8 I #19 = Utf8 b #20 = Utf8 c #21 = Utf8 SourceFile #22 = Utf8 Test1.java #23 = NameAndType #6:#7 // "<init>":()V #24 = Class #30 // java/lang/System #25 = NameAndType #31:#32 // out:Ljava/io/PrintStream; #26 = Class #33 // java/io/PrintStream #27 = NameAndType #34:#35 // println:(I)V #28 = Utf8 com/zn/Test1 #29 = Utf8 java/lang/Object #30 = Utf8 java/lang/System #31 = Utf8 out #32 = Utf8 Ljava/io/PrintStream; #33 = Utf8 java/io/PrintStream #34 = Utf8 println #35 = Utf8 (I)V { public com.zn.Test1(); 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 3: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/zn/Test1; public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=4, args_size=1 0: iconst_2 1: istore_1 2: iconst_5 3: istore_2 4: iload_2 5: iload_1 6: isub 7: istore_3 8: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 11: iload_3 12: invokevirtual #3 // Method java/io/PrintStream.println:(I)V 15: return LineNumberTable: line 5: 0 line 6: 2 line 7: 4 line 8: 8 line 9: 15 LocalVariableTable: Start Length Slot Name Signature 0 16 0 args [Ljava/lang/String; 2 14 1 a I 4 12 2 b I 8 8 3 c I } SourceFile: "Test1.java"
内容大致分为四个部分:
第一部分:显示了生成这个class的java源文件,版本信息,生成时间等;
第二部分:显示了该类中涉及到常量池,共35个;
第三部分:显示该类的构造器,编译器自动插入的;
第四部分:显示了main方的信息;
常量池
在JDK6.0及之前版本。String Pool里放的都是字符串常量;
在JDK1.7中,由于String #intern()发生了变化,因此String Pool中也可以存放于堆内的字符串对象的引用;
官方文档
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4-140
Constant Type value 说明
描述符
字段描述符
FieldTypeterm Type Interpretation
B byte signed byte
C char Unicode character code point in the Basic Multilingual Plane, encoded with UTF-16
方法描述符
官网:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.3.3
实例
The method descriptor for the method:
Object m(int i, double d, Thread t) {...}
is:
(IDLjava/lang/Thread;)Ljava/lang/Object;
解读方法字节码
public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V //方法描述,V表示该方法的返回值为void flags: ACC_PUBLIC, ACC_STATIC //方法修饰符,public,static的个数 Code: //stack=2,操作栈的大小为2,locals=4,本地变量表大小,args_size=1,参数的个数 stack=2, locals=4, args_size=1 0: iconst_2 //将数字2值压入擦作栈,位于栈的最上面 1: istore_1 //从操作栈中弹出一个元素(数字2),放入到本地变量表中,位于下标为1的位置(下标为0的是this) 2: iconst_5 //将数据5值压入操作栈,位于栈的最上面 3: istore_2 //操作栈中国弹出一个元素(5),放到本地变量表中,位于下标为2的位置 4: iload_2 //将本地变量表中下标为2的位置元素压入操作栈(5) 5: iload_1 //将本地变量表中下标为1的位置元素压入操作栈(2) 6: isub //操作栈中的2个数字相减 7: istore_3 8: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 通过#2号找到对应的常量,即可找到对应的引用 11: iload_3 12: invokevirtual #3 // Method java/io/PrintStream.println:(I)V 通过#3号找到对应的常量,即可找到对应的引用,进行方法调用 15: return LineNumberTable: line 5: 0 line 6: 2 line 7: 4 line 8: 8 line 9: 15 LocalVariableTable: //本地变量表 Start Length Slot Name Signature 0 16 0 args [Ljava/lang/String; 2 14 1 a I 4 12 2 b I 8 8 3 c I } SourceFile: "Test01.java"
图解
研究i++和++i的不同
都知道,i++表示,先返回在+1;++i表示,先+1在返回;
测试代码:
public class Test02 { public static void main(String[] args) { new Test02().method01(); new Test02().method02(); } public void method01(){ int i=1; int a=i++; System.out.println(a); } public void method02(){ int i=1; int a=++i; System.out.println(a); } }
查看class字节码
javap -v Test02.class > Test02.txt
Last modified 2020-3-9; size 792 bytes MD5 checksum cfa985a5953fb3c7a88de6d9fa95bdac Compiled from "Test02.java" public class com.wn.Test.Test02 minor version: 0 major version: 51 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #8.#27 // java/lang/Object."<init>":()V #2 = Class #28 // com/wn/Test/Test02 #3 = Methodref #2.#27 // com/wn/Test/Test02."<init>":()V #4 = Methodref #2.#29 // com/wn/Test/Test02.method01:()V #5 = Methodref #2.#30 // com/wn/Test/Test02.method02:()V #6 = Fieldref #31.#32 // java/lang/System.out:Ljava/io/PrintStream; #7 = Methodref #33.#34 // java/io/PrintStream.println:(I)V #8 = Class #35 // java/lang/Object #9 = Utf8 <init> #10 = Utf8 ()V #11 = Utf8 Code #12 = Utf8 LineNumberTable #13 = Utf8 LocalVariableTable #14 = Utf8 this #15 = Utf8 Lcom/wn/Test/Test02; #16 = Utf8 main #17 = Utf8 ([Ljava/lang/String;)V #18 = Utf8 args #19 = Utf8 [Ljava/lang/String; #20 = Utf8 method01 #21 = Utf8 i #22 = Utf8 I #23 = Utf8 a #24 = Utf8 method02 #25 = Utf8 SourceFile #26 = Utf8 Test02.java #27 = NameAndType #9:#10 // "<init>":()V #28 = Utf8 com/wn/Test/Test02 #29 = NameAndType #20:#10 // method01:()V #30 = NameAndType #24:#10 // method02:()V #31 = Class #36 // java/lang/System #32 = NameAndType #37:#38 // out:Ljava/io/PrintStream; #33 = Class #39 // java/io/PrintStream #34 = NameAndType #40:#41 // println:(I)V #35 = Utf8 java/lang/Object #36 = Utf8 java/lang/System #37 = Utf8 out #38 = Utf8 Ljava/io/PrintStream; #39 = Utf8 java/io/PrintStream #40 = Utf8 println #41 = Utf8 (I)V { public com.wn.Test.Test02(); 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 3: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/wn/Test/Test02; public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 0: new #2 // class com/wn/Test/Test02 3: dup 4: invokespecial #3 // Method "<init>":()V 7: invokevirtual #4 // Method method01:()V 10: new #2 // class com/wn/Test/Test02 13: dup 14: invokespecial #3 // Method "<init>":()V 17: invokevirtual #5 // Method method02:()V 20: return LineNumberTable: line 5: 0 line 6: 10 line 7: 20 LocalVariableTable: Start Length Slot Name Signature 0 21 0 args [Ljava/lang/String; public void method01(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=3, args_size=1 0: iconst_1 1: istore_1 2: iload_1 3: iinc 1, 1 6: istore_2 7: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream; 10: iload_2 11: invokevirtual #7 // Method java/io/PrintStream.println:(I)V 14: return LineNumberTable: line 9: 0 line 10: 2 line 11: 7 line 12: 14 LocalVariableTable: Start Length Slot Name Signature 0 15 0 this Lcom/wn/Test/Test02; 2 13 1 i I 7 8 2 a I public void method02(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=3, args_size=1 0: iconst_1 1: istore_1 2: iinc 1, 1 5: iload_1 6: istore_2 7: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream; 10: iload_2 11: invokevirtual #7 // Method java/io/PrintStream.println:(I)V 14: return LineNumberTable: line 14: 0 line 15: 2 line 16: 7 line 17: 14 LocalVariableTable: Start Length Slot Name Signature 0 15 0 this Lcom/wn/Test/Test02; 2 13 1 i I 7 8 2 a I } SourceFile: "Test02.java"
对比
i++;
public void method01(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=3, args_size=1 0: iconst_1 //将数字1压入到操作栈 1: istore_1 //将数字1从操作栈弹出,压入到本地变量表中,下标为1 2: iload_1 //将本地变量表中获取下标为1的数据,压入到操作栈中 3: iinc 1, 1 //将本地变量中的1,在+1 6: istore_2 //将数字1从操作栈弹出,压入到本地变量表中,下标为2 7: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream; 10: iload_2 //从本地变量表中获取下标为2的数据,压入到操作栈中 11: invokevirtual #7 // Method java/io/PrintStream.println:(I)V 14: return LineNumberTable: line 9: 0 line 10: 2 line 11: 7 line 12: 14 LocalVariableTable: Start Length Slot Name Signature 0 15 0 this Lcom/wn/Test/Test02; 2 13 1 i I 7 8 2 a I
++i;
public void method02(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=3, args_size=1 0: iconst_1 //将数字1压入到操作栈中 1: istore_1 //将数字1从操作栈弹出,压入到本地变量表中,下标为1 2: iinc 1, 1 //将本地变量中的1,在+1 5: iload_1 //从本地变量表中获取下标为1的数据,压入到操作栈中 6: istore_2 //将数据2从操作栈弹出,压入到本地变量表中,下标为2 7: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream; 10: iload_2 //将本地变量表中获取下标为2的数据,压入到操作栈中 11: invokevirtual #7 // Method java/io/PrintStream.println:(I)V 14: return LineNumberTable: line 14: 0 line 15: 2 line 16: 7 line 17: 14 LocalVariableTable: Start Length Slot Name Signature 0 15 0 this Lcom/wn/Test/Test02; 2 13 1 i I 7 8 2 a I
区别
i++:
只是在本地变量中对数字做了相加,并没有将数据压入操作栈;
将前面拿到的数字1,再次从操作栈中拿到,压入到本地变量中;
++i:
将本地变量中的数字做了相加,并且将数据压入到操作栈;
将操作栈中的数据,再次压入到本地变量中;
小结:可以通过查看字节码的方式对代码的底层做研究,探究其原理;
字符串拼接
常见的字符串拼接方式
字符串的拼接在开发过程中使用是非常频繁的,常用的方式有三种:
+号拼接: str+"456"
StringBuilder拼接
StringBuffer拼接
StringBuffer是保证线程安全的,效率是比较低的,我们更多的是使用场景是不会涉及到
线程安全的问题的,所以更多的时候会选择StringBuilder,效率会高一些。
测试代码
public class Test03 { public static void main(String[] args) { new Test03().m1(); new Test03().m2(); } public void m1(){ String s1="123"; String s2="456"; String s3=s1+s2; System.out.println(s3); } public void m2(){ String s1="123"; String s2="456"; StringBuilder sb=new StringBuilder(); sb.append(s1); sb.append(s2); String s3=sb.toString(); System.out.println(s3); } }
查看class字节码
javap -v Test03.class > Test03.txt
Last modified 2020-3-9; size 1111 bytes MD5 checksum 2f52301c461e860c7985882234cbdd5d Compiled from "Test03.java" public class com.wn.Test.Test03 minor version: 0 major version: 51 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #14.#36 // java/lang/Object."<init>":()V #2 = Class #37 // com/wn/Test/Test03 #3 = Methodref #2.#36 // com/wn/Test/Test03."<init>":()V #4 = Methodref #2.#38 // com/wn/Test/Test03.m1:()V #5 = Methodref #2.#39 // com/wn/Test/Test03.m2:()V #6 = String #40 // 123 #7 = String #41 // 456 #8 = Class #42 // java/lang/StringBuilder #9 = Methodref #8.#36 // java/lang/StringBuilder."<init>":()V #10 = Methodref #8.#43 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #11 = Methodref #8.#44 // java/lang/StringBuilder.toString:()Ljava/lang/String; #12 = Fieldref #45.#46 // java/lang/System.out:Ljava/io/PrintStream; #13 = Methodref #47.#48 // java/io/PrintStream.println:(Ljava/lang/String;)V #14 = Class #49 // java/lang/Object #15 = Utf8 <init> #16 = Utf8 ()V #17 = Utf8 Code #18 = Utf8 LineNumberTable #19 = Utf8 LocalVariableTable #20 = Utf8 this #21 = Utf8 Lcom/wn/Test/Test03; #22 = Utf8 main #23 = Utf8 ([Ljava/lang/String;)V #24 = Utf8 args #25 = Utf8 [Ljava/lang/String; #26 = Utf8 m1 #27 = Utf8 s1 #28 = Utf8 Ljava/lang/String; #29 = Utf8 s2 #30 = Utf8 s3 #31 = Utf8 m2 #32 = Utf8 sb #33 = Utf8 Ljava/lang/StringBuilder; #34 = Utf8 SourceFile #35 = Utf8 Test03.java #36 = NameAndType #15:#16 // "<init>":()V #37 = Utf8 com/wn/Test/Test03 #38 = NameAndType #26:#16 // m1:()V #39 = NameAndType #31:#16 // m2:()V #40 = Utf8 123 #41 = Utf8 456 #42 = Utf8 java/lang/StringBuilder #43 = NameAndType #50:#51 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #44 = NameAndType #52:#53 // toString:()Ljava/lang/String; #45 = Class #54 // java/lang/System #46 = NameAndType #55:#56 // out:Ljava/io/PrintStream; #47 = Class #57 // java/io/PrintStream #48 = NameAndType #58:#59 // println:(Ljava/lang/String;)V #49 = Utf8 java/lang/Object #50 = Utf8 append #51 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder; #52 = Utf8 toString #53 = Utf8 ()Ljava/lang/String; #54 = Utf8 java/lang/System #55 = Utf8 out #56 = Utf8 Ljava/io/PrintStream; #57 = Utf8 java/io/PrintStream #58 = Utf8 println #59 = Utf8 (Ljava/lang/String;)V { public com.wn.Test.Test03(); 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 3: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/wn/Test/Test03; public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 0: new #2 // class com/wn/Test/Test03 3: dup 4: invokespecial #3 // Method "<init>":()V 7: invokevirtual #4 // Method m1:()V 10: new #2 // class com/wn/Test/Test03 13: dup 14: invokespecial #3 // Method "<init>":()V 17: invokevirtual #5 // Method m2:()V 20: return LineNumberTable: line 5: 0 line 6: 10 line 7: 20 LocalVariableTable: Start Length Slot Name Signature 0 21 0 args [Ljava/lang/String; public void m1(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=4, args_size=1 0: ldc #6 // String 123 2: astore_1 3: ldc #7 // String 456 5: astore_2 6: new #8 // class java/lang/StringBuilder 9: dup 10: invokespecial #9 // Method java/lang/StringBuilder."<init>":()V 13: aload_1 14: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 17: aload_2 18: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 21: invokevirtual #11 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 24: astore_3 25: getstatic #12 // Field java/lang/System.out:Ljava/io/PrintStream; 28: aload_3 29: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 32: return LineNumberTable: line 9: 0 line 10: 3 line 11: 6 line 12: 25 line 13: 32 LocalVariableTable: Start Length Slot Name Signature 0 33 0 this Lcom/wn/Test/Test03; 3 30 1 s1 Ljava/lang/String; 6 27 2 s2 Ljava/lang/String; 25 8 3 s3 Ljava/lang/String; public void m2(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=5, args_size=1 0: ldc #6 // String 123 2: astore_1 3: ldc #7 // String 456 5: astore_2 6: new #8 // class java/lang/StringBuilder 9: dup 10: invokespecial #9 // Method java/lang/StringBuilder."<init>":()V 13: astore_3 14: aload_3 15: aload_1 16: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 19: pop 20: aload_3 21: aload_2 22: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 25: pop 26: aload_3 27: invokevirtual #11 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 30: astore 4 32: getstatic #12 // Field java/lang/System.out:Ljava/io/PrintStream; 35: aload 4 37: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 40: return LineNumberTable: line 15: 0 line 16: 3 line 17: 6 line 18: 14 line 19: 20 line 20: 26 line 21: 32 line 22: 40 LocalVariableTable: Start Length Slot Name Signature 0 41 0 this Lcom/wn/Test/Test03; 3 38 1 s1 Ljava/lang/String; 6 35 2 s2 Ljava/lang/String; 14 27 3 sb Ljava/lang/StringBuilder; 32 9 4 s3 Ljava/lang/String; } SourceFile: "Test03.java"
从字节码中可以看出,m1()方法源码中是使用+好拼接,但是字节码中也被编译成了StringBuilder方式;
所以可以得出结论,字符串拼接,+号和StringBuilder是相等的,效率一样;
测试代码2
public class Test04 { public static void main(String[] args) { new Test04().m1(); new Test04().m2(); } public void m1(){ String str=""; for (int i=0;i<5;i++){ str=str+i; } System.out.println(str); } public void m2(){ StringBuilder sb=new StringBuilder(); for (int i=0;i<5;i++){ sb.append(i); } System.out.println(sb.toString()); } }
m1()和m2哪个方法的效率高?
查看class字节码2
Last modified 2020-3-9; size 1189 bytes MD5 checksum 40930ac74643e9d0a1d5942e3767043e Compiled from "Test04.java" public class com.wn.Test.Test04 minor version: 0 major version: 51 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #14.#39 // java/lang/Object."<init>":()V #2 = Class #40 // com/wn/Test/Test04 #3 = Methodref #2.#39 // com/wn/Test/Test04."<init>":()V #4 = Methodref #2.#41 // com/wn/Test/Test04.m1:()V #5 = Methodref #2.#42 // com/wn/Test/Test04.m2:()V #6 = String #43 // #7 = Class #44 // java/lang/StringBuilder #8 = Methodref #7.#39 // java/lang/StringBuilder."<init>":()V #9 = Methodref #7.#45 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #10 = Methodref #7.#46 // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; #11 = Methodref #7.#47 // java/lang/StringBuilder.toString:()Ljava/lang/String; #12 = Fieldref #48.#49 // java/lang/System.out:Ljava/io/PrintStream; #13 = Methodref #50.#51 // java/io/PrintStream.println:(Ljava/lang/String;)V #14 = Class #52 // java/lang/Object #15 = Utf8 <init> #16 = Utf8 ()V #17 = Utf8 Code #18 = Utf8 LineNumberTable #19 = Utf8 LocalVariableTable #20 = Utf8 this #21 = Utf8 Lcom/wn/Test/Test04; #22 = Utf8 main #23 = Utf8 ([Ljava/lang/String;)V #24 = Utf8 args #25 = Utf8 [Ljava/lang/String; #26 = Utf8 m1 #27 = Utf8 i #28 = Utf8 I #29 = Utf8 str #30 = Utf8 Ljava/lang/String; #31 = Utf8 StackMapTable #32 = Class #53 // java/lang/String #33 = Utf8 m2 #34 = Utf8 sb #35 = Utf8 Ljava/lang/StringBuilder; #36 = Class #44 // java/lang/StringBuilder #37 = Utf8 SourceFile #38 = Utf8 Test04.java #39 = NameAndType #15:#16 // "<init>":()V #40 = Utf8 com/wn/Test/Test04 #41 = NameAndType #26:#16 // m1:()V #42 = NameAndType #33:#16 // m2:()V #43 = Utf8 #44 = Utf8 java/lang/StringBuilder #45 = NameAndType #54:#55 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #46 = NameAndType #54:#56 // append:(I)Ljava/lang/StringBuilder; #47 = NameAndType #57:#58 // toString:()Ljava/lang/String; #48 = Class #59 // java/lang/System #49 = NameAndType #60:#61 // out:Ljava/io/PrintStream; #50 = Class #62 // java/io/PrintStream #51 = NameAndType #63:#64 // println:(Ljava/lang/String;)V #52 = Utf8 java/lang/Object #53 = Utf8 java/lang/String #54 = Utf8 append #55 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder; #56 = Utf8 (I)Ljava/lang/StringBuilder; #57 = Utf8 toString #58 = Utf8 ()Ljava/lang/String; #59 = Utf8 java/lang/System #60 = Utf8 out #61 = Utf8 Ljava/io/PrintStream; #62 = Utf8 java/io/PrintStream #63 = Utf8 println #64 = Utf8 (Ljava/lang/String;)V { public com.wn.Test.Test04(); 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 3: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/wn/Test/Test04; public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 0: new #2 // class com/wn/Test/Test04 3: dup 4: invokespecial #3 // Method "<init>":()V 7: invokevirtual #4 // Method m1:()V 10: new #2 // class com/wn/Test/Test04 13: dup 14: invokespecial #3 // Method "<init>":()V 17: invokevirtual #5 // Method m2:()V 20: return LineNumberTable: line 5: 0 line 6: 10 line 7: 20 LocalVariableTable: Start Length Slot Name Signature 0 21 0 args [Ljava/lang/String; public void m1(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=3, args_size=1 0: ldc #6 // String 2: astore_1 3: iconst_0 4: istore_2 5: iload_2 6: iconst_5 7: if_icmpge 35 10: new #7 // class java/lang/StringBuilder 13: dup 14: invokespecial #8 // Method java/lang/StringBuilder."<init>":()V 17: aload_1 18: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 21: iload_2 22: invokevirtual #10 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 25: invokevirtual #11 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 28: astore_1 29: iinc 2, 1 32: goto 5 35: getstatic #12 // Field java/lang/System.out:Ljava/io/PrintStream; 38: aload_1 39: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 42: return LineNumberTable: line 9: 0 line 10: 3 line 11: 10 line 10: 29 line 13: 35 line 14: 42 LocalVariableTable: Start Length Slot Name Signature 5 30 2 i I 0 43 0 this Lcom/wn/Test/Test04; 3 40 1 str Ljava/lang/String; StackMapTable: number_of_entries = 2 frame_type = 253 /* append */ offset_delta = 5 locals = [ class java/lang/String, int ] frame_type = 250 /* chop */ offset_delta = 29 public void m2(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=3, args_size=1 0: new #7 // class java/lang/StringBuilder 3: dup 4: invokespecial #8 // Method java/lang/StringBuilder."<init>":()V 7: astore_1 8: iconst_0 9: istore_2 10: iload_2 11: iconst_5 12: if_icmpge 27 15: aload_1 16: iload_2 17: invokevirtual #10 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 20: pop 21: iinc 2, 1 24: goto 10 27: getstatic #12 // Field java/lang/System.out:Ljava/io/PrintStream; 30: aload_1 31: invokevirtual #11 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 34: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 37: return LineNumberTable: line 16: 0 line 17: 8 line 18: 15 line 17: 21 line 20: 27 line 21: 37 LocalVariableTable: Start Length Slot Name Signature 10 17 2 i I 0 38 0 this Lcom/wn/Test/Test04; 8 30 1 sb Ljava/lang/StringBuilder; StackMapTable: number_of_entries = 2 frame_type = 253 /* append */ offset_delta = 10 locals = [ class java/lang/StringBuilder, int ] frame_type = 250 /* chop */ offset_delta = 16 } SourceFile: "Test04.java"
可以看出,m1()方法中的循环体内,每一次循环都会创建StringBuilder对象,效率低于m2()方法;
小结
使用字节码的方式可以很好的查看代码底层的执行,从而可以看出哪些实现效率高,哪些实现效率低。可以更好的对我们的代码做优化。让程序执行下列更高;