StringBuffer 与 StringBuilder 的源码分析与差异
在 Java 开发中,StringBuffer
和 StringBuilder
是两个常用的字符串操作类,尤其在需要频繁拼接字符串时,它们比直接使用 String
更高效。本文将从源码角度深入分析两者的差异,探讨它们的实现原理及 API,并总结适用场景。
1. 基本概述
StringBuffer
:线程安全的可变字符串类,诞生于 JDK 1.0,主要用于多线程环境下。StringBuilder
:非线程安全的可变字符串类,引入于 JDK 1.5,适用于单线程场景,性能更高。
两者的核心功能都是通过字符数组实现字符串的动态拼接,但线程安全性和性能是它们的主要区别。
2. 源码分析
2.1 继承关系与基本结构
StringBuffer
和 StringBuilder
都继承自抽象类 AbstractStringBuilder
,后者封装了字符数组操作的核心逻辑:
java
abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value; // 存储字符的数组
int count; // 当前字符数
// ...
}
value
是底层的字符数组,初始容量默认 16(无参构造)。count
表示当前已使用的字符数。
StringBuffer
和 StringBuilder
只是覆写了部分方法,添加了线程安全相关的控制。
2.2 构造方法
两者的构造方法类似,默认容量为 16,源码如下:
java
// StringBuffer
public StringBuffer() {
super(16);
}
// StringBuilder
public StringBuilder() {
super(16);
}
如果指定初始字符串,会在字符串长度基础上再加 16:
java
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
2.3 核心方法:append
append
是字符串拼接的核心方法,源码在 AbstractStringBuilder
中实现:
java
public AbstractStringBuilder append(String str) {
if (str == null) str = "null";
int len = str.length();
ensureCapacityInternal(count + len); // 确保容量足够
str.getChars(0, len, value, count); // 复制字符
count += len;
return this;
}
- 容量扩展 :如果当前数组容量不足,会调用
ensureCapacityInternal
进行扩容,扩容策略为(value.length << 1) + 2
,即当前容量翻倍加 2。 - 字符复制 :通过
getChars
将新字符串追加到value
数组。
StringBuffer 的线程安全
StringBuffer
的 append
方法加了 synchronized
关键字:
java
public synchronized StringBuffer append(String str) {
toStringCache = null; // 清除缓存
super.append(str);
return this;
}
每次操作都会加锁,确保多线程环境下数据一致性。
StringBuilder 的非线程安全
StringBuilder
的 append
直接调用父类实现,没有同步:
java
public StringBuilder append(String str) {
super.append(str);
return this;
}
少了锁的开销,因此性能更高。
2.4 toString 方法
StringBuffer
:维护一个toStringCache
字段缓存上次的toString
结果,只有在修改后才会重建:
java
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache);
}
StringBuilder
:直接构造新字符串,无缓存:
java
public String toString() {
return new String(value, 0, count);
}
StringBuffer
的缓存设计是为了减少多线程环境下重复构造的开销,但实际性能提升有限。
3. API 对比
两者的 API 几乎一致,主要包括:
append
:追加字符串、字符等,支持多种重载。insert
:在指定位置插入数据。delete
:删除指定范围的字符。replace
:替换指定范围的字符。reverse
:反转字符串。substring
:提取子字符串。
区别在于:StringBuffer
的所有公共方法几乎都加了 synchronized
,而 StringBuilder
没有。
4. 性能与适用场景
- 线程安全 :
StringBuffer
:适合多线程环境,但因同步锁开销较大,性能较低。StringBuilder
:非线程安全,单线程环境下效率更高。
- 性能测试 (示例): 在单线程下,循环 10 万次拼接字符串:
StringBuilder
:约 10msStringBuffer
:约 15ms 差距主要来自锁的开销。
5. 总结
- 选择依据 :
- 多线程环境或遗留代码:用
StringBuffer
。 - 单线程或性能敏感场景:用
StringBuilder
。
- 多线程环境或遗留代码:用
- 源码差异 :
StringBuffer
通过synchronized
实现线程安全,增加了性能开销。StringBuilder
去掉同步,逻辑更轻量。
- 建议 :现代开发中,单线程场景占主导,
StringBuilder
是首选。若需线程安全,可结合Collections.synchronizedList
或Lock
实现更灵活的控制。