深拷贝和浅拷贝

本文介绍了Java中的引用拷贝、浅拷贝和深拷贝。引用拷贝不创建新对象,两对象指向同一地址。浅拷贝创建新对象,基本类型或String拷贝值,引用类型拷贝地址。深拷贝会对引用类型继续拷贝,实现方式有层层clone()和序列化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


Java中的对象拷贝主要分为:引用拷贝,浅拷贝(Shallow Copy)、深拷贝(Deep Copy)。

1、引用拷贝

并没有创建一个新的对象,两个对象都指向同一个引用地址。
对于对象内的数据,不管是基本类型还是引用类型的修改,都会影响到另一个对象。

2、浅拷贝

创建一个新对象,并对对象内的每个数据进行拷贝。

  • 数据属性是基本类型或String,拷贝的就是基本类型或String的值;
  • 数据属性是引用类型,拷贝的就是引用(即内存地址),如果内存地址内的值发生改变,就会影响到另一个对象。

实现对象拷贝的类,必须实现Cloneable接口,并覆写clone()方法。

必须重写Object中的clone()方法,要重写clone()方法,就必须实现Cloneable接口
如果重写了Object中的clone()方法,没有实现Cloneable接口,会报错java.lang.CloneNotSupportedException
protected native Object clone() throws CloneNotSupportedException;
3、深拷贝

创建一个新对象,并对对象内的每个数据进行拷贝。
如果数据类型是引用类型,则继续进行浅拷贝(本质是创建新对象,赋值),直到所有引用类型都是新创建的对象。
深拷贝实现方式:层层clone()实现深拷贝,序列化实现深拷贝

3.1、层层clone()实现深拷贝

对象的所有引用类型,都要进行引用类型的再次clone(),赋给对象

3.2、序列化实现深拷贝

对象的所有引用类型,都要实现Serializable接口

举例:

  • Person类持有三个变量(String类型,基本类型,引用类型),String name,int age,Address address

Person类

public class Person implements Cloneable, Serializable {

    //姓名
    private String name;
    // 年龄
    private int age;
    // 联系地址
    private Address address;

    public Person(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }
    
	//省略get、set、toString方法

    /**
     * 如果一个对象内部只有基本数据类型,则 clone() 方法获取到的就是这个对象的深拷贝。
     * 而如果其内部还有引用数据类型,那用 clone() 方法就是一次浅拷贝的操作。
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    /**
     * 对对象内的每一级引用类型(除String),做浅拷贝,即为深拷贝
     */
    public Object deepClone() throws CloneNotSupportedException {
        Person person = (Person) super.clone();
        Address address = (Address) getAddress().clone();
        person.setAddress(address);
        return person;
    }

    /**
     * 利用串行化来做深复制
     */
    public Object deepCloneBySerialize() {
        try {
            //写入对象
            ByteArrayOutputStream bo = new ByteArrayOutputStream();
            ObjectOutputStream os = new ObjectOutputStream(bo);
            os.writeObject(this);
            //读取对象
            ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
            ObjectInputStream oi = new ObjectInputStream(bi);
            return (oi.readObject());
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}

Address类

public class Address implements Cloneable, Serializable {
    private String name;

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

	//省略get、set、toString方法

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

测试Demo

public class CloneDemo {

    public static void main(String[] args) throws CloneNotSupportedException {

        //引用拷贝
        System.out.println("-------- 引用拷贝 ----------");
        Person p = new Person("本人",18, new Address("湖南"));
        Person p1 = p;
        System.out.println(p);
        System.out.println(p1);
        System.out.println(p == p1);
        p1.setName("本人copy");
        p1.setAge(666);
        p1.getAddress().setName("上海");
        System.out.println(p);
        System.out.println(p1);

        //浅拷贝
        System.out.println();
        System.out.println("-------- 浅拷贝 ----------");
        p = new Person("本人",18, new Address("湖南"));
        p1 = (Person)p.clone();
        System.out.println(p);
        System.out.println(p1);
        System.out.println("--------convert--");
        p1.setName("本人copy");
        p1.setAge(666);
        p1.getAddress().setName("上海");
        System.out.println(p);
        System.out.println(p1);

        //深拷贝实现方式1
        System.out.println();
        System.out.println("-------- 深拷贝 ----------");
        p = new Person("本人",18, new Address("湖南"));
        p1 = (Person)p.deepClone();
        System.out.println(p);
        System.out.println(p1);
        System.out.println("--------convert--");
        p1.setName("本人copy");
        p1.setAge(666);
        p1.getAddress().setName("上海");
        System.out.println(p);
        System.out.println(p1);

        //深拷贝实现方式2 serialize
        System.out.println();
        System.out.println("-------- 深拷贝 serialize ----------");
        p = new Person("本人",18, new Address("湖南"));
        p1 = (Person)p.deepCloneBySerialize();
        System.out.println(p);
        System.out.println(p1);
        System.out.println("--------convert--");
        p1.setName("本人copy");
        p1.setAge(666);
        p1.getAddress().setName("上海");
        System.out.println(p);
        System.out.println(p1);
        System.out.println("-------- xxxxx ----------");
    }
}

测试结果:

-------- 引用拷贝 ----------
Person{name='本人', age=18, address=Student{name='湖南'}}
Person{name='本人', age=18, address=Student{name='湖南'}}
true
Person{name='本人copy', age=666, address=Student{name='上海'}}
Person{name='本人copy', age=666, address=Student{name='上海'}}

-------- 浅拷贝 ----------
Person{name='本人', age=18, address=Student{name='湖南'}}
Person{name='本人', age=18, address=Student{name='湖南'}}
--------convert--
Person{name='本人', age=18, address=Student{name='上海'}}
Person{name='本人copy', age=666, address=Student{name='上海'}}

-------- 深拷贝 ----------
Person{name='本人', age=18, address=Student{name='湖南'}}
Person{name='本人', age=18, address=Student{name='湖南'}}
--------convert--
Person{name='本人', age=18, address=Student{name='湖南'}}
Person{name='本人copy', age=666, address=Student{name='上海'}}

-------- 深拷贝 serialize ----------
Person{name='本人', age=18, address=Student{name='湖南'}}
Person{name='本人', age=18, address=Student{name='湖南'}}
--------convert--
Person{name='本人', age=18, address=Student{name='湖南'}}
Person{name='本人copy', age=666, address=Student{name='上海'}}
-------- xxxxx ----------

参考
Java漫谈-深拷贝与浅拷贝
Java 浅拷贝和深拷贝的理解和实现方式

<think>嗯,用户想了解C++中的深拷贝浅拷贝的区别以及实现方法。首先,我需要回忆一下这两个概念的基本定义。浅拷贝应该是指简单的复制指针,导致两个对象指向同一块内存,而深拷贝则是复制指针所指向的数据本身,这样两个对象就独立了。 根据用户提供的引用内容,引用[1]里提到浅拷贝只拷贝指针,深拷贝会拷贝内容,默认的拷贝构造函数是浅拷贝,需要自己实现深拷贝引用[2][3]给出了具体的代码示例,展示了如何通过自定义拷贝构造函数来实现深拷贝,比如在Person类中为Name或m_height重新分配内存并复制数据。引用[4]中的Array类也演示了类似的深拷贝实现,即在拷贝构造函数中新建内存空间并复制数据。 接下来,我需要将这些信息组织成一个清晰的回答。首先解释两者的区别,用比喻的话,浅拷贝像快捷方式,深拷贝像复制文件,这样用户容易理解。然后,说明默认拷贝构造函数的问题,比如会导致重复释放内存,出现错误。接着,用用户提供的代码例子来说明如何手动实现深拷贝,比如在拷贝构造函数中分配新内存,并复制数据。可能还需要提到析构函数的重要性,确保深拷贝的对象能正确释放内存。 用户可能还关心什么时候需要深拷贝,应该是在类中有指针成员指向动态分配的内存时。这时候必须自己实现深拷贝,否则会出现问题。同时,需要注意在实现深拷贝时,除了拷贝构造函数,可能还需要重载赋值运算符,但用户的问题可能没有提到,所以可能暂时不需要展开,但如果有相关问题可以提到。 最后,生成的相关问题需要围绕实现深拷贝的方法、常见错误场景以及如何避免等问题。这样用户如果有进一步的问题,可以继续查阅相关内容。确保回答中正确引用提供的资料,比如引用[1]、[3]中的例子来说明浅拷贝的问题深拷贝的实现步骤。</think>### C++深拷贝浅拷贝的区别及实现方式 #### 一、核心概念 1. **浅拷贝** - 仅复制对象的指针成员变量,拷贝后新旧对象的指针**指向同一块内存地址**,例如: ```cpp //示例代码片段 m_height = p.m_height; //浅拷贝直接复制指针[^3] ``` - 类似文件快捷方式,多个指针共享同一资源,可能导致**重复释放内存**的崩溃问题[^1] 2. **深拷贝** - 为指针成员**创建独立的内存空间**,完全复制原对象的数据内容: ```cpp m_height = new int(*p.m_height); //新建堆空间并复制数据[^3] ``` - 类似完整文件复制,新旧对象完全独立,避免资源冲突[^1] #### 二、实现方式对比 | 特征 | 浅拷贝 | 深拷贝 | |--------------|---------------------------|---------------------------| | 内存分配 | 共享原对象内存 | 新建独立内存空间 | | 实现复杂度 | 编译器默认提供 | 需手动实现拷贝构造函数 | | 资源管理风险 | 易出现重复释放 | 内存管理安全 | | 典型应用场景 | 无指针成员的简单对象 | 包含指针/动态资源的对象 | #### 三、关键实现步骤(以类`person`为例) 1. **定义深拷贝构造函数** ```cpp person(const person& p) { m_age = p.m_age; m_height = new int(*p.m_height); //关键:新建堆内存并复制数据 } ``` 2. **配套析构函数** ```cpp ~person() { delete m_height; //确保正确释放深拷贝分配的内存 m_height = nullptr; } ``` 3. **错误示例(浅拷贝陷阱)** 若直接使用默认拷贝构造函数: ```cpp person p2(p1); //此时p2.m_height与p1.m_height指向同一地址 ``` 析构时会导致**重复释放同一内存地址**,引发程序崩溃[^1] #### 四、应用场景分析 - **必须使用深拷贝**:类包含指针成员且指向动态分配内存时(如字符串、数组等) - **可使用浅拷贝**:仅包含基本数据类型或没有资源所有权的指针时 - **典型案例**:自定义容器类(如动态数组`Array`类[^4])、包含文件句柄的类
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值