直接用final修饰类是最硬、最可靠的防护方式——编译期切断继承路径,JVM通过ACC_FINAL强制拒绝子类加载;安全敏感类、不可变值对象、纯工具类必须加final;正确写法为public final class,不可与abstract共用,构造器应为private或包私有。

直接用 final 修饰类,是最硬、最可靠的防护方式——它在编译期就彻底切断继承路径,JVM 字节码层面通过 ACC_FINAL 标志强制拒绝子类加载,不存在绕过可能。
哪些类必须加 final 才算真正防住恶意继承
不是所有类都需要 final,但以下三类一旦开放继承,极易被攻击者利用:
-
安全敏感类:如
TokenValidator、AesCipher。不加 final 时,攻击者可写class EvilValidator extends TokenValidator,重写verify()方法跳过签名校验。 -
不可变值对象:如
OrderId、Money。子类可能添加可变字段或重写equals(),破坏一致性与领域语义。 -
纯工具类:如
JsonUtils、CryptoHelper。只含静态方法,继承毫无意义;若未声明 final,他人仍可extends并注入恶意实例方法,污染调用逻辑。
正确写法与关键细节
语法简单,但细节决定是否真正生效:
- 写法是
public final class PaymentService { }——final必须放在class关键字前。 - 不要给方法再加
final:类已 final,其方法天然不可重写;显式加final属于冗余且非法。 - 构造器不能是
protected:final 类不允许子类调用super(),应设为private或包私有,避免误用。 - 别和
abstract共用:语义矛盾,抽象类本为被继承而生,final 类禁止继承,二者不可并存。
为什么文档、private 构造器或反射都替代不了 final
它们都不是真正的防护手段:
立即学习“Java免费学习笔记(深入)”;
- 注释写“禁止继承”没用:IDE 不标红、编译器不拦、子类照写不误,运行时才可能暴露问题。
-
private构造器只能阻止实例化,不阻止继承(只要存在 package-private 或protected构造器,继承依然成立)。 - 反射也绕不过
final:连Unsafe.defineAnonymousClass或 ASM 动态生成子类都会失败——JVM 规范明确禁止对ACC_FINAL类做任何形式的继承。
加 final 前要评估的实际影响
final 是设计契约,不是技术补丁,需提前考虑生态兼容性:
- Mockito 3.x 默认无法 mock final 类(需启用 inline mock 模式)。
- Spring AOP 无法对 final 类做 CGLIB 代理(只能用 JDK 动态代理,前提是它实现了接口)。
- 若依赖
@Transactional或自定义切面,且该类没实现接口,加 final 就等于放弃这些能力。 - 单元测试中若需替换行为,建议将核心逻辑抽到接口 + 非 final 实现类中,再让 final 类组合它,而非试图 mock final 类本身。


















