关键在于用final精准封住不可变环节:主模板方法必须声明为public final void execute(),锁死校验→准备→处理→通知等固定顺序;checkPermission、verifySignature、transitionToConfirmed等关键步骤也须加final;配合protected abstract钩子实现可扩展性。

抽象类中防止子类篡改核心逻辑,关键不是靠“层层加锁”,而是用 final 方法精准封住不可变环节,让编译器在写错的第一时间就报错——这才是真正可靠的安全防线。
主模板方法必须声明为 public final
这是整个防护体系的中枢。模板方法封装了固定执行顺序(比如校验→准备→处理→通知),一旦被重写,整个流程就可能被跳过、打乱或替换。
- 写法必须是
public final void execute(),final紧跟在访问修饰符后、返回类型前 - 所有稳定逻辑(如参数非空检查、防重放验证、事务开启)都直接写进这个方法体,不委托给非 final 的实例方法
- 子类哪怕继承五层深,只要父类某一级定义了该方法为
final,它就永远无法重写——IDE 标红,javac直接拒绝编译
关键步骤方法也要加 final
除了主入口,那些一旦被绕过就会引发安全或数据问题的独立步骤,同样需要锁定:
-
final void checkPermission():防止越权操作 -
final boolean verifySignature(byte[] data):避免签名验证被降级或跳过 -
final void transitionToConfirmed():确保状态跃迁经过完整校验链 - 注意:这些方法应保持
public或protected,不能是private(否则子类根本调不到)
配合抽象钩子实现可扩展性
final 不等于僵化。真正可维护的设计是把“不变”和“可变”分开:
立即学习“Java免费学习笔记(深入)”;
- 主流程和关键校验用
final锁死 - 具体业务动作定义为
protected abstract void doProcess()或带默认实现的protected void doLog() - 子类只能定制钩子,无法动摇骨架——既守住一致性,又保留灵活性
避开常见误用陷阱
加了 final 不代表万事大吉,这些情况会让防护失效或产生误导:
- 在
final方法内部调用了一个未加final的钩子方法,结果子类重写那个钩子,间接篡改了行为 - 把方法写成
private final void init():private方法本就不能被继承,final是冗余的,还容易让人误判重点 - 父类没加
final,子类自己加了个同名final方法:这只是隐藏(hide),不是重写(override),对原始逻辑毫无保护作用 - 依赖反射强行绕过?JVM 规范明确禁止加载违反
ACC_FINAL的类,运行时会抛IncompatibleClassChangeError,实际不可行


















