以下解析基于JDK8.0
二者的继承层次是一样的:
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
都是final类,不能再派生子类,这一点跟String类一样。二者底层都是用字符数组char[]实现的,跟String不同的是,该字符数组并没有用final修饰,因此是可变的。二者的绝大多数方法和用来存储字符串的char[]都是来自它们的父类AbstractStringBuilder:
abstract class AbstractStringBuilder implements Appendable, CharSequence {
/**
* The value is used for character storage.
*/
char[] value;
StringBuilder和StringBuffer最大的区别就是前者是非线程安全的,后者是线程安全的,体现在代码上就是StringBuffer中的方法绝大多数都比StringBuilder中的方法多了一个synchronized修饰符,比如某一append方法:
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
从上面也可以看出都委托给了父类AbstractStringBuilder类的append方法。
StringBuffer比StringBuilder多了一个toStringCache字段
/**
* A cache of the last value returned by toString. Cleared
* whenever the StringBuffer is modified.
*/
private transient char[] toStringCache;
这个字段是在调用StringBuffer的toString方法时作为缓存使用的。我们可以看下StringBuffer中的toString方法是如何使用这个字段的:
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
可以看出,如果多次连续调用toString方法的时候由于这个字段的缓存就可以少了Arrays.copyOfRange的操作。(每次调用其他的修改StringBuffer对象的方法时,这些方法的第一步都会先将toStringCache设置为null,如上面的append方法等等。)new String中的第二个参数true代表每次调用toString方法返回的String对象共享toStringCache的值,我们可以看一下String类中的这个构造方法:
/*
* Package private constructor which shares value array for speed.
* this constructor is always expected to be called with share==true.
* a separate constructor is needed because we already have a public
* String(char[]) constructor that makes a copy of the given char[].
*/
String(char[] value, boolean share) {
// assert share : "unshared not supported";
this.value = value;
}
注释中也提到了shares value array for speed。
对于append方法,如果append的参数是一个空指针的话则会添加'n','u','l','l'四个字符:
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 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;
}
StringBuilder和StringBuffer默认的初始化容量都是16
/**
* Constructs a string builder with no characters in it and an
* initial capacity of 16 characters.
*/
public StringBuilder() {
super(16);
}
/**
* Constructs a string buffer with no characters in it and an
* initial capacity of 16 characters.
*/
public StringBuffer() {
super(16);
}
/**
* Creates an AbstractStringBuilder of the specified capacity.
*/
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
也可以在构造函数中指定初始化的容量大小:
/**
* Creates an AbstractStringBuilder of the specified capacity.
*/
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
如果用指定的String对象进行初始化则是首先将字符串的长度+16作为初始化的容量大小,再append该字符串:
/**
* Constructs a string builder initialized to the contents of the
* specified string. The initial capacity of the string builder is
* {@code 16} plus the length of the string argument.
*
* @param str the initial contents of the buffer.
*/
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
StringBuffer与该方法一样。
二者的扩容机制也是一样的,每次需要扩容的时候都是按照"旧容量*2+2"进行扩容,如果扩容之后仍不满足所需容量,则直接扩容到所需容量
/**
* This implements the expansion semantics of ensureCapacity with no
* size check or synchronization.
*/
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);
}
总结:
StringBuilder和StringBuffer的相同点:
1、继承层次相同;
2、底层都是用字符数组实现,字符串都是可变的;
3、都是将大部分操作委托给父类;
4、初始容量都是16和扩容机制都是"旧容量*2+2"
不同点:
1、StringBuilder不是线程安全的,StringBuffer是线程安全的
2、StringBuffer从JDK1.0就有了,StringBuilder是JDK5.0增加的
3、StringBuffer比StringBuilder多了一个toStringCache字段,用来在toString方法中进行缓存
4、由于StringBuilder没有考虑同步,在不会出现线程安全问题的情况下,性能上StringBuilder应该要优于StringBuffer
特殊点:
append一个空指针的时候会添加’n','u','l','l'四个字符,长度也同样增加4。