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

Java String 源碼深度解析:不可變性、字元陣列、緩存機制與效能優化

     編輯:Java 綜合教學

Java String 源碼深度解析

java.lang.String 是 Java 使用頻率最高、面試必考的核心類別。本文基於 JDK 17 源碼,深度剖析不可變性實現、底層儲存演進、字串常量池、核心方法實現與效能優化陷阱。

不可變性鐵三角設計

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    // 1. final 類:不可被繼承,防止子類破壞不可變語義
    // 2. final 欄位:引用不可變更
    // 3. 無 setter / 修改狀態方法:所有「修改」操作回傳新實例
    
    // JDK 8 及之前
    private final char value[];          // 字元陣列
    private final int hash;              // 緩存 hashCode (默認 0 表示未計算)
    
    // JDK 9+ (Compact Strings) - 節省記憶體
    private final byte[] value;          // 位元組陣列
    private final byte coder;            // 0=LATIN1(1 byte/char), 1=UTF16(2 bytes/char)
    private final int hash;              // 同樣緩存
}

核心構造與建立路徑

關鍵源碼片段解析

1. hashCode 緩存計算

public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        h = isLatin1() ? hashCodeLatin1() : hashCodeUTF16();
        hash = h; // 欄位非 final 但邏輯不可變,利用 racy single-check 免鎖
    }
    return h;
}
private int hashCodeLatin1() {
    int h = 0;
    for (byte v : value) h = 31 * h + (v & 0xff);
    return h;
}

利用 hash == 0 為未計算標記,無需同步,極少數線程重複計算可接受。

2. equals 高效比較

public boolean equals(Object anObject) {
    if (this == anObject) return true;           // 同引用快速返回
    if (anObject instanceof String aString) {    // 模式匹配 (JDK 16+)
        return isLatin1() == aString.isLatin1()  // 編碼一致才比較
            && Arrays.equals(value, aString.value);
    }
    return false;
}

先比引用、再比編碼標識、最後比底層陣列,極致優化。

3. substring 演進:JDK 6 vs 7+

  • JDK 6:共享 `value` 陣列,`offset` + `count` 定義視窗 → 長字串 `substring` 導致原大陣列無法 GC(洩漏隱患)。
  • JDK 7+:複製新陣列,獨立擁有 → 空間換時間,消除洩漏風險。

4. 連接優化:StringBuilder 自動介入

String s = "a" + "b" + "c";        // 編譯期常量折疊 → "abc"
String s = a + b + c;              // 編譯器生成 StringBuilder.append().toString()
String s = ""; for (..) s += x;    // ❌ 循環內每次新建 StringBuilder,應手動提取

字串常量池與 intern() 實戰

String s1 = "abc";
String s2 = new String("abc");
String s3 = s2.intern();

s1 == s2; // false (堆 vs 常量池)
s1 == s3; // true  (intern 返回常量池引用)

// JDK 7+ 常量池移至堆中,GC 可回收無引用駐留字串
// 大量 intern() 可能導致 OOM,慎用

效能陷阱與最佳實踐

  1. 循環拼接用 StringBuilder:預估容量 `new StringBuilder(1024)` 避免擴容。
  2. 避免 `new String("literal")`:雙重物件浪費,直接用字面量。
  3. 正則分割/替換慎用 `String.split/replace`:內部編譯 Pattern,熱點路徑預編譯 `Pattern.compile` 復用。
  4. 大字串切片:JDK 7+ 複製陣列,超大字串頻繁 `substring` 考慮 `CharSequence` 視圖或 `StringBuilder` 操作。
  5. 序列化:`String` 實作 `Serializable`,`serialVersionUID` 固定,跨版本穩定。

JDK 9+ Compact Strings 記憶體省 50%+

  • 純 ASCII/Latin-1 字串:1 byte/char,`coder=0`。
  • 含補充字符:2 bytes/char,`coder=1` (UTF-16)。
  • 自動偵測,無需代碼變更,透明節省堆空間。

透徹掌握 String 源碼設計哲學,能寫出高效、無 Bug 的字串處理代碼,面試與架構設計雙贏。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
方式記憶體行為常量池關係
字面量 `"abc"`編譯期進入常量池,執行期直接引用自動駐留
`new String("abc")`堆上新建物件,`value` 指向常量池字元陣列 (JDK 8) 或複製 (JDK 9+)不自動駐留
`intern()`若常量池有相同內容返回引用,否則放入並返回手動駐留