
通过设计不可变类(如使用 record)、深拷贝或不可变包装器,可防止外部对 person 类中 car 属性的意外修改,从而保障封装性与数据一致性。
通过设计不可变类(如使用 record)、深拷贝或不可变包装器,可防止外部对 person 类中 car 属性的意外修改,从而保障封装性与数据一致性。
在面向对象编程中,封装的核心目标之一是控制状态变更的边界。当一个类(如 Person)持有另一个可变对象(如 Car)的引用时,若仅通过简单赋值(this.car = car;)设置该引用,就等于将外部对象的“控制权”直接让渡给了类内部——此时外部代码一旦修改该 Car 实例(例如调用 car.setBrand("Tesla") 或 car.setMileage(50000)),Person 对象所感知的 car 状态也会同步改变,这严重破坏了封装原则和预期行为。
✅ 正确做法一:优先采用不可变设计(推荐)
Java 14+ 引入的 record 是构建不可变值类的理想工具。它自动生成 final 字段、私有构造器、equals/hashCode/toString,且所有组件默认不可变:
public record Car(String brand, String model, int year) {
// 所有字段隐式 final;无 setter;构造即冻结状态
}配合修正后的 Person 类(注意修复原代码中的语法错误:getCar() 应为 getCar(),setCar() 返回类型应为 void):
public class Person {
private final String name;
private final Car car; // 使用 final + 不可变 Car,双重保障
public Person(String name, Car car) {
this.name = name;
this.car = car; // 安全赋值:Car 本身不可变,无副作用
}
public String getName() { return name; }
public Car getCar() { return car; } // 可安全返回(无风险)
// 无需 setCar —— 若需更新,应创建新 Person 实例(函数式风格)
}✅ 优势:零拷贝开销、线程安全、语义清晰、易于测试。
✅ 正确做法二:若必须使用可变类,实施防御性拷贝(Deep Copy)
当 Car 无法重构为不可变类型时,应在 setCar() 中创建深拷贝,而非直接引用:
public void setCar(Car car) {
if (car == null) {
this.car = null;
} else {
this.car = new Car(car.getBrand(), car.getModel(), car.getYear());
// 假设 Car 提供带参构造器,或使用 BeanUtils.copyProperties 等工具
}
}⚠️ 注意:clone() 方法因绕过构造器、易出错且需手动实现 Cloneable,已不推荐;应优先使用构造器复制或现代库(如 Apache Commons Lang 的 SerializationUtils.clone(),适用于可序列化类型)。
✅ 补充技巧:保护集合类属性
若 Car 包含 List<string> features</string>,还需用 Collections.unmodifiableList() 封装:
private final List<String> features;
public Car(String brand, List<String> features) {
this.brand = brand;
this.features = Collections.unmodifiableList(new ArrayList<>(features));
}总结
-
首选不可变性:用
record或手工实现不可变类,从根源杜绝外部修改; -
次选防御性拷贝:对可变对象,在 getter 中返回副本(如
new Car(this.car)),在 setter 中接收副本; -
永远避免裸引用传递:尤其对非
final、非不可变的对象属性; -
一致性原则:若
Person需支持更新Car,应提供withCar(Car newCar)工厂方法,返回新Person实例,保持不可变契约。
这样,你的 Person 类才能真正成为可靠、可预测、可维护的领域模型。

















