防范重写漏洞的关键是设计阶段明确重写意图并设边界:完全替换用abstract/空实现,补充增强用protected钩子,条件调整抽离为boolean方法;用final+模板方法锁住关键路径;强制@Override防拼错、重载、签名变更;禁止构造器调用可重写方法。

防范方法重写导致的逻辑漏洞,核心不是堵住子类修改,而是让重写本身可预期、可验证、不破坏契约。关键在设计阶段就设好边界,而非靠运行时兜底。
明确重写意图并文档化
动笔前必须回答:这个方法是供子类完全替换、补充增强还是条件微调?不同意图对应不同结构设计:
- 若需完全替换(如
Dog.speak()与Animal.speak()语义无关),父类方法应声明为abstract或提供空实现+清晰注释,避免误导子类去复用错误逻辑 - 若需补充增强(如
User.create()后VipUser发短信),父类方法应预留钩子(如afterCreate()),并用protected暴露,而非让子类重写整个流程 - 若仅需条件调整(如支付后是否对账),应把判断逻辑抽离成独立
boolean shouldReconcile()方法,子类只重写该布尔方法,主干流程保持统一
用 final + 模板方法锁住关键路径
对不可变行为,别依赖子类自觉——直接用final禁止重写;对可变部分,用模板方法模式隔离:
- 将校验、日志、事务开启/关闭等通用步骤声明为
final,确保每次调用都执行 - 把真正需要子类定制的部分(如“计算折扣”“生成订单号”)定义为
protected abstract或protected钩子方法 - 这样既防止子类绕过安全检查,又保留扩展点,多态行为始终落在可控框架内
强制使用 @Override 并配合编译期校验
@Override不是装饰,是防错开关。它能拦截三类高发漏洞:
立即学习“Java免费学习笔记(深入)”;
- 父类方法名拼错(如
getUserNamevsgetUsername),不加注解时子类方法只是新方法,多态失效 - 参数类型偏差(如
List<string></string>vsArrayList<string></string>),实际是重载,调用永远进不去 - 父类方法被删或改签名,子类代码变成“幽灵方法”,编译器加注解立刻报错
所有重写必须带此注解,IDE应配置为保存时自动补全。
避免构造器中调用可重写方法
这是最隐蔽的逻辑漏洞来源:父类构造器中调用this.doInit(),而该方法被子类重写,此时子类字段尚未初始化,this指向未完成构建的对象,极易出现NullPointerException或返回默认值。
- 解决方案:构造器里只做字段赋值和基础状态设置,所有业务逻辑延迟到
init()等显式方法中 - 或把
doInit()声明为final,强制子类无法干预构造过程


















