【源碼】StringBuilder和StringBuffer源碼深度剖析

來源:互聯網
上載者:User

標籤:style   blog   http   color   java   使用   os   io   

//------------------------------------------------------------------------

寫篇部落格不容易,請尊重作者勞動成果。轉載請註明出處:http://blog.csdn.net/chdjj

//------------------------------------------------------------------------

我覺得要通過源碼研究一個類,應該先從整體上瞭解這個類,比如說這個類的繼承體系,有哪些超類,繼承了那些介面,提供了什麼樣的方法。然後,我們再去源碼中瞭解具體的實現過程,這樣才不會亂~


註:以下源碼基於jdk1.7.0_11

我們來看看StringBuilder和StringBuffer的繼承關係:

public final class StringBuilder//1.5開始    extends AbstractStringBuilder    implements java.io.Serializable, CharSequencepublic final class StringBuffer//1.0就有了    extends AbstractStringBuilder    implements java.io.Serializable, CharSequence  

驚人的一致,兩個類都繼承了AbstractStringBuilder,並且實現CharSequence和Serializable介面,Serializable介面大家都熟悉,是一個序列化的標誌。那麼我們先來看CharSquence介面:

package java.lang;/** * @author Mike McCloskey * @since 1.4 * @spec JSR-51 */public interface CharSequence {    int length();    char charAt(int index);    CharSequence subSequence(int start, int end);    public String toString();}

只有四個方法,這個介面為不同的字元序列類提供了統一的規範,看到這個類有個toString的方法,說明它強制子類去實現toString,而不使用預設的toString。其他三個方法也比較好理解,甚至大家在String中還經常使用這些方法,這裡就不提了。分析完了Charsequence介面,下面再看看AbstractStringBuilder類吧,聽名字就知道是個抽象類別。這個類是到1.5才有的,很顯然是由於增加了StringBuilder,設計者覺著可以將StringBuilder和StringBuffer進行泛化,抽取共同部分,這體現了物件導向的設計理念。
abstract class AbstractStringBuilder implements Appendable, CharSequence

AbstractStringBuilder又實現了CharSequence和Appendable介面,Appendable看名字就知道肯定跟StringBuilder和StringBuffer的可變性有關,我們果斷看看Appendable介面:

package java.lang;import java.io.IOException;public interface Appendable {    Appendable append(CharSequence csq) throws IOException;    Appendable append(CharSequence csq, int start, int end) throws IOException;    Appendable append(char c) throws IOException;}

果然,這個介面提供了append方法的不同重載形式,返回值均為自己(註:看到這裡,我想到了一句話,抽象類別是對類進行抽象,而介面是對行為進行抽象,想想確實如此呢),既然AbstractStringBuilder實現了此介面,必然提供了實現,下面我們回到AbstractStringBuilder類中看看這個類都幹了啥。先看成員變數:
/**     * The value is used for character storage.     */    char[] value;//用於存放字元的數組    /**     * The count is the number of characters used.     */    int count;//當前字元總數

注意,這裡的value數組並沒有聲明為final(String類的value數組是final的),說明此數組可改變。再看構造器:
 AbstractStringBuilder() {}    AbstractStringBuilder(int capacity) {        value = new char[capacity];    }

一個無參構造器,一個構造器提供char數組初始容量,value數組根據此容量建立對象。接下來看幾個重要的方法。首先是擴容的方法:
public void ensureCapacity(int minimumCapacity) {        if (minimumCapacity > 0)            ensureCapacityInternal(minimumCapacity);    }    /**     * This method has the same contract as ensureCapacity, but is     * never synchronized.     */    private void ensureCapacityInternal(int minimumCapacity) {        // overflow-conscious code        if (minimumCapacity - value.length > 0)            expandCapacity(minimumCapacity);    }    /**     * This implements the expansion semantics of ensureCapacity with no     * size check or synchronization.     */    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);    }

ensureCapacity方法確保當前字元數組的容量最小為minimumCapacity,首先判斷這個參數是否為負,若是,則返回,否則調用ensureCapacityInternal方法,這個方法內部將判斷當前容量是否小於minimumCapacity,若是,則進行擴容,否則返回。擴容是通過調用expandCapacity方法來實現的,這個方法內部實現邏輯是這樣的:首先試著將當前數組容量擴充為原數組容量的2倍加上2,如果這個新容量仍然小於最小值(minimumCapacity),那麼就將新容量定為(minimumCapacity),最後判斷是否溢出,若溢出,則將容量定為整型的最大值0x7fffffff。容量定好之後,進行一次數組拷貝。
通過這一系列步驟,應該對我們有所啟發,在建立StringBuilder或StringBuffer時,盡量估算下串的長度,給一個合理的初始值,避免多次擴容帶來的效率問題。接下來看append方法(重載太多,這裡只列舉1個):
  public AbstractStringBuilder append(String str) {        if (str == null) str = "null";        int len = str.length();        ensureCapacityInternal(count + len);        str.getChars(0, len, value, count);        count += len;        return this;    }

上面這個append方法接受一個String類型的參數,也是我們最常使用的重載形式.內部邏輯比較簡單,首先進行參數判斷(我們寫代碼的時候也應該時刻注意代碼的魯棒性,注意參數的取值),若參數為空白(null),則將null這幾個字元作為參數,接下來判斷是否需要擴容,完了之後進行一次複製,最後更新count。還有個方法叫trimToSize,這個方法可以減少記憶體空間的使用,內部會進行數組複製,釋放那些尚未使用的空間:
public void trimToSize() {        if (count < value.length) {            value = Arrays.copyOf(value, count);        }    }

至此,AbstractStringBuilder分析完畢。好吧,現在我們可以分析StringBuilder和StringBuffer了!先來看看這個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的預設大小為16,這個很關鍵哦,大家應該能夠記住,如果能在面試中說出來,那也是極好的~構造器除了有預設容量,也可以手動設定容量,為避免擴容,強烈建議大家估摸下串的大致長度~
接下來是append方法,我們推測應該調用的是AbstractStringBuilder中的append:
 public StringBuilder append(Object obj) {        return append(String.valueOf(obj));    }    public StringBuilder append(String str) {        super.append(str);        return this;    }

事實上也正是這樣。再來看下這個toString方法:
 public String toString() {        // Create a copy, don't share the array        return new String(value, 0, count);    }

注意哦,這裡返回的是一個新的字串對象!
最後看下readObject和writeObject這兩個方法,這兩個方法都是私人的,方法的作用應該跟序列化有關。
  private void writeObject(java.io.ObjectOutputStream s)        throws java.io.IOException {        s.defaultWriteObject();        s.writeInt(count);        s.writeObject(value);    }    private void readObject(java.io.ObjectInputStream s)        throws java.io.IOException, ClassNotFoundException {        s.defaultReadObject();        count = s.readInt();        value = (char[]) s.readObject();    }
到這,StringBuilder分析完畢,可以看出,StringBuilder中並沒有什麼複雜的邏輯,實現代碼主要在AbstractStringBuilder中。下面該分析StringBuffer了,大家都知道,StringBuffer和StringBuilder的最大區別就是StringBuffer安全執行緒,那麼可想而知,StringBuffer的方法應該加鎖了,帶著這個猜想,開啟StringBuffer源碼:構造器就略過了,因為StringBuffer跟StringBuilder完全一樣,預設容量也是16.下面隨便來幾個方法:
public synchronized int length() {        return count;    }public synchronized void trimToSize() {        super.trimToSize();    } public synchronized StringBuffer append(String str) {        super.append(str);        return this;    }

看,都加鎖了吧。你可能看到這個方法沒加鎖:
public StringBuffer append(CharSequence s) {        // Note, synchronization achieved via other invocations        if (s == null)            s = "null";        if (s instanceof String)            return this.append((String)s);        if (s instanceof StringBuffer)            return this.append((StringBuffer)s);        return this.append(s, 0, s.length());    }

恩,確實沒加,但注意到注釋沒,設計者說了,加鎖的操作是通過它內部調用的其它方法實現的,所以這裡沒必要再進行一次加鎖(鎖需要浪費資源)。
剩下的代碼都沒啥說的了,除了加了synchronized關鍵字修飾,跟StringBuilder是一樣一樣的。

分析到此完畢,下面做個總結:1.StringBuilder是jdk1.5引進的,而StringBuffer在1.0就有了;2.StringBuilder和StringBuffer都是可變的字串,可以通過append或者insert等方法修改串的內容;3.StringBuffer是安全執行緒的而StringBuilder不是,因而在多線程的環境下優先使用StringBuffer,而其他情況下推薦使用StringBuilder,因為它更快;4.StringBuilder和StringBuffer都繼承自AbstractStringBuilder類,AbStractStringBuilder主要實現了擴容、append、insert方法,StrngBuilder和StringBuffer的相關方法都直接調用的父類。5.StringBuilder和StringBuffer的初始容量都是16,程式員盡量手動設定初始值,以避免多次擴容所帶來的效能問題;6.StringBuilder和StringBuffer的擴容機制是這樣的:首先試著將當前數組容量擴充為原數組容量的2倍加上2,如果這個新容量仍然小於預定的最小值(minimumCapacity),那麼就將新容量定為(minimumCapacity),最後判斷是否溢出,若溢出,則將容量定為整型的最大值0x7fffffff。







聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.