浅拷贝与深拷贝-具体实现代码

在Java中,Cloneable 接口是一个标记接口(marker interface),用于指示一个类的实例可以被克隆。要实现浅拷贝和深拷贝,你需要覆盖 Object 类的 clone() 方法,并根据需要执行浅拷贝或深拷贝操作。

浅拷贝:

浅拷贝创建一个新对象,然后将原对象的非静态字段复制到新对象。

如果字段是基本数据类型,复制的是值;

如果是引用类型,复制的是引用。新对象和原对象共享相同的引用对象。

深拷贝:

深拷贝创建一个新对象,同时递归地复制原对象的所有引用对象及其包含的所有对象。

深拷贝独立于原对象,新对象的修改不会影响原对象。


示例代码1

以下是一个简单的示例,演示如何实现浅拷贝和深拷贝:

java 复制代码
import java.util.ArrayList;
import java.util.List;

class Address {
    String city;

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

class Person implements Cloneable {
    String name;
    int age;
    Address address;

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

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

    // 深拷贝
    public Person deepClone() throws CloneNotSupportedException {
        Person clone = (Person) super.clone();
        // 对引用类型进行独立拷贝
        clone.address = new Address(this.address.city);
        return clone;
    }
}

public class CloneExample {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address address = new Address("City");
        Person original = new Person("John", 25, address);

        // 浅拷贝
        Person shallowCopy = (Person) original.clone();

        // 深拷贝
        Person deepCopy = original.deepClone();

        // 修改原对象的引用类型字段
        original.address.city = "New City";

        // 输出结果
        System.out.println("Original: " + original.address.city);     // New City
        System.out.println("Shallow Copy: " + shallowCopy.address.city); // New City(共享同一个引用对象)
        System.out.println("Deep Copy: " + deepCopy.address.city);       // City(独立的引用对象)
    }
}

在这个例子中,Person 类包含一个引用类型字段 address,实现了浅拷贝和深拷贝。在深拷贝中,我们对引用类型字段 address 进行了独立拷贝,确保新对象拥有一个独立的 Address 实例。

示例代码2

typescript 复制代码
package com.linynag.springbootinit.testdemo;

import lombok.Data;

@Data
public class WorkExperience implements Cloneable {
    private String company;
    private String workTime;


    /**
     * 实现深拷贝的clone方法
     */
    public WorkExperience clone() {
        WorkExperience object = null;
        try {
            object = (WorkExperience) super.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println("克隆异常了");
            throw new RuntimeException(e);
        }
        return object;
    }
}
kotlin 复制代码
package com.linynag.springbootinit.testdemo;

import lombok.Data;

/**
 * 简历类
 */
@Data
public class Resume implements Cloneable {
    private String name;
    private String sex;
    private String age;
    private WorkExperience workExperience;

    public Resume(String name) {
        this.name = name;
        this.workExperience = new WorkExperience();
    }


    public void setWorkExperience(String company, String workTime) {
        this.workExperience.setCompany(company);
        this.workExperience.setWorkTime(workTime);
    }

    public void showResume() {
        System.out.println("姓名:" + this.name + "\t年龄" + this.age + "\t性别" + this.sex
                           + "\t工作经历:" + this.workExperience.getCompany() + "\t时间:" + this.workExperience.getWorkTime());
    }


    /**
     * 实现浅拷贝的clone方法
     */
    public Resume shallowCopy() {
        Resume object = null;
        // 使用克隆对象进行克隆内容
        try {
            object = (Resume) super.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println("克隆异常了");
            throw new RuntimeException(e);
        }
        return object;
    }


    /**
     * 实现深拷贝的clone方法
     */
    public Resume  deepCopy() {
        Resume object = null;
        // 使用克隆对象进行克隆内容
        try {
            object = (Resume) super.clone();
            // 对WorkExperience进行深拷贝
            object.workExperience = this.workExperience.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println("克隆异常了");
            throw new RuntimeException(e);
        }
        return object;
    }

}

运行:

java 复制代码
package com.linynag.springbootinit.testdemo;

public class cloneMainTest {
    public static void main(String[] args) {
        Resume resume1 = new Resume("18");
        resume1.setAge("18");
        resume1.setSex("18");
        resume1.setWorkExperience("18", "18");

        // 浅拷贝
        Resume cloneResume1 = resume1.shallowCopy();
        cloneResume1.setAge("19");
        cloneResume1.setSex("19");
        cloneResume1.setWorkExperience("19", "19");

        //-------------------------------------------------------------

        Resume resume2 = new Resume("20");
        resume2.setAge("20");
        resume2.setSex("20");
        resume2.setWorkExperience("20", "20");

        // 深拷贝
        Resume cloneResume2 = resume2.deepCopy();
        cloneResume2.setAge("21");
        cloneResume2.setSex("21");
        cloneResume2.setWorkExperience("21", "21");

        resume1.showResume();
        cloneResume1.showResume();
        resume2.showResume();
        cloneResume2.showResume();

    }
}

运行结果:


Question1:

public WorkExperience clone() 为什么是深拷贝 ?

public Resume shallowCopy() 为什么是浅拷贝 ?

他们应该都是一样的啊 ?

在你提供的代码中,WorkExperience 类实现了 Cloneable 接口,并重写了 clone 方法,该方法使用了 super.clone() 来进行对象的克隆。同样,Resume 类也实现了 Cloneable 接口,并提供了两个方法:shallowCopydeepCopy,分别用于浅拷贝和深拷贝。

让我们分析一下为什么 WorkExperienceclone 方法被称为深拷贝,而 ResumeshallowCopy 方法被称为浅拷贝。

WorkExperience 的 clone 方法(深拷贝)

csharp 复制代码
public WorkExperience clone() {
    WorkExperience object = null;
    try {
        object = (WorkExperience) super.clone();
    } catch (CloneNotSupportedException e) {
        System.out.println("克隆异常了");
        throw new RuntimeException(e);
    }
    return object;
}

在这个方法中,super.clone() 调用会创建一个新的 WorkExperience 对象,并将其字段值设置为与原始对象相同。这是一个浅拷贝的起点。然而,由于 WorkExperience 类实现了 Cloneable 接口,你可以对 workExperience 字段进行深拷贝,确保两个 WorkExperience 对象之间没有共享引用。在这里,WorkExperience 类中的 clone 方法确保了对象的深拷贝。

Resume 的 shallowCopy 方法(浅拷贝)

csharp 复制代码
public Resume shallowCopy() {
    Resume object = null;
    try {
        object = (Resume) super.clone();
    } catch (CloneNotSupportedException e) {
        System.out.println("克隆异常了");
        throw new RuntimeException(e);
    }
    return object;
}

WorkExperience 类不同,Resume 类的 shallowCopy 方法没有明确对其内部的 workExperience 字段进行深拷贝。它只是调用了 super.clone() 来创建一个新的 Resume 对象,并复制了基本数据类型的字段。然而,由于 workExperience 是一个引用类型,这意味着新对象和原对象都引用相同的 WorkExperience 实例,而不是创建一个新的 WorkExperience 实例。因此,这是一个浅拷贝,因为两个对象共享相同的 WorkExperience 引用。

总结起来,WorkExperienceclone 方法是深拷贝,因为它明确对引用类型的字段进行了克隆。而 ResumeshallowCopy 方法是浅拷贝,因为它没有对其引用类型字段进行深拷贝,导致新对象和原对象共享相同的引用。


Question2:

object.workExperience = this.workExperience.clone();

为什么加了这样一串代码就可以 实现深拷贝 原理是什么?

这一串代码实现了对 Resume 类的深拷贝。让我们一步步解释这段代码的原理:

ini 复制代码
object.workExperience = this.workExperience.clone();
  1. this.workExperience 获取当前对象的 WorkExperience 成员变量,即获取原对象的工作经历实例。
  2. this.workExperience.clone() 调用 WorkExperience 类的 clone() 方法。因为 WorkExperience 类实现了 Cloneable 接口并提供了相应的 clone() 方法,这个方法会创建一个新的 WorkExperience 实例,并将原对象的字段值复制到新的实例中。这是 WorkExperience 类中的浅拷贝。
  3. object.workExperience = this.workExperience.clone() 将新创建的 WorkExperience 实例赋值给克隆对象 objectworkExperience 成员变量。这样,object 和原对象都拥有了独立的 WorkExperience 实例。

这段代码的关键在于 this.workExperience.clone() 的调用,它确保了对 WorkExperience 对象的引用类型字段进行了深拷贝,而不仅仅是复制了引用。因此,object 和原对象都有一个独立的 WorkExperience 实例,修改其中一个对象的工作经历不会影响另一个对象。

需要注意的是,为了使这段代码正常工作,确保 WorkExperience 类中的 clone() 方法也实现了深拷贝,即对其引用类型字段进行了递归复制。如果 WorkExperience 类中有其他引用类型字段,也需要在相应的类中实现深拷贝。这样逐层递归,确保所有引用类型字段都得到了独立的复制。这是实现深拷贝的基本原理。

相关推荐
星辰@Sea3 分钟前
服务注册中心对比及使用场景分析
java·云原生
马剑威(威哥爱编程)7 分钟前
除了递归算法,要如何优化实现文件搜索功能
java·开发语言·算法·递归算法·威哥爱编程·memoization
bug菌¹9 分钟前
滚雪球学SpringCloud[4.1讲]: Spring Cloud Gateway详解
java·spring cloud·微服务
MuseLss29 分钟前
HashMap高频面试知识点
java·开发语言·哈希算法
tyler-泰勒31 分钟前
初始c++:入门基础(完结)
java·开发语言·c++
重生之我要进大厂1 小时前
LeetCode 876
java·开发语言·数据结构·算法·leetcode
_祝你今天愉快1 小时前
技术成神之路:设计模式(十四)享元模式
java·设计模式
小筱在线2 小时前
SpringCloud微服务实现服务熔断的实践指南
java·spring cloud·微服务
luoluoal2 小时前
java项目之基于Spring Boot智能无人仓库管理源码(springboot+vue)
java·vue.js·spring boot
ChinaRainbowSea2 小时前
十三,Spring Boot 中注入 Servlet,Filter,Listener
java·spring boot·spring·servlet·web