Java 基礎之 String 類
String
String 被宣告為 final,因此不能被繼承。(Integer 等包裝類也不能被繼承)
在 java8 中,String 內部使用 char 陣列 來儲存資料
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
}
在 java9 中,String 內部使用 byte 陣列儲存字串,同時使用 coder 來標識使用了哪種編碼
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final byte[] value; /** The identifier of the encoding used to encode the bytes in {@code value}. */ private final byte coder; }
value 陣列被宣告 final,因此 value 陣列初始化之後就不能再引用其他陣列。並且 String 內部沒有改變 value 陣列的方法,因此可以保證 String 不可變
String 不可變的好處
1. 可以快取 hash 值
因為 String 的 hash 值經常被使用,例如 String 用做 HashMap 的 key。不可變的特性可以使得 hash 值也不可變,因此只需要進行一次計算。
2. String pool
如果一個 String 物件已經被建立過,那麼就會從常量池中取的引用,只有 String 是不可變的,才能使用 String pool
3. 安全性
String 經常作為引數,String 不可變可以保證引數不可變,例如在作為網路連線引數的情況下如果 String 是可變的,那麼在網路連線過程中,String 被改變,改變 String 的那一方以為現在連線的是其它主機,而實際情況卻不一定是。
4. 執行緒安全
因為字串是不可變的,所以是多執行緒安全的,同一個字串例項可以被多個執行緒共享。這樣便不用因為執行緒安全問題而使用同步。字串自己便是執行緒安全的。
String, StringBuffer and StringBuilder
1. 可變性
String 不可變(String 類中使用 final 關鍵字修飾字符陣列來儲存字串,所以 String 物件是不可變的。)
StringBuilder 與 StringBuffer是可變的(都繼承AbstractStringBuilder,也是使用 char 陣列儲存資料,但是沒有使用 final 修飾符)
AbstractStringBuilder:
abstract class AbstractStringBuilder implements Appendable, CharSequence {
/**
* The value is used for character storage.
*/
char[] value;
/**
* The count is the number of characters used.
*/
int count;
/**
* This no-arg constructor is necessary for serialization of subclasses.
*/
AbstractStringBuilder() {
}
}
2. 執行緒安全性
- String 不可變,執行緒安全(操作少量的資料使用)
- StringBuilder 執行緒不安全(單執行緒操作字串緩衝區下操作大量資料)
- StringBuffer 執行緒安全,內部使用 synchronized 進行同步(多執行緒操作字串緩衝區下操作大量資料)
String pool
字串常量池(String Pool)儲存著所有字串字面量(literal strings), 這些字面量在編譯時期就確定。不僅如此, 還可以使用 String 的 intern() 方法在執行過程將字串新增到 String Pool 中。
當一個字串呼叫 intern() 方法時,如果 String Pool 中已經存在一個字串和該字串值相等(使用 equals() 方法進行確定),那麼就會返回 String Pool 中字串的引用;否則,就會在 String Pool 中新增一個新的字串, 並返回這個新字串的引用。
下面示例中,s1 和 s2 採用 new String() 的方式新建了兩個不同字串,而 s3 和 s4 是通過 s1.intern() 方法取得同一個字串引用。intern() 首先把 s1 引用的字串放到 String Pool 中, 然後返回這個字串引用。因此 s3 和 s4 引用的是同一個字串。
String s1 = new String("aaa"); //堆記憶體中 s1為一個引用
String s2 = new String("aaa"); //堆記憶體中 s2為另一個引用
System.out.println(s1 == s2); // false
String s3 = s1.intern(); //s3為s1的引用
String s4 = s1.intern(); //s4為s1的引用
System.out.println(s3 == s4); // true
如果是採用 "bbb" 這種字面量的形式建立字串,會自動地將字串放入 String Pool 中。
String s5 = "bbb"; //放在常量池中
String s6 = "bbb"; //從常量池中查詢
System.out.println(s5 == s6); // true
在 Java 7 之前,String Pool 被放在執行時常量池中,它屬於永久代。而在 Java 7,String Pool 被移到堆中。這是因為永久代的空間有限,在大量使用字串的場景下會導致 OutOfMemoryError 錯誤。
new String("abc")
使用這種方式會建立兩個字串物件,前提是常量池中還沒有 “abc” 字串物件
- “abc” 屬於字串字面量,因此編譯時期會在 String Pool 中建立一個字串物件,指向這個 “abc” 字串字面量;
- 使用 new 的方式會在堆中建立一個字串物件
------------恢復內容結束------------