2014-12-05 77 views
1

我讀Jon Skeet's answer about concatenating strings with +。我想知道編譯器是否也識別用StringBuffer/StringBuilder附加constand字符串。編譯器是否爲常量字符串優化了Stringbuffer/StringBuilder.append()?

這對於構建URL代碼具有良好的意願:

StringBuffer sb = new StringBuffer(constant1); 
sb.append(nonconstant); 
sb.append("?"); 
sb.append(constant2); 
sb.append("="); 
sb.append(constant3); 
sb.append("&"); 
sb.append(constant4); 
sb.append("="); 
sb.append(constant5); 

但是,如果的StringBuffer.append()不是由編譯器爲常數優化,我會說下面的代碼會更有效:

StringBuffer sb = new StringBuffer(constant1); 
sb.append(non-constant); 
sb.append("?" + constant2 + "=" + constant3 + "&" + constant4 + "=" + constant5); 

因爲編譯器會在編譯時優化+字符串連接。

+2

嘗試它怎麼樣?編譯這兩個片段,看看編譯如何處理它們。 – Tom 2014-12-05 09:58:58

回答

3

爲什麼不試試呢?在Java 1.7,下面的類的主要方法:

 
public class Concat1 
{ 
    private static final String constant2 = "c2"; 
    private static final String constant3 = "c3"; 

    public void main(String[] args) 
    { 
     StringBuilder sb = new StringBuilder(); 
     sb.append(args[0]); 
     sb.append("?"); 
     sb.append(constant2); 
     sb.append("="); 
     sb.append(constant3); 
     System.out.println(sb.toString()); 
    } 
} 

(I改變的恆定爲了清楚的數目)得到下面的字節代碼:

 
public class Concat1 { 
    public Concat1(); 
    Code: 
     0: aload_0  
     1: invokespecial #1     // Method java/lang/Object."":()V 
     4: return   

    public void main(java.lang.String[]); 
    Code: 
     0: new   #2     // class java/lang/StringBuilder 
     3: dup   
     4: invokespecial #3     // Method java/lang/StringBuilder."":()V 
     7: astore_2  
     8: aload_2  
     9: aload_1  
     10: iconst_0  
     11: aaload   
     12: invokevirtual #4     // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
     15: pop   
     16: aload_2  
     17: ldc   #5     // String ? 
     19: invokevirtual #4     // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
     22: pop   
     23: aload_2  
     24: ldc   #6     // String c2 
     26: invokevirtual #4     // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
     29: pop   
     30: aload_2  
     31: ldc   #7     // String = 
     33: invokevirtual #4     // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
     36: pop   
     37: aload_2  
     38: ldc   #8     // String c3 
     40: invokevirtual #4     // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
     43: pop   
     44: getstatic  #9     // Field java/lang/System.out:Ljava/io/PrintStream; 
     47: aload_2  
     48: invokevirtual #10     // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 
     51: invokevirtual #11     // Method java/io/PrintStream.println:(Ljava/lang/String;)V 
     54: return   
} 

而下面的類:

 
public class Concat2 
{ 
    private static final String constant2 = "c2"; 
    private static final String constant3 = "c3"; 

    public void main(String[] args) 
    { 
     StringBuilder sb = new StringBuilder(); 
     sb.append(args[0]); 
     sb.append("?" + constant2 + "=" + constant3); 
     System.out.println(sb.toString()); 
    } 
} 

被編譯爲:

 
public class Concat2 { 
    public Concat2(); 
    Code: 
     0: aload_0  
     1: invokespecial #1     // Method java/lang/Object."":()V 
     4: return   

    public void main(java.lang.String[]); 
    Code: 
     0: new   #2     // class java/lang/StringBuilder 
     3: dup   
     4: invokespecial #3     // Method java/lang/StringBuilder."":()V 
     7: astore_2  
     8: aload_2  
     9: aload_1  
     10: iconst_0  
     11: aaload   
     12: invokevirtual #4     // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
     15: pop   
     16: aload_2  
     17: ldc   #5     // String ?c2=c3 
     19: invokevirtual #4     // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
     22: pop   
     23: getstatic  #6     // Field java/lang/System.out:Ljava/io/PrintStream; 
     26: aload_2  
     27: invokevirtual #7     // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 
     30: invokevirtual #8     // Method java/io/PrintStream.println:(Ljava/lang/String;)V 
     33: return   
} 

顯然,你是對的。在第二類中,StringBuilder的追加方法只被調用兩次,而在第一種情況下,它被調用每個常量字符串。

0

編譯器是否爲常量字符串優化了Stringbuffer/StringBuilder.append()?

不過,我認爲你提出的優化的前提是不正確。我建議你採用這兩個版本的代碼並編譯它們。然後使用javap來查看編譯代碼在每種情況下的樣子。 (FWIW,我希望你的「優化」不會改進生成的代碼,它很可能會創建第二個StringBuilder來連接中間字符串,並將其轉換爲字符串,最後得到的數字大致相同append操作,外加一個額外的臨時StringBuilder和一個額外的臨時String的創建。)

+0

請參閱參考答案:「所以編譯後的版本:String x =」a「+」b「;應該完全相同:String x =」ab「;」 – 2014-12-15 12:28:12