| 特性 | 方法覆寫 | 字段隱藏 |
| 決策時機 | 運行時 (動態綁定) | 編譯時 (靜態綁定) |
| 訪問規則 | 看對象實際類型 | 看引用聲明類型 |
| 多態性 | 支援 | 不支援 |
| 關鍵字 | @Override 可驗證 | 無註解支援 |
本題執行流程
new B() 創建 B 實例,包含兩個 message 字段:
- 繼承自 A 的 message = "AAA"
- B 自己定義的 message = "BBB"
out() 方法定義在 A 中,未被覆寫
- 調用
b.out() 時:
- 編譯器看到引用類型為 B,但 B 沒有 out(),向上找到 A.out()
- 編譯期綁定:A.out() 內部的 message 解析為 A.message
- 運行時執行 A.out(),打印 this.message,此處 this 是 B 實例,但字段訪問已編譯期鎖定為 A.message
- 輸出:AAA
內存佈局圖解
B 實例內存:
+------------------------+
| A 部分 |
| message = "AAA" ←───┐│ ← A.out() 訪問此處 (編譯期綁定)
+------------------------+│
| B 部分 │
| message = "BBB" ←───┘│ ← B 類代碼中直接訪問此處
+------------------------+
如何訪問到 "BBB"?
// 方式 1:在 B 中覆寫 out()
class B extends A {
String message = "BBB";
@Override
void out() {
System.out.println(message); // 此處 message 綁定為 B.message
}
}
// 輸出:BBB
// 方式 2:強制轉型訪問
B b = new B();
System.out.println(b.message); // BBB (編譯期類型 B)
System.out.println(((A) b).message); // AAA (編譯期類型 A)
// 方式 3:反射 (運行時解析)
Field f = B.class.getDeclaredField("message");
System.out.println(f.get(b)); // BBB
最佳實踐建議
- 嚴禁父子類定義同名字段:極易導致困惑,IDE 會警告
- 字段私有化 + Getter/Setter:封裝內部狀態,避免直接訪問
- 常量用 static final:編譯期常量池優化,無隱藏問題
- 啟用編譯器警告:Eclipse/IDEA 設定 "Field hides another field" 為 Error
延伸思考:為什麼 Java 設計字段隱藏而非覆寫?
- 效能:字段訪問無需虛方法表查找,直接偏移量計算,極快
- 安全性:父類字段變更不影響子類邏輯(脆弱基類問題緩解)
- 語義清晰:字段代表「狀態」,方法代表「行為」;狀態不應多態,行為才多態
記住:只有方法才有多態,字段沒有多態。這是 Java 面試必考、實戰必避的核心陷阱。
- 上一篇文章: 計算機程序思維邏輯 (45):深度解析堆數據結構與算法實現
- 下一篇文章: Spring 整合 JPA 完整指南:LocalEntityManagerFactoryBean 注入、JpaTransactionManager 事務管理、EntityManager 生命週期
Copyright ©
程式碼之家 All Rights Reserved