沒有。因為String被設計成不可變(immutable)類,所以它的所有對象都是不可變對象。請看下列代碼:
String s = "Hello";
s = s + " world!";
s 所指向的對象是否改變了呢?從本系列第一篇的結論很容易匯出這個結論。我們來看看發生了什麼事情。在這段代碼中,s原先指向一個String對象,內容是 "Hello",然後我們對s進行了+操作,那麼s所指向的那個對象是否發生了改變呢?答案是沒有。這時,s不指向原來那個對象了,而指向了另一個 String對象,內容為"Hello world!",原來那個對象還存在於記憶體之中,只是s這個引用變數不再指向它了。
通過上面的說明,我們很容易匯出另一個結論,如果經常對字串進行各種各樣的修改,或者說,不可預見的修改,那麼使用String來代表字串的話會引起很大的記憶體開銷。因為 String對象建立之後不能再改變,所以對於每一個不同的字串,都需要一個String對象來表示。這時,應該考慮使用StringBuffer類,它允許修改,而不是每個不同的字串都要產生一個新的對象。並且,這兩種類的對象轉換十分容易。
同時,我們還可以知道,如果要使用內容相同的字串,不必每次都new一個String。例如我們要在構造器中對一個名叫s的String引用變數進行初始化,把它設定為初始值,應當這樣做:
public class Demo {
private String s;
...
public Demo {
s = "Initial Value";
}
...
}
而非
s = new String("Initial Value");
後者每次都會調用構造器,產生新對象,效能低下且記憶體開銷大,並且沒有意義,因為String對象不可改變,所以對於內容相同的字串,只要一個String對象來表示就可以了。也就說,多次調用上面的構造器建立多個對象,他們的String類型屬性s都指向同一個對象。
上面的結論還基於這樣一個事實:對於字串常量,如果內容相同,Java認為它們代表同一個String對象。而用關鍵字new調用構造器,總是會建立一個新的對象,無論內容是否相同。
至於為什麼要把String類設計成不可變類,是它的用途決定的。其實不只String,很多Java標準類庫中的類都是不可變的。在開發一個系統的時候,我們有時候也需要設計不可變類,來傳遞一組相關的值,這也是物件導向思想的體現。不可變類有一些優點,比如因為它的對象是唯讀,所以多線程並發訪問也不會有任何問題。當然也有一些缺點,比如每個不同的狀態都要一個對象來代表,可能會造成效能上的問題。所以Java標準類庫還提供了一個可變版本,即 StringBuffer。