密封类通过permits白名单授权继承,要求显式列出同模块同包的允许子类,子类须用final/sealed/non-sealed明确策略,配合switch实现编译期穷尽检查,跨模块需模块系统协同导出与依赖声明。

用密封类控制继承,核心是“白名单授权”——不是禁止别人继承,而是只准你列出的几个类继承,一个都不能少,也不能多。
声明密封父类必须带 permits 列表
Java 17 要求密封类必须显式写出所有允许的直接子类,语法如下:
- 用 sealed 修饰类,并紧跟 permits 子句,后面跟类名(不带包路径),如
permits Dog, Cat - 所有被列在
permits中的类,必须和密封类在同一模块;单模块项目中通常要求同包 - 密封类本身可以是
abstract或具体类,但不能是final(否则矛盾)
每个子类必须明确自己的继承策略
被 permits 允许的子类,必须用以下三者之一修饰,编译器会强制检查:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
-
final:彻底封闭,不能再有子类(最常用,适合终端类型,如
Circle) -
sealed:自身也成密封类,需继续写
permits指定下级(适合中间抽象层,如Rectangle permits Square) - non-sealed:主动开放该分支,允许任意类继承(慎用,会削弱密封性,且 switch 穷尽检查失效)
配合 switch 实现编译期穷尽检查
密封类真正发挥威力的地方,在于让 switch 表达式能静态确认“已覆盖所有可能子类”:
立即学习“Java免费学习笔记(深入)”;
- 变量类型必须精确是密封类本身,例如
Shape s = ...,不能是Object s或泛型擦除后的类型 - 所有
case必须对应permits中列出的每一个直接子类(或其模式变量),缺一个就编译报错 - 如果某个子类用了
non-sealed,JVM 不再保证穷尽性,编译器将拒绝启用 exhaustive 检查
模块化项目要注意可见性
跨模块使用时,仅靠 permits 不够,还需模块系统配合:
- 密封类所在模块需在
module-info.java中exports对应包 - 子类所在模块需
requires密封类模块,且若子类要被外部访问,可能还需opens - 漏掉任一模块声明,即使
permits正确,也会报 “class X is not allowed to extend sealed class Y”

















