public static void main(String[] args) {
String testStr = "test";
String rst = testStr + 1 + "a" + "pig" + 2;
System.out.println(rst);
}
複製代碼
public static void main(String[] args) {
String testStr = "";
for (int i = 0; i < 10; i++) {
testStr += i + "test";
}
System.out.println(testStr);
}
複製代碼
例1
中代碼的class文件獲得以下字節碼錶示:public static Method main:"([Ljava/lang/String;)V"
stack 3 locals 3
{
ldc String "test";
astore_1;
new class java/lang/StringBuilder;
dup;
aload_1;
invokestatic Method java/lang/String.valueOf:"(Ljava/lang/Object;)Ljava/lang/String;";
invokespecial Method java/lang/StringBuilder."<init>":"(Ljava/lang/String;)V";
iconst_1;
invokevirtual Method java/lang/StringBuilder.append:"(I)Ljava/lang/StringBuilder;";
ldc String "a";
invokevirtual Method java/lang/StringBuilder.append:"(Ljava/lang/String;)Ljava/lang/StringBuilder;";
ldc String "pig";
invokevirtual Method java/lang/StringBuilder.append:"(Ljava/lang/String;)Ljava/lang/StringBuilder;";
iconst_2;
invokevirtual Method java/lang/StringBuilder.append:"(I)Ljava/lang/StringBuilder;";
invokevirtual Method java/lang/StringBuilder.toString:"()Ljava/lang/String;";
astore_2;
getstatic Field java/lang/System.out:"Ljava/io/PrintStream;";
aload_2;
invokevirtual Method java/io/PrintStream.println:"(Ljava/lang/String;)V";
return;
}
複製代碼
可見javac自動將這種連續的字符串拼接編譯成了StringBuilder.append,所以寫代碼時不用本身刻意寫成StringBuilder.append形式;例2
中代碼的class文件獲得以下字節碼錶示:public static Method main:"([Ljava/lang/String;)V"
stack 3 locals 3
{
ldc String ""; // 棧上來個空字符串
astore_1; // 棧頂元素彈出,放到局部變量1
iconst_0; // 棧上來個0
istore_2; // 棧頂元素彈出,放到局部變量2
goto L35; // 跳轉到35行,此時棧空
L8: stack_frame_type append;
locals_map class java/lang/String, int;
new class java/lang/StringBuilder; // 棧上來個StringBuilder對象
dup; // 複製棧頂對象,也壓入棧中(StringBuilder對象)
aload_1; // 局部變量1壓入棧中
invokestatic Method java/lang/String.valueOf:"(Ljava/lang/Object;)Ljava/lang/String;"; // 棧頂元素強轉String
invokespecial Method java/lang/StringBuilder."<init>":"(Ljava/lang/String;)V"; // 彈出棧頂2個元素,調用StringBuilder構造函數,剩下一個剛纔複製的、如今已經初始化好的StringBuilder在棧頂
iload_2; // 局部變量2壓入棧中:數字0
invokevirtual Method java/lang/StringBuilder.append:"(I)Ljava/lang/StringBuilder;"; // 數字0 append進棧頂StringBuilder,返回值StringBuilder放回棧頂
ldc String "test"; // 常量"test"壓入棧中
invokevirtual Method java/lang/StringBuilder.append:"(Ljava/lang/String;)Ljava/lang/StringBuilder;"; // 「test」append進棧頂StringBuilder,返回值StringBuilder放回棧頂
invokevirtual Method java/lang/StringBuilder.toString:"()Ljava/lang/String;"; // 棧頂StringBuilder彈出,toString,String結果放入棧頂
astore_1; // String結果彈出放進局部變量1
iinc 2, 1; // 2號局部變量++
L35: stack_frame_type same;
iload_2; // 2號局部變量放到棧頂
bipush 10; // 常量10壓棧,做爲棧頂
if_icmplt L8; // 彈出棧中前兩個數,若是棧中第二個數比棧頂元素小,就跳到第8行,不然繼續往下
getstatic Field java/lang/System.out:"Ljava/io/PrintStream;"; // PrintStream對象壓入棧頂
aload_1; // 局部變量1壓入棧頂(字符串)
invokevirtual Method java/io/PrintStream.println:"(Ljava/lang/String;)V"; // 彈出棧中2個數,以棧中第二個元素做爲參數1(即this),棧頂元素做爲參數2,調用println方法
return;
}
複製代碼
有點長,我稍微加了些註釋以便閱讀:可見javac仍然自動將循環體內的字符串拼接自動編譯成了StringBuilder.append,可是請注意,new class java/lang/StringBuilder;
這條指令出如今了L8
標籤和L35
後面的if_icmplt L8;
跳轉指令之間,也就是說:它在循環體的內部 —— again也就是說 —— 這段代碼被優化成了每次循環都會從新new一個StringBuilder作字符串拼接,循環多少次、就會建立多少個StringBuilder對象;雖說這可能並不是什麼大不了的開銷,但仍然仍是程序員手工把StringBuilder寫在循環體外面、而後循環體內僅僅使用append更划算。因爲現代的javac編譯器可以自動將字符串拼接編譯爲StringBuilder.append的形式,所以平時直接書寫'+'號拼接便可,但若須要經過一個循環來拼接字符串,則最好將StringBuilder顯式地在循環體外建立再在循環體內使用,以免屢次重複建立StringBuilder對象java