我们经常会看到或者听到java代码要如何如何优化,这样写不对,那样写性能不高等等,可是我们如何去伪求真呢?就以 Java 代码性能优化总结文章中的内容例子,来求证一下吧
先热热身
String字符串拼接和StringBuffer一样吗
看看下面这段代码:
1public class TestClass{ 2 3 public String incStringJoint(){ 4 String str = "a"; 5 String str1 = str + "b"; 6 return str1; 7 } 8 9 public String incStringBuffer(){ 10 StringBuffer str = new StringBuffer(3); 11 str.append("a"); 12 str.append("b"); 13 return str.toString(); 14 } 15 16}
抛开其他方面,这两段代码性能一样吗? 带着这个问题,我们去探索一番。
1javac TestClass.java //将java文件编译成class文件 2javap -verbose TestClass.class //反编译class文件
反编译结果:
1{ 2 public TestClass(); 3 descriptor: ()V 4 flags: ACC_PUBLIC 5 Code: 6 stack=1, locals=1, args_size=1 7 0: aload_0 8 1: invokespecial #1 // Method java/lang/Object."<init>":()V 9 4: return 10 LineNumberTable: 11 line 1: 0 12 13 14 public java.lang.String incStringJoint(); // incStringJoint()方法执行情况 15 descriptor: ()Ljava/lang/String; 16 flags: ACC_PUBLIC 17 Code: 18 stack=2, locals=3, args_size=1 19 0: ldc #2 // String a 20 2: astore_1 21 3: new #3 // class java/lang/StringBuilder 22 6: dup 23 7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V 24 10: aload_1 25 11: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 26 14: ldc #6 // String b 27 16: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 28 19: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 29 22: astore_2 30 23: aload_2 31 24: areturn 32 LineNumberTable: //反编译结果对应源码的行数 33 line 4: 0 34 line 5: 3 35 line 6: 23 36 37 public java.lang.String incStringBuffer(); // incStringBuffer()方法执行情况 38 descriptor: ()Ljava/lang/String; 39 flags: ACC_PUBLIC 40 Code: 41 stack=3, locals=2, args_size=1 42 0: new #8 // class java/lang/StringBuffer 43 3: dup 44 4: iconst_3 45 5: invokespecial #9 // Method java/lang/StringBuffer."<init>":(I)V 46 8: astore_1 47 9: aload_1 48 10: ldc #2 // String a 49 12: invokevirtual #10 // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer; 50 15: pop 51 16: aload_1 52 17: ldc #6 // String b 53 19: invokevirtual #10 // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer; 54 22: pop 55 23: aload_1 56 24: invokevirtual #11 // Method java/lang/StringBuffer.toString:()Ljava/lang/String; 57 27: areturn 58 LineNumberTable: //反编译结果对应源码的行数 59 line 10: 0 60 line 11: 9 61 line 12: 16 62 line 13: 23 63} 64SourceFile: "TestClass.java"
我们先大致看一下反编译的结果,可以发现其实String字符串拼接实质上是用StringBuilder的append方法实现,那么我们再看一下StringBuffer和StringBuilder的源码:可以发现,两个类的append方法调用的是同一个方法实现字符串拼接。
1 public AbstractStringBuilder append(String str) { 2 if (str == null) 3 return appendNull(); 4 int len = str.length(); 5 ensureCapacityInternal(count + len); 6 str.getChars(0, len, value, count); 7 count += len; 8 return this; 9 }
这里就有一个问题了,那为什么在字符串拼接的时候,要使用StringBuffer(线程安全)或者使用StringBuilder(线程不安全)呢?
现在我们修改一下代码:
1 public String incStringJoint(){ 2 String str = "a"; 3 String str1 = str + "b"; 4 String str2 = str1 + "c"; 5 return str2; 6 }
多次拼接,看看结果怎么?
1public java.lang.String incStringJoint(); 2 descriptor: ()Ljava/lang/String; 3 flags: ACC_PUBLIC 4 Code: 5 stack=2, locals=4, args_size=1 6 0: ldc #2 // String a 7 2: astore_1 8 3: new #3 // class java/lang/StringBuilder 9 6: dup 10 7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V 11 10: aload_1 12 11: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 13 14: ldc #6 // String b 14 16: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 15 19: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 16 22: astore_2 17 23: new #3 // class java/lang/StringBuilder 18 26: dup 19 27: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V 20 30: aload_2 21 31: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 22 34: ldc #8 // String c 23 36: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 24 39: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 25 42: astore_3 26 43: aload_3 27 44: areturn 28 LineNumberTable: 29 line 4: 0 30 line 5: 3 31 line 6: 23 32 line 7: 43
注意第3行和第23行,new了两个StringBuilder。这就说明,利用String本身进行字符串拼接的时候,每拼接一次,就要new一个StringBuilder,再调用append方法。
new String()到底创建了几个对象
看下面代码:
1public class TestClass{ 2 3 public String incString(){ 4 String str = "a"; 5 return str; 6 } 7 8 public String incNewString(){ 9 String str = new String("a"); 10 return str; 11 } 12 13} 14 15 16 17{ 18 public TestClass(); 19 descriptor: ()V 20 flags: ACC_PUBLIC 21 Code: 22 stack=1, locals=1, args_size=1 23 0: aload_0 24 1: invokespecial #1 // Method java/lang/Object."<init>":()V 25 4: return 26 LineNumberTable: 27 line 1: 0 28 29 public java.lang.String incString(); 30 descriptor: ()Ljava/lang/String; 31 flags: ACC_PUBLIC 32 Code: 33 stack=1, locals=2, args_size=1 34 0: ldc #2 // String a 35 2: astore_1 36 3: aload_1 37 4: areturn 38 LineNumberTable: 39 line 20: 0 40 line 21: 3 41 42 public java.lang.String incNewString(); 43 descriptor: ()Ljava/lang/String; 44 flags: ACC_PUBLIC 45 Code: 46 stack=3, locals=2, args_size=1 47 0: new #3 // class java/lang/String 48 3: dup 49 4: ldc #2 // String a 50 6: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V 51 9: astore_1 52 10: aload_1 53 11: areturn 54 LineNumberTable: 55 line 25: 0 56 line 26: 10 57}
持续更新