Java编程进阶之路💻——深入理解继承与多态(第八讲)
一、继承机制:提升代码复用效率
1.1 继承的必要性
假设我们需要为不同宠物创建类,未使用继承时可能出现以下情况:
// 犬类
public class Canine {
String petName;
int years;
double mass;
public void consumeFood() {
System.out.println(petName + "正在进食");
}
public void rest() {
System.out.println(petName + "正在休息");
}
void makeSound() {
System.out.println(petName + "发出汪汪声");
}
}
// 猫科类
public class Feline {
String petName;
int years;
double mass;
public void consumeFood() {
System.out.println(petName + "正在进食");
}
public void rest() {
System.out.println(petName + "正在休息");
}
void vocalize() {
System.out.println(petName + "发出喵喵声");
}
}
观察可见,两个类中存在大量相似代码(petName、years、mass属性,consumeFood()和rest()方法)。继承机制正是为解决此类代码重复问题而设计。
1.2 继承的基本原理
继承是面向对象编程中实现代码重用的关键技术,它允许新创建的类基于现有类进行扩展,添加特有功能。新类称为子类(派生类),被继承的类称为父类(基类)。
继承关系可表示为:
生物类
/ \
犬类 猫科类
1.3 实现继承的语法
Java通过extends
关键字实现继承关系:
// 生物基类
public class LivingBeing {
String petName;
int years;
public void consumeFood() {
System.out.println(petName + "正在进食");
}
public void rest() {
System.out.println(petName + "正在休息");
}
}
// 犬类派生
public class Canine extends LivingBeing {
void makeSound() {
System.out.println(petName + "发出汪汪声");
}
}
// 猫科派生
public class Feline extends LivingBeing {
void vocalize() {
System.out.println(petName + "发出喵喵声");
}
}
1.4 继承体系中的访问规则
在继承结构中,子类访问父类成员需遵循以下原则:
1. 字段访问:
– 子类存在该字段时优先访问
– 子类不存在时访问父类字段
– 均不存在则编译错误
2. 方法调用:
– 方法名不同时,先在子类查找再到父类
– 方法名相同但参数不同时构成重载
– 方法签名完全相同时形成重写
1.5 super关键字的运用
当父子类存在同名成员时,可通过super
显式访问父类成员:
public class Child extends Parent {
int value; // 与父类同名
public void process() {
super.value = 200; // 父类字段
this.value = 100; // 子类字段
}
}
1.6 派生类构造方法规范
子类构造时必须优先调用父类构造器:
public class Parent {
public Parent() {
System.out.println("父类构造器");
}
}
public class Child extends Parent {
public Child() {
// 编译器自动添加super()
System.out.println("子类构造器");
}
}
重要注意事项:
1. super()
必须作为子类构造器的首条语句
2. 父类若无默认构造器,子类需显式调用有参构造器
二、继承的类型
现实世界的继承关系复杂多样,例如:
但Java仅支持特定继承模式:
三、多态特性:灵活的对象行为
3.1 多态的核心概念
多态指相同操作在不同对象上呈现不同行为。典型示例:
打印设备
├─ 彩色打印机 → 输出彩色文档
└─ 单色打印机 → 输出黑白文档
3.2 实现多态的条件
Java中实现多态需满足:
1. 存在继承层次结构
2. 子类重写父类方法
3. 通过父类引用调用方法
3.3 方法重写规范
对比项 | 方法重写 | 方法重载 |
---|---|---|
参数列表 | 必须相同 | 必须不同 |
返回类型 | 相同或子类 | 可不同 |
访问修饰符 | 不能更严格 | 可不同 |
![]() |
||
方法重写需遵守: | ||
– 保持方法签名一致 | ||
– 返回类型协变允许 | ||
– 访问权限不严于父类 | ||
– 不可重写static/private/final方法 | ||
示例: |
class Animal {
public void eat() {
System.out.println("动物进食");
}
}
class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫咪吃鱼");
}
}
3.4 类型转换机制
- 向上转型:子类实例转为父类引用
Animal pet = new Cat("小花", 3);
- 向下转型:父类引用转为子类类型
if (pet instanceof Cat) {
Cat myCat = (Cat) pet;
myCat.vocalize();
}
3.5 多态的优劣分析
优势:
1. 简化条件判断逻辑
2. 增强系统扩展性
局限:
1. 运行时性能略低
2. 属性不具备多态性
3. 构造器中调用重写方法存在风险
3.6 构造器中的方法调用陷阱
典型问题案例:
class Base {
public Base() {
execute(); // 潜在风险
}
public void execute() {
System.out.println("基类方法");
}
}
class Derived extends Base {
private int value = 1;
@Override
public void execute() {
System.out.println("派生类方法 " + value);
}
}
public class Demo {
public static void main(String[] args) {
Derived obj = new Derived(); // 输出:派生类方法 0
}
}
此时value尚未初始化(默认值0),因为父类构造器执行时子类字段未完成初始化。
四、继承与组合的抉择
继承表示”属于”关系,组合表示”包含”关系:
// 继承:跑车属于汽车
class SportsCar extends Vehicle {
// ...
}
// 组合:汽车包含引擎
class Vehicle {
private Engine powerUnit;
private Wheel[] wheels;
// ...
}
设计建议:优先考虑组合,除非明确存在”is-a”关系。
五、final修饰符的应用
final可修饰:
1. 变量:定义常量
2. 方法:禁止重写
3. 类:阻止继承
final class Math { // 不可继承的类
// ...
}
核心要点
【多态优势】
显著降低代码的”圈复杂度”,减少条件判断
何谓”圈复杂度”?
这是评估代码复杂度的指标。直线型代码易于理解,而包含大量条件分支或循环的代码则复杂度较高。简单计算条件语句和循环语句的数量即可得到圈复杂度。高圈复杂度的方法应考虑重构。
继承与多态是Java面向对象编程的基石,掌握它们对编写优雅代码至关重要。关键要点:
– 继承实现代码复用和类型关系
– 多态提升程序灵活性
– final增强代码安全性
– 组合优先于继承是优秀设计原则
Java核心知识专栏