java中String與StringBuilder/StringBuffer
在java中,String是一個被宣告為final的類,它所儲存的字串是一個常量,一旦定義不可改變(final型別,且是陣列,陣列一旦定義了,其長度便是不可變,是一塊連續的記憶體地址)
private final char value[];
所以String是一個指向常量字串的引用,其大小是不可改變的,所以當給String賦上新值時,會改變該引用所指向的新的字串,而不會改變原來的字串。所以當一個String類經常被修改時,會產生很多的垃圾,浪費記憶體空間,同時也會建立新的字串,所以效率也很低,在這樣的背景下便有了StringBuilder和StringBuffer。
StringBuilder和StringBuffer類似,只不過前者是非執行緒安全,而後者是執行緒安全的,兩者都是用於儲存可變的字串。裡面放置了一個預設大小為16的字元陣列,當要追加的字串加上原有的字串的長度大於這個儲存陣列的長度時便會自動擴充陣列長度,以便容納要追加的內容(兩倍大小擴充)。
接下來,便淺析String在是使用“+”操作符時的一些細節,以便能更好的理解為什麼String效率會比較低。
public class Test {
public static void main(String[] args) {
String s1 = "1";
String s2 = "1";
System.out.println(s1 == s2);
}
}
列印結果為true,這說明了s1和s2都指向了同一塊地址,也就是儲存字串a的字元陣列的地址
public class Test { public static void main(String[] args) { String s1 = "1"; String s2 = "1"; s1 = new String("1"); System.out.println(s1 == s2); } }
列印結果為false,這說明了s1已指向了一塊新的記憶體地址,所以打印出來的是false
打印出來的結果為:public class Test { public static void main(String[] args) { // String s1 = "1"; // String s2 = "1"; // s1 = new String("1"); // System.out.println(s1 == s2); String s3 = "1" + "0"; String s4 = "1" + "0"; System.out.println(s3 == s4); String s5 = "1"; String s6 = "0"; String s7 = s5 + s6; System.out.println(s3 == s7); String s8 = s5 + "0"; System.out.println(s3 == s8); } }
true
false
false
這時因為編譯器做了手腳,它幫我們優化了String在拼接時的一些操作。可以通過javap -c xxx進行反編譯來檢視編譯過程:
首先編譯器自動的把字串1和0合併成為10,而不是建立了一個1和一個0
所以s3和s4指向了同一塊記憶體地址,這塊地址儲存的是10
其次當用s7用s5和s6拼接時,編譯器自動建立了一個StringBuillder類,然後呼叫它的append方法來進行拼接,最後在呼叫toString返回給s7
s8的過程和s7類似
這也就解釋了後面兩個打印出來的結果為什麼是false了,他們都指向由StringBuillder創建出來的String的記憶體地址,而不是s3和s4所指向的地址。
所以當用變數對String進行拼接時都會產生一個StringBuillder類來進行拼接,對於效率要求高的程式來說需要注意這點。