Java 封装机制:数据隐藏、访问控制与不变量保护
封装是面向对象四大支柱之一(封装、继承、多态、抽象)。核心思想:将数据(字段)与操作数据的方法绑定,隐藏内部实现细节,仅暴露受控接口。实现数据隐藏,保护对象不变量。
实现封装的两步走
- 私有化字段:
private修饰实例变量 - 公开访问器:提供
publicgetter/setter 方法
标准封装模板
public class Person {
private String name; // 私有字段
private int age;
// Getter
public String getName() { return name; }
public int getAge() { return age; }
// Setter(含验证逻辑)
public void setName(String name) {
if (name == null || name.trim().isEmpty())
throw new IllegalArgumentException("姓名不能为空");
this.name = name.trim();
}
public void setAge(int age) {
if (age < 0 || age > 150)
throw new IllegalArgumentException("年龄非法: " + age);
this.age = age;
}
}
封装的核心价值
| 价值 | 说明 |
|---|---|
| 控制访问 | 决定字段可读/可写/只读/只写 |
| 验证约束 | Setter 中校验参数,维护不变量(如年龄≥0) |
| 灵活变更 | 内部实现变更(如改用 Map 存储)不影响调用者 |
| 防御性编程 | 返回可变对象的副本,防止外部篡改内部状态 |
| 审计/日志 | 访问/修改时记录日志、触发事件 |
防御性拷贝示例
public class Period {
private final Date start, end;
public Period(Date start, Date end) {
this.start = new Date(start.getTime()); // 拷贝
this.end = new Date(end.getTime());
if (this.start.after(this.end))
throw new IllegalArgumentException("开始不能晚于结束");
}
public Date getStart() { return new Date(start.getTime()); } // 返回副本
public Date getEnd() { return new Date(end.getTime()); }
}
访问修饰符与封装粒度
| 修饰符 | 同类 | 同包 | 子类 | 其他 |
|---|---|---|---|---|
| private | ✅ | ❌ | ❌ | ❌ |
| 默认 | ✅ | ✅ | ❌ | ❌ |
| protected | ✅ | ✅ | ✅ | ❌ |
| public | ✅ | ✅ | ✅ | ✅ |
原则:字段尽量 private;方法按需暴露;包内协作类可用默认/protected。
不可变对象:封装的极致
所有字段 final、无 setter、构造器完全初始化、防御性拷贝可变组件。天然线程安全。
public final class ImmutablePoint {
private final int x, y;
public ImmutablePoint(int x, int y) { this.x = x; this.y = y; }
public int getX() { return x; }
public int getY() { return y; }
// 无 setter,状态创建后不可变
}
Java Bean 规范与现代替代
- Java Bean:无参构造器 + private 字段 + 标准 getter/setter,适配反射框架
- Record (Java 14+):
record Point(int x, int y) {}自动生成不可变数据载体 - Lombok @Data/@Value:编译期生成样板代码
常见反模式
- 贫血模型:只有 getter/setter 无业务逻辑,领域逻辑外泄到 Service 层
- 暴露可变内部状态:直接返回集合/数组/Date 引用
- 过度封装:简单 DTO 也强行加 getter/setter,增加噪音
最佳实践清单
- 字段默认
private final,真正需要变再去掉 final - Setter 必加校验,抛出有意义的异常
- 可变对象字段:构造器拷贝、Getter 返回副本
- 集合字段:返回不可变视图
Collections.unmodifiableList(list)或拷贝 - 领域模型:将业务规则封装在实体方法中,而非外部过程式代码
java
- C# 封装详解:访问修饰符与面向对象设计最佳实践
- 深入理解 Java final 关键字:变量、方法与类的不可变性
- Java instanceof 运算符完全指南:用法、继承与接口检查
- 精通 Java 封装:数据隐藏实战指南
- 深入理解 Java 静态嵌套类:用法、差异与实战示例
- 精通 Java 匿名内部类:定义、语法与实战示例
- Java 单例模式详解:安全实现与使用指南
- 精通 Java 反射:运行时检查类、方法与字段
- 掌握 Java ObjectOutputStream:序列化、核心方法与实战示例
- 掌握 Java 泛型:构建可复用、类型安全的代码
- 掌握 Java 文件操作:java.io 包下的创建、读取、写入与删除
- Java 封装机制:数据隐藏、访问控制与不变量保护