String、StringBuffer、StringBuilder,bufferbuilder
也說String。
- String:不可變字元序列。
- StringBuffer:安全執行緒的可變字元序列。
- StringBuilder:StringBuffer的非安全執行緒實現,JDK1.5+。
public final class String { private final char value[]; public String(String original) { // 把原字串original切分成字元數組並賦給value[]; } }//StringBuffer public final class StringBuffer extends AbstractStringBuilder { char value[]; //繼承了父類AbstractStringBuilder中的value[] public StringBuffer(String str) { super(str.length() + 16); //繼承父類的構造器,並建立一個大小為str.length()+16的value[]數組 append(str); //將str切分成字元序列並加入到value[]中 } }
1 常量池
在Java原始碼中的每一個字面值字串,都會在編譯成class檔案階段,形成標誌號為8(CONSTANT_String_info)的常量表。 當JVM載入 class檔案的時候,會為對應的常量池建立一個記憶體資料結構(StringTable,它是一個hashtable,key是字串的hashcode,value是字串的引用地址。),並存放在方法區中。同時JVM會自動為CONSTANT_String_info常量表中的字串常量字面值在堆中建立新的String對象(intern字串對象)。然後把CONSTANT_String_info常量表的入口地址轉變成這個堆中String對象的直接地址(常量池解析)。
樣本:
String sc="ab"+"cd"; String sd="abcd"; System.out.println(sc==sd); //true,在應用啟動完成時就已經載入至常量池了
2 StringBuilder與String+
String sa = "a";String s = sa + "b";
javap -c查看虛擬機器指令:
0: ldc #2 // String a 2: astore_1 3: new #3 // class java/lang/StringBuilder 6: dup 7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V 10: aload_1 11: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 14: ldc #6 // String b 16: invokevirtual #5 // 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
由上可以看到,String+實際上的操作就是new StringBuilder().append().append….toString()。因此如下方法不會引起效率問題(除非連結字串較長時,因為String+操作預設初始化大小16導致append時多次resize):
String method(String sa) { return sa + "b";}
一般所說的String+效率低下的主要產生在如下情況:
String method(String sa) { for (int i=0; i<100; i++) { sa += "b"; } return sa;}
每做一次+就產生一個StringBuilder對象,然後append後丟掉。下次迴圈重新產生一個StringBuilder對象。如果我們直接使用StringBuilder對象全程append的話,就可以節省N-1次建立與銷毀對象的時間。
通過上面的說明,我們可以知道如下結果:
String sa = "ab"; String sb = "cd"; String sab = sa+sb; String s = "abcd"; System.out.println(sab==s); // false
3 String.intern
String.intern可以將一個String類的儲存到一個全域StringTable表中,如果具有相同值的Unicode字串已經在這個表中,那麼該方法返回表中已有字串的地址,如果在表中沒有相同值的字串,則將自己的地址註冊到表中。
通過這個方法我們可以測試常量池到底佔用JVM的哪一地區。