JDK源碼分析系列---String,StringBuilder,StringBuffer

1.String

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

    //存儲字符,final修飾
    private final char value[];

    //緩存hash code,默認0
    private int hash;

    //序列號
    private static final long serialVersionUID = -6849794470754667710L;

    //聲明可序列化字段
    private static final ObjectStreamField[] serialPersistentFields =
        new ObjectStreamField[0];
}

1.1 基本屬性

  • char value[],用來存儲字符串對象的字符數組
  • int hash,用來緩存字符串的hash code,默認值爲0
  • long serialVersionUID,用來序列化的序列版本號
  • ObjectStreamField[],可序列化類的字段說明

1.2 經常使用構造器

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

初始化新建立的對象,表示空字符串""。請注意,此構造函數是不須要使用的,由於字符串是不可變的.
String str = new String(); 本質上是建立了一個空的字符數組,str的長度爲0 java

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

初始化新建立的對象,表示和參數同樣的字符串,換句話說是建立了和參數同樣的對象副本,除非須要顯示的聲明副本,不然該構造函數是不須要的,由於字符串是不可變的數組

public String(StringBuffer buffer) {
    synchronized(buffer) {
    this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
    }
}

把StringBuffer的內容複製到String對象中,隨後修改StringBuffer對象的值,並不會影響String對象緩存

public String(StringBuilder builder) {
this.value = Arrays.copyOf(builder.getValue(), builder.length());
}

把StringBuilder的內容複製到String對象中,隨後修改StringBuilder的值,並不會影響String對象;
此構造函數是爲了把StringBuilder轉移到String對象,可是推薦使用StringBuilder的toString()方法,由於運行更快安全

1.3 經常使用方法

//返回字符串的長度
public int length() {
    return value.length;
}

//比較兩個String值是否相等
public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

//生成hash code
public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        char val[] = value;

        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}
equals方法的判斷流程:
  • 首先判斷兩個對象是否相同,若相同返回true;若不一樣,下一步
  • 判斷參數是否爲String對象,若不是,返回false;如果,下一步
  • 判斷兩個String的長度是否相等,若不是,返回false;如果,下一步
  • 按字符數組索引依次比較字符,若是有任一不相同,返回false,不然返回true

這裏能夠看到equals方法的實現也是調用了==來比較對象是否相同,所以在討論equals和==區別的時候 要全面分析.多線程

1.2 爲何說String是不可變對象?

存儲字符的數組value[]是final修飾的,值不可更改.app

2. AbstractStringBuilder

可變的字符序列,StringBuilder和StringBuffer都繼承了該類,要了解StringBuilder和StringBuffer首先先了解AbstractStringBuilder.ide

2.1 基本屬性

abstract class AbstractStringBuilder implements Appendable, CharSequence {
    /**
     * The value is used for character storage.
     */
    char[] value;

    /**
     * The count is the number of characters used.
     */
    int count;
}

char[] value: 存儲字符的數組
int count: 存儲的字符的數量函數

2.2 構造器

/**
 * 無參構造器,用於子類序列化
 */
AbstractStringBuilder() {
}

/**
 * 指定字符數組容量
 */
AbstractStringBuilder(int capacity) {
    value = new char[capacity];
}

2.3 經常使用方法

/**
 * 返回字符的數量
 */
@Override
public int length() {
    return count;
}

/**
 * 返回當前可存儲字符的最大數量,即容量
 */
public int capacity() {
    return value.length;
}

/**
 * 保證當前容量大於等於指定的最小數量minimumCapacity,會調用擴容方法
 */
public void ensureCapacity(int minimumCapacity) {
    if (minimumCapacity > 0)
        ensureCapacityInternal(minimumCapacity);
}

/**
 * 擴容,只有minimumCapacity大於當前容量,纔會copy數組擴容
 */
private void ensureCapacityInternal(int minimumCapacity) {
    // overflow-conscious code
    if (minimumCapacity - value.length > 0) {
        value = Arrays.copyOf(value,
                newCapacity(minimumCapacity));
    }
}
/**
 * 當前對象拼接字符串str
 * 若是參數爲null,那麼最終字符串爲"null",若是參數類型爲boolean,那麼返回的是"true"或"false"
 * 例1: "abc".append(null) = "abcnull"
 * 例2: "abc".append(false) = "abcfalse"
 */
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;
}

3. StringBuilder

可變的字符序列,非線程安全,StringBuilder和StringBuffer的實現方法很類似,區別在因而否線程安全,在單線程的狀況下可以使用StringBuilder,由於它比StringBuffer運行更快.StringBuilder繼承了AbstractStringBuilder類.ui

3.1 基本屬性

繼承父類this

3.2 構造器

/**
 * 默認容量16
 */
public StringBuilder() {
    super(16);
}

/**
 * 指定初始容量
 */
public StringBuilder(int capacity) {
    super(capacity);
}

/**
 * 把String字符串初始化到對象中,容量變爲str的長度+16
 */
public StringBuilder(String str) {
    super(str.length() + 16);
    append(str);
}

/**
 * 把字符初始化到對象中,容量變爲字符的長度+16
 */
public StringBuilder(CharSequence seq) {
    this(seq.length() + 16);
    append(seq);
}

3.3 經常使用方法

同父類

4. StringBuffer

可變的字符序列,線程安全,StringBuffer繼承了AbstractStringBuilder類.

4.1 基本屬性

繼承父類,同時還有屬性toStringCache
這裏涉及到一個關鍵字transient,其做用是讓某些被修飾的成員屬性變量不被序列化,爲何不須要序列化呢?主要是由於這個變量可能爲null,也可能非空,不能準確的表明StringBuffer的屬性.因此沒有必要序列化,也節省了空間.

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

    /**
     * 最後一次調用toString返回值的緩存
     * 當StringBuffer被修改時該緩存被清除
     */
    private transient char[] toStringCache;

    /** 序列號 */
    static final long serialVersionUID = 3388685877147921107L;
}

4.2 構造器

同StringBuilder

4.3 經常使用方法

與StringBuilder方法基本相同,區別在於在StringBuilder的方法上加了synchronized鎖. 不一樣的地方還包括每一個修改對象的方法,好比append方法都會清除toStringCache緩存.

@Override
public synchronized StringBuffer append(String str) {
    toStringCache = null;
    super.append(str);
    return this;
}
@Override
public synchronized String toString() {
    if (toStringCache == null) {
        toStringCache = Arrays.copyOfRange(value, 0, count);
    }
    return new String(toStringCache, true);
}
  1. 在調用toString方法的時候,會先判斷緩存toStringCache是否存在,若是不存在,那麼把當前對象賦值給toStringCache,而後獲得toStringCache的字符串;若是toStringCache已經存在,那麼直接讀取緩存的字符串.
  2. toStringCache是否存在依賴於StringBuffer對象是否被修改,若是被修改了,那麼緩存被清空.

5.總結

  1. String,StringBuilder,StringBuffer這三個類均可以建立和操做字符串;
  2. 不一樣點在於String是不可變的,不存在線程安全問題;StringBuilder和StringBuffer字符串是可變的,StringBuilder線程不安全,StringBuffer線程安全;
  3. 對於簡單的字符串賦值和拼接操做,可以使用String
  4. 對於字符串頻繁修改和拼接操做,不建議使用String,由於每次操做都須要建立一個String對象,單線程推薦使用StringBuilder,多線程推薦使用StringBuffer.
相關文章
相關標籤/搜索