密封类用于精确控制哪些类可以继承父类或实现接口,约束继承拓扑结构而非变量访问权限;其核心是限制直接子类范围,需满足sealed声明、permits同模块、子类明确修饰符三条件。

Java 17 的密封类(Sealed Classes)不是用来控制“继承变量权限”的,而是用于**精确控制哪些类可以继承某个父类或实现某个接口**。它不涉及字段(变量)的访问修饰符(如 private、protected)、也不修改封装层级,其核心作用是约束**类的继承拓扑结构**——即谁可以成为它的直接子类。
密封类管的是“谁能继承”,不是“谁能访问变量”
变量访问权限仍由传统的 private、protected、包级私有、public 决定,密封类本身不改变这些规则。它解决的是另一个维度的问题:防止任意类随意扩展关键抽象类型,从而保障领域模型完整性与模式匹配安全性。
- 即使一个字段是 public,密封机制也不会限制它的可见性;反之,即使字段是 private,密封类也不因此多加一层保护
- 你依然可以在
final class Circle extends Shape中定义public double radius,也能在non-sealed class Rectangle extends Shape中用protected字段 —— 封装粒度完全独立于密封控制 - 真正被“控制”的,是编译器是否允许
class Triangle extends Shape这样的语句存在:只要Triangle不在Shape的permits列表中,就直接报错
密封类的三个强制要求
要正确定义并使用密封类,必须同时满足以下条件,缺一不可:
- 父类声明为 sealed,且显式带 permits 子句(Java 17 要求,Java 21+ 可省略但不推荐)
-
permits列出的所有子类,必须与密封类在同一个模块(或同一包,若未模块化) - 每个被允许的子类,必须用 final、sealed 或 non-sealed 明确声明自身继承策略
三种子类修饰符的实际含义
它们决定的是该子类是否继续参与“继承管控”,而非变量权限:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
立即学习“Java免费学习笔记(深入)”;
-
final:彻底终止继承链,比如
final class Circle extends Shape—— 无人能再继承Circle -
sealed:继承管控向下传递,例如
sealed class Rectangle extends Shape permits Square—— 只有Square能继承Rectangle -
non-sealed:主动退出管控体系,比如
non-sealed class Triangle extends Shape—— 任何类都可继承Triangle,但Triangle本身仍属于Shape的合法子类
典型应用场景:配合 switch 模式匹配保证穷尽性
当密封类用于建模有限变体(如表达式、状态、结果类型),搭配 Java 17+ 的 switch 表达式,编译器能静态验证是否覆盖全部子类:
String describe(Expr e) {
return switch (e) {
case ConstantExpr(var v) -> "常量: " + v;
case AddExpr(var l, var r) -> "加法: " + l + "+" + r;
case MulExpr(var l, var r) -> "乘法: " + l + "*" + r;
// 编译器知道 Expr 只有这三类实现,无需 default 分支
};
}
如果有人偷偷新增了第四个子类却没更新 switch,编译直接失败 —— 这种安全不是靠变量权限,而是靠继承边界的显式声明。

















