1241 字
约 4 分钟
3
S04-B05 · 封装、继承与多态:到底在解决什么问题

适用读者:背得出"三大特性"但说不出"多态靠什么实现"的人。
前置:S04-B02。
实测环境:OpenJDK 21.0.12.1(2026-11-05)。
覆盖旧文:A-1650(304) A-1652(202) A-1658(181) A-1778(182) = 869 点。
覆盖扩展:E-S04-006(虚方法表与动态分派)。
上一篇:S04-B04 · 数组、static 与初始化顺序
下一篇:S04-B06 · 接口、抽象类与 Lambda。


1. 一句话结论

三大特性各解决一个问题:封装解决"谁能改我的状态"、继承解决"复用与替换"、多态解决"调用方不关心具体实现"。

特性 解决的问题 代价
封装 状态被随意修改 需要设计接口与不变量
继承 代码重复 耦合(父类改动影响子类)
多态 调用方与实现解耦 需要抽象层与动态分派

2. 实测:实例方法与静态方法的分派规则不同

class A { void f() { println("A.f"); } static void g() { println("A.g"); } }
class B extends A { @Override void f() { println("B.f"); } static void g() { println("B.g"); } }

A a = new B();
a.f();   // → B.f
A.g();   // → A.g
B.g();   // → B.g

实测输出:

B.f
A.g
B.g
调用 绑定时机 依据
实例方法 a.f() 运行期(动态分派) 对象的实际类型(B)
静态方法 A.g() 编译期(静态分派) 引用的声明类型(A)
字段访问 编译期 同上(字段没有多态)

这张表能解释三个经典 bug:

bug 原因
父类引用调静态方法,执行的是父类的 静态方法不参与多态
父类引用读字段,拿到父类的值 字段访问按声明类型
@Override 拼错没报错 加了注解编译器才会检查

3. 多态靠什么实现(E-S04-006)

JVM 为每个类维护方法表(虚方法表):实例方法在表里有槽位,子类重写就替换槽位中的实现指针。

调用 a.f()
  ① 取 a 的实际类型(B)
  ② 在 B 的方法表里查 f 的槽位
  ③ 跳到该槽位的实现

这解释了:

现象 原因
动态分派有微小开销 一次间接跳转
final/private/static 方法不走方法表 编译期就能确定,可内联
接口调用可能更慢(历史上) 需要接口方法表查找(现代 JVM 有优化)

4. 重写 vs 重载(A-1778)

项 重写 Override 重载 Overload
发生位置 子类 vs 父类 同一个类
签名 必须相同 参数列表不同
绑定 运行期 编译期
返回值 相同或协变 可不同
异常 不能抛出更宽的受检异常 无限制

重载的坑:f(null) 有多个重载时,编译器选"最具体"的那个,可能不是你以为的。

void f(Object o) {}
void f(String s) {}
f(null);   // 选 f(String)——更具体

5. 继承的三个使用判据

判据 说明
是不是 is-a "狗是动物" → 继承;"订单有一个地址" → 组合
父类是否稳定 父类频繁改动会波及所有子类
是否需要多态替换 需要 → 抽象类/接口;不需要 → 组合

"优先组合而非继承" 的理由:继承把子类绑定到父类的实现细节(脆弱基类问题)。


6. 五个误区

误区 真相
"多态对字段也生效" 字段按声明类型访问
"静态方法能被重写" 只是隐藏(编译期绑定)
"重载是运行期决定" 编译期按参数类型选
"继承越多越好复用" 增加耦合,优先组合
"@Override 只是装饰" 它让编译器帮你检查签名

7. 自检

  1. 实例方法与静态方法的分派时机有什么不同?
  2. 为什么"字段没有多态"?
  3. 重载为什么是编译期绑定?f(null) 的坑是什么?
  4. 方法表如何解释"动态分派有开销"?
  5. 什么时候该用组合而不是继承?

8. 下一篇

S04-B06 · 接口、抽象类、Lambda 与枚举

多态讲完,下一篇讲"抽象层的三种形态":接口、抽象类、函数式接口——以及它们各自适合的场景。


本篇属于系列《S04 · Java 语言核心》。旧文来源:A-1650/1652/1658/1778;扩展点 E-S04-006。分派行为为 OpenJDK 21 实测。

S04-B05 · 封装、继承与多态:到底在解决什么问题
https://www.clxhxhhr.top/posts/5220/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。