字串比較是 Java 開發中最高頻操作之一,卻也是最容易踩坑的領域。== 比較引用、equals() 比較內容、compareTo() 比較字典序,三者語義迥異,混用會導致邏輯錯誤、效能損耗、安全漏洞。本文系統梳理核心機制、常見陷阱、最佳實踐,並補充字串常量池、intern()、StringJoiner 等進階知識點。
| 方法/運算子 | 比較對象 | 返回值 | 適用場景 | 效能 |
|---|---|---|---|---|
== | 記憶體地址(引用) | boolean | 檢查是否同一物件實例、Enum 比較、String.intern() 後 | 極快(單指令) |
equals() | 字元序列內容 | boolean | 業務邏輯判斷字串內容相等 | O(n) 字元逐一比對 |
compareTo() | 字典序(Unicode 碼點) | int(負/0/正) | 排序、範圍判斷、字典序比較 | O(n) 至首個差異字元 |
String s1 = "Hello";
String s2 = "Hello";
String s3 = new String("Hello");
String s4 = new String("Hello");
s1 == s2 // true (常量池複用)
s1 == s3 // false (new 建立新物件)
s3 == s4 // false (兩個 new 物件)
s1.equals(s3) // true (內容相同)
鐵律:業務邏輯永遠使用 equals() 比較字串內容。== 僅用於:Enum、同步鎖物件、String.intern() 後的規範化引用比較、效能極度敏感且確定引用同源的場景。
"Hello")編譯期進入常量池,同類字面量指向同一物件。intern():若常量池已有內容相同字串,返回池中引用;否則將此字串加入池並返回引用。s3.intern() == s1 為 true。String.intern() 效能提升,但仍不建議濫用(佔用元空間/堆、全域鎖爭用)。String input = getInput();
if (input.equals("test")) { ... } // input 為 null 時拋 NullPointerException
// 安全寫法
if ("test".equals(input)) { ... } // 常量在前
if (Objects.equals(input, "test")) { ... } // JDK 7+ 推薦,null 安全
"apple".compareTo("banana") // 負數:a < b
"banana".compareTo("apple") // 正數:b > a
"apple".compareTo("apple") // 0:相等
// 錯誤用法
if (str1.compareTo(str2) == true) { ... } // 編譯錯誤:int 不能轉 boolean
// 正確用法
if (str1.compareTo(str2) < 0) { ... } // str1 字典序小於 str2
if (str1.compareTo(str2) == 0) { ... } // 內容相等(等同 equals)
compareTo 僅在 equals() 為 true 時返回 0,但返回 0 不保證 == 為真(不同物件同內容)。
charAt(k) 差值)。this.charAt(k) - another.charAt(k)。this.length() - another.length()。'A'(65) < 'a'(97)。需忽略大小寫用 compareToIgnoreCase() 或統一 toLowerCase()。String s = "literal" 而非 new String("literal")。+= 會產生大量中間物件。Comparable 使用 compareTo,或提供 Comparator。equalsIgnoreCase()、compareToIgnoreCase() 內部優化,優於手動 toLowerCase()。Pattern,避免重複編譯開銷。// 优雅拼接
StringJoiner sj = new StringJoiner(", ", "[", "]");
sj.add("apple").add("banana").add("orange");
// [apple, banana, orange]
// 一行搞定
String.join("-", "2024", "01", "15"); // 2024-01-15
Objects.equals(a, b)(null 安全)或 "const".equals(var)。a == b(Enum、intern、同步鎖)。a.compareTo(b) / compareToIgnoreCase。a.equalsIgnoreCase(b)。牢記核心原則:引用用 ==,內容用 equals,順序用 compareTo。將此決策樹內化為肌肉記憶,配合靜態分析工具(SpotBugs、SonarQube)攔截 == 比較字串,即可徹底杜絕字串比較類缺陷。