這是 Java 面試高頻題,也是日常開發選型依據。本文從源碼實現、記憶體模型、線程安全、效能基準四個維度深度剖析三者差異,並給出生產環境選型建議。
public final class String implements Serializable, Comparable<String>, CharSequence {
@Stable
private final byte[] value; // 字元資料
private final byte coder; // 0=LATIN1(1 byte/char), 1=UTF16(2 bytes/char)
private int hash; // hashCode 快取,0 表示未計算
// 所有修改操作返回新實例
public String replace(char oldChar, char newChar) {
if (oldChar == newChar) return this;
byte[] v = value.clone();
// ... 修改 v ...
return new String(v, coder);
}
}
關鍵點:final 修飾類與字段,不可繼承、不可變。JDK 9 Compact Strings 以 byte[] 替代 char[],ASCII 字串記憶體減半。
abstract class AbstractStringBuilder implements Appendable, CharSequence {
byte[] value; // 非 final,可擴容
int count; // 實際長度
// 擴容策略:2倍 + 2
void expandCapacity(int minimumCapacity) {
int newCapacity = value.length * 2 + 2;
if (newCapacity - minimumCapacity < 0) newCapacity = minimumCapacity;
value = Arrays.copyOf(value, newCapacity);
}
// 核心 append 邏輯
AbstractStringBuilder append(String str) {
if (str == null) return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getBytes(value, count, coder);
count += len;
return this;
}
}
public final class StringBuffer extends AbstractStringBuilder {
// 所有公共方法加 synchronized
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
public synchronized int indexOf(String str, int fromIndex) {
return super.indexOf(str, fromIndex);
}
// toString 也同步,並快取結果
public synchronized String toString() {
if (toStringCache == null) toStringCache = new String(value, 0, count, coder);
return toStringCache;
}
}
public final class StringBuilder extends AbstractStringBuilder {
// 無 synchronized,單執行緒極致效能
public StringBuilder append(String str) {
super.append(str);
return this;
}
}
synchronized,多執行緒共享單實例安全。但複合操作(如 if (sb.length() > 0) sb.deleteCharAt(0))仍需外部同步。count、value 數據競爭,出現長度錯誤、字元亂碼、甚至 ArrayIndexOutOfBoundsException。// 單執行緒 100 萬次 append("x")
String : ~4200 ms (大量物件分配、GC)
StringBuffer: ~12 ms (同步開銷可忽略,JIT 鎖消除)
StringBuilder: ~8 ms (基準線)
// 多執行緒 4 線程各 25 萬次 append
StringBuffer: ~15 ms (鎖競爭輕微)
StringBuilder: 數據錯誤、拋異常(非線程安全)
結論:單執行緒 StringBuilder 勝出;多執行緒共享緩衝區必須用 StringBuffer 或 ThreadLocal<StringBuilder>。
編譯期常量折疊,直接生成 "abc",零開銷。只有變數拼接才會隱式創建 StringBuilder。
JDK 6+ 引入偏向鎖、輕量級鎖、鎖消除。單執行緒下 StringBuffer 經 JIT 優化後,synchronized 幾乎零開銷。但 StringBuilder 仍是標準推薦,語義更清晰。
大量拼接時頻繁擴容(複製陣列)影響效能。建議預估容量:new StringBuilder(1024) 或 new StringBuffer(2048)。
需要字串拼接/修改?
├─ 否 → 直接用 String(常量、少量操作)
└─ 是 → 是否多執行緒共享同一實例?
├─ 是 → StringBuffer(或 ThreadLocal<StringBuilder>)
└─ 否 → StringBuilder(預設選擇,指定初始容量)
├─ 循環拼接 → 顯式 new StringBuilder()
├─ 日誌輸出 → 使用佔位符 log.info("user={}", name) 避免拼接
└─ JSON 序列化 → 使用 Jackson/Gson 而非手動拼接
byte[] + coderStringBuilder 擴容時同樣受益於 byte[] 複製更快String:不可變、線程安全、適合常量/少量操作。
StringBuffer:可變、同步、多執行緒共享安全。
StringBuilder:可變、非同步、單執行緒效能最優。
核心口訣:單執行緒用 Builder,多執行緒用 Buffer,其餘用 String。配合預估容量、避免循環隱式拼接,字串處理效能無憂。