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

Java 字串處理核心三劍客:String、StringBuffer、StringBuilder 深度解析與效能實測

     編輯:Java 程式設計入門

核心差異速覽

特性StringStringBufferStringBuilder
可變性不可變可變可變
執行緒安全是 (不可變天然安全)是 (方法同步)否 (非同步)
效能 (單執行緒)最差 (頻繁建立新物件)中等 (同步開銷)最佳 (無同步)
適用場景常量、少量拼接、鍵值多執行緒共享緩衝區單執行緒大量拼接、建構器

原理解析

String:不可變的代價


String a = "a";      // 指向常量池 "a"
a = "b";             // 重新指向常量池 "b",原 "a" 仍存在 (若無引用則 GC)
String s = "ab";
s += "c";            // 實際執行:new StringBuilder().append(s).append("c").toString()
  • 每次修改都產生新物件,舊物件等待 GC,記憶體壓力大
  • 適合作為 Map Key、Set 元素、常量池複用

StringBuffer:同步的可變字串

  • 內部維護 char[] value 陣列,預設容量 16,擴容策略 newCapacity = (oldCapacity << 1) + 2
  • 所有公開方法加 synchronized,多執行緒安全但有鎖開銷
  • JDK 1.0 產物,早期單執行緒也廣泛使用

StringBuilder:現代首選

  • JDK 1.5 引入,API 與 StringBuffer 幾乎相同,但移除所有 synchronized
  • 單執行緒下效能顯著優於 StringBuffer
  • 非執行緒安全,多執行緒環境需自行同步或用 StringBuffer

效能實測代碼


public class StringPerfTest {
    private static final int ITERATIONS = 50_000;

    public static void main(String[] args) {
        testStringConcat();
        testStringBuffer();
        testStringBuilder();
        testLiteralConcat(); // 編譯期優化
    }

    static void testStringConcat() {
        String s = "abcd";
        long start = System.currentTimeMillis();
        for (int i = 0; i < ITERATIONS; i++) s += "add";
        System.out.printf("String += : %,d ms%n", System.currentTimeMillis() - start);
    }

    static void testStringBuffer() {
        StringBuffer sb = new StringBuffer("abcd");
        long start = System.currentTimeMillis();
        for (int i = 0; i < ITERATIONS; i++) sb.append("add");
        System.out.printf("StringBuffer.append: %,d ms%n", System.currentTimeMillis() - start);
    }

    static void testStringBuilder() {
        StringBuilder sb = new StringBuilder("abcd");
        long start = System.currentTimeMillis();
        for (int i = 0; i < ITERATIONS; i++) sb.append("add");
        System.out.printf("StringBuilder.append: %,d ms%n", System.currentTimeMillis() - start);
    }

    static void testLiteralConcat() {
        long start = System.currentTimeMillis();
        for (int i = 0; i < ITERATIONS; i++) {
            String s = "abcd" + "abcd" + "abcd"; // 編譯期常量折疊,極快
        }
        System.out.printf("Literal concat: %,d ms%n", System.currentTimeMillis() - start);
    }
}

實測結果分析 (典型輸出)


String += : 1,250 ms
StringBuffer.append: 2 ms
StringBuilder.append: 1 ms
Literal concat: 0 ms

MyEclipse 10000次測試 MyEclipse 50000次測試 DOS 環境測試

  • String +=:O(n²) 級別,隨迭代增長呈指數級變慢,嚴禁在迴圈中使用
  • StringBuffer/StringBuilder:O(n) 線性,差異僅在同步開銷 (約 1-2 倍)
  • 字面量拼接:編譯期常量折疊 (Constant Folding),直接生成單一常量,零開銷
  • 不同環境 (IDE/命令列/JIT 預熱) 絕對值差異大,相對比例穩定

最佳實踐指南

選型決策樹

  1. 編譯期常量拼接 → 直接用 + (編譯器優化)
  2. 單執行緒、方法區域變數拼接 → StringBuilder
  3. 多執行緒共享緩衝區 → StringBuffer
  4. 最終結果需不可變、作為 Key/常量 → String (由 Builder toString() 產生)

常見反模式修正

反模式問題修正
for(...) s += x;O(n²) 物件建立StringBuilder sb = new StringBuilder(); for(...) sb.append(x); return sb.toString();
StringBuffer 作為區域變數無謂同步開銷改用 StringBuilder
String.format() 在熱路徑解析格式字串開銷大改用 StringBuilder 或 MessageFormat 緩存
日誌 log.debug("msg" + obj)即使不輸出也執行拼接if (log.isDebugEnabled()) log.debug("msg" + obj); 或用 SLF4J 佔位符 log.debug("msg {}", obj)

進階補充

  • Java 9+ Compact Strings:Latin-1 字串採用 byte[] 節省 50% 記憶體
  • StringJoiner / String.join():Java 8 新增,適合分隔符拼接
  • Text Blocks (Java 15+):多行字串字面量,免去換行轉義
  • 逃逸分析:JIT 可將未逃逸的 StringBuilder 優化為標量替換,進一步消除分配
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved