Java封装需类级与模块级双重防护:类内用private严格控制成员可见性,模块层通过module-info.java的exports/opens精确导出API包并限制反射,接口驱动模块通信,构建时用jdeps/jlink强制验证封装约束。

Java 封装是保障模块独立性的底层支撑,不是靠“写得私有”就完事,而是要和模块系统(JPMS)协同发力,形成从类级到模块级的双重防护。
封装从类开始:控制成员可见性是第一道防线
每个类内部必须严格使用 private 修饰字段和内部方法,只暴露必要的 public 接口。这不是形式主义,而是切断外部随意访问的路径。
- 所有业务实体(如 User、Order)的属性一律 private,配套 getter/setter,并在 setter 中加入校验逻辑(比如非空、范围检查)
- 工具类或辅助方法若仅被本类使用,声明为 private;若需跨类但限于包内,用默认(package-private)修饰符,不加任何关键字
- 避免 public static 成员变量——它会成为全局状态污染源,破坏模块边界
模块层封装:module-info.java 是真正的“隔离墙”
类级封装管不住跨包调用,而 JPMS 的 exports 和 requires 才真正定义“谁可以看、谁可以依赖”。没有显式导出的包,即使 public 类也无法被其他模块访问。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 每个模块只 exports 明确设计为对外提供的 API 包(如
com.example.user.api),绝不导出 impl、internal、util 等实现包 - 如果某模块需要反射调用(如框架集成),用
opens或opens to精确授权,而不是无差别 open - 依赖声明用
requires static标注编译期才需要的模块(如测试框架),运行时不加载,减小镜像体积
接口驱动通信:让模块之间只认契约,不认实现
模块间交互必须通过 interface,且接口应定义在提供方模块中,由消费方模块依赖——这是解耦的关键操作。
立即学习“Java免费学习笔记(深入)”;
- 例如 user-service 模块导出
com.example.user.spi.UserService接口,order-service 模块只依赖该接口,不依赖任何具体实现类 - 实现类放在单独的 impl 模块里,通过
ServiceLoader在运行时动态加载,user-service 不直接 new 实例 - 接口方法签名保持稳定,参数/返回值尽量用 DTO 或不可变对象,避免暴露领域模型细节
构建与验证:把封装约束变成可执行的检查项
再好的设计,没落地就是纸上谈兵。需要用工具链固化封装纪律。
- 在 Maven 构建中启用
maven-compiler-plugin的模块验证,失败时中断构建 - 用
jdeps --check分析 jar 包是否意外引用了未声明的模块内部类 - CI 流水线中加入
jlink镜像生成步骤,如果模块图不闭合或存在隐式依赖,立即报错

















