前言:咱們知道String類的修飾符是final,其char[] value也是由final修飾的,每次給String變量賦一個新值,都會建立一個新的String對象,不少有涉及到字符串自己的改變都是伴有(new String)的字樣,因此咱們說String類是不可變類。但StringBuffer類也是由final修飾的呀,爲何它就是可變類了呢?java
public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequence {
@Override
public synchronized StringBuffer append(String str)
{
toStringCache = null;
super.append(str);
return this;
}數組
}app
在此處能夠看出StringBuffer的append功能是繼承其父類AbstractStringBuilder的append方法。ide
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; }
private void ensureCapacityInternal(int minimumCapacity) { // overflow-conscious code if (minimumCapacity - value.length > 0) expandCapacity(minimumCapacity); }
void expandCapacity(int minimumCapacity) { int newCapacity = value.length * 2 + 2; if (newCapacity - minimumCapacity < 0) newCapacity = minimumCapacity; if (newCapacity < 0) { if (minimumCapacity < 0) // overflow throw new OutOfMemoryError(); newCapacity = Integer.MAX_VALUE; } value = Arrays.copyOf(value, newCapacity); }
此處咱們能夠知道,在AbstractStringBuilder中append方法的實現,是肯定容器"capacity"的大小,便於下面value(在StringBuffer中value是沒有final修飾符的)的從新賦值,因此這一步咱們能夠看做相似「踩點」的行爲。接下來纔是真正的力量:函數
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; }
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); }
* @param src the source array. * @param srcPos starting position in the source array. * @param dest the destination array. * @param destPos starting position in the destination data. * @param length the number of array elements to be copied. * @exception IndexOutOfBoundsException if copying would cause * access of data outside array bounds. * @exception ArrayStoreException if an element in the <code>src</code> * array could not be stored into the <code>dest</code> array * because of a type mismatch. * @exception NullPointerException if either <code>src</code> or * <code>dest</code> is <code>null</code>. */ public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
能夠看出真正進行「增加字符串」的操做是由系統方法arraycopy實現的,其中:性能
value是傳入的String str的值,也就是要添加進來的字符串;ui
srcBegin是指,指定的字符數組dst要在value的哪一個位置插入;this
dst是將要被插入的字符數組;spa
dstBegin是字符數組的起始位置;.net
srcEnd-srcBegin是value的長度。
也就是說,StringBuffer的append的實現實際上是System類中的arraycopy方法實現的,有點好奇arraycopy方法是怎麼實現的,參考了http://www.javashuo.com/article/p-yzlczjqx-ch.html的圖解,這裏的淺複製就是指複製引用值,與其相對應的深複製就是複製對象的內容和值:
因此咱們能夠知道,StringBuffer爲何是可變類。由於StringBuffer的append方法的底層實現就是建立一個新的目標對象,而後將各個字符引用串接起來,這就是一個新的對象了,本質上就是棧多了一個變量,而堆上面並無變化(爲何是在棧上呢?由於觸發到的arraycopy方法是native的,也就是其生成的變量會放到本地方法棧上面去)。
那爲何咱們沒有把StringBuffer劃分爲不可變類呢?它明明在棧中建立了一個新的對象。由於通常的對象和數組都會在堆中進行存儲,除非是發生了棧上分配現象。咱們相比較String對象的存儲,就能夠知道,StringBuffer對象在此處並不符合棧上分配的條件( 將線程私有的對象打散分配在棧上,能夠在函數調用結束後自行銷燬對象,不須要垃圾回收器的介入,有效避免垃圾回收帶來的負面影響,棧上分配速度快,提升系統性能),因此咱們能夠知道,StringBuffer的append方法並不會在堆上建立新的StringBuffer對象且內容是結果字符串,而是在arraycopy方法的幫助下,將各個字符引用鏈接起來。
結論:StringBuffer是一個可變類。