對於創建String對象的機制,在這一過程中涉及的東西還是值得探究一番的。
首先看通過new String對象和直接賦值的方式有什么區別,看如下代碼:
public static void main(String[] args) { String str1 = new String("abc"); String str2 = "abc"; String str3 = new String("abc");
String str4 = "abc"; System.out.println(str1 == str2); System.out.println(str1 == str3); System.out.println(str2 == str3);
System.out.println(str2 == str4); }
結果是:false false false true
我們知道 == 比較的是對象的引用,從代碼以及結果可以看出來這段程序中只有三個對象,str1指向一個對象,str3指向一個對象,str2和str4共同指向一個對象。可是到這里有的同學就會迷惑了,我們知道在java中String類是被final修飾的是不可改變的應該每次都會重新生成一個對象啊。可是在這里對象內容都是"abc",所以程序本身也沒有發生改變。如果發生改變也不一定會重新生成對象。這都和string機制中的字符串緩沖池有關系。
當用new的方法創建一個string對象時會先在字符串緩沖池中找有沒有和新創建的字符串內容相等的對象,如果沒有的話就會在緩沖池新創建一個字符串對象然后再在堆中創建字符串對象,如果緩沖池中已經有了和新創建的字符串內容相等的對象就會直接在堆上新創建對象。如果不用new的方式也是先會看緩沖池中有沒有創建過這個對象,如果沒有創建就在里邊創建一個,如果已經創建了 那新聲明的引用就會直接指向這個對象。
所以main方法的第一行是執行的時候發現緩沖池中並沒有一個內容是"abc"的對象所以先在緩沖池中為創建了一個對象內容是"abc",然后在堆中又創建了一個對象。執行到第二行的時候依然會想去緩沖池中找有沒有內容是"abc"的字符串對象,發現已經有了。因為他不用在堆上創建對象所以直接把str2指向緩沖池中的對象。第三行一樣的道理緩沖池中已經有了所以直接在堆上新建一個就好了。第四行和第二行一樣。所以就出現了false false false true的結果。
再用一段代碼驗證一下:
public static void main(String[] args) { String str1 = new String("abc"); String str2 = "abc"; String str3 = new String("abc"); System.out.println(str1 == str2.intern()); System.out.println(str1 == str3.intern()); System.out.println(str2 == str3.intern()); System.out.println(str2 == str1.intern()); }
結果:false false true true
首先intern()方法:
public String intern()返回字符串對象的規范化表示形式。(這句話到底啥意思我也不太清楚)
當調用 intern 方法時,如果池已經包含一個等於此 String 對象的字符串(該對象由 equals(Object) 方法確定),則返回池中的字符串。否則,將此 String 對象添加到池中,並且返回此 String 對象的引用。 它遵循對於任何兩個字符串 s 和 t,當且僅當 s.equals(t) 為 true 時,s.intern() == t.intern() 才為 true。
也就是說intern()方法返回的字符串對象肯定是池中的對象而且字符串內容和調用該方法的對象的內容一樣。那么結果是false false true true也就不難理解了。str3.intern()和str1.intern()返回的對象就是str2所指向的對象呀。所以我們的結論也得以驗證。
在補充一點關於字符串拼接時的情況:
public static void main(String[] args) { String str1 = "abcd"; String str2 = "ab"; String str3 = "cd"; String str4 = str2 + str3; String str5 = "ab" + "cd"; System.out.println(str1 == str4); System.out.println(str1 == str5); }
結果:false true
這時可能就又會迷惑了,哈哈。這就是程序有意思的地方。
首先說第一個結果是false。str4所指向的對象不應該是緩沖池中的對象嗎?講道理應該是返回true的呀。這就又涉及到字符串拼接的機制了。原來兩個字符串str1, str2的拼接首先會調用 String.valueOf(obj),這個Obj為str1,而String.valueOf(Obj)中的實現是return obj == null ? "null" : obj.toString(), 然后產生StringBuilder, 調用的StringBuilder(str1)構造方法, 把StringBuilder初始化,長度為str1.length()+16。此時的StringBuilder對象是在堆上創建的!, 接下來調用StringBuilder.append(str2), 把第二個字符串拼接進去, 然后調用StringBuilder.toString返回結果。所以會返回false。
而對於第二個結果來說這種拼接方式,jvm會直接把"ab" + "cd" 看成"abcd"。實際上jvm對於這時候的 + (加號)的處理是在編譯期就已經完成了。這時候並沒有涉及到stringbuilder。
