final类在编译期直接拒绝继承,如声明public final class PaymentService后,任何class FraudulentPayment extends PaymentService都会在javac阶段报错“Cannot inherit from final 'PaymentService'”,JVM不生成子类字节码,反射或动态代理均无法绕过。

final 类在编译期就拒绝继承,不是运行时拦截
声明为 public final class PaymentService 后,任何 class FraudulentPayment extends PaymentService 都会在 javac 阶段报错:Cannot inherit from final 'PaymentService'。JVM 根本不生成子类字节码,不存在“绕过反射”或“动态代理”的可能——连继承关系都未建立,更谈不上重写方法。
哪些类必须加 final:工具类、不可变值对象、安全敏感处理器
以下三类场景加 final 不是“可选优化”,而是设计底线:
- 工具类(如
DateUtils、JsonHelper):只提供静态方法,无状态,继承毫无意义;子类若能继承,就可能通过重写静态方法的包装逻辑注入后门 - 不可变值对象(如
OrderId、Money):内部字段全private final,构造即固化;一旦允许继承,子类可添加可变字段或重写equals()破坏一致性 - 安全敏感处理器(如
TokenValidator、AesCipher):校验或加解密逻辑若被子类覆盖,可能跳过签名验证或降级算法
final 类中方法无需再加 final,但构造器不能加
final 类的方法自动不可重写,显式写 public final void verify() 属于冗余。真正要注意的是:
- 构造器本身不能用
final修饰——语法直接报错,因为构造器不参与继承机制 - 私有构造器(如
private PaymentService())必须配final类使用,否则即使类不可继承,仍可能被实例化并用于反射攻击 - 若类含静态工厂方法(如
of()),应确保其返回类型是该final类,而非父接口或抽象类,避免被实现类绕过
别忽略 final 类对测试和框架的影响
加 final 后,Mockito 3.x 默认无法 mock 该类(需启用 inline mock 模式),Spring AOP 也无法对其做 CGLIB 代理(只能用 JDK 动态代理,前提是它实现了接口)。这不是 bug,而是语义一致性的代价:
- 如果你依赖 Spring 的
@Transactional或自定义切面,且该类没实现接口,那加final就等于放弃这些能力 - 单元测试中若需替换行为,应提前将核心逻辑抽到接口 + 非 final 实现类中,再让
final类组合它,而不是试图 mockfinal类本身 - 框架扩展点(如 Spring Boot 的
ApplicationContextInitializer)绝不能声明为final,否则第三方无法接入
final 的真正作用不是“防黑客”,而是用编译错误守住设计契约。一旦你把一个类标为 final,就意味着你已决定:它的全部行为,包括未来所有方法,都不该被任何形式的继承改变——这个决定比性能或语法细节重要得多。










