学妹半夜问我到底什么是深浅拷贝,深拷贝有多深?

一、引用拷贝

引用的拷贝其实将一个引用的保存的地址给另外一个引用。他们指向了同一个对象。

public class Test {
    public static void main(String[] args) {
        Hero hero1 = new Hero(15,"Timor");
        Hero hero2 = hero1;

        System.out.println(hero1);
        System.out.println(hero2);
    }
}


class Hero {
    public int age;
    public String name;

    public Hero(int age, String name) {
        this.age = age;
        this.name = name;
    }

    @Override
    public String toString() {
        return "Hero{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

结果
在这里插入图片描述

内存图:
在这里插入图片描述
平时刷题的时候我们会用到引用的拷贝,比如二叉树和链表的题,定义ListNode cur = head;等。

二、浅拷贝 vs 深拷贝

1、实现Cloneable接口

我们在操作数组的时候,有时候会需要拷贝数组:

关于拷贝数组,我写了一篇关于数组的总结,里面包含了数组的四种拷贝方式:关于数组的总结

    public static void main(String[] args) {
        int[] array = {1,2,3};
        int[] copy = array.clone();
        System.out.println(Arrays.toString(array));
        System.out.println(Arrays.toString(copy));
    }

结果:
在这里插入图片描述
当我们像拷贝自定义的类的时候:
在这里插入图片描述
出现了编译错误。对于这种情况,我们需要让自定义类实现Cloneable接口:
在这里插入图片描述

此时还是编译错误,对,没错,我没重写Cloneable 的方法。

public interface Cloneable {
}

不是我不想写,是这该写啥?
在这里插入图片描述
我们发现数组的clone();方法是重写Object的方法。来吧,照葫芦画瓢,在自定义类试试。
方法:
1、alt + insert
在这里插入图片描述
2、选中Override Methods
在这里插入图片描述
3、选中clone();方法回车就完成了。
在这里插入图片描述

2、抛异常与类型转换。

抛异常:
在这里插入图片描述

类型转换:
在这里插入图片描述

因为clone();方法的返回值是Object,所以是向下转型,需要强制类型转换。

    public static void main1(String[] args) throws CloneNotSupportedException {
        Hero hero1 = new Hero(15,"Timor");
        Hero hero2 = (Hero) hero1.clone();
    }

3、这可是浅拷贝!

至于为什么说是浅拷贝,我们先看代码:

    public static void main1(String[] args) throws CloneNotSupportedException {
        Hero hero1 = new Hero(15,"Timor");
        Hero hero2 = (Hero) hero1.clone();

        System.out.println(hero1);
        System.out.println(hero2);
    }

结果:
在这里插入图片描述
我们来验证是不是真正的拷贝了。

    public static void main(String[] args) throws CloneNotSupportedException {
        Hero hero1 = new Hero(15,"Timor");
        Hero hero2 = (Hero) hero1.clone();

        System.out.println("克隆前");

        System.out.println(hero1);
        System.out.println(hero2);

        hero2.age = 22;
        hero2.name = "Ashe";

        System.out.println("克隆后");

        System.out.println(hero1);
        System.out.println(hero2);
    }

内存图
在这里插入图片描述

结果:
在这里插入图片描述
确实是修改成功了。并且原来的值没有改变,但是这个其实不是正在的深拷贝。

4、小可爱,这是陷阱

深拷贝会对引用类型重新在创新一次(包括值类型),在新对象做的任何修改都不会影响到源对象本身。

来看看这个例子你就明白了!
我们重新创建一个 技能类 :

class Skill {
    public Skill skill;
}

并且在 英雄类 中加入这个 技能 属性 :
在这里插入图片描述

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Hero hero1 = new Hero(15,"Timor");
        hero1.skills.skill = "种蘑菇";
        Hero hero2 = (Hero) hero1.clone();

        System.out.println("克隆前");

        System.out.println(hero1);
        System.out.println(hero2);

        hero2.age = 22;
        hero2.name = "Ashe";
        hero2.skills.skill = "万箭齐发";

        System.out.println("克隆后");

        System.out.println(hero1);
        System.out.println(hero2);
    }
}


class Hero implements Cloneable{
    public int age;
    public String name;

    public Skill skills;

    public Hero(int age, String name) {
        this.age = age;
        this.name = name;
        this.skills = new Skill();
    }

    @Override
    public String toString() {
        return "Hero{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", skills=" + skills.skill +
                '}';
    }

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

class Skill {
    public String skill;
}

内存图:
在这里插入图片描述

结果:
在这里插入图片描述
哦豁,提莫会万箭齐发了?那么蛮王有什么想法?
言归正传,说明这个还是浅拷贝,修改一个引用的值会影响到另外一个引用。

5、我们如果想把它改为深拷贝,可以这样做

1、让技能类(class Skill )实现Cloneable 接口,并重写克隆方法

class Skill implements Cloneable{
    public String skill;

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

2、在英雄类(class Hero)中修改克隆方法

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Hero heroClone = (Hero) super.clone();
        heroClone.skills = (Skill) this.skills.clone();
        return heroClone;
    }

6、完整代码

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Hero hero1 = new Hero(15,"Timor");
        hero1.skills.skill = "种蘑菇";
        Hero hero2 = (Hero) hero1.clone();

        System.out.println("克隆前");

        System.out.println(hero1);
        System.out.println(hero2);

        hero2.age = 22;
        hero2.name = "Ashe";
        hero2.skills.skill = "万箭齐发";

        System.out.println("克隆后");

        System.out.println(hero1);
        System.out.println(hero2);
    }
}


class Hero implements Cloneable{
    public int age;
    public String name;

    public Skill skills;

    public Hero(int age, String name) {
        this.age = age;
        this.name = name;
        this.skills = new Skill();
    }

    @Override
    public String toString() {
        return "Hero{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", skills=" + skills.skill +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Hero heroClone = (Hero) super.clone();
        heroClone.skills = (Skill) this.skills.clone();
        return heroClone;
    }
}

class Skill implements Cloneable{
    public String skill;

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

内存图:
在这里插入图片描述

我们来看下结果:
在这里插入图片描述
完成,收工!

三、关于深浅拷贝的区别(面试题)

1、一般的数组拷贝,因为数组中的数据类型是基本类型,我们修改原数组的值不会影响到克隆数组的值,那么可以理解为深拷贝;(注意可以理解)
2、如果自定义类型实现了拷贝,并且这个类中还有自定义类(可以理解为这个类中的类是这个类的属性),我们如果只是简单的克隆这个类,而不管这个类的"属性",那么它还是浅拷贝。如果我们"更深层次的’'的拷贝这个类。那么就是深拷贝了!

浅拷贝只需要考虑所需要复制的对象,不管对象里还有没有对象。

深拷贝则不仅需要考虑所复制的对象,还需要考虑对面里面还有没有对象。

深拷贝会对引用类型重新在创新一次(包括值类型),在新对象做的任何修改都不会影响到源对象本身。

3、本质上数组的拷贝还是浅拷贝,面试大哥要是问的话就说是浅拷贝。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值