结论:Java 只提供浅拷贝的原生默认行为——Object.clone() 复制字段值,引用字段仍指向同一个堆对象;深拷贝必须自己实现,常用逐层 clone()、序列化/反序列化、拷贝构造或 JSON 转换,BeanUtils.copyProperties 不是深拷贝。
浅拷贝和深拷贝有什么区别?
结论:浅拷贝只复制一层,深拷贝递归复制整个对象图。例如 User 含 Address 字段,浅拷贝后 copy.address == original.address 返回 true,深拷贝后返回 false。基本类型和不可变对象(String、Integer、LocalDate)共享引用安全,因为不可变对象不能被修改。
浅拷贝复制对象本身和基本类型字段值,引用类型字段只复制引用地址。深拷贝会为每个引用字段创建新对象,直到对象图中所有可变对象都独立。判断标准只有一条:修改副本的引用字段后,原对象是否受影响;受影响就是浅拷贝,不受影响才是深拷贝。
Java 中怎么实现浅拷贝?
结论:浅拷贝用 Object.clone() 的默认行为,或 Spring BeanUtils.copyProperties()。步骤 1:类实现 Cloneable 标记接口,它是空接口,不实现会抛 CloneNotSupportedException。步骤 2:重写 clone(),调用 super.clone()。Object.clone() 从 Java 1.0 起存在,是 protected native 方法。
class User implements Cloneable {
String name;
Address address;
@Override
public User clone() throws CloneNotSupportedException {
return (User) super.clone();
}
}
数组字段要注意:super.clone() 只复制数组引用,不复制数组内容。需要内容独立时用 Arrays.copyOf(addresses, addresses.length)。Spring BeanUtils.copyProperties(source, target) 同样是浅拷贝,它通过反射复制属性值,引用字段仍指向同一对象。
Java 中怎么实现深拷贝?
结论:JDK 没有提供一键深拷贝方法,深拷贝必须由代码显式完成,落地方案有 4 类:逐层 clone()、序列化/反序列化、拷贝构造、JSON/第三方库转换。
逐层 clone():每个引用类都实现 Cloneable,在 clone() 里继续克隆引用字段。final 引用字段无法重新赋值,需要改用拷贝构造。
class Address implements Cloneable {
String city;
@Override
public Address clone() throws CloneNotSupportedException {
return (Address) super.clone();
}
}
class User implements Cloneable {
String name;
Address address;
@Override
public User clone() throws CloneNotSupportedException {
User copy = (User) super.clone();
copy.address = address.clone();
return copy;
}
}
序列化:要求对象和全部引用字段实现 java.io.Serializable。transient 字段恢复为默认值,static 字段不参与复制。Apache Commons Lang3 3.12.0 提供 SerializationUtils.clone(obj),一行完成,前提同样是 Serializable。
public static <T extends Serializable> T deepCopy(T obj)
throws IOException, ClassNotFoundException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {
oos.writeObject(obj);
}
try (ObjectInputStream ois = new ObjectInputStream(
new ByteArrayInputStream(bos.toByteArray()))) {
return (T) ois.readObject();
}
}
拷贝构造:public User(User other) { this.name = other.name; this.address = new Address(other.address); }。字段少于 10 个时,拷贝构造可控且无反射开销。JSON:new ObjectMapper().convertValue(user, User.class) 是深拷贝,Jackson 2.x 要求无参构造、getter/setter;循环引用会抛异常,泛型需要 TypeReference。
深拷贝选型时看哪些参数?
结论:选型看对象图深度、是否可序列化、性能预算和字段数量。字段少于 10 个、结构稳定,用拷贝构造;已有 Cloneable 体系,用逐层 clone();对象图深且全部 Serializable,用序列化;对外 DTO、JSON 字段,用 Jackson convertValue。不要用 Spring BeanUtils.copyProperties 做深拷贝,它只复制引用。Java 16 起的 record 默认浅拷贝,引用字段仍共享。
Java 深拷贝的核心是明确对象图中哪些引用可变,并让每个可变引用都获得新实例。浅拷贝用 clone() 或 BeanUtils,深拷贝用逐层克隆、序列化、拷贝构造或 JSON;选错方案会在修改副本时污染原对象。