java中声明final类唯一方式是在class前加final,编译期禁止继承,报错“cannot inherit from final class”;它防误用不防黑客,不保证不可变性,与sealed互斥,影响mock和aop代理。

Java 里怎么声明一个 final 类
直接在 class 关键字前加 final,这是唯一方式。编译器会立刻阻止任何子类继承它,不是运行时检查,是编译期硬限制。
常见错误现象:javac 报错 cannot inherit from final class,说明你试图 extends 了一个 final 类——这不是 bug,是设计生效了。
-
final类不能被extends,但可以被new、可以有静态方法、可以实现接口 - 类本身
final不影响其字段或方法是否final;反过来,字段或方法final也不让类自动变成final - 如果类已经用了
private构造器 + 静态工厂(比如java.util.Collections里的不可变集合),再加final是冗余的;但加了更明确,推荐加
为什么不让继承?真能防住所有“绕过”吗
核心目的不是防黑客,而是防误用:避免下游开发者无意中覆写关键逻辑、破坏封装契约,比如 java.lang.String 或 java.time.LocalDate 这类值对象必须保证不可变性。
容易踩的坑:
- 以为
final能防止反射创建子类——不能。反射无法绕过final class的继承限制,但能调用私有构造器(如果存在)、修改final字段(需setAccessible(true)),所以final class不等于“绝对安全”,只是守住最常见继承路径 - 和
sealed混用会冲突。Java 17+ 的sealed类明确列出允许继承的子类,和final语义互斥;一个类不能同时是final和sealed - 泛型类型擦除后,
List<string></string>和List<integer></integer>运行时都是List,但final类不影响泛型使用,该用照用
哪些场景下不该用 final 类
不是所有工具类或工具对象都适合标 final。滥用反而降低可测试性和可扩展性。
典型反例:
- 需要被 Mockito 等框架 mock 的类:默认情况下,
final类无法被 mock(除非启用mock-maker-inline,但这带来额外开销和兼容风险) - 框架回调入口类(如 Spring 的
@Controller、@Service组件),如果标记final,某些 AOP 代理(JDK 动态代理除外)可能失败,因为 CGLIB 需要生成子类 - 内部抽象基类的实现类,如果未来可能提取公共行为到父类,提前
final会锁死演进路径
final 类和不可变对象的关系
final 类 ≠ 不可变对象。前者管继承,后者管状态变化。两者常一起出现,但独立判断。
实操建议:
- 要实现不可变对象,除了类声明
final,还要确保:所有字段private final、不提供修改方法、防御性拷贝可变参数(如ArrayList)、不泄漏可变内部引用 - 如果字段是
final但指向可变对象(比如final List<string> data</string>),外部仍可通过data.add()改变内容——final只锁引用,不锁对象状态 - 像
LocalDateTime这种既final又不可变的类,才是理想范本;而StringBuilder是final类,但可变——说明final类完全可以有append()这种改变自身状态的方法










