2012-10-14 60 views
7

根據JLS(15.28常量表達式)僅包含一個表達式:運行時字符串連接評價

i)Literals of primitive type and literals of type String (§3.10.1, §3.10.2, §3.10.3, 
§3.10.4, §3.10.5) 
or 
ii)Simple names (§6.5.6.1) that refer to constant variables (§4.12.4). 
or 
iii)... 

是一個常量表達式。

現在String s1="a"+"b";爲常量表達式與在編譯時將被評估,以"ab"

所以s1="ab";

[1] 我是正確地說,現在有在字符串池三個對象爲根據上面的語句: - 「A」, 「B」, 「AB」 ???

現在,

final String s="a"; 
final String s1="b"; 
String s2=s+s1; // is also constant expression and get evaluated at compile time. 

上面的代碼將編譯後transaled到s2="a"+"b";

所以s2="ab";將被自動保存在字符串池。

但是,

// note there is no final now. 
String s="a"; 
String s1="b"; 
String s2="a"+"b"; // constant expression. 
String s3=s+s1; // is NOT a constant expression and get evaluated at RUN TIME. 

String s3=s+s1;,代碼將被翻譯成:

s3=new StringBuilder(String.valueOf(s)).append(s1).toString(); 

,將創建一個新的String對象。

因此,s2==s3將出來是假的;

這是否意味着在運行時使用StringBuilder求值的字符串連接的結果不是存儲在字符串池中,而是進入堆(池外)?

+1

請注意,編譯階段的字符串池與運行程序時的字符串池不同。 –

+0

@ThorbjørnRavn Andersen:尼斯點。你可以給我任何鏈接或來源,我可以看到你的觀點的細節。 –

回答

3

從JLS§15.18.1:

15.18.1。字符串連接運算符+

如果只有一個操作數表達式是String類型,則字符串 將對另一個操作數執行轉換(第5.1.11節)以在運行時產生 字符串。

字符串連接的結果是對字符串對象 的引用,它是兩個操作數字符串的串聯。左側操作數的字符 位於新創建的字符串中操作數右側 的字符之前。

String對象是新創建的(§12.5),除非表達式是 編譯時常量表達式(§15.28)。

一個實現可以選擇在一個步驟中執行轉換和連接 以避免創建並丟棄中間字符串對象 。爲了提高重複字符串連接的性能,Java編譯器可以使用StringBuffer類或類似的技術來減少通過評估表達式創建的中間String對象的數量 。

對於原始類型,實現還可以通過直接從原語 類型轉換爲字符串來優化創建包裝對象的 。

所以,

  1. 有常量池中( 「AB」)一個對象。臨時文件沒有保存。
  2. 同樣,常量池中只有「ab」。
  3. 新字符串是一個新的String對象,除非明確實施,否則不會在池中。

這是有益的看一些字節碼:

String sa1 = "a"+ "b"; 

final String sb1 = "a"; 
final String sb2 = "b"; 
String sb3 = sb1 + sb2; 

String sc1 = "a"; 
String sc2 = "b"; 
String sc3 = "a" + "b"; 
String sc4 = sc1 + sc2; 

成爲

Code: 
    0: ldC#2; //String ab 
    2: astore_0 
    3: ldC#2; //String ab 
    5: astore_3 
    6: ldC#3; //String a 
    8: astore 4 
    10: ldC#4; //String b 
    12: astore 5 
    14: ldC#2; //String ab 
    16: astore 6 
    18: new #5; //class java/lang/StringBuilder 
    21: dup 
    22: invokespecial #6; //Method java/lang/StringBuilder."<init>":()V 
    25: aload 4 
    27: invokevirtual #7; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
    30: aload 5 
    32: invokevirtual #7; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
    35: invokevirtual #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 
    38: astore 7 
    40: return 

你可以看到,在在前兩種情況下,「AB」是直接從常量池中裝。在第三部分,我們得到翻譯sc4 = new StringBuilder().append(sc1).append(sc2).toString(),它創建一個新的對象。

0

是否字符串連接的意思結果在運行 評估使用StringBuilder的不是獲取存儲在字符串池,而是去 到堆(室外泳池)

是。那是對的。它會在堆中創建新的對象。

1

我說得沒錯,現在根據上述聲明,在String pool中有三個對象: - 「a」,「b」,「ab」?

不是。你錯了。級聯在編譯時執行,只有「ab」對象將被存儲在字符串池中。

這是否意味着在運行時使用StringBuilder求值的字符串串聯結果不是存儲在字符串池中,而是進入堆(池外)?

是的,您在這一點上是正確的。


總之,字符串文字和編譯時常量字符串表達式的值將被實現(並存儲在字符串池中)。其他字符串連接的結果將不會被攔截......除非您明確地呼叫String.intern()。 (你應該很少這樣做......因爲實習中的琴絃通常會造成更多的傷害而不是好的)。

無論哪種方式,都應該避免使用==來比較字符串。

+0

第一條語句的+1 – Ankur

+0

如果專家說這是一個好主意,我會建議添加一個只調用'String.intern()'的註釋。 –

0

是的,您是正確的,在運行時使用StringBuilder評估的字符串不會存儲在字符串池中。因爲:

  1. 有一個new運營商(在堆爲對象分配新的內存)
  2. 而且我們可以看到形成代碼:

    AbstractStringBuilder(INT容量){ 值=新字符[容量]; }

有在堆上分配一個新的字符數組引用。