JDK 1.7源碼閱讀筆記(一)String,StringBuilder,StringBuffer

  其實對於JDK源碼一直有一種情愫,希望能看到前輩們的精華之處,感受代碼之美,今天起我也去感受下,如果有和我志同道合的盆友呢,希望咱們一起努力,多多指教吧!
  先來看下JDK幫助文檔對於三個類的解釋,源碼中對於三個類的解釋都是英文,所以我找的解釋就是中文的JDK幫助文檔。

一:幫助文檔中對三個類的解析

1>String類:

public final class String extends Objectimplements Serializable, Comparable<String>, CharSequence

  String 類代表字符串。Java 程序中的所有字符串字面值(如 “abc” )都作爲此類的實例實現。

  字符串是常量;它們的值在創建之後不能更改。字符串緩衝區支持可變的字符串。因爲 String 對象是不可變的,所以可以共享。例如:

 String str = "abc";

等效於:

 char data[] = {'a', 'b', 'c'};
 String str = new String(data);

下面給出了一些如何使用字符串的更多示例:

 System.out.println("abc");
 String cde = "cde";
 System.out.println("abc" + cde);
 String c = "abc".substring(2,3);
 String d = cde.substring(1, 2);

   String 類包括的方法可用於檢查序列的單個字符、比較字符串、搜索字符串、提取子字符串、創建字符串副本並將所有字符全部轉換爲大寫或小寫。大小寫映射基於 Character 類指定的 Unicode 標準版。

  Java 語言提供對字符串串聯符號(”+”)以及將其他對象轉換爲字符串的特殊支持。字符串串聯是通過 StringBuilder(或 StringBuffer)類及其 append 方法實現的。字符串轉換是通過 toString 方法實現的,該方法由 Object 類定義,並可被 Java 中的所有類繼承。有關字符串串聯和轉換的更多信息,請參閱 Gosling、Joy 和 Steele 合著的 The Java Language Specification。

  除非另行說明,否則將 null 參數傳遞給此類中的構造方法或方法將拋出 NullPointerException。

  String 表示一個 UTF-16 格式的字符串,其中的增補字符 由代理項對 表示(有關詳細信息,請參閱 Character 類中的 Unicode 字符表示形式)。索引值是指 char 代碼單元,因此增補字符在 String 中佔用兩個位置。

  String 類提供處理 Unicode 代碼點(即字符)和 Unicode 代碼單元(即 char 值)的方法。

2>StringBuffer類:

public final class StringBuffer extends Object implements Serializable, CharSequence

  線程安全的可變字符序列。一個類似於 String 的字符串緩衝區,但不能修改。雖然在任意時間點上它都包含某種特定的字符序列,但通過某些方法調用可以改變該序列的長度和內容。

  可將字符串緩衝區安全地用於多個線程。可以在必要時對這些方法進行同步,因此任意特定實例上的所有操作就好像是以串行順序發生的,該順序與所涉及的每個線程進行的方法調用順序一致。

  StringBuffer 上的主要操作是 append 和 insert 方法,可重載這些方法,以接受任意類型的數據。每個方法都能有效地將給定的數據轉換成字符串,然後將該字符串的字符追加或插入到字符串緩衝區中。append 方法始終將這些字符添加到緩衝區的末端;而 insert 方法則在指定的點添加字符。

  例如,如果 z 引用一個當前內容爲 “start” 的字符串緩衝區對象,則此方法調用 z.append(“le”) 會使字符串緩衝區包含 “startle”,而 z.insert(4, “le”) 將更改字符串緩衝區,使之包含 “starlet”。

  通常,如果 sb 引用 StringBuilder 的一個實例,則 sb.append(x) 和 sb.insert(sb.length(), x) 具有相同的效果。

  當發生與源序列有關的操作(如源序列中的追加或插入操作)時,該類只在執行此操作的字符串緩衝區上而不是在源上實現同步。

  每個字符串緩衝區都有一定的容量。只要字符串緩衝區所包含的字符序列的長度沒有超出此容量,就無需分配新的內部緩衝區數組。如果內部緩衝區溢出,則此容量自動增大。從 JDK 5 開始,爲該類補充了一個單個線程使用的等價類,即 StringBuilder。與該類相比,通常應該優先使用 StringBuilder 類,因爲它支持所有相同的操作,但由於它不執行同步,所以速度更快。

3>StringBuilder類:

  一個可變的字符序列。此類提供一個與 StringBuffer 兼容的 API,但不保證同步。該類被設計用作 StringBuffer 的一個簡易替換,用在字符串緩衝區被單個線程使用的時候(這種情況很普遍)。如果可能,建議優先採用該類,因爲在大多數實現中,它比 StringBuffer 要快。

  在 StringBuilder 上的主要操作是 append 和 insert 方法,可重載這些方法,以接受任意類型的數據。每個方法都能有效地將給定的數據轉換成字符串,然後將該字符串的字符追加或插入到字符串生成器中。append 方法始終將這些字符添加到生成器的末端;而 insert 方法則在指定的點添加字符。

  例如,如果 z 引用一個當前內容爲 “start” 的字符串的生成器對象,則該方法調用 z.append(“le”) 將使字符串生成器包含 “startle”,而 z.insert(4, “le”) 將更改字符串生成器,使之包含 “starlet”。

  通常,如果 sb 引用 StringBuilder 的實例,則 sb.append(x) 和 sb.insert(sb.length(), x) 具有相同的效果。每個字符串生成器都有一定的容量。只要字符串生成器所包含的字符序列的長度沒有超出此容量,就無需分配新的內部緩衝區。如果內部緩衝區溢出,則此容量自動增大。

  將 StringBuilder 的實例用於多個線程是不安全的。如果需要這樣的同步,則建議使用 StringBuffer。

二:String,StringBuilder,StringBuffer部分源碼閱讀

1>String,StringBuilder,StringBuffer的定義(JDK1.7)

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence 

public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence

public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence

  CharSequence是一個定義字符串操作的接口,StringBuffer、StringBuilder、String中都實現了這個接口.
  String 是java中的字符串,它繼承於CharSequence。 也就是說String也是CharSequence類型。
CharSequence是一個接口,它只包括length(), charAt(int index), subSequence(int start, int end)這幾個API接口。除了String實現了CharSequence之外,StringBuffer和StringBuilder也實現了CharSequence接口。 也就是說,CharSequence其實也就是定義了字符串操作的接口,其他具體的實現是由String、StringBuilder、StringBuffer完成的,String、StringBuilder、StringBuffer都可以轉化爲CharSequence類型。

2>常用構造方法的解析

(1)String類

/**存儲字符數組*/
private final char value[];

/**保存String字符串的哈希值 */
private int hash; // Default to 0
/**從這個構造方法可以看出,如果original指向的是已有的String對象,那麼不會產生拷貝 */
public String(String original) {
    this.value = original.value;
    this.hash = original.hash;
}
/**用字符數組構造String對象,其實就是對String的value分配內存並賦值 */
public String(char value[]) {
    this.value = Arrays.copyOf(value, value.length);
}
/**用StringBuffer構造String對象,保證線程安全的同時,爲String的value分配內存並賦值 */
public String(StringBuffer buffer) {
    synchronized(buffer) {
        this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
    }
}
/**與StringBuffer類似,只不過沒有同步機制,是非線程安全的 */
public String(StringBuilder builder) {
    this.value = Arrays.copyOf(builder.getValue(), builder.length());
}

(2)StringBuilder類

public StringBuilder() {
    super(16);
public StringBuilder(int capacity) {
    super(capacity);
}
public StringBuilder(String str) {
    super(str.length() + 16);
    append(str);
}
public StringBuilder(CharSequence seq) {
    this(seq.length() + 16);
    append(seq);
}

  從源碼中可以得知,StringBuilder的構造函數與父類有着莫大關係,那麼去看下父類AbstractStringBuilder的源碼。

abstract class AbstractStringBuilder implements Appendable, CharSequence {

    char[] value;

    int count;

    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }
    ...
}

  從源碼中可以看出,StringBuilder也是用字符數組存儲內容,並且在初始化時StringBuilder時,會將字符數組的長度初始化爲16,當用字符串初始化StringBuilder時,會將字符數組的長度初始化爲字符串長度+16.
(3)StringBuffer類

public StringBuffer() {
    super(16);
}
public StringBuffer(int capacity) {
    super(capacity);
}
public StringBuffer(String str) {
    super(str.length() + 16);
    append(str);
}
public StringBuffer(CharSequence seq) {
    this(seq.length() + 16);
    append(seq);
}

從源碼中可以看出,StringBuffer較爲常用的構造方法與StringBuilder的完全類似,其實他們兩個類的代碼基本上都一致,只不過StringBuffer多了個關鍵字synchronized,下面還會提到。

3>常用方法的解析

(1)String類
  其實對於String類,記住String類的操作(修改)都是返回一個新的String對象就可以了,比如較爲常用的subString,caocat方法等,下面以subString源碼簡單說明一下:

public String substring(int beginIndex) {
    if (beginIndex < 0) {
        throw new StringIndexOutOfBoundsException(beginIndex);
    }
    int subLen = value.length - beginIndex;
    if (subLen < 0) {
        throw new StringIndexOutOfBoundsException(subLen);
    }
    return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}

  subString還有很多重載的方法,但最後都是new String(),返回的是新的對象(和C++語言的機制不一樣)。
(2)StringBuilder類
對於StringBuiler類的操作最多的就是insert,append,以append方法舉例,看下StringBuilder的append如何實現。

/**以append(String str)舉例,原理一樣 */
public StringBuilder append(String str) {
    super.append(str);
    return this;
}

從源碼可以看出,StringBuilder的append方法調用的是父類AbstractStringBuilder類的append方法,下面看下代碼:

public AbstractStringBuilder append(String str) {
    if (str == null) str = "null";
    int len = str.length();
    //確保容量足夠存儲str
    ensureCapacityInternal(count + len);
    //將內容拷貝到value數組中
    str.getChars(0, len, value, count);
    count += len;
    return this;
}

比較有意思的是ensureCapacityInternal方法,讓我們看下代碼:

private void ensureCapacityInternal(int minimumCapacity) {
    //如果發現字符數組中以存字符的數量與將要存儲的字符數量之和大於整個數組的長度,那麼需要增大數組容量
    if (minimumCapacity - value.length > 0)
        expandCapacity(minimumCapacity);
}
//將字符數組的容量增大至原來的兩倍
void expandCapacity(int minimumCapacity) {
    int newCapacity = value.length * 2 + 2;
    if (newCapacity - minimumCapacity < 0)
        newCapacity = minimumCapacity;
    if (newCapacity < 0) {
        if (minimumCapacity < 0) // overflow
            throw new OutOfMemoryError();
        newCapacity = Integer.MAX_VALUE;
    }
    value = Arrays.copyOf(value, newCapacity);
}

從上述代碼可以看出,對於StringBuilder的操作(修改)都是在原本字符數組的基礎上操作,當發現數組容量不足時,會自動增大數組容量以滿足要求。
(3)StringBuffer類
  StringBuffer類與StringBuilder類基本一致,具體體現在哪呢?我們同樣以append方法爲例,看看StringBuffer類中append的實現方法。
  
public synchronized StringBuffer append(String str) {
super.append(str);
return this;
}
  從代碼中可以看出,StringBuffer調用的仍然是父類AbstractStringBuilder類的append方法,,與StringBuilder不同之處,就在於前面加了symchronized關鍵字,這也就表示着StringBuffer是線程安全的。

三:總結

  String類是final類,也即意味着String類不能被繼承,並且它的成員方法都默認爲final方法。String類其實是通過char數組來保存字符串的。“對String對象的任何改變都不影響到原對象,相關的任何change操作都會生成新的對象”
  StringBuffer類的成員方法前面多了一個關鍵字:synchronized,StringBuffer是線程安全的
對於直接相加字符串,效率很高,因爲在編譯器便確定了它的值,也就是說形如”I”+”love”+”java”; 的字符串相加,在編譯期間便被優化成了”Ilovejava”。這個可以用javap -c命令反編譯生成的class文件進行驗證。對於間接相加(即包含字符串引用),形如s1+s2+s3; 效率要比直接相加低,因爲在編譯器不會對引用變量進行優化。
  String、StringBuilder、StringBuffer三者的執行效率:StringBuilder > StringBuffer > String,當然這個是相對的,不一定在所有情況下都是這樣。當字符串相加操作或者改動較少的情況下,建議使用 String str=”hello”這種形式;當字符串相加操作較多的情況下,建議使用StringBuilder,如果採用了多線程,則使用StringBuffer。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章