String 是 Java 中使用頻率最高、佔據堆內存最大的對象類型。深入理解其不可變性、常量池機制、substring 實現差異以及拼接優化策略,是寫出高性能代碼的必修課。本文從源碼層面剖析核心原理,並給出實戰優化建議。
char[] value、offset、count 永不改變。這天然保證了線程安全,無需同步即可在多線程間共享。String s = "abc")會直接引用常量池中唯一副本,intern() 可手動入池,極大節省內存。// JDK 7 源碼片段
public String(char value[], int offset, int count) {
this.value = value; // 直接引用原數組
this.offset = offset;
this.count = count;
}
隱患:長字符串僅截取短前綴時,原巨大 char[] 因被子串引用而無法 GC,導致「內存洩漏」。
// JDK 8 源碼片段 this.value = Arrays.copyOfRange(value, offset, offset + count);
此改動徹底解決洩漏,但微增 CPU 開銷。對於高頻截取場景,建議顯式 new String(str.substring(...)) 強制複製(JDK 7)或直接信任 JDK 8+ 默認行為。
| 方案 | 適用場景 | 性能 | 備註 |
|---|---|---|---|
split(regex) | 複雜正則分割 | ⭐⭐ | 編譯正則開銷大,熱路徑慎用 |
StringTokenizer | 單字符分隔符 | ⭐⭐⭐⭐ | 輕量級,無正則開銷 |
indexOf + substring 手寫循環 | 極致性能敏感 | ⭐⭐⭐⭐⭐ | 代碼冗長,可封裝為工具類 |
進階技巧:判斷前綴/後綴時,charAt 組合優於 startsWith/endsWith:
// 低開銷寫法
if (str.length() >= 3
&& str.charAt(0) == 'a'
&& str.charAt(1) == 'b'
&& str.charAt(2) == 'c') { ... }
StringConcatFactory 進一步優化 + 操作;synchronized;new StringBuilder(1024) 避免擴容時的 Arrays.copyOf 開銷;+= 循環拼接:編譯器雖會優化為 StringBuilder,但每次循環仍會創建新實例,顯式復用更高效。intern() 或常量類集中管理;StringBuilder 預分配容量;StringTokenizer 或手寫 indexOf 循環;new String(big.substring(...)) 強制斷開引用;log.debug("msg {}", param) 佔位符,避免字符串拼接後再判斷級別丟棄。「字符串看似微小,實則承載業務核心數據流。針對熱點路徑的每一次優化,都是對系統吞吐量的直接貢獻。」