众所周知,Java中String
表示的字符串序列是不可变的,因此JDK提供了两种常用的可变字符序列的类,来实现字符序列的动态变化的功能。其中StringBuilder
在多线程场景下是不安全的,如果需要在多线程同步的场景下安全操作可变字符序列,推荐使用StringBuffer
。StringBuilder
和StringBuffer
都是扩展了AbstractStringBuilder
并实现CharSequence
的,通过源码剖析,我们发现StringBuilder
和StringBuffer
在实现上主要存在一下几点不同的地方。
1.线程安全
StringBuilder
和StringBuffer
具体功能的实现,基本上都是调用父类AbstractStringBuilder
的方法,最大的区别在于,所有涉及到线程安全保证的方法都使用到了synchronized
关键字修饰,使用对象锁的方式来实现当前实例得线程安全。
例如在StringBuilder
中,append(Sting str)
的方法实现如下:
public StringBuilder append(StringBuffer sb) {
super.append(sb);
return this;
}
而在StringBuffer
中,append(Sting str)
的方法实现如下:
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
2.toStringCache的使用
StringBuilder
和StringBuffer
还有一个最大的不同在于:StringBuffer
定义了一个transient
类型的临时变量toStringCache
来缓存toString()
当前的最新内容。该方法每次在调用toString()
时,需要先判断toStringCache == null
,因为每次在对当前的字符序列进行更新操作的时候,都会将toStringCache
置为null
,StringBuffer
对象每调用一次toString()
,都会将当前对象的字符序列内容拷贝一份到toStringCache
中。
/**
* A cache of the last value returned by toString. Cleared
* whenever the StringBuffer is modified.
*/
private transient char[] toStringCache;
public synchronized StringBuffer append(StringBuffer sb) {
toStringCache = null;
super.append(sb);
return this;
}
@Override
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
在调用toString()
时将当前对象的字符序列内容拷贝一份到toStringCache
中。有两个好处:1. 使用toStringCache
通过共享一个char[]
数组,提高构造String
的速度。2. 连续多次调用toString()
方法,是不需要重复调用Arrays.copyOfRange(value, 0, count)
,这样就不会产生多个内容相同的String
对象的,因为 new String(toStringCache, true)
返回的字符串,起内容会直接共享引用toStringCache
,因为考虑到构建速度,目前String
类中的该初始化方法暂不支持unshared
模式,具体可以看一下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;
}
但是toStringCache
这些好处仅仅是在多次调用 toString()
方法且当前的StringBuffer
对象没有发生改变时才能体现。实际情况下,很少会在未修改StringBuffer
的情况下重复调用StringBuffer
对象的toString()
方法,所以toStringCache
并没有太大的实际作用。关于toStringCache
存在 JDK 源码里的原因,有些人的解释是由于历史代码遗留的原因,保留它并没有什么坏处,修改了反而需要重新测试代码。
至此,本文就简单介绍了一下StringBuilder
和StringBuffer
的主要不同点,StringBuilder
和StringBuffer
最大的不同点还是在于他们是分别为线程不安全和线程安全的场景下设计出来的改变字符序列的类,其他并没有什么本质的区别。在不考虑线程安全的情况下,推荐使用StringBuilder
,因为性能会更好。
网友评论