【Java】String、StringBuffer 和StringBuilder 的區別

一.定義

先從類定義來看看:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
......
}
public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{
......
}
 public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{
......
}
  • 都是用final修飾,不可被繼承,即沒有子類,可以防止子類覆蓋情況的發生。
  • String 實現了Comparable<String>
  • StringBuffer 和 StringBuilder 都繼承了AbstractStringBuilder類。
  • 三者均實現了java.io.Serializable, CharSequence接口。

二、無參數構造函數

1. 首先,看一下String的無參構造函數源代碼:

public String() {
    this.value = new char[0];
}
  • 該函數創建了一個空字符序列(character  sequence)對象。由於字符串是不可變的,所以該構造函數沒有必要用。
private final char value[];
  • this.value = new char[0],初始化一個空char數組value
  • value是一個字符數組

2.  然後我們看看StringBuilder()源代碼:

public StringBuilder() {
    super(16);
}

super(16)是父類 AbstractStringBuilder的構造函數:

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

同String一樣,value是一個字符數組:

char[] value;

StringBuilder()方法創建了一個大小16的字符數組 ,用於存儲字符。

3. 繼續看 StringBuffer():

public StringBuffer() {
    super(16);
}

StringBuffer的父類也是AbstractStringBuilder,所以同StringBuilder()一樣,創建了一個大小16的字符數組

由以上可以知道:

String()創建一個字符數組;

而StringBuffer()和StringBuffer()創建一個大小爲16的字符數組。

String的字符數組value是final的,而StringBuffer和StringBuffer則不是。

 

三、帶有初始字符串的構造函數

開發中常用的,創建一個指定字符串內容的對象,代碼如下: 

String s = new String("qqq");

StringBuilder sb = new StringBuilder("qqq");

StringBuffer  sb1 = new StringBuffer("qqq");

1. 首先看看new String("qqq")的源代碼

public String(String original) {

    this.value = original.value;

    this.hash = original.hash;

}

實際上,就是把參數original字符串對象的字符數組value和hash code直接賦值給新對象。我們已經知道value是字符數組,那麼hash是什麼呢,先看定義:

private int hash; // Default to 0

hash是字符串的哈希code,默認值是0。那麼哈希code計算將在後續說明。

2. 再看看new StringBuilder("qqq")源代碼:

public StringBuilder(String str) {

    super(str.length() + 16);

    append(str);

}

此處,

  • 第一步,初始化數組。super(str.length() + 16)AbstractStringBuilder構造函數:
AbstractStringBuilder(int capacity) {

    value = new char[capacity];

}

可以看出,這裏創建的字符數組容量=參數str大小的+16

  • 第二步,賦值。append(str)定義如下
@Override

public StringBuilder append(String str) {

    super.append(str);

    return this;

}

賦值時候,調用了父類方法super.append(str)

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;

}

如果str參數是null,則賦值“null”;

如果str參數非null,判斷是否有足夠的空間容納str字符串,如不夠則擴容,需要最小空間是:已用字符數(count)+str的字符數。

使用str.getChars方法將字符串中的字符複製到字符數組value中。

更新已用字符數count

3. 最後看看new StringBuffer ("qqq")源代碼

public StringBuffer(String str) {

    super(str.length() + 16);

    append(str);

}

StringBuffer構造方法與StringBuilder一樣,需要注意的是append方法是加了同步鎖synchronized

@Override

public synchronized StringBuffer append(String str) {

    toStringCache = null;

    super.append(str);

    return this;

}

 

四、字符數組value的區別

String使用字符數組value存儲字符串數據。其定義的value是有final修飾的,代表該變量不可變的,所以String對象也就是不可變的。定義代碼如下:

private final char value[];

StringBufferStringBuilder使用字符數組value存儲字符串數據。value來自父類AbstractStringBuilderValue變量是可變的。定義代碼如下:

char[] value;

 

五、線程安全的區別

  • String,由於value是final的。一旦創建就不會再被改變了,由於存儲的字符串不會被改變。所以一定程度上能使String對象強制變得線程安全了。
  • StringBuffer,對調用方法加了同步鎖synchronized,所以線程安全的。其中部分方法定義如下:
public synchronized StringBuffer append(StringBuffer sb) {

    toStringCache = null;

    super.append(sb);

    return this;

}

@Override

synchronized StringBuffer append(AbstractStringBuilder asb) {

    toStringCache = null;

    super.append(asb);

    return this;

}
  • StringBuilder則沒有加同步鎖,所以是非線程安全的。
@Override

public StringBuilder append(Object obj) {

    return append(String.valueOf(obj));

}

@Override

public StringBuilder append(String str) {

    super.append(str);

    return this;

}

 

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