
Java 通过封装隔离外部环境变化,核心不是“把代码包起来”,而是用访问控制 + 抽象边界 + 可控接口,让外部环境的变动止步于类或模块的边界之内。
用 private + getter/setter 控制属性访问
把配置、资源、状态等敏感字段设为 private,不暴露原始值;所有读写都走方法入口,在 setter 中加入校验、转换或缓存逻辑。这样即使外部传入的数据格式变了(比如时间字符串从 "yyyy-MM-dd" 改成 "yyyyMMdd"),只需改一个 set 方法,不影响调用方。
- 避免直接 public 字段:防止非法赋值(如 age = -1)或绕过业务规则
- getter 可返回副本或脱敏值:例如 getPassword() 返回 "***" 而非明文
- setter 可触发重载或刷新:如 setEndpoint(String url) 同时重建内部 HTTP 客户端
用接口抽象外部依赖行为
不依赖具体实现类(如 Properties、FileInputStream、RestTemplate),而是依赖自定义接口(如 ConfigReader、HttpClient)。真实环境用实现类,测试或灰度环境换另一个实现——比如内存 Map 模拟配置、Mock 响应模拟网络调用。
- 业务类只持有接口引用:private final ConfigReader configReader;
- 构造或注入时决定实现:Spring 中用 @Profile("prod")/@Profile("test") 切换 Bean
- 外部环境变化(如配置源从文件切到 Consul)→ 只需新增一个 ConsulConfigReader 实现,业务代码零修改
用模块系统(JPMS)切断跨模块直接访问
Java 9+ 的模块机制是封装的物理强化。在 module-info.java 中不导出含敏感逻辑或原始配置的包,只导出精简的接口包。即使有人试图用反射加载类,也会被模块边界拦截。
- 敏感配置类放在
internal.config包,不写exports internal.config - 对外只提供
exports com.example.api,里面只有DataSourceFactory这类无状态接口 - 外部模块无法 import 或 new 内部类,连编译都过不去,更别说运行时篡改
用工厂或构建器封装初始化过程
把对象创建与外部环境强关联的部分(如读配置、连数据库、加载证书)收进工厂类。业务代码只面向工厂请求实例,不关心它怎么构建——环境变,工厂变;业务不变。
- 例如 DatabaseConnectionFactory 根据 system property 自动选 HikariCP 或 Druid
- SSLContextBuilder 根据环境变量决定加载哪套证书路径
- 调用方始终写
factory.createClient(),无需 if-else 判断环境
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











