一、背景
在寫代碼的時候使用 StringBuilder 進行字元串拼接時,IDEA 很可能會給出下面的提示: 'StringBuilder' can be replaced with 'String' 。
那麼為什麼會給出這種提示?這種提示意味着什麼?
二、思考
之前有講過:“每一個疑問背後都隐藏着至少一個盲點和學習的絕佳機會”。是以我們不會輕易放過這個機會。
另外很多人這個時候可能就要開始百度了!
停!!
“《阿裡巴巴Java開發手冊》詳解” 專欄裡有講到“先猜想後驗證”的思想,對學習源碼等有很大幫助,這樣印象會更深刻。
根據提示:'StringBuilder' can be replaced with 'String' ,說明這種情況下可以将
StringBuilder
替換成
String
。
我們知道之是以用
StringBuilder
是為了避免字元串拼接過程中産生很多不必要的字元串對象。
那麼既然可以替換為
String
,說明這種情況下兩者可能等價,也就是說底層是一樣的。
三、分析
“《阿裡巴巴Java開發手冊》詳解” 專欄裡有講過寫 DEMO ,反編譯和反彙編是學習的一個重要手段。
那咱們直接寫個 DEMO驗證下吧:
public class StringDemo {
public static void main(String[] args) {
String a = "a";
Integer b = 0;
Long c = 1000L;
useStringBuilder(a, b, c);
usePlus(a, b, c);
}
/**
* 使用 StringBuilder 拼接
*/
private static void useStringBuilder(String a, Integer b, Long c) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(a).append(b).append(c);
System.out.println(stringBuilder.toString());
}
/**
* 直接使用+号進行字元串拼接
*/
private static void usePlus(String a, Integer b, Long c) {
System.out.println(a + b + c);
}
}
使用 IDEA 自帶的反編譯工具,發現和源代碼幾乎相同(這裡就不給出了)。
接下來嘗試反彙編
javap -c -p StringDemo
(之前已經通過 javac 進行了編譯):
public class com.chujianyun.common.str.StringDemo {
public com.chujianyun.common.str.StringDemo();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String a
2: astore_1
3: iconst_0
4: invokestatic #3 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
7: astore_2
8: ldc2_w #4 // long 1000l
11: invokestatic #6 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
14: astore_3
15: aload_1
16: aload_2
17: aload_3
18: invokestatic #7 // Method useStringBuilder:(Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Long;)V
21: aload_1
22: aload_2
23: aload_3
24: invokestatic #8 // Method usePlus:(Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Long;)V
27: return
private static void useStringBuilder(java.lang.String, java.lang.Integer, java.lang.Long);
Code:
0: new #9 // class java/lang/StringBuilder
3: dup
4: invokespecial #10 // Method java/lang/StringBuilder."<init>":()V
7: astore_3
8: aload_3
9: aload_0
10: invokevirtual #11 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
13: aload_1
14: invokevirtual #12 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
17: aload_2
18: invokevirtual #12 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
21: pop
22: getstatic #13 // Field java/lang/System.out:Ljava/io/PrintStream;
25: aload_3
26: invokevirtual #14 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
29: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
32: return
private static void usePlus(java.lang.String, java.lang.Integer, java.lang.Long);
Code:
0: getstatic #13 // Field java/lang/System.out:Ljava/io/PrintStream;
3: new #9 // class java/lang/StringBuilder
6: dup
7: invokespecial #10 // Method java/lang/StringBuilder."<init>":()V
10: aload_0
11: invokevirtual #11 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
14: aload_1
15: invokevirtual #12 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
18: aload_2
19: invokevirtual #12 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
22: invokevirtual #14 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
25: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
28: return
}
注:大家也可以使用專欄
第一個加餐文章中推薦的 jclasslib 位元組碼插件插件來檢視位元組碼
大家可以對比下
usePlus
函數和
useStringBuilder
函數的位元組碼指令會發現帶變量的 + 号拼接方式最終将轉化為 StringBuilder 進行字元串拼接。
相當于是編譯器的優化,是以 IDEA 檢測到兩者等價,為了讓代碼更加簡潔,是以給出了上述提示。
為了再次印證這個想法我們打開《Java 語言規範》
15.18.1. String Concatenation Operator +:
An implementation may choose to perform conversion and concatenation in one step to avoid creating and then discarding an intermediateobject. To increase the performance of repeated string concatenation, a Java compiler may use the
String
class or a similar technique to reduce the number of intermediate
StringBuffer
String
objects that are created by evaluation of an expression.
Java 語言實作可以選擇在某個步驟中執行轉換和拼接,以避免先建立然後又丢棄中間的字元串對象。為了提高字元串連接配接操作的性能,Java 編譯器可以使用功能 StringBuilder 類或者類似的技術來減少計算表達式時建立的中間 String 對象的數量。
是以問題就徹底清楚了。
四、總結
之前有寫過
《為什麼要推薦大家學習位元組碼》和
《每一個疑問背後都隐藏着至少一個盲點和學習的絕佳機會》兩篇文章。希望大家能夠真正了解學習位元組碼的用處;能夠真真抓住每一次疑問,通過對每一個疑問的深入探究來徹底掌握某個知識。另外強烈推薦大家多看《Java 語言規範》和《Java 虛拟規範》,它們更全面且更權威。
希望本文能對大家有幫助。如果有任何問題想和我交流,歡迎留言評論。
如果我的文章對你有幫助,歡迎關注,點贊評論!!
