源碼解讀 String错维、StringBuffer奖地、StringBuilder區(qū)別

引言:
相信大多數(shù)面試都會問到關于Java中的String、StringBuffer赋焕、StringBuilder之間的區(qū)別参歹。
可能很多人會直接通過百度查看直接最后結果,但今天從源碼維度談談為什么三者會有各自的區(qū)別和特點隆判。

一犬庇、String、StringBuffer侨嘀、StringBuilder三者之間的類結構關系

Stringl類

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

StringBuffer

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

StringBuilder

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

String臭挽、StringBuffer、StringBuilder 三者的類關系圖


String飒炎、StringBuffer埋哟、StringBuilder類關系圖

可以發(fā)現(xiàn)StringBuffer 與StringBuilder是繼承了AbstractStringBuilder抽象類笆豁。

二郎汪、String部分源碼解讀

這里就針對String類核心關鍵代碼解讀,final 數(shù)組闯狱、多個構造方法煞赢、部分主要方法。

① final 數(shù)組 與部分構造方法

    private final char value[];

    public String() {
        this.value = "".value;
    }

    public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }

    public String(char value[]) {
        this.value = Arrays.copyOf(value, value.length);
    }

從上面的構造方法可以看出哄孤,實際上String類是將內容存放在final value[]數(shù)組中照筑。
因為value[]數(shù)組是final的,所以這也就代表著在給對象賦值新的值時瘦陈,因final value[] 常量凝危,所以每次賦值都是產(chǎn)生新對象。例如:

String str = "abc";
str = "bcd";

如果執(zhí)行以下代碼操作晨逝,jvm方法區(qū)中是生成兩個對象后蛾默,再進行計算獲得最后的內容。

String str = "abc";
str = str + "def";

三捉貌、StringBuffer支鸡、StringBuilder部分源碼解讀

為什么要將StringBuffer和StringBuilder一起解讀冬念? 因為兩個源碼幾乎一樣,不一樣的是StringBuffer的操作方法用了synchronized字段牧挣。

① 數(shù)組 與部分構造方法

StringBuffer類

 public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{
    private transient char[] toStringCache;
    
    static final long serialVersionUID = 3388685877147921107L;
    
    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);
    }   

StringBuilder類

public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{
    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);
    }

StringBuffer 和StringBuilder類都是繼承了AbstractStringBuilder類急前,同時構造方法中都傳入了(+16)長度給父類。下面繼續(xù)跟進下AbstractStringBuilder類瀑构。

abstract class AbstractStringBuilder implements Appendable, CharSequence {
    char[] value;

    int count;

    AbstractStringBuilder() {
    }

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

從抽象類AbstractStringBuilder解讀裆针,帶有數(shù)組常量value ,并將對應內容存在了value中寺晌。

② StringBuffer與StringBuilder的其他方法

StringBuffer類的其他方法据块,如圖所示都加入了synchronized字段


StringBuffer其他方法

StringBuilder類的其他方法,如圖所示沒有加入了synchronized字段


StringBuilder類的其他方法

③ 重點講解下append方法

StringBuffer和StringBuilder有很多方法折剃,因為常用的append操作另假,所以這里重點介紹下append()方法。
先看下StringBuffer怕犁、StringBuilder的源代碼

 // StringBuffer類
    @Override
    public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
    }

// StringBuilder類
    @Override
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }

StringBuffer和StringBuilder都重寫了父類AbstractStringBuilder的方法边篮,并且同時調用了父類的append()方法。
接著跟進查看AbstractStringBuilder類的append()方法查看源碼奏甫。

    public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len);  
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }

上面的代碼有兩個關鍵語句:

  1. ensureCapacityInternal(count + len);這個語句里兩個操作戈轿,a. 為數(shù)組value[]擴容 b. 將舊數(shù)組搬移到新擴容數(shù)組中。
  2. str.getChars(0, len, value, count); 這個語句是通過調用String.getChars()方法阵子,正式將value數(shù)組與str數(shù)組相連接思杯。

④ 進一步解讀ensureCapacityInternal(count + len);str.getChars(0, len, value, count);

A. 解讀ensureCapacityInternal(count + len);
    private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        if (minimumCapacity - value.length > 0) {
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity));
        }
    }

這個ensureCapacityInternal()確認容量內部方法通過判斷新的容量大小是否大于以有容量。如果打算申請的容量大于已有容量挠进,則進行 newCapacity(minimumCapacity)容量申請 + Arrays.copyOf()數(shù)組復制兩個操作色乾。接著跟進查看 newCapacity(minimumCapacity)容量申請方法。

    private int newCapacity(int minCapacity) {
        // overflow-conscious code
        int newCapacity = (value.length << 1) + 2;
        if (newCapacity - minCapacity < 0) {
            newCapacity = minCapacity;
        }
        return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
            ? hugeCapacity(minCapacity)
            : newCapacity;
    }
創(chuàng)建新容量大小newCapacity()方法:
  • 首先领突,自動擴容的容量newCapacity 大小為(value.length << 1) + 2舊長度x2 + 2暖璧,也就是如果舊容量是16,則自動擴容的容量是 16x2 +2 = 34 君旦。
  • 接著澎办,如果自動擴容的容量newCapacity 小于 預期申請的minCapacity ,則將預期申請的minCapacity賦值給自動擴容的容量newCapacity金砍。
  • 然后局蚀,同時還進行了容量上下限大小判斷newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0
    private int hugeCapacity(int minCapacity) {
        if (Integer.MAX_VALUE - minCapacity < 0) { // overflow
            throw new OutOfMemoryError();
        }
        return (minCapacity > MAX_ARRAY_SIZE)
            ? minCapacity : MAX_ARRAY_SIZE;
    }
    
    如果自動申請容量超過Integer.MAX_VALUE public static final int MAX_VALUE = 0x7fffffff;值后拋異常。
  • 最后恕稠,返回 自動申請的容量大小琅绅。
舊數(shù)組拷貝Arrays.copyOf()

在經(jīng)過上面容量申請確認后,則將舊數(shù)組拷貝到新容量的數(shù)組中谱俭。

    public static char[] copyOf(char[] original, int newLength) {
        char[] copy = new char[newLength];
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
B. 解讀 str.getChars(0, len, value, count);

str.getChars()調用的是String的getChars()方法奉件,其實就是System.arraycopy()系統(tǒng)數(shù)組拷貝方式宵蛀。
直接上代碼:

    public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
        if (srcBegin < 0) {
            throw new StringIndexOutOfBoundsException(srcBegin);
        }
        if (srcEnd > value.length) {
            throw new StringIndexOutOfBoundsException(srcEnd);
        }
        if (srcBegin > srcEnd) {
            throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
        }
        System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
    }

總結:

String類存放在常量final value[]數(shù)組中,StringBuffer县貌、StringBuilder存放在變量value[]中术陶,String對象一但創(chuàng)建后對象是不能變更的(重新賦值話每次都是重新創(chuàng)建對象和賦值操作),而StringBuffer煤痕、StringBuilder是可以變更的梧宫。故此,運行速度:StringBuilder > StringBuffer > String
StringBuffer 方法有synchronized 摆碉,故此線程安全塘匣。StringBuffer方法沒有synchronized,故此線程不安全巷帝。兩者沒有其他差異忌卤。

  • String: 不可變字符序列-常量
  • StringBuffer:可變字符序列-變量、效率低楞泼、線程安全
  • StringBuilder:可變字符序列-變量驰徊、效率搞、線程不安全
String StringBuffer StringBuilder
String是不可變常量 final value[] StringBuffer變量 value[] StringBuilder變量 value[] 數(shù)組
String每次操作都會生成新的String對象堕阔,不僅效率低下棍厂,而且浪費空間 StringBuffer 操作不會產(chǎn)生新的對象。StringBuffer自身帶有緩沖容量超陆,當字符串大小沒有超過容量時牺弹,不會分配新容量;當字符串超過容量時时呀,會自動增加容量张漂。 StringBuilder 操作不會產(chǎn)生新的對象。StringBuilder 自身帶有緩沖容量退唠,當字符串大小沒有超過容量時鹃锈,不會分配新容量荤胁;當字符串超過容量時瞧预,會自動增加容量。
不可變 可變 可變
- 線程安全 線程不安全
- 支持多線程操作字符串 只支持單線程操作字符串
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末仅政,一起剝皮案震驚了整個濱河市垢油,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌圆丹,老刑警劉巖滩愁,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異辫封,居然都是意外死亡硝枉,警方通過查閱死者的電腦和手機廉丽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來妻味,“玉大人正压,你說我怎么就攤上這事≡鹎颍” “怎么了焦履?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長雏逾。 經(jīng)常有香客問我嘉裤,道長,這世上最難降的妖魔是什么栖博? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任屑宠,我火速辦了婚禮,結果婚禮上仇让,老公的妹妹穿的比我還像新娘侨把。我一直安慰自己,他們只是感情好妹孙,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布秋柄。 她就那樣靜靜地躺著,像睡著了一般蠢正。 火紅的嫁衣襯著肌膚如雪骇笔。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天嚣崭,我揣著相機與錄音笨触,去河邊找鬼。 笑死雹舀,一個胖子當著我的面吹牛芦劣,可吹牛的內容都是我干的。 我是一名探鬼主播说榆,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼虚吟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了签财?” 一聲冷哼從身側響起串慰,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎唱蒸,沒想到半個月后邦鲫,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡神汹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年庆捺,在試婚紗的時候發(fā)現(xiàn)自己被綠了古今。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡滔以,死狀恐怖沧卢,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情醉者,我是刑警寧澤但狭,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站撬即,受9級特大地震影響立磁,放射性物質發(fā)生泄漏。R本人自食惡果不足惜剥槐,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一唱歧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧粒竖,春花似錦颅崩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至朽砰,卻和暖如春尖滚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瞧柔。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工漆弄, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人造锅。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓撼唾,卻偏偏與公主長得像,于是被迫代替她去往敵國和親哥蔚。 傳聞我的和親對象是個殘疾皇子倒谷,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內容