Java 继承与多态
继承、方法覆盖与动态分派:子类型如何替换父类型,多态如何在运行时选择实现。
#type / concept
#status / growing
#tech / dev
#resource / java
[!info] 关联笔记
Java 继承与多态
这个概念为什么出现
业务常有“通用流程 + 特殊规则”:所有支付单要校验金额,但信用卡与钱包的扣款方式不同。继承与多态允许用统一父类型引用操作不同子类实现,把变化点留在覆盖方法里。
[!abstract] 一句话理解 子类继承父类可复用结构与行为;覆盖实例方法后,调用由运行时类型动态分派;编译期类型决定可见成员,运行时类型决定覆盖实现。
最小可运行示例
场景:支付网关用同一“结算”入口处理不同渠道
abstract class Payment {
final int amountCents;
Payment(int amountCents) {
if (amountCents <= 0) throw new IllegalArgumentException("amount");
this.amountCents = amountCents;
}
// 模板步骤
final String settle() {
return channel() + " settled=" + amountCents;
}
abstract String channel();
}
class CardPayment extends Payment {
CardPayment(int amountCents) { super(amountCents); }
@Override String channel() { return "CARD"; }
}
class WalletPayment extends Payment {
WalletPayment(int amountCents) { super(amountCents); }
@Override String channel() { return "WALLET"; }
}
public class PaymentRouter {
// route:业务只依赖父类型。
// 教学点:编译期类型 Payment,运行时分派 channel()。
static String route(Payment p) {
return p.settle();
}
public static void main(String[] args) {
System.out.println(route(new CardPayment(100)));
System.out.println(route(new WalletPayment(50)));
}
}
建议运行:
javac PaymentRouter.java && java PaymentRouter
期望输出:
CARD settled=100
WALLET settled=50
结合场景再看三个关注点
@Override帮助编译器校验签名,避免“本想覆盖却重载”。- 父类构造先于子类字段初始化。
- 继承是强耦合;能用组合/接口时优先考虑。
核心概念与准确模型
1. 继承关系
extends单继承类- 子类获得父类可见成员
super调用父类构造/方法
2. 覆盖(override)条件(直觉)
- 实例方法同签名
- 返回类型协变允许
- 访问权限不能更窄
- static/私有/final 不能按实例多态覆盖语义使用
3. 向上转型与向下转型
Payment p = new CardPayment(1); // 向上
CardPayment c = (CardPayment) p; // 向下,失败则 ClassCastException
优先用多态方法,而不是到处强转。
4. 动态分派
- 对实例方法按运行时类型选实现
- 字段访问与 static 方法不按同样多态规则“覆盖字段”
边界情况与反直觉行为
- 构造器中调用可覆盖方法 → 子类未初始化风险。
- 误把重载当覆盖。
- 深继承树使变更爆炸。
常见误区
[!warning] 常见误区:继承为了复用私有实现细节 复用优先组合;继承表达的是“是一种”且愿意遵守父类契约(LSP)。
工程实践
- 优先
final类/方法锁住不该被扩的点。 - 父类型表达行为契约,而不是暴露一堆可写字段。
- 用接口定义能力,用小继承树实现。
- equals 与继承一起设计要特别小心(见 equals 篇)。
本节总结
- 继承复用与特化;多态靠动态分派
- 编译期类型 vs 运行时类型
- 组合优于盲目继承
自测题
- 覆盖与重载如何区分?
- 为什么支付路由依赖
Payment而不是具体类?
参考答案
- 覆盖:继承体系同签名实例方法替换;重载:同名不同参,编译期解析。
- 对扩展开放:新增渠道不必改路由签名,只新增子类型。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| JLS Inheritance / Override | 规范 | 继承与方法 |
| Polymorphism (Oracle Tutorial) | 教程 | 多态 |