【面试题】谈一下深拷贝与浅拷贝

唠嗑部分

首先什么是浅拷贝(浅克隆)与深拷贝(深克隆)呢?

见名知意,就是从源数据复制一份一模一样的,两份是独立的,互不影响(即修改任何一个,另一个不受影响)

浅拷贝:拷贝基本数据类型及引用数据类型的地址

深拷贝:拷贝引用数据类型的值

在前端js中,基本数据类型有字符串、布尔、数字,在Java中,8大基本数据类型byte、short、int、long、float、double、boolean、char

今天我们就来说一说前后端分别如何实现对象的深拷贝与浅拷贝

言归正传

我们用以下这个数据来说明

{
    name: "张三",
    age: 23,
    job: {
        company: "阿里巴巴",
        salary: 30
    }
}

前端如何实现深拷贝

预期结果:修改源数据的值后克隆数据的值不会变化

1、大家最先想到的一种方式肯定是赋值了,如下

说明:基本数据类型的赋值是值的拷贝,引用数据类型是地址的拷贝

// 赋值
const personCopy = person;

console.log("源数据", person)
console.log("克隆数据", personCopy)
// 修改源数据的值
person.age = 20
person.job.salary = 40

console.log("修改后源数据", person)
console.log("修改后克隆数据", personCopy)

这种的方式可想而知,经过赋值语句会吧person的地址赋给personCopy,是浅拷贝,看结果

image-20230427100419944

2、Object.assign()

// 语法
const personCopy = {};
Object.assign(personCopy, person)
// 打印同上

这种方式可以达到浅层次的深拷贝,如果对象有深层次的嵌套,依然是浅拷贝,看下图,age完成了深克隆,但是薪资依然是浅克隆

image-20230427100504979

3、展开运算符

// 语法
const personCopy = {...person};

展开运算符是ES6的一种语法,这种方式同上依然可以达到浅层次的深克隆,嵌套对象依然处理不了

image-20230427100546293

4、序列化与反序列化

// 语法
const personCopy = JSON.parse(JSON.stringify(person))

这种方式可以实现深拷贝,但是会出现一些问题,请百度自查

image-20230427100617259

那说了这么多方法,难道就没有一种方式实现深拷贝了嘛?

5、使用函数的方式

/**
 * 深拷贝
 * @param dest 拷贝的目标对象
 * @param src  源对象
 * @returns {{}}
 */
function deepCopy(dest,src){
    var dest = dest || {};
    for(let key in src){
        //如果对象的属性又是复杂数据类型,则递归处理
        if(typeof src[key] === "object"){
            dest[key]= (src[key].constructor === Array)?[]:{};
            deepCopy(dest[key],src[key]);
        }else{
            dest[key]=src[key];
        }
    }
    return dest;
}

// 语法
const personCopy = {}
deepCopy(personCopy, person)

编写深拷贝函数,如果是复杂对象,则递归处理

image-20230427101154141

Java如何实现深拷贝

首先创建Person类,Job类,由于篇幅原因,构造器、toString、Getter、Setter方法省略,自行补充

class Person implements Cloneable{
    private String name;
    private Integer age;
    private Job job;

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

class Job implements Cloneable{
    private String company;
    private Integer salary;

    ...

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

在Java中,要实现对象的拷贝,该对象必须实现Cloneable接口,否则会报错的哦

测试代码,与前端js一致

/**
 * @Project: wanju
 * @Author: cxs2014501@163.com
 * @Create: 2023/4/27 10:15
 * @Description:
 **/
public class DeepCopyTest {
    public static void main(String[] args) {
        try {
            Person person = new Person("张三", 23, new Job("阿里巴巴", 30));
            Person personCopy = (Person) person.clone();
            System.out.println("源数据" + person);
            System.out.println("克隆数据" + personCopy);

            person.setAge(20);
            person.getJob().setSalary(40);

            System.out.println("修改后源数据" + person);
            System.out.println("修改后克隆数据" + personCopy);
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

首先看效果,套娃的对象还是没有完成深拷贝

image-20230427102923093

深拷贝实现方案一:clone方法

修改person类的clone方法,针对于复杂对象单独处理,可以实现深拷贝

但是如果对象层层嵌套,那就每个对象都要处理clone方法

@Override
protected Object clone() throws CloneNotSupportedException {
    Person personClone = (Person) super.clone();
    Job jobClone = (Job) personClone.getJob().clone();
    personClone.setJob(jobClone);
    return personClone;
}

看效果

image-20230427103414647

深拷贝实现方案二(推荐):对象的序列化与反序列化

注意:对象要实现序列化接口Serializable,否则会无法序列化

image-20230427104334782

/**
 * @Project: wanju
 * @Author: cxs2014501@163.com
 * @Create: 2023/4/27 10:15
 * @Description:
 **/
public class DeepCopyTest {
    public static void main(String[] args) {
        ObjectInputStream oin = null;
        ObjectOutputStream oos = null;
        ByteArrayOutputStream bos = null;
        ByteArrayInputStream bis = null;
        try {
            Person person = new Person("张三", 23, new Job("阿里巴巴", 30));
            Person personCopy = null;

            // 对象序列化
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(person);

            // 对象反序列化
            bis = new ByteArrayInputStream(bos.toByteArray());
            oin = new ObjectInputStream(bis);
            personCopy = (Person) oin.readObject();


            System.out.println("源数据" + person);
            System.out.println("克隆数据" + personCopy);

            person.setAge(20);
            person.getJob().setSalary(40);

            System.out.println("修改后源数据" + person);
            System.out.println("修改后克隆数据" + personCopy);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭流资源,先开的后关
            ...
        }
    }
}

看效果,没有问题,这种方式也是推荐使用的

image-20230427104450256

结语

1、浅拷贝拷贝出来的两个对象地址是一样的,实际在堆中指向同一个实例

2、深拷贝拷贝出来的两个对象是完全独立的,因为拷贝的是堆中的整个对象,person与personCopy的地址不一样

3、制作不易,一键三连再走吧,您的支持永远是我最大的动力!

4、Java全栈技术交流QQ群:941095490,欢迎您的加入,案例代码见群文件!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全栈小白.

感谢老板,祝老板今年发大财!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值