|
|
在Java中,要建立字串很方便: String str = "Java"; 依Java命名慣例而言,String這個名稱首字大寫,無疑地應該是個類別,這代表了,str是參考至一個String的實例。有些書會說,這個寫法其實等同於以下的寫法,而且這樣有建立物件的明確語義: String str = new String("Java"); 這並不是全部的事實。這句話比較像是: String str1 = "Java"; String str2 = new String(str1); 也就是先前的那行程式碼,其實JVM建立了兩個String實例。這意謂著,直接使用雙引號包括字元來建立字串,以及自行用new關鍵字建立字字串是不同的,這可以由以下的程式碼來驗證: String str1 = "Java"; String str2 = new String(str1); System.out.println(str1 == str2); ==運算子會比較兩個參考名稱是否參考至同一物件,上面的程式片段會印出false,也就是str1與str2是參考至不同物件。直接使用雙引號包括字元來建立字串,JVM會自行在記憶體中使用一個字串池(String pool)來維護,只要雙引號含括的字元內容相同(序列相同,大小寫相同),無論在程式碼中出現幾次,在字串池中都只有一個實例。 下面這段程式碼可以驗證: String str1 = "Java"; String str2 = "Java"; System.out.println(str1 == str2); 這個程式碼片段會印出true,因為雙引號含括的內容都是Java這個字元序列,雖然程式碼中出現了兩次"Java",但在字串池中卻只有一個實例,只不過依程式碼所定義的,被str1與str2所參考著。 一般書籍都會說,要比較字串是否相等要使用equals()方法而不是==,這個意思是指比較字串所含字元序列的相等性,而非參考名稱所參考的記憶體位置相等性。下面這個程式碼會顯示true,因為str1與str2所參考之物件,其所含字元序列都相等: String str1 = "Java"; String str2 = new String(str1); System.out.println(str1.equals(str2)); 每個字串都是不可變動的,這表示你一旦建立字串,就不可以修改它的字元內容。字串的字元內容,是維護在String中的字元陣列中: public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[]; 下面這段程式碼,只不過是將字串內含字元陣列給另一個字串在內部複製或參考: public String(String original) { int size = original.count; char[] originalValue = original.value; char[] v; if (originalValue.length > size) { v = Arrays.copyOfRange(originalValue, off, off+size); } else { v = originalValue; } this.offset = 0; this.count = size; this.value = v; } 其它的字串建構式也是類似的。String上還有個intern()方法,可以讓你將字串放入字串池,或者是從字串池中取得JVM所維護的字串。如果你呼叫它,則會使用equals()方法,比較字串池中是否有字元序列相同的字串,如果有則傳回,如果無則將該字串置入字串池。 所以下面這個程式碼執行結果會是true: String str1 = "Java"; String str2 = new String(str1); System.out.println(str1 == str2.intern()); 在Java中,可以使用+串接字串,例如: String str1 = "Java"; String str2 = "Cool"; String str3 = str1 + str2; System.out.println(str3); 最後顯示的是JavaCool。一應該都會告訴你,用+串接字串很方便,但要小心+會產生新的字串。這也不是全部的事實,因為它產生的更多,如果使用的是JDK5以上,可以實際反組譯看看: String s = "Java"; String s1 = "Cool"; String s2 = (new StringBuilder()).append(s).append(s1).toString(); System.out.println(s2); 如 果是JDK1.4以下,則會在JVM內部產生StringBuffer完成類似的字串附加動作。StringBuilder或StringBuffer, 內部也是使用自動增加的字元陣列來維護,若長度不夠,則會產生新的更長的字元陣列,然後作字元陣列複製的動作。所以若是有頻繁串接字串的動作,例如在迴圈 中串接SQL之類的,會有效能上的隱憂,應當避免。 不過下面這個稍微有點不同: String str = "Java" + "Cool"; System.out.println(str); 執行結果一樣顯示JavaCool,不過反組譯它,你會發現編譯器很聰明: String s = "JavaCool"; System.out.println(s); 既然兩個都是雙引號括著,又直接使用+串接,那你要的不就是"JavaCool"? 附帶一提的是,字串與物件之間也是可以使用+串接的!例如: Map map = new HashMap(); map.put("key", "value"); System.out.println("Map: " + map); 這沒什麼!如果字串與物件使用+串接,其實最後會呼叫物件的toString()取得物件的字串描述,也就是說類似於: Map map = new HashMap(); map.put("key", "value"); System.out.println("Map: " + map.toString()); 這也不完全是事實,上面只是比喻!編譯器(或JVM)作的更多: HashMap hashmap = new HashMap(); hashmap.put("key", "value"); System.out.println( (new StringBuilder()).append("Map: ").append(hashmap).toString()); 至於StringBuilder的append()作了什麼,留待你自行去探索一下它的原始碼吧! 延伸閱讀 String and memory leaks。 |