final修饰类是在编译期彻底切断继承链,适用于工具类、不可变值对象和安全敏感处理器三类场景;加final后无法继承、cglib代理失败、mockito需启用inline模式,且必须配套调整为组合+策略模式。

在 Java 继承体系中,用 final 修饰类 是一种明确的设计决策,不是临时补丁。它的核心作用是**在编译期彻底切断继承链**,防止任何子类出现。避坑的关键不在于“怎么写对”,而在于“什么时候该用、用了之后会卡住哪些地方”。
哪些类适合加 final
三类场景属于天然适配,加 final 是设计底线,不是可选项:
-
工具类:如
StringUtils、JsonHelper,只提供静态方法,无状态,继承毫无意义;若开放继承,子类可能通过重写包装逻辑注入风险 -
不可变值对象:如
OrderId、Money,字段全为private final,构造即固化;一旦允许继承,子类可添加可变字段或篡改equals/hashCode,破坏一致性 -
安全敏感处理器:如
TokenValidator、AesCipher,核心校验或加解密逻辑若被子类覆盖,可能跳过签名验证或降级算法
加 final 后容易踩的硬性限制
这些不是 bug,而是语言机制决定的刚性约束,必须提前规划:
Java开发手册规约集合,基于阿里巴巴Java开发手册(嵩山版)。 涵盖7大维度:编程规约、异常日志、单元测试、安全规约、MySQL数据库、工程结构、设计规约。 当用户需要:(1) 编写或审查Java代码 (2) 检查命名/代码规范 (3) 处理异常和日志 (4) 编写单元测试 (5) 安全编码 (6) 数据库设...
- 任何
extends MyFinalClass的代码,javac直接报错cannot inherit from final 'MyFinalClass',连字节码都不会生成 - Spring AOP 默认使用 CGLIB 代理 final 类会失败(CGLIB 需要生成子类),只能退回到 JDK 动态代理——前提是该类实现了接口
- Mockito 3.x 及以上版本默认无法 mock final 类,需启用 inline mock 模式(
mockito-inline依赖),否则测试直接失败 - 框架扩展点(如 Spring Boot 的
ApplicationContextInitializer)绝不能声明为 final,否则第三方无法接入
配套设计必须跟上
final 类不是孤立存在的,它要求整个协作方式同步调整:
- 如果原本靠“继承 + 重写”实现扩展,现在必须转为组合 + 策略模式:把可变逻辑抽成接口,final 类持有一个策略实例
- 工具类建议搭配私有构造器(
private MyUtils() {}),防止被意外实例化,尤其避免反射攻击 - 若提供静态工厂方法(如
of()),返回类型必须是该 final 类本身,不能返回父接口或抽象类,否则可能被绕过 - final 类中的方法无需再显式加
final,这是冗余的;但构造器不能加final(语法错误)
发布前必须确认的两件事
final 一旦加了就无法撤销,尤其在已发布的公共库中:
- 是否所有预期的扩展点都已通过接口或参数化方式暴露?比如支付流程中,把风控策略、日志行为做成入参或回调,而不是留着方法让子类重写
- 下游项目是否已在用继承方式扩展该类?如果有,加 final 就是不兼容变更,必须走 major 版本升级,并提供迁移路径
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










