方法重写必须遵守里氏替换原则:前置条件不得变严格、后置条件不得变薄弱、异常不得多抛、状态不得破坏、语义必须一致;否则子类无法安全替换父类。

方法重写要遵守里氏替换原则,关键不是“能不能重写”,而是“重写后是否还守约”——子类方法必须在父类定义的行为契约内做事,不能悄悄改规则、缩范围、降保障或加意外。
前置条件不能变严格
父类方法允许的输入范围,子类不能收窄。比如父类声明 void setAge(int age),隐含接受任意整数;子类若重写为只接受 0–150 的值,并在超限时抛异常,就违反了 LSP。调用方原本传 -5 不会出错,换成子类就崩了。
- 正确做法:保持参数校验宽松,或把校验逻辑上提到父类统一处理
- 错误信号:子类重写方法里频繁出现
if (x ,而父类没这限制
后置条件不能变薄弱
父类承诺的结果,子类不能打折扣。例如父类 List<string> getNames()</string> 文档写明“返回非空不可变列表”,子类就不能返回 null 或可修改的实例。
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- 正确做法:返回更具体的类型(如
ArrayList)可以,但语义不能退化 - 错误信号:测试中父类方法返回值不为 null,子类却常返回 null,导致 NPE
异常不能多抛、状态不能乱破
子类方法不能抛出父类未声明的受检异常(如父类没写 throws IOException,子类不能擅自加);也不能破坏父类维护的关键状态约束,比如父类保证“余额 ≥ 0”,子类转账后让余额变成 -100 就越界了。
- 正确做法:只抛父类已声明的异常,或更具体的子类异常(如
SQLException→SQLTimeoutException) - 错误信号:客户端代码没写
catch某异常,换用子类后突然报错,且该异常父类根本没提过
语义必须一致,不能偷换概念
方法名和用途是契约的一部分。父类 save() 表示“校验后持久化”,子类重写成“仅写入文件不校验”,表面能跑通,实则破坏调用方预期。
- 正确做法:重写时延续原语义,增强不削弱;若行为本质不同,就不该继承,该用接口或组合
- 错误信号:子类重写后,原来对父类成立的业务断言(如“调用 save 后数据必入库”)在子类下不成立
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










