標籤: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。