Java密封类的子类必须显式声明为final、sealed或non-sealed,否则编译报错;三者分别表示终止继承、继续密封或开放继承;且所有子类须同包或同模块,permits列表仅写类名。

Java 中密封类的子类必须显式声明为 final、sealed 或 non-sealed,这是编译器强制要求,不是可选项。不加修饰符会直接报错,比如:error: class X must be declared as sealed, non-sealed or final because its superclass is sealed。
子类必须用三种关键字之一明确继承意图
每个出现在 permits 列表里的直接子类,都得在定义时带上以下三者之一:
-
final class X extends SealedParent:彻底终止继承链,不能再被扩展。适用于语义上“不可再细分”的类型,如
Circle、PaidStatus。 -
sealed class X extends SealedParent permits Y, Z:该子类自身也是密封的,继续限定它的子类。适合需要分层建模的场景,比如
Rectangle允许Square和RoundedRect继承。 - non-sealed class X extends SealedParent:主动开放继承权限,允许任意类继承它(默认仅限同模块)。慎用,会削弱密封性,且后续子类不再受父密封类约束。
配置时必须满足的位置与可见性规则
密封类和它的所有 permits 子类需满足严格的位置约定:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 若未使用模块系统,所有类必须在同一个包内;
- 若使用模块系统,子类必须位于同一模块,且该模块的
module-info.java需导出对应包(exports)或开放(opens)给运行时反射访问(如需); -
permits后只写类名,不带包路径,例如permits Circle, Rectangle,不能写成permits com.example.Circle。
典型正确配置示例
以订单状态为例:
立即学习“Java免费学习笔记(深入)”;
public sealed class OrderStatus permits PendingStatus, PaidStatus, CancelledStatus { }
final class PendingStatus extends OrderStatus { /* ... */ }
final class PaidStatus extends OrderStatus { /* ... */ }
sealed class CancelledStatus extends OrderStatus permits RefundedStatus { }
final class RefundedStatus extends CancelledStatus { /* ... */ }这里 CancelledStatus 是 sealed,所以它自己也控制子类;而 PendingStatus 和 PaidStatus 是 final,表示终点;整个结构清晰、可穷举、支持 switch 表达式全覆盖检查。
常见错误与规避方式
- 子类写了
class X extends Y却漏掉final等修饰符 → 编译失败,必须补全; -
permits列表里写了类名,但该类没定义,或定义在其他包/模块 → 报错“not allowed to extend”; - 误以为
non-sealed可以跨模块自由继承 → 实际默认受限,需模块间显式opens或requires授权; - 把抽象类或接口误列为
permits成员,但它们并未实际继承密封类 → 编译不通过。

















