java的深拷贝与浅拷贝

1.核心区别

深拷贝(Deep Copy) 和 浅拷贝(Shallow Copy) 是对象复制的两种核心方式,主要区别在于对 引用类型字段 的处理

特性 浅拷贝 深拷贝
对象复制范围 只复制对象本身(基本类型字段 + 引用地址) 递归复制对象及其引用的所有子对象
引用类型字段 仅复制引用地址(新旧对象共享同一对象) 创建全新独立对象(无共享)
内存关系 原对象和拷贝对象部分共享内存 完全独立的内存空间
修改影响 修改引用字段会影响原对象 修改引用字段 不影响 原对象
默认实现 Object.clone() 默认行为 需手动实现
性能 快(仅复制一层) 慢(递归复制整个对象树)

例如:对象是一本书,浅拷贝就是把目录复制了一份,而深拷贝是吧整本书复制了一份。

2.技术细节解析

java 复制代码
class Address implements Cloneable {
    String city;

    public Address(String city) {
        this.city = city;
    }

    // 浅拷贝
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

class Person implements Cloneable {
    String name;
    Address address;

    public Person(String name, Address address) {
        this.name = name;
        this.address = address;
    }

    // 浅拷贝
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    // 深拷贝
    protected Person deepClone() throws CloneNotSupportedException {
        Person cloned = (Person) super.clone();
        cloned.address = (Address) address.clone(); // 关键:拷贝引用对象
        return cloned;
    }
}

public class CopyDemo {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address addr = new Address("Beijing");
        Person p1 = new Person("Tom", addr);

        // 浅拷贝
        Person p2 = (Person) p1.clone();
        // 深拷贝
        Person p3 = p1.deepClone();

        // 修改原始对象的引用类型字段
        p1.address.city = "Shanghai";

        System.out.println("p2.address.city (浅拷贝): " + p2.address.city); // 输出 Shanghai
        System.out.println("p3.address.city (深拷贝): " + p3.address.city); // 输出 Beijing
    }
}

使用序列化实现深拷贝

java 复制代码
import java.io.*;

public class DeepCopyUtil {
    public static <T extends Serializable> T deepCopy(T object) {
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(bos)) {
            
            oos.writeObject(object); // 序列化
            try (ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
                 ObjectInputStream ois = new ObjectInputStream(bis)) {
                return (T) ois.readObject(); // 反序列化生成新对象
            }
        } catch (Exception e) {
            throw new RuntimeException("Deep copy failed", e);
        }
    }
}

// 使用
Person deepCopy = DeepCopyUtil.deepCopy(original);
deepCopy.address.setCity("Tokyo"); 
System.out.println(original.address.getCity()); // 输出 "Paris"(原对象不受影响)

3.关键使用场景

场景 推荐方式 原因
对象不含引用类型字段 浅拷贝 性能高且安全
引用字段是不可变对象 浅拷贝 String/包装类等不可变对象无需深拷贝
对象包含可变引用字段 深拷贝 避免意外修改原对象
复杂嵌套对象图 序列化深拷贝 避免递归实现的复杂性

4.注意事项

  1. Cloneable 接口的缺陷:

    是标记接口(无方法),但必须实现才能调用 Object.clone()

    clone() 方法是 protected,需重写为 public

  2. 深拷贝陷阱:

    递归实现需确保所有嵌套对象都支持克隆

    循环引用会导致递归无限循环(序列化可解决)

  3. 最佳实践:

    优先使用 不可变对象(如 String、LocalDateTime)

    复杂对象推荐用 序列化法 实现深拷贝

    避免深拷贝:考虑用 防御性拷贝构造器(如 new ArrayList<>(originalList))

💡 终极原则:如果对象需要完全隔离修改,用深拷贝;如果共享数据无风险或用不可变对象,用浅拷贝。

相关推荐
陈小桔11 小时前
idea中重新加载所有maven项目失败,但maven compile成功
java·maven
小学鸡!11 小时前
Spring Boot实现日志链路追踪
java·spring boot·后端
xiaogg367811 小时前
阿里云k8s1.33部署yaml和dockerfile配置文件
java·linux·kubernetes
逆光的July11 小时前
Hikari连接池
java
微风粼粼12 小时前
eclipse 导入javaweb项目,以及配置教程(傻瓜式教学)
java·ide·eclipse
番茄Salad12 小时前
Spring Boot临时解决循环依赖注入问题
java·spring boot·spring cloud
立志成为大牛的小牛12 小时前
数据结构——二十六、邻接表(王道408)
开发语言·数据结构·c++·学习·程序人生
天若有情67312 小时前
Spring MVC文件上传与下载全面详解:从原理到实战
java·spring·mvc·springmvc·javaee·multipart
祈祷苍天赐我java之术12 小时前
Redis 数据类型与使用场景
java·开发语言·前端·redis·分布式·spring·bootstrap
MediaTea13 小时前
Python 第三方库:matplotlib(科学绘图与数据可视化)
开发语言·python·信息可视化·matplotlib