首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 繼承機制詳解:方法覆寫、構造器鏈、設計判斷與組合優於繼承

     編輯:Java 相關知識

繼承核心:is-a 關係與代碼復用

extends 建立子父類關係,子類獲得父類非私有成員(字段、方法、嵌套類),實現實現繼承。Java 單繼承:一個類只能 extends 一個父類(默認 Object)。

方法覆寫:多態基石


class Monster {
    public void move() { System.out.println("Monster moves"); }
}

class FlyingMonster extends Monster {
    @Override public void move() { System.out.println("Flying"); }
}

class WalkingMonster extends Monster {
    @Override public void move() { System.out.println("Walking"); }
}

覆寫規則(編譯器強制)

@Override 註解:強烈建議

  • 編譯期驗證確實覆寫了父類方法(防拼寫錯誤、簽名不匹配)。
  • 文檔化意圖,提高可讀性。

構造器鏈:初始化義務

規則

  1. 子類構造器首行必須調用父類構造器(super(...) 或隱式 super())。
  2. 父類無無參構造器 → 子類必須顯式 super(args)。
  3. 構造器不繼承,但子類可通過 super 復用父類初始化邏輯。

完整初始化順序演示


class Base {
    int x = 1;           // 1. 父類字段顯式初始化
    { x = 2; }           // 2. 父類實例初始化塊
    Base() { x = 3; }    // 3. 父類構造器
}

class Derived extends Base {
    int y = 10;          // 4. 子類字段顯式初始化
    { y = 20; }          // 5. 子類實例初始化塊
    Derived() { y = 30; } // 6. 子類構造器
}

// new Derived() → x=3, y=30

實戰:構造器參數傳遞


class Vehicle {
    protected String brand;
    public Vehicle(String brand) { this.brand = brand; }
}

class Car extends Vehicle {
    private int doors;
    // 透傳品牌參數
    public Car(String brand, int doors) {
        super(brand);  // 必須首行
        this.doors = doors;
    }
}

字段遮蔽:不推薦,易誤用


class Base { int value = 1; }
class Derived extends Base { int value = 2; } // 遮蔽而非覆寫

Base b = new Derived();
System.out.println(b.value);        // 1 (父類字段)
System.out.println(((Derived)b).value); // 2 (子類字段)

最佳實踐:字段 private + getter/setter,避免遮蔽。

繼承設計判斷:is-a vs has-a

三種類關係

規則要求
方法簽名名稱、參數列表完全相同
返回類型相同或協變(子類型)
訪問權限≥ 父類(public > protected > default > private)
拋出異常≤ 父類(子類異常為父類異常子類型或無)
final/private/static父類方法有此修飾符 → 不可覆寫

判斷口訣

「A 是 B 的一種」→ 繼承
「A 有一個 B」→ 組合
「A 使用 B」→ 依賴/關聯

經典反例:正方形繼承長方形


// 違反 LSP
class Rectangle {
    protected int width, height;
    public void setWidth(int w) { width = w; }
    public void setHeight(int h) { height = h; }
}

class Square extends Rectangle {
    @Override public void setWidth(int w) { width = height = w; }
    @Override public void setHeight(int h) { width = height = h; }
}

// 使用方期望獨立設置寬高
void resize(Rectangle r) {
    r.setWidth(10);
    r.setHeight(20);
    assert r.getWidth() == 10 && r.getHeight() == 20; // Square 失敗!
}

修正:組合(Square 包含 Rectangle)或共同實現 Shape 介面。

設計方法論

自上而下(框架/領域建模)

  1. 識別核心抽象 → 定義抽象父類/介面。
  2. 提取共同字段/方法上拉。
  3. 定義模板方法/鉤子。
  4. 派生具體子類實現變化點。

自下而上(重構/遺留代碼)

  1. 編寫具體類滿足當前需求。
  2. 發現重複代碼/字段。
  3. 重構提取公共父類(Extract Superclass)。
  4. 引入介面解耦。

組合優於繼承:設計原則

  • 繼承 = 白盒復用:子類依賴父類實現細節,耦合極強,父類變更波及所有子類(脆弱基類問題)。
  • 組合 = 黑盒復用:通過介面依賴,內部實現可變,運行期可動態替換組件。

策略模式:用組合替換繼承


// 繼承版:每種移動一個子類,爆炸
class FlyingMonster extends Monster { ... }
class SwimmingMonster extends Monster { ... }

// 組合版:移動策略接口
interface MoveStrategy { void move(Monster m); }
class FlyStrategy implements MoveStrategy { ... }
class SwimStrategy implements MoveStrategy { ... }

class Monster {
    private MoveStrategy strategy;
    public Monster(MoveStrategy s) { this.strategy = s; }
    public void move() { strategy.move(this); }
    public void setStrategy(MoveStrategy s) { this.strategy = s; } // 運行期切換
}

// 使用
Monster m = new Monster(new FlyStrategy());
m.move();  // 飛行
m.setStrategy(new SwimStrategy());
m.move();  // 游泳

總結:何時用繼承

  1. is-a 關係明確(語義上子類是父類特化)。
  2. 需共享實現代碼(非單純契約復用)。
  3. 父類穩定(不常變更,或變更可控)。
  4. 層次淺(≤ 3 層,深層繼承維護困難)。

「優先組合,必要時繼承」—— 設計模式核心智慧。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
關係關鍵字判斷標準代碼體現
無關—無交互獨立類
使用 (has-a)組合/聚合A 擁有 B 對象class House { Door door; }
繼承 (is-a)extendsA 是 B 的一種class Boss extends Monster