我的jdk源碼(四):StringBuffer 線程安全可多次修改String

一、概述

    StringBuffer類是我們動態操作字符串常用到的類,jdk1.8中StringBuffer繼承了父類AbstractStringBuilder類,並且在源碼內很多方法都是直接調用的父類AbstractStringBuilder的方法。和 String 類不同的是,StringBuffer 和 StringBuilder 類的對象能夠被多次的修改,並且不產生新的未使用對象。 接下來就讓我們進入到StringBuffer的源碼學習!

二、源碼分析

    (1) 類的聲明源碼如下:

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

    如源碼所示,final關鍵字修飾的類爲最終類,無法被繼承 。StringBuffer類不但繼承了AbstractStringBuilder類,還實現了Serializable接口和CharSequence接口。實現Serializable是爲了能序列化它的對象,具體在《我的jdk源碼(二):String 一個特殊而強大的類!》一文中有詳細介紹;實現CharSequence是爲了代表該類,或其子類是一個字符序列,具體在《我的jdk源碼(三):AbstractStringBuilder類》一文中有詳細介紹 。

    (2) 成員變量源碼如下:

    //StringBuffer定義了一個自己的toStringCache用於緩存
    private transient char[] toStringCache;

    static final long serialVersionUID = 3388685877147921107L;

    字符數組toStringCache緩存最後一次toString的內容,當被修改的時候這個cache清空,所以在後面的很多方法內都可以看到清空toStringCache的操作。 如果沒被修改,那麼這個toStringCache就是上一次toString的結果。 沒被修改的時候,就可以直接把toStringCache作爲new String的參數,然後把這個String返回就行了。 也就是cache有效的時候,就不必進行arraycopy的複製操作,cache失效了才進行arraycopy的複製操作。此番操作,作用在於保證線程安全的情況下,提升讀取效率。

    transient關鍵字修飾字符數組toStringCache,是爲了在進行序列化操作的時候,不將緩存的toStringCache內容進行數據持久化,也就是不會保存到內存中。 StringBuffer實現了Serializable接口,所以默認情況下,對象所有的變量都會轉變成持久狀態,但是toStringCache只是作爲讀取緩存,所以沒必須轉變成持久狀態。

    (3) 構造方法源碼如下:

    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);
    }

    構造函數都是調用父類構造方法,並且很直觀的反映了以下幾點:

        *  無參默認創建容量爲16的底層數組。

        *  傳入字符串時,容量爲字符串長度+16。即StringBuffer sb = new StringBuffer("abc");時,sb.length()值爲3,sb.capacity()值爲19。

        *  傳入整數時,則設定容量爲參數值 。

    (4) toString()方法源碼如下:

    @Override
    public synchronized String toString() {
        if (toStringCache == null) {
            toStringCache = Arrays.copyOfRange(value, 0, count);
        }
        return new String(toStringCache, true);
    }

        StringBuffer的toString方法與StringBuilder的toString方法有一點區別。這裏是通過toStringCache成員構造String對象然後返回的。因爲這裏創建String對象調用的是String類的String(char[] value, boolean share)構造方法,是共享字符數組的,以提高效率。 所以通過toStringCache來保證每次調用toString方法時得到的String對象是不變所結果。試想一下如果沒有使用toStringCache,而是直接共享了value,那麼在調用toString方法後,再對StringBuffer進行操作的時候之前返回的String對象就改變了,違背了String對象不變的設計理念。

    (5) writeObject()方法源碼如下:

    private synchronized void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
        java.io.ObjectOutputStream.PutField fields = s.putFields();
        fields.put("value", value);
        fields.put("count", count);
        fields.put("shared", false);
        s.writeFields();
    }

         在進行序列化的時候保存StringBuilder對象的狀態到一個流中。 但是toStringCache受到關鍵字transient的影響,不會被保存。

  (6) readObject()方法源碼如下:

    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        java.io.ObjectInputStream.GetField fields = s.readFields();
        value = (char[])fields.get("value", null);
        count = fields.get("count", 0);
    }

        反序列化時從流中獲取StringBuild對象序列化之前的狀態。

三、總結

     除了構造方法和少數幾個方法,其他方法都用synchronized修飾,所以線程安全,並且大多數方法都是調用的了父類AbstractStringBuilder的方法實現,想了解更多可查閱《我的jdk源碼(三):AbstractStringBuilder類》瞭解更多知識。另外值得一提的是,StringBuffer內容清空效率有幾種方法,但是 要通過使用sb.setLength(0);來清空StringBuffer對象中的內容效率最高,即調用setLength(int newLength)方法。StringBuffer類還有它的姊妹類StringBuilder類,那麼接下來,敬請期待《我的jdk源碼(五):StringBuilder 高效可多次修改String》。

    更多精彩內容,請掃描關注我的微信公衆號【Java覺淺】,獲取第一時間更新!

 

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