字串操作是 Java 開發最高頻場景之一,但 String、StringBuffer、StringBuilder 三者的效能特性、執行緒安全性、適用場景常被誤解。本文從 JVM 記憶體模型、字節碼層面、實測數據三個維度,給出最貼近生產環境的選型建議。
| 特性 | String | StringBuffer | StringBuilder |
|---|---|---|---|
| 可變性 | 不可變 | 可變 | 可變 |
| 執行緒安全 | 是(不可變天然安全) | 是(方法同步) | 否 |
| 底層結構 | final char[] / byte[] (Java 9+) | char[] / byte[] + 擴容機制 | char[] / byte[] + 擴容機制 |
| 主要適用場景 | 常量、少量拼接、金鑰 | 多執行緒共享緩衝區 | 單執行緒高頻拼接、建構器 |
// 編譯期常量折疊:等同於 "This is only a simple test"
String s1 = "This is only a" + " simple" + " test";
// 執行期新建:每次 + 產生新 StringBuilder → toString()
String s2 = "prefix";
String s3 = s2 + "suffix"; // 實際產生 StringBuilder、append、toString
結論:僅有「純字面量拼接」會被 javac 優化為單一常量;變數參與拼接仍會產生中間物件,頻繁操作導致大量臨時物件、GC 壓力。
// 原始碼
String s = "a" + "b" + var + "c";
// 反編譯等價碼
String s = new StringBuilder().append("a").append("b").append(var).append("c").toString();
每次拼接隱含 new StringBuilder()、多次 append、toString()(新建 String 複製陣列)。循環中拼接更是災難:
// 反模式:循環產生 N 個 StringBuilder + N 個 String
String result = "";
for (int i = 0; i < 10000; i++) result += i;
// 正確:單一 StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) sb.append(i);
String result = sb.toString();
// StringBuffer 核心方法皆 synchronized
public synchronized int length() { return count; }
public synchronized StringBuffer append(String str) { ... }
// StringBuilder 無同步修飾
public int length() { return count; }
public StringBuilder append(String str) { ... }
| 實現 | 平均耗時 (ms) | 相對效能 |
|---|---|---|
| StringBuilder | ~45 | 1.00x (基準) |
| StringBuffer | ~120 | 0.37x (慢 2.7 倍) |
原因:synchronized 涉及監視器進入/退出、記憶體屏障、偏向鎖撤銷等開銷。單執行緒場景零理由用 StringBuffer。
JVM 若證明 StringBuffer 實例未逃逸出方法(如局部變數),JIT 會執行鎖消除,效能接近 StringBuilder。但這非語言保證,依賴 JIT 編譯器決策,生產環境不可依賴。
byte[] + coder=0,節省 50% 記憶體。char[] 等價 byte[] + coder=1。String、StringBuffer、StringBuilder 共用此機制,無需代碼變更。
需要字串拼接/修改?
├─ 否 → 直接用 String(常量、金鑰、少量操作)
└─ 是 → 多執行緒共享同一緩衝區?
├─ 是 → StringBuffer(極少見,通常用 ThreadLocal/鎖包裹 Builder 更靈活)
└─ 否 → StringBuilder(預設首選,效能最佳)
if (sb.length() > 0) sb.deleteCharAt(0))仍需外部同步。ThreadLocal、方法局部變數、synchronized 區塊均可安全使用,且效能優於 StringBuffer。+ 可讀性更高,JIT 同樣優化;唯循環/大量拼接才須顯式 StringBuilder。String:不可變、常量池優化、適合唯讀/少量拼接。StringBuilder:單執行緒高效能可變字串,**預設首選**。StringBuffer:歷史遺留同步容器,**新代碼幾乎無使用場景**。Compact Strings 讓三者記憶體佔用大幅下降,升級即享紅利。下次 Code Review 看到循環裡 str += ... 或無腦用 StringBuffer,請大方產出這篇文章連結——選對工具,效能提升不止一個數量級。