這些問題看似基礎,卻是無數開發者 (含資深者) 的痛點。搞不清這些,就談不上精通 Java。內容整理自 CSDN 經典老貼,逐一拆解。
String s = "Hello world!";
錯誤認知: 「宣告了一個內容為 Hello world 的 String 物件」
正確認知: 宣告了一個參考變數 s,型別為 String,目前指向字串常量池中的 "Hello world!" 物件。
String s = "Hello";
String s2 = s; // 未產生新物件!s2 指向同一個 "Hello"
s = s + " world"; // s 指向新物件 "Hello world",原 "Hello" 仍在常量池
結論: 參考變數存的是記憶體地址,非物件本身。new String("...") 強制建立新物件,"" 字面量共用常量池。
| 運算子/方法 | 比較內容 | 範例結果 |
|---|---|---|
== | 參考變數的值 (記憶體地址) | new String("foo") == new String("foo") → false |
equals() | 物件邏輯內容 (由類別定義) | new String("foo").equals(new String("foo")) → true |
// Object.equals() 預設實作
public boolean equals(Object o) { return this == o; }
警示: 自訂類別若未覆寫 equals(),行為同 ==。使用 HashSet/HashMap 等基於雜湊的集合時,必須同時覆寫 equals() 與 hashCode(),且邏輯一致。
String s = "Hello";
s = s + " world"; // s 變了嗎?
答案: 原物件 "Hello" 沒變,s 參考變數改指向新物件 "Hello world"。String 為 不可變 類別。
效能啟示: 大量字串拼接請用 StringBuilder (非執行緒安全、效能佳) 或 StringBuffer (執行緒安全)。
// 正確初始化:直接指向常量池
private String s = "Initial value"; // 多個實例共用同一物件
// 錯誤初始化:每次 new 產生新物件
private String s = new String("Initial value"); // 浪費記憶體與 CPU
final StringBuilder sb = new StringBuilder("immutable");
sb = new StringBuilder("oops"); // 編譯錯誤:無法改變參考指向
final StringBuilder sb2 = new StringBuilder("immutable");
sb2.append(" broken!"); // 編譯通過!物件內容可變
鐵律: final 僅鎖定參考變數的指向,不鎖定物件內部狀態。多執行緒共享 final 參考指向的可變物件,仍需同步機制 保護。
| 變數類型 | 初始化時機 | 預設值 | 備註 |
|---|---|---|---|
| 類別屬性 (instance) | 建立實例時 | 0/false/null/0.0... | 可指定初始值 |
| 類別屬性 (static) | 類別載入時 (首次使用前) | 同上 | 僅初始化一次 |
| 區域變數 (方法內) | 必須手動初始化 | 無 (編譯器強制檢查) | 使用前未初始化 → 編譯錯誤 |
| 方法參數 | 呼叫時由呼叫端提供 | 無 | 本質為區域變數 |
區域變數初始化技巧: 宣告即賦值預設值 (int i = 0; String s = null;),避免分支導致未初始化。
String s = "I am an Object!";
boolean isObject = s instanceof Object; // true
instanceof 為二元運算子 (保留字),測試左側物件是否為右側類別 (或子類別/實作介面) 的實例。
反模式: 用 instanceof + 強制轉型取代多型
// 差勁做法
if (bill instanceof PhoneBill) calcPhone((PhoneBill)bill);
else if (bill instanceof GasBill) calcGas((GasBill)bill);
// 正確做法:方法多載/覆寫
double calc(PhoneBill b) { ... }
double calc(GasBill b) { ... }
com.company.moduleOrderServiceorderIdMAX_RETRY_COUNTbyte[] buffer; (非 byte buffer[];)*)clone()、toString()main() (置底)method(arg) 不要 method( arg )if (i == 42) 非 if ((i) == 42)System.exit() 僅限 main() 呼叫RuntimeException 衍生類別,頂層 main() 捕獲並記錄所有異常try-with-resources),不仰賴 GCfinal 類別 (除非架構要求)protected 利於繼承StringBuilder、減少 synchronized 範圍System.lineSeparator() 或 println()PrintStream 已棄用,改用 PrintWriter內化這 6 大陷阱與風格規範,是從「會寫 Java」邁向「寫好 Java」的關鍵門檻。