Java多态是语言特性而非设计模式,却是策略、模板方法、工厂方法、观察者等模式落地的底层支撑;它通过接口/抽象类实现运行时行为绑定,避免if-else硬编码,提升扩展性与可测试性,但需警惕假抽象、构造器调用抽象方法、类型判断绕过多态等常见问题。

Java 多态本身不是设计模式,而是语言特性;但它几乎是所有面向对象设计模式得以落地的底层支撑。没有多态,策略模式、模板方法、观察者、工厂方法等模式会退化成大量 if-else 或硬编码分支。
下面从几个典型设计模式出发,说明多态如何被实际使用、为什么必须用、以及容易出问题的地方。
策略模式中用 interface + 多态替换 if-else
当业务逻辑需要根据类型选择不同算法(比如支付方式:微信、支付宝、银行卡),不用多态就得写一长串 if (type.equals("wechat")) { ... } —— 难扩展、难测试、易出错。
正确做法是定义统一接口,让每个策略实现它:
立即学习“Java免费学习笔记(深入)”;
interface PaymentStrategy {
void pay(double amount);
}
class WechatPay implements PaymentStrategy {
public void pay(double amount) { / 微信支付逻辑 / }
}
class Alipay implements PaymentStrategy {
public void pay(double amount) { / 支付宝逻辑 / }
}
调用方只依赖 PaymentStrategy 接口,运行时由具体实现决定行为 —— 这就是多态的核心价值。
- 新增支付方式?加个新类,实现接口,不改原有代码
- 单元测试时可轻松 mock 任意策略
- 注意:不要在策略类里暴露内部状态或强耦合上下文,否则多态就变成“假抽象”
模板方法模式靠 abstract 方法 + 多态控制流程骨架
模板方法把算法骨架定义在父类中,把可变步骤延迟到子类实现。没有多态,子类方法根本不会被调用。
例如导出报表:
abstract class ReportGenerator {
// 模板方法:定义执行顺序
final void generate() {
loadDataSource();
formatData();
export();
}
abstract void loadDataSource(); // 子类必须实现,靠多态分发
abstract void formatData();
abstract void export();
}
class PdfReport extends ReportGenerator {
void loadDataSource() { / 加载PDF数据源 / }
void formatData() { / PDF格式化 / }
void export() { / 写入PDF文件 / }
}
关键点:
-
generate()是final的,防止子类破坏流程 - 子类重写的
loadDataSource()等方法,在运行时通过多态自动绑定 - 常见错误:在抽象类构造器里调用
abstract方法 —— 此时子类对象尚未初始化,可能 NPE 或逻辑错乱
工厂方法模式用多态解耦对象创建与使用
如果直接 new ConcreteProduct(),使用者就跟具体类强绑定。工厂方法把创建逻辑上移到子类,靠多态返回不同实例。
比如日志记录器工厂:
abstract class LoggerFactory {
abstract Logger createLogger();
void log(String msg) {
createLogger().write(msg); // 多态发生在这里
}}
class FileLoggerFactory extends LoggerFactory {
Logger createLogger() {
return new FileLogger(); // 返回具体类型,但变量是 Logger 接口
}
}
使用者只和 LoggerFactory 打交道,完全不知道背后是文件、数据库还是远程 HTTP 日志。
- 注意:工厂方法返回类型必须是父类或接口,不能是具体类,否则失去多态意义
- Spring 的
BeanFactory底层大量使用该思想,只不过用反射+配置替代了子类实现 - 别为了用模式而用——如果只有 1 种产品,硬套工厂方法反而增加复杂度
观察者模式中多态让通知逻辑可插拔
被观察者不关心谁来响应事件,只面向 Observer 接口调用 update()。不同观察者(UI刷新、发邮件、写审计日志)各自实现,运行时动态绑定。
典型错误是把观察者写成 if (obj instanceof EmailObserver) —— 这直接绕过了多态,也违背开闭原则。
- 确保
Observer是接口或抽象类,避免在被观察者中做类型判断 - 注意循环引用风险:观察者又反过来持有被观察者引用,可能阻碍 GC
- Java 9+ 的
FlowAPI 提供了响应式观察者,但底层仍是多态分发
多态不是炫技,它是把「变化点」隔离出来的最小成本手段。真正难的不是写 interface 和 implements,而是判断哪些行为确实会变、哪些只是暂时没变但未来大概率会变 —— 这个判断错了,多态就会变成过度设计的累赘。










