final类无法被继承,编译期即拦截所有extends尝试,JVM不生成继承字节码;适合声明为final的包括不可变类、安全工具类、支付网关封装类和配置加载器;其方法无需再加final;但Spring AOP和Mockito对其有代理与模拟限制。

final 类无法被继承,编译期直接拦截
加 final 修饰类不是“建议别继承”,而是让 javac 在编译阶段就拒绝任何 extends 尝试。一旦声明 final class PaymentService,所有 class FraudPaymentService extends PaymentService 都会报错:Cannot inherit from final 'PaymentService'。JVM 根本不生成继承字节码,不存在运行时绕过可能。
哪些类适合声明为 final
核心判断标准是:该类的实现是否必须保持完整、不可拆解、不可定制化。常见场景包括:
-
String、Integer等 JDK 不可变类——防止子类篡改 hash 计算或序列化行为 - 涉及密钥派生、签名验签的
SecurityUtils类——避免子类注入弱算法或跳过校验 - 支付网关封装类(如
AlipayClient),其请求构造、签名逻辑、回调验签流程必须原子执行 - 配置加载器(如
YamlConfigLoader),若允许继承,子类可能覆盖load()方法返回伪造配置
final 类中方法无需再加 final
因为类本身不可继承,所有实例方法天然无法被重写。额外给方法加 final 是冗余的,且会干扰 IDE 的重构提示(比如重命名时误判为“受保护方法”)。但要注意:
- 构造方法不能加
final(语法错误) - 静态方法本来就不可重写,加
final无意义 - 如果类未来可能开放继承(比如从
final改为非final),提前在关键方法上加final反而增加迁移成本
Spring AOP 和 CGLIB 代理对 final 类的限制
这是最容易被忽略的实际约束:
-
@Transactional、@Cacheable等注解在final类上会静默失效——CGLIB 代理无法生成子类,JDK 动态代理又要求接口,没接口就彻底无法代理 - 若必须用 AOP,只能改用接口 + 实现类方式,把业务逻辑抽到非
final实现类中,接口定义为public interface PaymentService - 测试时 Mockito 2.x 默认无法 Mock
final类,需启用mockito-inline并配置mock-maker-inline文件
真正难的不是加 final,而是判断“这个类的职责边界是否真的不允许任何扩展”。一旦加了,后续所有定制需求都得靠组合、策略模式或配置驱动来解,而不是继承。

















