@html
一、java.lang.Stringjava
二、java.lang.StringBuffer數組
三、java.lang.StrungBuilder安全
相同點: 都是final類, 不容許被繼承;多線程
不一樣點:app
/—————————————本篇主要討論String類型————————————/性能
字符串常量池(如下簡稱常量池/字符串池)的存在乎義:實際開發中,String類是使用頻率很是高的一種引用對象類型。可是不斷地建立新的字符串對象,會極大地消耗內存。所以,JVM爲了提高性能和減小內存開銷,內置了一塊特殊的內存空間即常量池,以此來避免字符串的重複建立。JDK 1.8 後,常量池被放入到堆空間中。學習
字符串池中維護了共享的字符串對象,這些字符串不會被垃圾收集器回收。優化
若直接使用「==」進行比較對象,則比較的是兩個對象的引用地址;ui
若使用str1.equals(str2)方法進行比較,因爲String類內部已經覆蓋Object類中的equals()方法,實際比較的是兩個字符串的值。
// 源碼 public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String aString = (String)anObject; if (coder() == aString.coder()) { return isLatin1() ? StringLatin1.equals(value, aString.value) : StringUTF16.equals(value, aString.value); } } return false; }
String str_01 = "aa"; String str_02 = "aa"; System.out.println(str_01 == str_02);
使用這種方式建立字符串,會先在棧中建立一個引用變量str_01,再去常量池中尋找是否已存在值爲"aa"的字符串:
// result true
String str_01 = new String("xyz"); String str_02 = new String("xyz"); System.out.println(str_01 == str_02);
這種方式至少會建立一個對象,由於本質是調用了String類的構造器方法public String(String original){...},在堆中必定會建立一個字符串對象。
使用"new"關鍵字創造對象主要分爲三步:
// result false
String str_01 = new String("abc").intern(); String str_02 = "abc"; String str_03 = new String("abc"); System.out.println(str_01 == str_02); System.out.println(str_02 == str_03); String str_04 = new String("cba"); String str_05 = new String("cba").intern(); System.out.println(str_04 == str_05);
當使用構造器建立字符串調用 intern()方法時,若是常量池中已經存在一個值相同的字符串(內部使用equals()方法來肯定),則返回常量池中的字符串對象的引用地址;不然,將堆中新建立的字符串對象添加到常量池中,並返回池中字符串對象的引用地址。
// result true false false
字符串的本質:char類型數組 private final char[] str
String類實現了CharSequence接口
String類型的不可變性指的是內存地址不可變,若是將一個對象從新賦值,則本質上是改變了其引用對象。
String a = "hello"; System.out.println(a.hashCode()); a = "hey"; System.out.println(a.hashCode());
// result 99162322 103196
StringBuffer類型和StringBuilder類型的字符串定義好後能夠進行值改變,而且不會建立新的內存地址。
StringBuilder a = new StringBuilder(); System.out.println(a.hashCode()); a.append("Hello"); a.append("World"); System.out.println(a.hashCode());
// result 1395089624 1395089624
String類中的valueOf(Object obj)方法能夠將任意一個對象轉換爲字符串類型。
// 源碼 public static String valueOf(Object obj) { return (obj == null) ? "null" : obj.toString(); }
String類中,重載了+與+=運算,這也是Java中惟一重載的兩個運算符。
兩個字符串相加便是字符串的拼接,在進行拼接時,會先調用valueOf(Object obj)方法將其爲字符串類型,再進行拼接。從源碼能夠看出,若是字符串爲null,會將其轉換爲字面值爲"null"的字符串。
String s = null; s = s + "World"; System.out.println("Hello " +s); // result: Hello nullWorld
所以在進行字符串拼接時,初始字符串應該設置成空字符串"",而非null。
在字符串間使用加法運算時:
String str1 = "ABCD"; String str2 = "AB" + "CD"; String str3 = "A" + "B" + "C" + "D"; String temp1 = "AB"; String temp2 = "CD"; String str4 = temp1 + temp2; // String str4 = new StringBuilder().append(temp1).append(temp2).toString(); String temp = "AB"; String str5 = temp + "CD"; // String str4 = new StringBuilder(String.valueOf(temp)).append("CD").toString(); System.out.println(str1 == str2); System.out.println(str1 == str3); System.out.println(str1 == str4); System.out.println(str1 == str5);
// result true true false false
public class test { public static final String str1 = "abc"; public static final String str2 = "def"; public static void main(String[] args) { String str3 = str1 + str2; String str4 = "abcdef"; System.out.println(str3 == str4); } }
str1和str2都是final類型的,而且在編譯階段都是已經被賦值了,至關於一個常量,當執行Strings str3 = str1 + str2 的時候,str3已是"abcdef"常量了,已被建立在常量池中,因此地址是相等的。
// result true
public class test { public static final String s1; public static final String s2; static{ s1 = "ab"; s2 = "cd"; } public static void main(String[] args) { String s3 = s1 + s2; String s4 = "abcd"; System.out.println(s3 == s4); } }
雖然s1和s2都是final類型,可是起初並無初始化,在編譯期還不能肯定具體的值,此處是變量,因此這裏會調用StringBuilder類中的構造方法及append()方法來建立新的字符串s3,返回的新字符串s3在堆中的地址,因此與s4不相等。
// result false
參考內容:
If you have any question, please let me know, your words are always welcome.* 新人入坑,若有錯誤/不妥之處,歡迎指出,共同窗習。