StringBuffer类用于字符串的拼接,由于它动态改变字符数组的长度并且事先会留有冗余长度,而不是像String拼接一样不断的重新创建新的String对象,所以速度上StringBuffer是快过String类的
从StringBuffer源码可以看到它是继承了AbstractStringBuilder类的,这个类暂时先不管他,我们主要看一下StringBuffer类的append方法
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
我们主要看参数为String类型的append方法,其余重载方法要么调用该方法要么也与其实现方式大同小异
先不管这个toStringCache参数,字面意思上可以大概知道该参数是用来做缓存的,我们从这个方法进入到其父类AbstarctStringBuilder的append方法中
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;
}
先看第一步,这里调用了appendNull方法,看看这个方法
private AbstractStringBuilder appendNull() {
int c = count;
ensureCapacityInternal(c + 4);
final char[] value = this.value;
value[c++] = 'n';
value[c++] = 'u';
value[c++] = 'l';
value[c++] = 'l';
count = c;
return this;
}
这个方法实际上就是拼接了“null”字符串,回到第二步,看实现字符数组长度适应的ensureCapacityInternal方法
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
这个方法传入了一个最小长度,如果当前字符数组长度不够,便调用Arrays.copyOf方法改变value长度,看看这个方法
public static char[] copyOf(char[] original, int newLength) {
char[] copy = new char[newLength];
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
这个copyOf方法中创建了一个新的字符数组,并用arraycopy方法将原来数组的内容存了进行
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) 代码解释: Object src : 原数组 int srcPos : 从元数据的起始位置开始 Object dest : 目标数组 int destPos : 目标数组的开始起始位置 int length : 要copy的数组的长度 将src数组从srcPos位置开始,复制长度为length的内容到dest数组,起始位置为destPos
回到ensureCapacityInternal方法,可以看到它让value重新指向了这个新创建的字符数组从而实现了数组长度的变化
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
在看append方法的第三部,调用了String.getChars方法进行内容的拼接,而getChars在一系列校验之后最终是调用native层的getCharsNoCheck方法,
native void getCharsNoCheck(int start, int end, char[] buffer, int index) 代码解释: int start : 起始位置 int end : 终止位置 char[] buffer : 目标数组 int index : 目标数组的开始起始位置 将该字符串从start到end的内容复制到buffer数组,起始位置为index
于是再看AbstractStringBuilder.append方法,便一目了然,最后改变一下当前的字符内容长度并返回
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;
}