以前經常在網上看到關於Java字串拼接等方面的討論。看到有些Java開發人員在給新手程式員的建議中類似如下寫道:
不要使用+號拼接字串,要使用StringBuffer或StringBuilder的append()方法來拼接字串。
不過,用+號拼接字串就真的那麼令人討厭,難道使用+號拼接字串就沒有一點可取之處嗎?
通過查閱Java API文檔中關於String類的部分內容,我們可以看到如下片段:
“Java 語言提供對字串串聯符號("+")以及將其他對象轉換為字串的特殊支援。字串串聯是通過 StringBuilder(或 StringBuffer)類及其 append 方法實現的。字串轉換是通過 toString 方法實現的,該方法由 Object 類定義,並可被 Java中的所有類繼承。”
這段話很明確地告訴我們,在Java中使用+號拼接字串,實際上使用的就是StringBuffer或StringBuilder及其append方法來實現的。
除了Java API文檔,我們還可以使用工具查看class類檔案的位元組碼命令來得到上述答案。 例如代碼:
public static void main(String[] args) { String a = "Hello"; String b = " world"; String str = a + b + " !"; System.out.println(str);}
通過工具查看到其對應的位元組碼命令如下:
從位元組碼命令中,我們可以清楚地看到,我們編寫的如下代碼
String str = a + b + " !";
被編譯器轉換成了類似如下語句:
String str = new StringBuilder(String.valueOf(a)).append(b).append(" !").toString();
不僅如此,Java的編譯器也是一個比較聰明的編譯器,當+號拼接的全部是字串字面量時,Java的編譯器將會在編譯時間智能地將其轉換為一個完整的字串。例如:
public static void main(String[] args) { String str = "Hello" + " world" + ", Java!"; System.out.println(str);}
Java編譯器直接將這種全是字面量的字串拼接,在編譯時間就轉換為了一個完整的字串。
就算+號拼接的字串中存在變數,Java編譯器也會將最前面的字串字面量合并為一個字串。
public static void main(String[] args) { String java = ", Java!"; String str = "Hello" + " world" + java; System.out.println(str);}
從上述可知,對於類似String str = str1 + str2 + str3 + str4,這種將多個字串一次性拼接的操作,使用+號來進行拼接是完全沒有問題的。
在Java中,String對象是不可變的(Immutable)。在代碼中,可以建立多個某一個String對象的別名。但是這些別名都是的引用是相同的。
比如s1和s2都是”droidyue.com”對象的別名,別名儲存著到真實對象的引用。所以s1 = s2
String s1 = "droidyue.com";String s2 = s1;System.out.println("s1 and s2 has the same reference =" + (s1 == s2));
並且在Java中,唯一被重載的運算子就是字串的拼接相關的。+,+=。除此之外,Java設計者不允許重載其他的運算子。
在Java中,唯一被重載的運算子就是字串的拼接相關的。+,+=。除此之外,Java設計者不允許重載其他的運算子。
眾所周知,在Java 1.4版本之前,字串拼接可以使用StringBuffer,從Java 1.5開始,我們可以使用StringBuilder來拼接字串。StringBuffer和StringBuilder的主要區別在於:StringBuffer是安全執行緒的,適用於多線程操作字串;StringBuilder是線程不安全的,適合單線程下操作字串。不過,我們的大多數字串拼接操作都是在單線程下進行的,因此使用StringBuilder有利於提高效能。
在Java 1.4之前,編譯器使用StringBuffer來處理+號拼接的字串;從Java 1.5開始,編譯器大多數情況下都使用StringBuilder來處理+號拼接的字串。
當我們在JDK 1.4的環境下編寫代碼時,對於上述這種一次性拼接多個字串的情況,建議最好使用+號來處理。這樣,當JDK 升級到1.5及以上版本時,編譯器將會自動將其轉換為StringBuilder來拼接字串,從而提高字串拼接效率。
當然,推薦使用+號拼接字串也僅限於在一條語句中拼接多個字串時使用。如果分散在多條語句中拼接一個字串,仍然建議使用StringBuffer或StringBuilder。 例如:
public static void main(String[] args) { String java = ", Java!"; String str = ""; str += "Hello"; str += " world"; str += java; System.out.println(str);}
編譯器編譯後的位元組命令如下:
從上面的圖片中我們可以知道,每一條+號拼接語句,都建立了一個新的StringBuilder對象。這種情況在迴圈條件下表現得尤其明顯,造成了相對較大的效能損耗。因此,在多條語句中拼接字串,強烈建議使用StringBuffer或StringBuilder來處理。
關於使用StringBuilder帶來的最佳化
此外,在使用StringBuffer或StringBuilder的時候,我們還可以使用如下方式,進一步提高效能(下面代碼以StringBuilder為例,StringBuffer與此類似)。
1.預測最終獲得的字串的最大長度。
StringBuilder內部char數組的預設長度為16,當我們append追加字串後超過此長度時,StringBuilder會擴大內部的數組容量以滿足需要。在這個過程中,StringBuilder會建立一個新的較大容量的char數組,並將原數組中的資料複製到新數組中。如果我們能夠大致預測到最終拼接得到的字串的最大長度,就可以在建立StringBuilder對象時指定合適大小的初始容量。例如,我們需要拼接獲得含有100個字母a的字串。即可編寫如下代碼:
StringBuilder sb = new StringBuilder(100);for (int i = 0; i < 100; i++) { sb.append('a');}System.out.println(sb);
請根據實際情況進行平衡,以建立適合初始容量的StringBuilder。
2.對於單個字元,儘可能地使用char類型,而不是String類型。
有些時候,我們需要在字串後追加單個字元(例如:a),此時應儘可能地使用
而不是使用: