Java字符串連接你用+還是用StringBuilder

前言

據我所知字符串確實已經成為 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."":()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."":(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."":()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."":(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."":()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."":(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}


分享到:


相關文章: