你的前兩行是幾乎正確。從技術上講,這兩行代碼不會自行創建任何對象 - 字符串文字實際上在編譯時間處理並放置在字節代碼文件中的常量池中,這意味着實際的String
對象是在在你寫的任何代碼運行之前,類首先被加載。因此,如果你要反編譯代碼的前兩行,你會得到這樣的:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #2 // String java
7: putfield #3 // Field s1:Ljava/lang/String;
10: aload_0
11: ldc #2 // String java
13: putfield #4 // Field ss:Ljava/lang/String;
16: return
正如你所看到的,是由兩種線創建任何String
對象。字節碼只是將常數池中已有的String
值(ldc
表示load constant
)賦值給那些變量
接下來的兩行有點不同。它可能更容易弄清楚是怎麼回事,如果你拆分鏈式調用到他們的組成部分:
String s2 = new String("Android");
s2 = s2.intern();
String s3 = new String("java");
s3 = s3.intern();
這被編譯成字節碼的:
0: new #2 // class java/lang/String
3: dup
4: ldc #3 // String Android
6: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V
9: astore_1
10: aload_1
11: invokevirtual #5 // Method java/lang/String.intern:()Ljava/lang/String;
14: astore_1
15: new #2 // class java/lang/String
18: dup
19: ldc #6 // String java
21: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V
24: astore_2
25: aload_2
26: invokevirtual #5 // Method java/lang/String.intern:()Ljava/lang/String;
29: astore_2
30: return
所以你可以看到,new
關鍵字觸發建造一個新的String
對象。然後字符串"Android"
從常量池加載並用於創建字符串。然後將其存儲到一個變量中。緊接着,該變量被取消引用,調用intern()
,並將結果存儲回變量。此代碼與您的代碼之間的唯一區別是String
構建和實習之間的額外存儲/加載。
所以對於每一個s2
和s3
,只創建一個String
對象 - 因此,你只看到兩個方法與<init>
總。所有intern()
所做的檢查是否該字符串已存在於字符串池中,如果存在,則返回該一個引用。
我認爲這是正確理解那個'String pool'的想法。 – RMachnik
所以我所有的假設都是正確的? – sar
*認爲使用'new String'的感覺* – Rogue