這是筆者撰寫的第三篇 Java 技術隨筆,感謝讀者們對前兩篇的支持與指正。筆者希望透過這種方式梳理知識體系,與同好共同精進。前兩篇分別探討了 JVM/JDK/JRE、集合框架、類加載器及設計模式,本篇將聚焦於 J2SE 階段尚未總結完畢的幾個核心議題:Object 類的 11 個方法、hashCode 與集合框架的關係、Class 類與反射機制,以及 synchronized 關鍵字的本質。
java.lang.Object 是所有 Java 類的始祖,意味著每個類都繼承了其 11 個方法。建議開發者直接閱讀源碼,理解默認實現邏輯。以 equals() 為例,默認使用 == 比較記憶體地址;toString() 則返回 getClass().getName() + "@" + Integer.toHexString(hashCode()) 格式的字串,清晰標識類名與哈希值。
hashCode() 被聲明為 native 方法,實現依賴於作業系統/JVM,源碼中僅有聲明。雖然可深究其生成算法,但掌握使用規範更關鍵:
equals() 返回 true,則 hashCode() 必須相等。equals() 返回 false 時,hashCode() 不強制要求不同(但最好不同以提高哈希表性能)。hashCode() 可能不同。其餘 notify()、notifyAll()、clone()、wait() 均為 native 方法,依賴 OS 實現。finalize() 類似 C++ 解構函數,簽名為 protected,默認體為空。其作用是通知 JVM 對象可被回收,但回收時機由 GC 決定。有趣的是,若為線程對象,finalize() 可能觸發優先級提升以加速銷毀,釋放記憶體。
有培訓講師誤以為 hashCode() 無實際意義,實則不然。其核心目的是支撐哈希表數據結構,提升查詢效率。Java 集合框架中 Hashtable、HashMap、HashSet 皆基於此原理。僅當使用這些集合時,才需重寫 hashCode() 以人為控制索引判定邏輯。
例:需生成 1,2,3,4 的不重複排列組合,使用 int[] 存儲並放入 HashSet。但數組未重寫 hashCode(),默認比較引用地址,導致內容相同的 A={1,2,3,4} 和 B={1,2,3,4} 哈希值不同,被視為不同元素。解決方案:重寫 toString() 返回 "1234",再令 hashCode() 返回 this.toString().hashCode()。因字串常量池機制,相同內容字串哈希值必同,從而正確去重。
反射是 Java 動態性的基石。C++ 無法在運行時通過字串類名獲取類完整信息並實例化調用,Java 則憑藉類在內存中包含「自我描述信息」實現此能力。核心類為 java.lang.Class 與 java.lang.reflect.Method。
典型應用——解析 JavaBean 屬性:
Class.forName("Bean類名") 獲取 Class 對象。getMethods() 獲得 Method[]。getXxx 方法名後綴即屬性名。newInstance() 創建實例,再用 invoke() 動態調用方法。更複雜的用法可參考 Class 與 Method 的 API 文檔。
synchronized 本質是對象鎖(實例方法鎖對象,靜態方法鎖 Class 對象)。其原理極簡:阻止第二把鎖加在同一對象/類上。例:
class A {
synchronized void f() {}
void g() {}
}
線程 1 調用 a.f() 時持有 a 的鎖,線程 2 無法調用 a.f()(嘗試加第二把鎖),但可調用 a.g()(無鎖競爭)。理解此點有助於精準設計同步策略,避免過度加鎖影響並發性能。
