AbstractStringBuilder有两个实现类,一个是StringBuilder,另一个是StringBuffer。AbstractStringBuilder要解决的是String对象不可变的问题。那么,StringBuffer和StringBuilder作为AbstractStringBuilder的实现类,区别又是什么呢?
可以看出,还是很不一样的。接下来,我们要深入阅读StringBuffer类了。
public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequence{
/**
* A cache of the last value returned by toString. Cleared
* whenever the StringBuffer is modified.
*/
private transient char[] toStringCache;
/** use serialVersionUID from JDK 1.0.2 for interoperability */
static final long serialVersionUID = 3388685877147921107L;
}
一上来,就被吓一跳呀有木有,怎么又有一个私有化的char数组?还被一个们见过的transient修饰了?带着好奇心,赶紧百度下transient是啥意思:
搜嘎,就是防止被序列化的嘛。为甚么要防止toStringCache被序列化呢?我们带着这个疑惑来继续往下读。
public StringBuffer() {
super(16);
}
public StringBuffer(int capacity) {
super(capacity);
}
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
public StringBuffer(CharSequence seq) {
this(seq.length() + 16);
append(seq);
}
请注意,重点来了,考试要考!多少次面试被问住了:请问,你知道StringBuffer默认长度多大吗?如果下次再遇到这个问题,请大声的告诉他:16!StringBuffer默认会创建一个长度为16的char数组来存储字符。而且,如果实例化StringBuffer时,如果传入的参数是String或者CharSequence,会创建一个改参数的长度加上16的char数组。我们现在来关注下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;
}
public AbstractStringBuilder append(CharSequence s) {
if (s == null)
return appendNull();
if (s instanceof String)
return this.append((String)s);
if (s instanceof AbstractStringBuilder)
return this.append((AbstractStringBuilder)s);
return this.append(s, 0, s.length());
}
public AbstractStringBuilder append(CharSequence s, int start, int end) {
if (s == null)
s = "null";
if ((start < 0) || (start > end) || (end > s.length()))
throw new IndexOutOfBoundsException(
"start " + start + ", end " + end + ", s.length() "
+ s.length());
int len = end - start;
ensureCapacityInternal(count + len);
for (int i = start, j = count; i < end; i++, j++)
value[j] = s.charAt(i);
count += len;
return this;
}
看出为什么要关注append()方法了吗?上一篇博文里提到了ensureCapacity()系列的方法,用处就在这里。保证了StringBuffer的空间长度是最小的。好了,这一部分的介绍告一段落了,接下来该放大招了。啥大招呢?先上源码:
@Override
public synchronized int length() {
return count;
}
@Override
public synchronized int capacity() {
return value.length;
}
@Override
public synchronized void ensureCapacity(int minimumCapacity) {
super.ensureCapacity(minimumCapacity);
}
@Override
public synchronized void trimToSize() {
super.trimToSize();
}
@Override
public synchronized void setLength(int newLength) {
toStringCache = null;
super.setLength(newLength);
}
....略....
相信,信息的你已经发现了什么。原来,这就是StringBuffer线程安全的奥秘啊!StringBuffer几乎重写了AbstractStringBuilder类的所有方法,用synchronized关键字修饰的方法包装了一层,来达到线程安全的目的!至于为什么要防止toStringCache被序列化呢?请看这里:
@Override
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
翻遍整篇StringBuffer的源码,只有这一处是给toStringCache赋值了,其他地方都是置空。不难想象,toStringCache存在的意义就是toString()的时候临时存储StringBuffer的char数组,当然不需要参与序列化了。请注意,即使此处的toString()方法,也是线程安全的!然鹅,String是非线程安全的:
String(char[] value, boolean share) {
// assert share : "unshared not supported";
this.value = value;
}
注意方法里的断言。。。