From Gossip@Openhome

Java Gossip: 不可變的(immutable)字串

一個字串物件一旦被配置,它的內容就是固定不可變的(immutable),例如下面這個宣告:
String str = "caterpillar";

這個宣告會配置一個長度為11的字串物件,您無法改變它的內容;別以為下面這個宣告就是改變一個字串物件的內容:
String str = "just";
str = "justin";

事實上,在這個程式片段中,會有兩個字串物件,一個是"just",長度為4,一個是"justin",長度為6,它們兩個是不同的字串物件,您並不是在 "just"字串後加上"in"字串,而是讓str名稱參考至新的字串物件,如下所示:
原來參考至此
str --------> "just"

重新指定後

str ---------> "justin"
參考新的字串物件

在Java中,使用 = 將一個字串物件指定給一個名稱,其意義為改變名稱的參考物件,原來的字串物件若沒有其它名稱來參考它,就會在適當的時機被Java的「垃圾回收」(Garbage collection)機制回收,在Java中,程式設計人員通常不用關心無用物件的資源釋放問題,Java會檢查物件是否不再被參考,如果沒有任何名稱參考的物件將會被回收。

如果您在程式中使用下面的方式來宣告,則實際上是指向同一個字串物件:
String str1 = "flyweight";
String str2 = "flyweight";
System.out.println(str1 == str2);

程式的執行結果會顯示true,在Java中,會維護一個String Pool,對於一些可以共享的字串物件,會先在String Pool中查找是否存在相同的String內容(字元相同),如果有就直接傳回,而不是直接創造一個新的String物件,以減少記憶體的耗用。

再來個一看例子,String的intern()方法,來看看它的API說明的節錄:
Returns a canonical representation for the string object.

A pool of strings, initially empty, is maintained privately by the class String.

When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.

這段話其實說明了 Flyweight 模式 的運作方式,來用個實例來說明會更清楚:

  • StringIntern.java
public class StringIntern { 
public static void main(String[] args) {
String str1 = "fly";
String str2 = "weight";
String str3 = "flyweight";
String str4;

str4 = str1 + str2;
System.out.println(str3 == str4);

str4 = (str1 + str2).intern();
System.out.println(str3 == str4);
}
}


在程式中第一次比較str3與str4物件是否為同一物件時,您知道結果會是false,而intern()方法會先檢查 String Pool中是否存在字元部份相同的字串物件,如果有的話就傳回,由於程式中之前已經有"flyweight"字串物件,intern()在String Pool中發現了它,所以直接傳回,這時再進行比較,str3與str4所指向的其實是同一物件,所以結果會是true。

注意到了嗎?== 運算在Java中被用來比較兩個名稱是否參考至同一物件,所以不可以用==來比較兩個字串的內容是否相同,例如:
String str1 = new String("caterpillar");
String str2 = new String("caterpillar");
System.out.println(str1 == str2);
 
上面會顯示false的結果,因為str1與str2是分別參考至不同的字串物件,如果要比較兩個(字串)物件是否相同,您要使用equals()方法,例如:
String str1 = new String("caterpillar");
String str2 = new String("caterpillar");
System.out.println(str1.equals(str2));

這樣子結果才會顯示所想要的比較結果:true。