核心定義對比
| 概念 | 關鍵字 | 發生位置 | 目的 | 綁定時間 |
| 繼承 | `extends` | 類之間 | 復用父類屬性/方法,建立 is-a 關係 | 編譯期 |
| 重寫 | `@Override` | 子類覆蓋父類實例方法 | 多態:子類特化行為 | 運行期 (動態分派) |
| 重載 | 同名、參數列表不同 | 同一類/父子類 | 同名語義、不同參數處理 | 編譯期 (靜態分派) |
| 多態 | 父類引用指向子類對象 | 運行時 | 統一介面、動態行為 | 運行期 |
經典代碼示例
class Shape {
public boolean isSharp() { return true; }
public int getSides() { return 0; } // 父類預設
public int getSides(Triangle t) { return 3; } // 重載 1
public int getSides(Rectangle r) { return 4; } // 重載 2
}
class Triangle extends Shape {
@Override public int getSides() { return 3; } // 重寫
}
class Rectangle extends Shape {
public int getSides(int dummy) { return 4; } // 重載 (非重寫!參數不同)
}
public class PolymorphismDemo {
public static void main(String[] args) {
Triangle t = new Triangle();
System.out.println("Triangle sharp? " + t.isSharp()); // 繼承
Shape s = new Triangle(); // 多態:父類引用指向子類對象
System.out.println("Sides: " + s.getSides()); // 動態分派 → Triangle.getSides() = 3
// 重載靜態分派演示
Shape s2 = new Shape();
System.out.println(s2.getSides(t)); // 編譯期綁定 → Shape.getSides(Triangle) = 3
System.out.println(s2.getSides((Rectangle)null)); // 編譯期綁定 → Shape.getSides(Rectangle) = 4
}
}
關鍵區別深度解析
- 重寫 vs 重載:
- 重寫:方法簽名 (名稱+參數) **完全相同**,返回類型协变、拋出異常子集、權限不更嚴格。
- 重載:方法名相同,**參數列表不同** (類型/個數/順序),返回類型可任意。
- 多態 vs 重載:
- 多態解決「不知道具体类型但要调用同名方法」→ 動態分派 (`invokevirtual`)。
- 重載解決「同一語義不同參數」→ 靜態分派 (`invokestatic`/`invokevirtual` 但目標在編譯期確定)。
- 父類引用調用重載方法:`Shape s = new Triangle(); s.getSides(1);` → 編譯錯誤 (Shape 無 `getSides(int)`)。重載方法必須在聲明類型中存在。
設計建議
- **慎用重載**:參數類型層次關係易導致靜態分派混淆 (如 `null` 傳遞、自動拆箱)。改用不同方法名 (`saveUser`/`saveAdmin`)。
- **重寫必加 `@Override`**:編譯器檢查簽名一致性,防止誤寫成重載。
- **多態配合介面**:依賴抽象 (`List`/`Map`/`Runnable`),而非具體實現 (`ArrayList`/`HashMap`/`Thread`)。
- **final 封印**:不想被重寫的方法標記 `final`;不想被繼承的類標記 `final` (如 `String`、`Integer`)。