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

Java String 類深度解析:不可變設計、核心方法與效能優化

     編輯:Java 綜合教學

Java String 完全指南:從源碼級實現到最佳實踐

java.lang.String 是 Java 使用頻率最高的類別之一。理解其不可變設計、內部結構、核心方法實現與效能特性,對寫出高品質 Java 代碼至關重要。本文基於 JDK 17+ 源碼深度解析。

一、核心結構與不可變設計

public final class String
    implements java.io.Serializable, Comparable, CharSequence {
    
    /** 字元儲存陣列 (JDK 9+ 為 byte[] + coder,節省記憶體) */
    @Stable
    private final byte[] value;   // JDK 9+ Compact Strings
    
    /** 編碼標識:0=LATIN1(ISO-8859-1), 1=UTF-16 */
    private final byte coder;
    
    /** 雜湊碼快取,0 表示未計算 */
    private int hash; 
    
    // ... 建構子與方法
}

關鍵設計決策

  • final 類別:禁止繼承,確保不可變性不被破壞
  • final 字段:value、coder 初始化後不可變,保證執行緒安全
  • Compact Strings (JDK 9+):純 ASCII/Latin-1 字串使用 byte[] (1 byte/char),含 Unicode 字元才用 char[] 等效的 byte[] (2 bytes/char),大幅降低記憶體佔用
  • Hash 快取:hash 欄位避免重複計算 hashCode(),首次計算後永久快取

二、常用建構子與建立方式

方式範例說明
字面常量String s = "Hello";編譯期確定,存入字串常量池,相同內容共用引用
new String()new String("Hello")運行期在堆上建立新物件,不進入常量池,除非呼叫 intern()
valueOf()String.valueOf(123)靜態工廠方法,處理 null 安全,各基本型別均有重載
字元陣列new String(char[])複製字元陣列內容,確保不可變性

三、核心方法源碼級解析

1. equals() - 內容比較標準實現

public boolean equals(Object anObject) {
    if (this == anObject) return true;           // 同一引用快速返回
    if (anObject instanceof String) {
        String another = (String) anObject;
        if (this.coder == another.coder) {       // 相同編碼直接比較位元組
            return equals(value, another.value);
        }
        return false;                            // 編碼不同必不相等
    }
    return false;
}

// 位元組陣列逐位元組比較 (內部靜態方法)
private static boolean equals(byte[] v1, byte[] v2) {
    if (v1.length != v2.length) return false;
    for (int i = 0; i < v1.length; i++) {
        if (v1[i] != v2[i]) return false;
    }
    return true;
}

效能優化:先比長度、編碼,再逐位元組比較,利用 CPU 快取局部性。

2. hashCode() - 多項式滾動雜湊

public int hashCode() {
    int h = hash;
    if (h == 0 && !value.length == 0) {
        hash = h = isLatin1() ? hashCode(value) : hashCode(value);
    }
    return h;
}

private static int hashCode(byte[] value) {
    int h = 0;
    for (byte v : value) {
        h = 31 * h + (v & 0xff);
    }
    return h;
}
  • 經典 31 倍數算法:s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
  • 31 為質數且 31*i == (i<<5)-i,JVM 可優化為位移與減法
  • 結果快取於 hash 欄位,後續呼叫 O(1)

3. substring() - JDK 7+ 的重大變更

JDK 6 舊實現:共用底層 char[],透過 offset、count 視窗。缺點:長字串的小子串會阻礙原大字串被 GC,導致記憶體洩漏。

JDK 7+ 新實現:完全複製所需字元範圍到新陣列。優點:子串獨立,無記憶體洩漏風險。代價:O(n) 時間與空間。

4. concat() vs + 操作符

// concat 實現:建立新 byte[] 複製雙方內容
public String concat(String str) {
    int len = str.length();
    if (len == 0) return this;
    byte[] buf = new byte[value.length + str.value.length];
    System.arraycopy(value, 0, buf, 0, value.length);
    System.arraycopy(str.value, 0, buf, value.length, str.value.length);
    return new String(buf, coder);
}
  • + 操作符編譯期優化為 StringBuilder.append() 鏈式呼叫,單行表達式效能最佳
  • 迴圈中拼接 必須手動使用 StringBuilder,避免每次迭代建立新 StringBuilder
  • concat() 適合簡單兩字串拼接,無建構子開銷

5. split() - 正則表達式效能陷阱

public String[] split(String regex, int limit) {
    // 簡單單字元分隔符 (非正則元字元) 走快速路徑
    // 否則委派給 Pattern.compile(regex).split(this, limit)
}

效能建議:

  • 固定單字元分隔 (如 ",", "|") 效能極佳,走快速路徑
  • 複雜正則預先編譯 Pattern 重複使用
  • 大量資料考慮手寫解析或 StringTokenizer (舊 API,不推薦新代碼)

6. join() - JDK 8+ 靜態工廠方法

String joined = String.join(", ", "apple", "banana", "orange");
// "apple, banana, orange"

List list = List.of("a", "b", "c");
String joined2 = String.join("-", list);
// "a-b-c"

內部使用 StringJoiner,自動處理分隔符邏輯,程式碼簡潔優雅。

四、字串常量池與 intern()

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

System.out.println(s1 == s2);  // false (堆 vs 常量池)
System.out.println(s1 == s3);  // true  (intern 返回常量池引用)
  • 字面常量自動進入常量池 (編譯期)
  • intern():若常量池已有相同內容返回池中引用,否則將此字串加入池並返回引用
  • JDK 7+ 常量池移至堆中,不再佔用永久代/元空間,GC 可回收無引用常量
  • 大量動態字串去重可用 intern(),但需注意同步鎖爭用效能影響

五、效能優化黃金法則

  1. 迴圈拼接用 StringBuilder:預估容量 new StringBuilder(1024) 避免擴容
  2. 避免無意識物件建立:String s = new String("literal") → String s = "literal"
  3. 大字串分割考慮索引而非 split:indexOf + substring 避免正則開銷
  4. 頻繁比較長字串先比 hashCode:if (s1.hashCode() == s2.hashCode() && s1.equals(s2))
  5. 使用 Text Blocks (JDK 15+) 替代多行字串拼接,可讀性與效能並重

六、JDK 17+ 新增實用方法

方法描述
isBlank()判斷空白字串 (含空白字元),替代 trim().isEmpty()
strip()移除首尾空白字元 (Unicode 感知),優於 trim()
lines()返回行流 Stream,支援多種換行符
repeat(int)重複字串 N 次
indent(int)調整每行縮進 (正數增加、負數減少)
transform(Function)函數式轉換鏈式呼叫
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved