前言
據我所知字串確實已經成為 Java 開發人員最常用的類了,而且是大量使用。我們都知道,String 其實是封裝了字元,所以倆字串連線就是將字串物件裡面的字元連起來。很多人習慣使用+
來連線字串,也有人會用 StringBuilder 的append
方法。
"+"編譯後
看看如果我們在程式中直接使用+
來連線字串的情況,用下面一個簡單的例子來說明,進行兩個字串連線操作,即s3 = s1 + s2
。
public class TestString {
public static void main(String[] args) {
String s1 = "www";
String s2 = "ccc";
String s3 = s1 + s2;
}
}
複製程式碼
接著javap -c TestString.class
看一下編譯後的情況,可以看到編譯器其實是對+
進行了轉換的,轉成了 StringBuilder 物件來操作了,首先使用 s1 建立 StringBuilder 物件,然後用 append
方法連線 s2,最後呼叫toString
方法完成。
public class com.seaboat.string.TestString {
public com.seaboat.string.TestString();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #16 // String www
2: astore_1
3: ldc #18 // String ccc
5: astore_2
6: new #20 // class java/lang/StringBuilder
9: dup
10: aload_1
11: invokestatic #22 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
14: invokespecial #28 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
17: aload_2
18: invokevirtual #31 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
21: invokevirtual #35 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
24: astore_3
25: return
}
複製程式碼
"+"與"append"等價嗎
前面可以看到+
在編譯器作用下都會轉成 StringBuilder 的append
方法執行,所以如果拋開執行效率來說,它們其實本質是一樣的。
本質一樣是否就能說明它們時等價的呢?或者說能否為了方便直接用+
來連線字串,剩下的事就交給編譯器了?繼續看個例子,在這個例子中有個 for 迴圈進行字串連線操作。
public class TestString2 {
public static void main(String[] args) {
String s = "www";
for (int i = 0; i < 10; i++)
s += i;
}
}
複製程式碼
編譯後的情況如下,不熟悉指令沒關係,我們只看重要的部分,if_icmplt 8
,這個就是 for 迴圈的條件判斷,小於10則不斷跳到8的位置,8後面其實就是建立 StringBuilder 物件,並以本地變數s的值初始化該物件,接著再將本地變數i append
到 StringBuilder 物件中,最後呼叫toString
方法將所得值存到本地變數s。
這樣來看迴圈中每次都要建立 StringBuilder 物件,而且要呼叫toString
方法,這樣的執行效率顯然比較低,而且增加了 GC 的壓力。
public class com.seaboat.string.TestString2 {
public com.seaboat.string.TestString2();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #16 // String www
2: astore_1
3: iconst_0
4: istore_2
5: goto 30
8: new #18 // class java/lang/StringBuilder
11: dup
12: aload_1
13: invokestatic #20 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
16: invokespecial #26 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
19: iload_2
20: invokevirtual #29 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
23: invokevirtual #33 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
26: astore_1
27: iinc 2, 1
30: iload_2
31: bipush 10
33: if_icmplt 8
36: return
}
複製程式碼
友好寫法
把事情都丟給編譯器是不友好的,為了能讓程式執行更加高效,最好是我們自己來控制 StringBuilder 的例項,比如下面,只建立一個 StringBuilder 例項,後面用append
方法連線字串。
public class TestString3 {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder("www");
for (int i = 0; i < 10; i++)
sb.append(i);
}
}
複製程式碼
編譯後的情況如下,首先建立一個 StringBuilder 物件,使用字串"www"來例項化該物件,接著迴圈呼叫append
方法將本地變數i新增到 StringBuilder 物件中。
public class com.seaboat.string.TestString3 {
public com.seaboat.string.TestString3();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #16 // class java/lang/StringBuilder
3: dup
4: ldc #18 // String www
6: invokespecial #20 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
9: astore_1
10: iconst_0
11: istore_2
12: goto 24
15: aload_1
16: iload_2
17: invokevirtual #23 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
20: pop
21: iinc 2, 1
24: iload_2
25: bipush 10
27: if_icmplt 15
30: return
}
複製程式碼
-------------推薦閱讀------------
跟我交流,向我提問:
公眾號的選單已分為“讀書總結”、“分散式”、“機器學習”、“深度學習”、“NLP”、“Java深度”、“Java併發核心”、“JDK原始碼”、“Tomcat核心”等,可能有一款適合你的胃口。
歡迎關注: