java模块系统需显式导出api包,仅exports com.example.payment.api;等契约包,限定导出至特定模块;未导出包内public类仍不可访问;善用接口private方法封装共用逻辑;警惕自动模块隐式导出风险。

关键在于主动控制可见性边界,而不是依赖默认行为。Java 模块系统(JPMS)不会自动隐藏东西——它只在你明确声明时才生效;不声明导出,就等于默认锁死。
只导出真正需要的 API 包
在 module-info.java 中,exports 必须显式列出对外公开的包,且仅限接口、DTO、工厂类等契约类型所在包:
- 避免写
exports com.example.payment;这种宽泛导出,除非整个包都设计为公共 API - 推荐按职责拆分:如
exports com.example.payment.api;,而把.impl、.internal、.util全部排除在外 - 若某接口仅供特定模块使用,用限定导出:
exports com.example.config to com.example.service;
把实现类和工具类放在未导出包里
模块内所有未被 exports 的包,无论其中类是否声明为 public,外部模块都无法 import、实例化或继承:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 将 default 方法的具体实现、私有静态辅助类、策略实现类等,统一放在
com.example.xxx.impl或com.example.xxx.internal - 这些类即使 public,也不影响封装——因为模块边界挡住了访问路径
- Javadoc 和文档生成工具也不会收录未导出包,自然规避“文档泄露实现”问题
善用接口私有方法 + 默认方法组合
当一个接口需提供多个 default 方法时,把共用逻辑下沉为 private 接口方法,从语言层切断外部窥探可能:
- 例如校验、日志、序列化等重复代码,封装进
private void validateInput(...) - 该方法不会出现在 Javadoc 中,不能被实现类调用,也不能被反射直接访问(受限于模块+语言双重约束)
- default 方法只保留业务分支语义,如
payByAlipay()和payByWechat(),内部调用私有方法完成复用
警惕自动模块与类路径回退风险
当传统 JAR 被放入模块路径时,JVM 视其为“自动模块”,会隐式导出全部包——这会意外暴露本应隐藏的内容:
- 上线前检查模块图:
java --list-modules或jdeps --module-path ... --print-module-deps - 避免混合部署:不要让同一应用中既有命名模块,又有未迁移的旧 JAR 放在模块路径下
- 如必须兼容,可在构建时用
jlink或jpackage打包为自包含镜像,绕过类路径不确定性
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










