1. 程式人生 > 實用技巧 >Java 基礎之 String 類

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. 執行緒安全性

  1. String 不可變,執行緒安全(操作少量的資料使用)
  2. StringBuilder 執行緒不安全(單執行緒操作字串緩衝區下操作大量資料)
  3. 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 的方式會在堆中建立一個字串物件
    ------------恢復內容結束------------