核心是严格隔离内部工具类:不导出、不开放、不暴露类型;仅导出api包,internal包不在exports中;用opens精准授权反射访问;工具类设为final、私有构造、静态方法;通过服务接口和服务加载器提供能力。

核心就一条:不导出、不开放、不暴露类型——让外部模块连类名都看不到,更别说用了。
只导出 API 包,彻底隔离工具类所在包
内部工具类(比如 com.example.order.internal.util.CryptoHelper)必须放在明确标记为“内部”的包里,且这个包不能出现在 module-info.java 的 exports 列表中:
- ✅ 正确写法:
module com.example.order {<br> exports com.example.order.api;<br> // 不写 exports com.example.order.internal.util<br>} - ❌ 错误写法:
哪怕只加一行exports com.example.order.internal.util;,整个包的 public 类就对外可见,工具类立刻“裸奔”
禁用反射穿透,不开口子
有些框架(如 Jackson、Spring)需要反射访问字段,但绝不能因此把整包对所有模块开放。要用 opens 精准授权:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 只对真正需要的模块开放:
opens com.example.order.internal.util to com.fasterxml.jackson.databind; - 绝不写
opens com.example.order.internal.util to ALL-UNNAMED;或开放给无关模块 - 记住:opens ≠ exports —— 开放反射不等于允许 import 或 new 实例
工具类本身做最小化设计
即使包被意外导出,也要守住最后一道防线:
- 类声明为 final,避免被继承扩展
- 构造器设为 private 并抛异常:
private CryptoHelper() { throw new UnsupportedOperationException(); } - 所有方法和字段保持 static,不依赖实例状态
- 避免在工具类里持有敏感对象引用(如密钥实例),改用运行时按需加载+即用即清
用服务接口替代直接调用
如果其他模块确实需要工具能力(比如加解密),不要让它 import 工具类,而是提供抽象服务:
- 在 com.example.order.api 中定义
EncryptionService接口 - 实现类
DefaultEncryptionService放在 internal 包下,不导出 - 通过
ServiceLoader.load(EncryptionService.class)获取实例,调用方完全不知道底层工具类存在
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










