代碼結構
class A {
String show(D obj) { return "A and D"; }
String show(A obj) { return "A and A"; }
}
class B extends A {
String show(B obj) { return "B and B"; }
String show(A obj) { return "B and A"; } // 重寫
}
class C extends B {}
class D extends B {}
調用規則口訣
- 編譯期看靜態類型:引用變量類型決定候選方法集合(重載解析)。
- 運行期看動態類型:實際對象類型決定最終執行哪個覆寫方法(動態分派)。
- 參數匹配遵循「就近原則」:子類參數優於父類參數。
逐行解析
| 代碼 | 靜態類型 | 動態類型 | 參數類型 | 選中方法 | 輸出 |
| a1.show(b) | A | A | B | A.show(A) | A and A |
| a1.show(c) | A | A | C | A.show(A) | A and A |
| a1.show(d) | A | A | D | A.show(D) | A and D |
| a2.show(b) | A | B | B | B.show(A) [重寫] | B and A |
| a2.show(c) | A | B | C | B.show(A) [重寫] | B and A |
| a2.show(d) | A | B | D | A.show(D) [B無重載] | A and D |
| b.show(b) | B | B | B | B.show(B) | B and B |
| b.show(c) | B | B | C | B.show(B) [就近] | B and B |
| b.show(d) | B | B | D | A.show(D) [繼承] | A and D |
關鍵點總結
a2 編譯類型為 A,故只能看到 A 中聲明的 show(A) 和 show(D);運行時 a2 為 B 對象,show(A) 被 B 重寫,故調用 B.show(A)。
B 未重載 show(D),故 b.show(d) 走向父類 A.show(D)。
C、D 均繼承自 B,作為參數時向上轉型為 B,匹配 B.show(B)(就近原則)。
避坑建議
- 避免在繼承鏈中混用重載與重寫,易產生認知偏差。
- 若需多態行為,優先使用單一方法名 + 參數為基類類型,配合
instanceof 或 Visitor 模式分發。
- 使用
@Override 註解強制編譯器檢查重寫正確性。