可变性
- String是不可变的
- StringBuilder 和 StringBuffer 都继承自
AbstractStringBuilder
类,使用字符数组保存字符串,不过没有使用final
和private
关键字修饰,都是可变类,任何对它们的操作都不会产生新的对象。 - 两者的区别在于:StringBuilder 没有加锁不是线程安全的,而 StringBuffer 大多数方法都加了 synchronized,是线程安全的,但执行效率会低点。
最关键的是这个 AbstractStringBuilder
类还提供了很多修改字符串的方法比如 append
方法:
源码:
abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value;
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;
}
//...
}
StringBufferl类(继承自 AbstractStringBuilder
:
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;
}
// ......
}
- 源码中有一个细节,就是 StringBuffer 的 append 方法在执行真正的字符串拼接逻辑之前,会先清除 toStringCache,它是用来缓存最后一次 toString 结果的地方,主要用来加快 toString 的执行效率。
线程安全性
String
中的对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilder
是StringBuilder
与StringBuffer
的公共父类,定义了一些字符串的基本操作,如expandCapacity
、append
、insert
、indexOf
等公共方法。StringBuilder
并没有对方法进行加同步锁,所以是非线程安全的。- 而
StringBuffer
大多数方法都加了 synchronized,是线程安全的,但执行效率会低点。
性能
- 每次对
String
类型进行改变的时候,都会生成一个新的String
对象,然后将指针指向新的String
对象。 StringBuffer
每次都会对StringBuffer
对象本身进行操作,而不是生成新的对象并改变对象引用。- 相同情况下使用
StringBuilder
相比使用StringBuffer
仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
对于三者使用的总结:
- 操作少量的数据: 适用
String
- 单线程操作字符串缓冲区下操作大量数据: 适用
StringBuilder
- 多线程操作字符串缓冲区下操作大量数据: 适用
StringBuffer