聊一聊我所理解的String,StringBuffer和StringBuilder

String,StringBuffer和StringBuilder之間的區別,應用及底層實現已經是老生常談了.從開始接觸java到現在,只要是要複習或者是準備面試,這個題就是必須要經歷的.可以說是衆裏尋他千百度,驀然回首,他依舊在那等着你啊.
在這裏插入圖片描述
這裏,我就聊一下我對這3個類的理解~
**

1. String

**
首先,String這個老大哥底層是維持的是 final 修飾過的 private 權限的 char型數組.
在這裏插入圖片描述
這個final就很厲害呀,他修飾過之後,這個char型數組就成爲了一個不可變的數組,所以String對象就是不可變的咯.
因此,String對象就是相當於一個常量啦,自然也就是不存在線程安全問題,換言之就是線程安全的.

然後,在性能方面,因爲String對象是一個不可變的char型數組,所以每次改變String對象的時候都是會重新生成一個String對象,然後將新創建的String對象的地址賦給原來的變量.可想而知,如果存在大量的update操作時,使用String是多麼的浪費資源.
**

2.在分開聊StringBuffer和StringBuilder之前,先看個有意思的東西

**
在這裏插入圖片描述
通過查看繼承樹,我們發現,StringBuffer 和StringBuilder 都是繼承於一個抽象類AbstractStringBuilder.
這裏使用的就是設計模式中的建造者模式,AbstractStringBuilder作爲抽象建造者,已經寫好了各種方法,而StringBuilder和StringBuffer作爲具體建造者去實現抽象建造者.(具體的設計模式,我以後會寫再寫,到時候貼過來~ )
下面以用的最多的append()方法舉例:
我們都知道.StringBuffer和StringBuilder 都重載了很多append()方法,
查看源碼能發現:
StringBuilder的append(String str)方法:

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

StringBuffer的append(String str)方法:

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

其實不難發現,StringBuilder和StringBuffer的方法其實在實現上沒有很大區別,最大的就是StringBuffer在方法上加上了synchronized關鍵字.
然後我們看一下AbstractStringBuilder的屬性和構造器

abstract class AbstractStringBuilder implements Appendable, CharSequence {
    /**
     * The value is used for character storage. //該值用於字符存儲。
     */
    char[] value;  	//	這個就是我們具體持有的char型數組

    /**
     * The count is the number of characters used. //用於記錄 字符的個數
     */
    int count;

    /**
     * 一個必須的空參構造器
     */
    AbstractStringBuilder() {
    }

    /**
     * Creates an AbstractStringBuilder of the specified capacity.
     * 創建指定容量的AbstractStringBuilder
     */
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }

下面我們來具體聊一聊這哥倆~

3.StringBuilder和StringBuffer的初始化及擴容問題

首先,我們來看看StringBuilder底層實現原理, 他持有的是他的"爸爸"AbstractStringBuilder的屬性 char[ ] value,
而我們看一下StringBuilder的構造器:

  /**
     * StringBuilder的空參構造器
     * 這就告訴我們,在使用空參構造器創建StringBuilder對象的時候,直接調用了父類的有參構造器,使我們持有的char型數組的長度變爲了16.
     */
    public StringBuilder() {
        super(16);
    }

    /**
     * 如果們使用有參構造器來初始化StringBuilder對象長度,那麼我們就會將char型數組的初始化長度變爲我們想要的長度(capacity)
     *
     * @param      初始化長度(大於0,小於0就會報錯)
     * @throws     NegativeArraySizeException  if the {@code capacity}
     *               argument is less than {@code 0}.
     */
    public StringBuilder(int capacity) {
        super(capacity);
    }

    /**
     * 如果我們使用有參構造器在創建StringBuilder對象時傳入String字符串的話,那麼我們的char型數組的長度就變爲了字符串長度+16
     * @param   str   初始化時傳入的String字符串
     */
    public StringBuilder(String str) {
        super(str.length() + 16);
        append(str);
    }

其實不止是StringBuilder如此,StringBuffer也是這樣.
那麼,我們不禁有一個疑問,因爲這哥倆都是可變數組,那麼當數組長度大於了我們的初始化長度時怎麼辦呢?
答案就是 調用父類的 newCapacity方法進行擴容

	/**
	* @minimumCapacity 就是最小容量
	*
	*/
   private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        if (minimumCapacity - value.length > 0) { 
        //當最小容量大於持有數組長度時,我們將持有數組"複製"給 以最小容量長度的新數組,並替代持有數組
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity));
        }
    }
   private int newCapacity(int minCapacity) {
        // overflow-conscious code
        int newCapacity = (value.length << 1) + 2;
        //這就是我們的擴容機制: 原數組長度 * 2 + 2;
        if (newCapacity - minCapacity < 0) {
            newCapacity = minCapacity;
        }
        return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
            ? hugeCapacity(minCapacity)
            : newCapacity;
    }

由代碼中我們可以看出,基本擴容就是 原數組長度 * 2 + 2.即 如果我們初始化長度是 16,那麼第一次擴容之後的長度就變爲了
16 * 2 + 2 = 34;

4.StringBuilder和StringBuffer的線程安全問題:

StringBuilder是線程不安全的.StringBuffer是線程安全的.這中的關鍵就是 synchronized 關鍵字.
StringBuffer中所有操作持有的char型數組的方法中都加入了synchronized關鍵字來保證線程安全,
同時StringBuffer有一個獨有的
在這裏插入圖片描述
緩存屬性, 用來保存最後進行操作的值,來提升一定的性能.但是總體性能還是要低於StringBuilder.

**

5.三者使用的總結

**
1.在只有少量字符串或者極少量字符串改變的情況下, 可以考慮使用String.
2.在單線程或者使用其他方式確保了數據線程安全情況下,優先考慮使用StringBuilder.
3.在多線程或者沒有其他方式確保數據線程安全情況下,使用StringBuffer.

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