String a = "java"; String b = "java"; System.out.println(a.equals(b));這當然是 true 啦!
那這樣呢?
System.out.println(a == b);false 囉,還用說!
錯,是 true!
Java 為了有限的記憶體空間,在某些條件下會將 String 暫存到 String Pool。
以前 Java 將 String Pool 放在 PermGen 空間,在 Java 7 以後就改放到空間較大的 Heap Space。
使用 String literal 建立的字串就符合這個條件。
String a = "java";執行第一行程式時,String Pool 還是空的,所以 Java 就建立一個 "java" String 物件,並放到 String Pool 裡。
String b = "java";來到第二行程式時,Java 在 String Pool 裡找到了第一行程式建立的 "java" String 物件,就直接拿來用,所以才會出現 a == b 為 true 的情況發生,因為不只值相同,根本就是同一個物件。
那改用 new String 取代 String literal 方式來建立 String 物件,結果是一樣的嗎?
String c = new String("java"); String d = new String("java"); System.out.println(c.equals(d)); System.out.println(c == d); // false結果不一樣,equals 是 true 沒有問題,但是 == 卻是 false。
疑?不是有 String Pool?
很抱歉,new String 不符合剛說的「某些條件」,所以 new String 不會去 Pool 找「值相同」的物件,而是直接建立全新的 "java" String 物件。
還有一點很重要的是,new String 建立的 String 物件不會放到 Pool 裡。
String a = new String("java"); String b = "java"; System.out.println(a == b); // false可以從上面的結果(false)看出來,因為 a 沒放到 Pool 裡,所以 b 等於是全新的。
但 Java 為此留下彈性空間,透過呼叫 String 物件的 intern() 就可以手動將 String 物件放到 String Pool。
String a = new String("java"); String b = a.intern(); String c = "java"; System.out.println(b == c); // true呼叫 intern() 後,Java 將 a 複製一份放到 String Pool,然後回傳這個複製的版本。
因此在 intern() 後透過 String literal 建立的 String 物件,就會是同一個 String 物件。
要注意剛說的,是複製不是直接把 a 放進去,可以比對 a 與 intern() 回傳的 b,就可以知道是不同的物件。
System.out.println(a == b); // false其實剛講的複製只說了一半,正確來說 intern() 是先到 String Pool 找值相同的物件,如果有就拿來用,如果沒有才複製一份放進去。
可以對 a 連續呼叫兩次 intern() 看出端倪。
String a = new String("java"); String b = a.intern(); String c = a.intern(); System.out.println(b == c); // true第一次呼叫 intern() 時,由於沒找到值相同的 String 物件,所以複製一份放進去然後回傳 b。
第二次呼叫 intern() 時,找到值相同的 b 然後回傳得到 c,所以 b == c 為 true。
intern() 的行為是不是和 String literal 很像?先找再建立。
沒錯,Java 偷偷為 String literal 呼叫了 intern()。
----------------------------------------------------------------------
講半天,這些對實務上到底有什麼幫助?
首先,一律用 equals 比較 String 物件的值是否相等,不要用 ==。
這樣就可以避開 String Pool 造成的困擾。
再來,在進行 String 物件串接時,一律用 StringBuilder,不要用 "java" + "8" 這種方式。
因為後者會在 String Pool 增加無謂的暫存,容易耗費記憶體。
----------------------------------------------------------------------
最後總結一下 String literal 與 new String 的差異:
String literal 先從 String Pool 找值相同的 String 物件,new String 則是直接建立全新的 String 物件。
String literal 會自動呼叫 intern(),new String 不會,但可以手動呼叫(誰會做這個事?)。
---
---
---
沒有留言:
張貼留言