核心差異總覽表
| 特性 | String | StringBuffer | StringBuilder |
| 可變性 | 不可變 | 可變 | 可變 |
| 執行緒安全 | 是(不可變天然安全) | 是(方法 synchronized) | 否 |
| 繼承體系 | final class | 繼承 AbstractStringBuilder | 繼承 AbstractStringBuilder |
| 效能 | 拼接產生新對象,較慢 | 同步開銷,比 Builder 慢 | 單執行緒最高效 |
| 適用場景 | 常量、少量拼接、鍵值 | 多執行緒共享緩衝區 | 單執行緒大量拼接、構建字串 |
| JDK 版本 | 1.0 | 1.0 | 5.0 |
底層原理解析
String:不可變的字元陣列
public final class String {
private final char value[]; // JDK 8
// private final byte[] value; // JDK 9+ Compact Strings
// ...
}
final 修飾陣列引用與類本身,保證不可變性。
- 每次拼接
s1 + s2 編譯器轉為 new StringBuilder().append(s1).append(s2).toString()。
- 字串常量池:字面量自動駐留,
intern() 手動駐留。
StringBuffer / StringBuilder:共享 AbstractStringBuilder
abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value; // 可變字元陣列
int count; // 實際長度
// 擴容邏輯:新容量 = 舊容量 * 2 + 2
void expandCapacity(int minimumCapacity) { ... }
// 核心 append 邏輯
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;
}
}
StringBuffer:同步包裝
public class StringBuffer extends AbstractStringBuilder {
@Override
public synchronized StringBuffer append(String str) {
super.append(str);
return this;
}
@Override
public synchronized int indexOf(String str) {
return super.indexOf(str, 0);
}
// 所有公共方法皆 synchronized
}
StringBuilder:無同步,JDK 5+ 引入
public class StringBuilder extends AbstractStringBuilder {
// 繼承所有 append/insert/delete 等方法,無 synchronized
// 單執行緒下效能優於 StringBuffer 約 10-15%
}
效能實測基準(JMH 參考)
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class StringConcatBenchmark {
@Benchmark
public void stringConcat() {
String s = "";
for (int i = 0; i < 1000; i++) s += "x"; // 極差
}
@Benchmark
public void stringBuffer() {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 1000; i++) sb.append("x");
}
@Benchmark
public void stringBuilder() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) sb.append("x");
}
}
/* 典型結果(ops/ms,越高越好):
stringBuilder >> stringBuffer > stringConcat
單執行緒下 Builder 最快,Buffer 因同步略慢,String 拼接最差(O(n²) 對象產生)
*/
選型決策樹
需要共享給多執行緒?
├── 是 → StringBuffer(或 ThreadLocal + 最後合併)
└── 否 → 單執行緒大量拼接?
├── 是 → StringBuilder(預設容量 16,可預估大小 new StringBuilder(1024))
└── 否 → 少量拼接/常量 → String(編譯器優化 + 常量池)
常見誤區與最佳實踐
| 誤區 | 正確做法 |
迴圈中用 + 拼接 | 改用 StringBuilder,或 Java 8+ Stream: list.stream().collect(Collectors.joining()) |
| 以為 StringBuffer 總是安全 | 複合操作(如 if (sb.length() > 0) sb.deleteCharAt(...))仍需外部同步 |
| 預設容量導致頻繁擴容 | 預估大概長度:new StringBuilder(estimatedSize) |
| 用 StringBuilder 做 Map Key | StringBuilder 非不可變,不可作 Key;轉 String 後使用 |
| JDK 9+ Compact Strings 未知 | Latin-1 字串改用 byte[] + coder,記憶體減半,String 效能提升 |
現代 Java 替代方案
String.join(delimiter, elements) (JDK 8+)
Collectors.joining() / Collectors.joining(delimiter, prefix, suffix)
String.format() / MessageFormat / StringBuilder 模板引擎
java.text.StringBuilder (JDK 11+ 新增 repeat(), strip() 等)
總結
- String:不可變、執行緒安全、適合常量與少量操作。
- StringBuffer:可變、執行緒安全(同步)、舊代碼遺留、多執行緒共享緩衝區。
- StringBuilder:可變、非執行緒安全、單執行緒高效能首選、JDK 5+ 標準替代品。
- 現代開發:單執行緒首選 StringBuilder,多執行緒用 ThreadLocal 或無鎖設計,極少直接用 StringBuffer。