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

Java String 核心機制深度解析:不可變性、常量池、拼接優化與比較陷阱

     編輯:Java 綜合教學

Java String 核心機制深度解析:不可變性、常量池、拼接優化與比較陷阱

String 是 Java 使用頻率最高的類,也是面試高發區。本文從源碼、內存模型、字節碼角度徹底講透 String 的不可變性、常量池機制、拼接性能、比較正確姿勢。

一、final 修飾:不可變的基石

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[]; // JDK 8 及之前
    // private final byte[] value; // JDK 9+ Compact Strings
    private final int hash; // 默認 0,惰性計算緩存
}
  • final class:不可被繼承,保證行為不可被子類覆寫破壞。
  • final char[] value:引用不可變,數組內容不可變(無修改方法),保證線程安全、哈希值穩定、可安全共享。
  • 設計初衷:安全性(網絡/文件路徑參數)、線程安全無需同步、字符串常量池共享、HashMap Key 高效。

二、任意操作產生新對象

String a = "abc123";
a.substring(3); // 返回新 String "123",a 仍為 "abc123"
System.out.println(a); // 輸出 abc123

所有「修改」方法 (substring、replace、concat、toUpperCase 等) 均返回新實例,原對象不變。

三、字符串常量池

JDK 6:永久代

JDK 7/8:堆中

JDK 9+:堆中,且默認使用 Compact Strings (byte[] + coder)

String s1 = "abcd";
String s2 = "abcd";        // s1 == s2 → true (同一常量池引用)
String s3 = "ab" + "cd";   // 編譯期常量折疊優化 → "abcd",s3 == s1 → true
String s4 = new String("abcd"); // 堆中新對象,s4 == s1 → false
String s5 = s4.intern();   // 手動入池,返回常量池引用,s5 == s1 → true

面試題:String a = new String("a"); 創建幾個對象?

  • 若常量池已有 "a":1 個 (堆中 new 對象)。
  • 若常量池無 "a":2 個 (常量池 "a" + 堆中 new 對象)。

四、字符串拼接性能優化

場景推薦方式原理
少量拼接 (已知行數)+ 操作符編譯期優化為 StringBuilder.append,字面量常量折疊
大量/循環拼接StringBuilder內部 char[] 擴容 (默認 16,2倍),避免產生大量中間 String 對象
多線程環境StringBuffer方法同步,線程安全,性能略低於 Builder
// 預估大小初始化,減少擴容
StringBuilder sb = new StringBuilder(100);
for (int i = 0; i < 100; i++) sb.append(i);
String result = sb.toString();

五、字符串比較:== vs equals

// ❌ 錯誤:比較引用地址
if (str1 == str2) { ... }

// ✅ 正確:比較內容值
if (str1 != null && str1.equals(str2)) { ... }

// ✅ 更安全:常量在左防 NPE
if ("expected".equals(str1)) { ... }

// ✅ 高性能:長度判空
if (str1 != null && str1.length() == 0) { ... } // 空串判斷
if (str1 == null || str1.isEmpty()) { ... }     // JDK 6+ isEmpty()

equals 源碼:先比引用 ==,再比長度,最後逐字符比較。

六、編譯期常量折疊

final String bb = "b";          // 編譯期常量
String b = "a" + bb;            // 編譯期優化為 "ab",入常量池
System.out.println("ab" == b);  // true

String bb2 = getBB();           // 運行期賦值,非常量
String b2 = "a" + bb2;          // 運行期 new StringBuilder().append("a").append(bb2).toString()
System.out.println("ab" == b2); // false

結論:final 修飾且編譯期可確定值的變量視為常量,參與常量折疊。

七、實戰建議清單

  1. 不可變對象可安全共享,作為 Map Key、Set 元素、緩存 Key 首選。
  2. 循環/大量拼接必用 StringBuilder,並預估容量。
  3. 比較內容用 equals,== 僅用於枚舉、常量池引用、同步鎖對象身份判斷。
  4. 避免 String.intern() 濫用 (JDK 7+ 池在堆,但仍可能佔用大量內存,需配置 -XX:StringTableSize)。
  5. 敏感信息 (密碼、Token) 用 char[] 代替 String,用完手動清零。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved