java.lang.String做爲一個最經常使用的類,出如今各類各樣的編程環境中。而其實現方式的不一樣可能形成運行效率的大大不一樣。只有深刻了解JVM對String的實現機制,才能更好的利用String。java
String是一個final類,所以它不能被繼承,同時,用以存儲字符串內容的char value[]數組是private、final的,所以一樣不能改變它的內容。下面是String類的源碼:編程
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[]; ...
那麼在JVM中是怎麼對String進行更新處理的呢?答案是新建一個String類,將原引用指向這個新String類。數組
public class Main { public static String test(String s){ return s.toUpperCase(); } public static void main(String []args){ String s1 = "asd"; String s2 = test(s1); System.out.println("s1 is : " + s1 + " and s2 is : " + s2); } } /×× s1 is : asd and s2 is : ASD ×/
s1做爲一個拷貝傳遞給了test函數,返回了一個新的對象引用,而s1自己並無改變。安全
可是不變性形成的一個麻煩就是浪費空間,下降效率,以下代碼: 多線程
String s = "a" + "b" + "c";
若是不考慮JVM所作的優化(直接生成一個字符串"abcd",而不是運行時合成;或優化使用 StringBuilder),那麼這句語句在執行的時候就會先生成一個"a",而後"a"和"b"組合生成字符串"ab",而後生成"abc",其效率可想而知(聽說java最早版本就是如此)。可是上面這個例子,因爲都是不可變的字符串,所以JVM會一次性合成"abc",進行優化。那麼合成串中包含變量呢?代碼以下:app
String a = "adsf"; String s = "a" +"b" +"c" + a;
JVM在處理這段代碼的時候就會用到StringBuiler類。jvm
StringBuiler是一個可變的普通類。能夠調用append()的方法直接在當前串後面加上新串,而不會生成新的StringBuilder,它是final的,可是,它所實現的AbstractStringBuiler抽象類中,儲存值的char[] value不是final的,這點與String不一樣。函數
使用相似上面String的代碼加以說明,運行後s1和s2的值同時改變了,可見s.append()並非返回一個新串,而是在引用指向的對象上動手腳。優化
public class Main { public static StringBuilder test(StringBuilder s){ return s.append("d"); } public static void main(String []args){ StringBuilder s1 = new StringBuilder("abc"); StringBuilder s2 = s1; test(s1); System.out.println("s1 is : " + s1 + " and s2 is : " + s2); } } /×× s1 is : abcd and s2 is : abcd ×/
回到剛纔聲明String的代碼,ui
String a = "adsf"; String s = "a" +"b" +"c" + a;
讓咱們看一看JVM是怎麼處理它的,經過javap -c Main命令查看Main的jvm彙編程序:
Compiled from "Main.java" public class Main { public Main(); 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 adsf 2: astore_1 3: new #3 // class java/lang/StringBuilder 6: dup 7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V 10: ldc #5 // String abc 12: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 15: aload_1 16: invokevirtual #6 // 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 }
先說說幾個指令的含義:
main函數中,首先直接合成字符串"adsf"放入棧頂,而後存入局部變量s。接下來建立一個StringBuiler類,初始化,接着調用append()方法將abc加入,再次調用append()加入adsf,而後調用toString()輸出。
可見在複雜的String增加中,JVM基本上都會對String進行優化,使用StringBuiler。可是這個優化並非始終最好的,不能認爲有JVM就高枕無憂:
public static void main(String []args){ String add[] = "q w e r t y u i o p".split(" "); String str = ""; for(int i = 0;i < add.length;i++){ str += add[i]; } }
以上代碼在一個循環中,每次爲str加上一個String,看看它的實現:
public static void main(java.lang.String[]); Code: 0: ldc #2 // String q w e r t y u i o p 2: ldc #3 // String 4: invokevirtual #4 // Method java/lang/String.split:(Ljava/lang/String;)[Ljava/lang/String; 7: astore_1 8: ldc #5 // String 10: astore_2 11: iconst_0 12: istore_3 13: iload_3 14: aload_1 15: arraylength 16: if_icmpge 46 19: new #6 // class java/lang/StringBuilder 22: dup 23: invokespecial #7 // Method java/lang/StringBuilder."<init>":()V 26: aload_2 27: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 30: aload_1 31: iload_3 32: aaload 33: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 36: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 39: astore_2 40: iinc 3, 1 43: goto 13 46: return
能夠看到,在循環體中(13~43),每次循環,都會新建一個StringBuiler對象。顯然JVM在此處犯蠢了,所以咱們不該該寄但願於JVM進行優化,更好的方法是直接將str聲明爲StringBuiler:
public static void main(String []args){ String add[] = "q w e r t y u i o p".split(" "); StringBuilder str = new StringBuilder(); for(int i = 0;i < add.length;i++){ str.append(add[i]); } }
這樣就只會有一個StringBuiler對象被建立:
public static void main(java.lang.String[]); Code: 0: ldc #2 // String q w e r t y u i o p 2: ldc #3 // String 4: invokevirtual #4 // Method java/lang/String.split:(Ljava/lang/String;)[Ljava/lang/String; 7: astore_1 8: new #5 // class java/lang/StringBuilder 11: dup 12: invokespecial #6 // Method java/lang/StringBuilder."<init>":()V 15: astore_2 16: iconst_0 17: istore_3 18: iload_3 19: aload_1 20: arraylength 21: if_icmpge 38 24: aload_2 25: aload_1 26: iload_3 27: aaload 28: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 31: pop 32: iinc 3, 1 35: goto 18 38: return
所以,瞭解String和StringBuiler的實現,對寫出高效的程序頗有必要。
有過了解的應該還知道有一個StringBuffer的類,它一樣實現了AbstractStringBuiler接口,大部分函數和StringBuiler功能相同,可是區別就在於這些函數前面加了synchronized關鍵字,保證線程安全。所以其運行速度要比StringBuiler稍微慢一些,可是保證了多線程下數據的準確性。這裏就再也不多說了。