一、繼承的本質:消除冗余,構建邏輯關系
想象一個公司管理系統:普通銷售員工(
CommissionEmployee
)和帶底薪銷售員工(BasePlusCommissionEmployee
)共享大部分屬性(姓名、工號、銷售額、傭金率)。沒有繼承時,代碼會變成這樣:
// 普通銷售員工類
class CommissionEmployee {private String name;private String identityNumber;private double grossSales;private double commissionRate;// 構造器和方法...
}// 帶底薪銷售員工類
class BasePlusCommissionEmployee {private String name; // 重復private String identityNumber; // 重復private double grossSales; // 重復private double commissionRate; // 重復private double baseSalary; // 特有屬性// 構造器和方法...
}
這種設計存在三大問題:
- 代碼冗余:相同屬性在多處重復
- 維護成本高:修改公共屬性需同步所有類
- 關系缺失:未體現"帶底薪員工也是銷售員工"的邏輯
繼承解決方案:
class CommissionEmployee { /* 公共屬性 */ }
class BasePlusCommissionEmployee extends CommissionEmployee { private double baseSalary; // 僅定義特有屬性
}
二、訪問控制:protected與封裝的藝術
繼承中,子類如何訪問父類屬性?核心在于訪問修飾符:
class Parent {private int x; // 僅本類可訪問protected int y; // 子類可訪問
}class Child extends Parent {void demo() {// System.out.println(x); // 錯誤!private不可訪問System.out.println(y); // 正確,protected允許訪問}
}
最佳實踐:
class CommissionEmployee {private double grossSales; // 保持封裝// 通過protected方法暴露訪問protected double getGrossSales() { return grossSales; }
}class BasePlusCommissionEmployee extends CommissionEmployee {@Overridepublic double earnings() {// 通過getter安全訪問父類屬性return baseSalary + getGrossSales() * getCommissionRate();}
}
三、構造器:super()的初始化魔法
子類必須通過super()
調用父類構造器,確保繼承鏈完整:
class Employee {public Employee(String name, String id) {System.out.println("初始化員工基礎信息");}
}class Manager extends Employee {private String department;public Manager(String name, String id, String dept) {super(name, id); // 必須第一行this.department = dept;System.out.println("添加經理專屬屬性");}
}
多級繼承初始化順序:
class Person {public Person() { System.out.println("Person初始化"); }
}
class Employee extends Person {public Employee() { System.out.println("Employee初始化"); }
}
class Manager extends Employee {public Manager() { System.out.println("Manager初始化"); }
}
// 創建Manager時輸出:
// Person初始化 → Employee初始化 → Manager初始化
四、方法重寫:@Override的力量
當子類需要改變父類行為時,使用方法重寫:
class Vehicle {public void startEngine() {System.out.println("啟動普通引擎");}
}class ElectricCar extends Vehicle {@Overridepublic void startEngine() {System.out.println("靜默啟動電機"); // 完全重寫}
}class HybridCar extends Vehicle {@Overridepublic void startEngine() {super.startEngine(); // 復用父類邏輯System.out.println("同時啟動電機"); // 擴展新功能}
}
重寫 vs 重載:
// 重寫(父子類間)
class Parent { void demo(int a) {} }
class Child extends Parent { @Override void demo(int a) {} // 相同簽名
}// 重載(同一類內)
class Calculator {int add(int a, int b) { ... } // 簽名1double add(double a, double b) { ... } // 簽名2
}
五、類層次設計:從Object到業務模型
所有Java類都隱式繼承Object,可重寫關鍵方法:
class Employee {private String name;@Overridepublic String toString() {return "員工: " + name; // 替換默認的類名@哈希值}
}
設計原則:
- 單一職責:每層添加明確的新功能
- 層次扁平化:建議≤3層(如:Person→Employee→Manager)
- 組合優于繼承:當"has-a"比"is-a"更合理時
六、實戰:可運行的繼承案例
// 基類(父類)
class MyParent {protected int x, y; // 子類可訪問的受保護屬性public MyParent(int x, int y) {this.x = x;this.y = y;}public int getX() { return x; }public int getY() { return y; }
}// 派生類(子類)
class MyChild extends MyParent {public MyChild(int x, int y) {super(x, y); // 必須調用父類構造器}// 子類特有方法public int calculateSum() {return x + y; // 直接訪問父類protected屬性}
}// 測試類
public class Main {public static void main(String[] args) {MyChild obj = new MyChild(5, 3);System.out.println("從父類繼承的方法:");System.out.println("x = " + obj.getX()); // 輸出: 5System.out.println("y = " + obj.getY()); // 輸出: 3System.out.println("子類特有方法:");System.out.println("x+y = " + obj.calculateSum()); // 輸出: 8}
}
執行結果:
從父類繼承的方法:
x = 5
y = 3
子類特有方法:
x+y = 8
七、繼承的四大核心價值
代碼復用:
// 公共代碼在父類寫一次
class Animal {void breathe() { System.out.println("呼吸中..."); }
}
// 所有子類自動獲得呼吸能力
class Fish extends Animal {}
class Bird extends Animal {}
多態支持:
Animal[] zoo = {new Fish(), new Bird()};
for(Animal a : zoo) {a.breathe(); // 不同子類統一調用
}
擴展靈活:
class Bird extends Animal {@Overridevoid breathe() {super.breathe();System.out.println("氣囊輔助呼吸"); // 擴展功能}
}
系統可維護性:
結語:明智使用繼承
繼承是把雙刃劍,遵循三條黃金法則:
- 嚴格驗證"is-a"關系(經理是員工?,員工是公司?)
- 優先用組合處理"has-a"關系(汽車有發動機→組合)
- 保持層次扁平(超過3層需重構)