permits 关键字不能直接用于包级私有类,因其要求被许可子类必须对密封类可访问;正确做法是将密封类与子类同置一包且均设为包私有,并通过模块系统未导出该包来强化封装。

密封类的 permits 关键字本身**不能直接用于包级私有(package-private)类**作为被许可子类,因为 Java 规范明确要求:所有在 permits 列表中声明的子类,必须对密封类**可访问**——而包级私有类在跨包时不可见,若密封类与该子类不在同一包内,编译器会报错“class is not visible”。所以,“包级私有实现 + permits 精准控制”这一目标,需通过**调整可见性策略与模块/包结构设计**来间接达成,而非让 permits 直接引用 private 类。
核心限制:permits 列表中的类必须可访问
Java 语言规范强制要求:
- 密封类与其所有 permits 子类**必须位于同一编译单元上下文**(通常是同一模块;若未启用模块系统,则必须在同一包中);
- 每个被许可类必须能被密封类“看到”,即其声明必须具有至少 包级访问权限(default)或更高;
- 因此,真正的包级私有类(无任何修饰符、且定义在不同包)无法出现在 permits 列表中。
可行方案:用“包内默认访问 + sealed 控制”替代“包级私有”
想实现“仅限本包内特定类继承、外部完全不可见”的效果,正确做法是:
- 将密封类和所有允许的子类**全部放在同一个包中**;
- 密封类本身使用 package-private(即不写 public),子类也保持 package-private;
- 用 sealed + permits 明确列出这些同包子类的**简单类名**(无需包前缀)。
示例:
// com.example.shape.Shape.java(无 public,包私有抽象类)sealed abstract class Shape permits Circle, Rectangle, Triangle { }
// com.example.shape.Circle.javafinal class Circle extends Shape { ... }
// com.example.shape.Rectangle.javasealed class Rectangle extends Shape permits Square { ... }
✅ 外部包无法 import 这些类,也无法继承 Shape(因 Shape 不可见);
✅ 同包内所有子类受 permits 精确约束,新增子类必须显式加入列表并满足 final/sealed/non-sealed 要求;
✅ 编译器全程检查,非法继承在编译期即失败。
进阶控制:配合模块系统(module-info.java)加固边界
若项目已启用 Java 模块系统,可进一步收紧:
- 在 module-info.java 中,**不导出(do not export)包含密封类的包**;
- 即使其他模块依赖该模块,也无法访问该包内的任何类型;
- 此时,密封类及其 permits 子类实质上仅对模块内部(即同模块同包)可见,安全等级接近“模块级私有”。
这样既满足了 permits 的可访问性要求,又实现了比传统包私有更强的封装边界。
为什么不用“private 类 + permits”?
Java 不允许:
- private 类只能定义在内部类中,而 permits 只接受顶级类(top-level class)名称;
- 即使强行写 permits InnerClass,编译器会提示 “not a valid permitted subclass: inner classes are not allowed”;
- 所以,permits 列表中只接受 public 或 package-private 的顶级类,private 和 protected 均无效。

















