首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java String、StringBuffer、StringBuilder 區別詳解:源碼視角與選型指南

     編輯:Java 相關知識

前言

這是 Java 面試高頻題,也是日常開發選型依據。本文從源碼實現、記憶體模型、線程安全、效能基準四個維度深度剖析三者差異,並給出生產環境選型建議。

一、核心結構對比

1. String:不可變的 byte[] (JDK 9+)

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 字串記憶體減半。

2. AbstractStringBuilder:可變字元緩衝區基類

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;
    }
}

3. StringBuffer:同步包裝器

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;
    }
}

4. StringBuilder:無鎖高性能版

public final class StringBuilder extends AbstractStringBuilder {
    // 無 synchronized,單執行緒極致效能
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }
}

二、線程安全深度解析

  • String:不可變物件天然線程安全,可自由共享。
  • StringBuffer:方法級 synchronized,多執行緒共享單實例安全。但複合操作(如 if (sb.length() > 0) sb.deleteCharAt(0))仍需外部同步。
  • StringBuilder:非線程安全。多執行緒環境下會導致 count、value 數據競爭,出現長度錯誤、字元亂碼、甚至 ArrayIndexOutOfBoundsException。

三、效能基準測試(JMH 風格)

// 單執行緒 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>。

四、常見誤區辨析

誤區 1:String s = "a" + "b" + "c" 效率低

編譯期常量折疊,直接生成 "abc",零開銷。只有變數拼接才會隱式創建 StringBuilder。

誤區 2:StringBuilder 絕對比 StringBuffer 快

JDK 6+ 引入偏向鎖、輕量級鎖、鎖消除。單執行緒下 StringBuffer 經 JIT 優化後,synchronized 幾乎零開銷。但 StringBuilder 仍是標準推薦,語義更清晰。

誤區 3:預設容量 16 夠用

大量拼接時頻繁擴容(複製陣列)影響效能。建議預估容量:new StringBuilder(1024) 或 new StringBuffer(2048)。

五、生產環境選型決策樹

需要字串拼接/修改?
├─ 否 → 直接用 String(常量、少量操作)
└─ 是 → 是否多執行緒共享同一實例?
    ├─ 是 → StringBuffer(或 ThreadLocal<StringBuilder>)
    └─ 否 → StringBuilder(預設選擇,指定初始容量)
         ├─ 循環拼接 → 顯式 new StringBuilder()
         ├─ 日誌輸出 → 使用佔位符 log.info("user={}", name) 避免拼接
         └─ JSON 序列化 → 使用 Jackson/Gson 而非手動拼接

六、JDK 9+ Compact Strings 影響

  • 三大類底層統一為 byte[] + coder
  • ASCII 字串記憶體佔用減半,GC 壓力降低
  • 無需代碼修改,透明受益
  • StringBuilder 擴容時同樣受益於 byte[] 複製更快

七、總結

String:不可變、線程安全、適合常量/少量操作。
StringBuffer:可變、同步、多執行緒共享安全。
StringBuilder:可變、非同步、單執行緒效能最優。
核心口訣:單執行緒用 Builder,多執行緒用 Buffer,其餘用 String。配合預估容量、避免循環隱式拼接,字串處理效能無憂。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved