在Java中字符串是一個常用的東西,而其一個常用的操作是字符串的拼接,Java對此提供了一種非常直觀的操作方式——即 + 操作符。
String str0 = "a";
String str1 = str0 + "b";
如上的程序片段就實現了一個字符串的拼接,可以看到整個描述非常簡潔,一目了然。學習Java的時候,這是接觸到的第一種拼接字符串的方式,先入為主的印象會讓你在以后需要拼接字符串時第一時間想到它,但是,你會聽到大家都說:拼接大量的字符串時使用StringBuilder更好,這是為什么呢?
讓我們看看編譯器是怎么處理 + 操作符的。下面是上一個程序片段編譯后的字節碼指令:
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String a
2: astore_1
3: new #3 // class java/lang/StringBuilder
6: dup
7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
10: aload_1
11: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
14: ldc #6 // String b
16: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22: astore_2
23: return
}
通過閱讀這段字節碼,可以發現,我們的+拼接操作實際上被編譯器理解成了這個樣子:
String str0 = "a";
StringBuilder sb = new StringBuilder();
sb.append(str0).append("b");
String str1 = sb.toString();
中間多出來了一個StringBuilder對象,這是一個臨時對象。
假設我們現在有這樣一段程序片段:
String str0 = "a";
for (int i = 0; i < 10000; i++) {
str0 += "a";
}
再來看看編譯后的字節碼:
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String a
2: astore_1
3: iconst_0
4: istore_2
5: iload_2
6: sipush 10000
9: if_icmpge 38
12: new #3 // class java/lang/StringBuilder
15: dup
16: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
19: aload_1
20: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
23: ldc #2 // String a
25: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: invokevirtual #6 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
31: astore_1
32: iinc 2, 1
35: goto 5
38: return
}
5~35是我們的循環體,在循環體里面,每次拼接都會生成一個StringBuilder的臨時對象,那么這個程序片段執行下去就會產生10000個StringBuilder的臨時對象,這10000個臨時對象都是必要的嗎?顯然不是,我們可以在循環體外直接創建一個StringBuilder對象,然后在循環體中通過append方法拼接字符串,這樣就省下了創建並回收10000個臨時對象的消耗。
所以,在需要拼接大量字符串時,還是使用StringBuilder對象為好。