2011-04-13 149 views
2

添加字符串文字和字符串對象有什麼區別?添加字符串文字和字符串對象的區別

例如

String s1 ="hello"; 
    String s2 ="hello1"; 
    String s3 ="hello" + "hello1"; 
    String s4 ="hellohello1"; 
    String s5 = s1 + s2; 

    System.out.println(s3 == s4); // returns true 
    System.out.println(s3 == s5); // return false 
    System.out.println(s4 == s5); // return false 

爲什麼s3/s4未指向同一位置s5

+1

此效應是由字符串interning引起的,在[本博客](http://javatechniques.com/blog/string-equality-and-interning/)中有深入解釋。 – 2011-04-13 12:36:14

回答

2

因爲s1 + s2不是常量表達式,由於s1s2final,因此,其結果是沒有實習,創建即另一個對象來表示,所以基準比較產生false

JLS 3.10.5 String Literals

字符串文字,或者更一般地,字符串是「實習」,以便共享獨特實例常量表達式(§15.28)-are的值,使用該方法的String.intern 。

JLS 15.28 Constant Expression

編譯時常量表達式是表示原始類型或字符串的一個值,該值不會突然完成並只使用由以下的表達式:

  • ...
  • 引用常量變量的簡單名稱(§4.12.4)。

JLS 4.12.4定義final變量。

如果聲明s1s2finals3 == s5true

+0

好吧,讓你點。這是否意味着s5 = s1 + s2與s5 = new String(s1 + s2)相似; – Kapil 2011-04-13 12:45:15

+0

+1完美遊戲:) @Kapil它與's5 = new StringBuilder(s1).append(s2).toString();' – sfussenegger 2011-04-13 13:11:28

3

因爲您正在比較參考。要比較內容,請使用s1.equals(s2)

如果您的引用比較是故意的,則不清楚爲什麼您希望編譯器/ JVM實習或不實習以不同方式產生的相同字符串。

1

編輯:我假設你知道你是比較引用,而不是字符串的內容。如果不是,s3.equals(s5)是你正在尋找(如已經提到)。

s3已被編譯器優化爲"hellohello1",它也被s4重複使用。 我很驚訝編譯器不夠聰明,但是爲 s5做同樣的事情。您使用的是哪個JDK版本? 。此優化僅適用於常量表達式(請參閱15.28 of Java Language Specification)。換句話說,對非最終變量的任何賦值都會拒絕以後優化的可能性。

這是​​的一個簡單類的輸出,它將您的代碼封裝到一個主要方法中(不是任何人要求它,但我很好奇自己)。讓我們看看這是怎麼回事:

public static void main(java.lang.String[]); 
    Code: 
    0: ldC#16; //String hello 
    2: astore_1 
    3: ldC#18; //String hello1 
    5: astore_2 
    6: ldC#20; //String hellohello1 
    8: astore_3 
    9: ldC#20; //String hellohello1 
    11: astore 4 
    13: new #22; //class java/lang/StringBuilder 
    16: dup 
    17: aload_1 
    18: invokestatic #24; //Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String; 
    21: invokespecial #30; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V 
    24: aload_2 
    25: invokevirtual #33; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
    28: invokevirtual #37; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 
    31: astore 5 
    33: getstatic #41; //Field java/lang/System.out:Ljava/io/PrintStream; 
    36: aload_3 
    37: aload 4 
    39: if_acmpne 46 
    42: iconst_1 
    43: goto 47 
    46: iconst_0 
    47: invokevirtual #47; //Method java/io/PrintStream.println:(Z)V 
    50: getstatic #41; //Field java/lang/System.out:Ljava/io/PrintStream; 
    53: aload_3 
    54: aload 5 
    56: if_acmpne 63 
    59: iconst_1 
    60: goto 64 
    63: iconst_0 
    64: invokevirtual #47; //Method java/io/PrintStream.println:(Z)V 
    67: getstatic #41; //Field java/lang/System.out:Ljava/io/PrintStream; 
    70: aload 4 
    72: aload 5 
    74: if_acmpne 81 
    77: iconst_1 
    78: goto 82 
    81: iconst_0 
    82: invokevirtual #47; //Method java/io/PrintStream.println:(Z)V 
    85: return 

LocalVariableTable: 
    Start Length Slot Name Signature 
    0  86  0  args [Ljava/lang/String; 
    3  83  1  s1  Ljava/lang/String; 
    6  80  2  s2  Ljava/lang/String; 
    9  77  3  s3  Ljava/lang/String; 
    13  73  4  s4  Ljava/lang/String; 
    33  53  5  s5  Ljava/lang/String; 


} 

我不是經驗豐富的閱讀字節碼,但我給它一個去:)

  • 以#開頭的(例如#16)號碼引用常量池。內容始終作爲註釋添加到該行中
  • ldC#16後面跟着astore_1表示「加載常量#16並將其存儲在插槽1中」。正如你所看到的,在開始的時候,第一到第四的時隙會被轉換爲s1,s2,s3和s4(見LocalVariableTable)。
  • 對於s5,沒有詳細描述,顯然在將結果存儲在時隙5(astore 5)之前涉及到StringBuilder並且加載了時隙1(aload_1)和時隙2(aload_2)。
+3

因爲's1'和's2'不是編譯時常量表達式,編譯器**不允許**來優化它。如果這些是「靜態最終」字段,那麼它可以(並且將不得不)進行該優化。 – 2011-04-13 12:36:06

+0

@Joachim謝謝你提醒我。聽到這個前一陣子,但顯然我完全忘了它:) – sfussenegger 2011-04-13 13:09:51

0

因爲編譯器優化了字符串的連接。

在實踐中,這應該不是問題(大部分時間),因爲您通常要使用equals方法比較字符串是否相等,而不是檢查對象引用是否相同。

另外請注意,你可以實習生S5使用例如爲:

s5 = s5.intern(); 

這是很少用到,雖然。

相關問題