| 錯誤寫法 | 問題 | 後果 |
|---|---|---|
if (str == "") | 比較記憶體位址,非內容 | 永遠為 false,邏輯失效 |
if (str.equals("")) | str 為 null 時拋 NPE | 程式崩潰 |
if (!str.equals("")) | 同上 | 程式崩潰 |
if (str != null && str.length() != 0) {
// 非空且非空字串
}
if (!"".equals(str)) {
// str 為 null 時回傳 false,不拋異常
}
if (str != null && !str.isEmpty()) { ... }
org.apache.commons.lang3.StringUtils.isNotBlank(str); // null、""、" " 皆視為空白
org.springframework.util.StringUtils.hasText(str); // 同樣語意
public class StringNullEmptyDemo {
public static void main(String[] args) {
test(null);
test("");
test(" ");
test("hello");
}
static void test(String value) {
if (value == null) {
System.out.println("value is null");
} else if ("".equals(value)) {
System.out.println("value is empty string");
} else {
System.out.println("value = \"" + value + "\"");
}
// 錯誤示範:永遠不會進入
if (value == "") {
System.out.println("ERROR: this line never prints");
}
}
}
value is null
value is empty string
value = " "
value = "hello"
public class StringAddressDemo {
public static void main(String[] args) {
String a = "hello";
String b = "he";
String c = a.substring(0, 2); // 執行期產生新物件
System.out.println(b.equals(c)); // true
System.out.println(b == c); // false
String d = new String("hello"); // 堆上新物件
System.out.println(d.equals(a)); // true
System.out.println(d == a); // false
String e = new StringBuilder("hello").toString(); // 執行期新物件
System.out.println(e == a); // false
String f = "hello"; // 常數池同一個
System.out.println(f == a); // true
System.out.println(f == "hello"); // true
System.out.println(f == "hell" + "o"); // true(編譯期常數摺疊)
String g = b + "llo"; // 執行期拼接 → 新物件
System.out.println(g == f); // false
String h = "he" + "llo"; // 編譯期常數摺疊 → 常數池
System.out.println(h == f); // true
}
}
equals() 或 Objects.equals()StringUtils.isBlank() 或 str == null || str.isEmpty()==,且必須確保字串來源為常數池或經過 intern()new String("literal"),直接用字面值String 看似簡單,實則涉及常數池、堆記憶體、編譯期優化等 JVM 核心機制。掌握「內容比較用 equals、位址比較用 ==、空值判斷用工具類」三原則,並理解常數摺疊與執行期建立物件的差異,能避開 99% 的 String 相關 Bug。