final修饰类是编译期强制拦截继承的最硬手段,声明为final class后任何extends尝试都会在javac阶段报错,JVM字节码层面通过ACC_FINAL标志确保运行时类加载器拒绝加载其子类,完全不可绕过。

final 修饰类是编译期强制拦截继承的最硬手段,只要声明为 final class,任何 extends 尝试都会在 javac 阶段报错,不存在绕过可能。
为什么必须用 final 而不是靠文档或约定
不加 final 的类,哪怕注释写满“禁止继承”,IDE 不标红、编译器不拦、子类照样能写。而 final 是 JVM 字节码层面的约束:类文件里会带 ACC_FINAL 标志,运行时类加载器直接拒绝加载其子类字节码。常见错误现象是 IDE 报 Cannot inherit from final 'Xxx',但开发者误以为是 import 错了,其实根本没机会走到运行时。
哪些类必须加 final 才算真正锁死逻辑
- 涉及加密、签名、权限校验的类(如
TokenValidator),防止子类重写verify()方法跳过检查 - 不可变值对象(如
Money、UserId),避免子类通过继承 + 重写toString()或equals()破坏语义一致性 - 工具类(如
JsonUtils),只提供静态方法,但若没加final,别人仍可extends JsonUtils并定义实例方法污染命名空间 - 模板方法骨架类中关键步骤已由
final方法封装,但整个类未final—— 这时子类仍可通过继承 + 新增 public 方法绕过流程控制
final 类里方法不能再加 final 修饰
这是容易踩的坑:final class 中所有实例方法自动具有 final 语义,显式写 public final void doX() 会触发编译错误 modifier final not allowed here。因为继承链已被切断,方法是否 final 已无意义。但注意:构造器可以是 private 或 package-private,不能是 protected(final 类不接受子类调用 super())。
反射和字节码生成也绕不过 final
有人尝试用 Unsafe.defineAnonymousClass 或 ASM 动态生成子类,依然失败——JVM 规范明确要求对 ACC_FINAL 类拒绝任何形式的继承。连匿名内部类引用 final 类时,也不能以它为父类(比如 new String() {{ ... }} 合法,但 new String() extends String { ... } 这种语法根本不存在)。真正安全的点在于:这个限制从源码编辑、编译、字节码验证到类加载,全程不可解除。

















