面试官:深拷贝与浅拷贝有啥区别?

1.前言

首先,明确一点深拷贝和浅拷贝是针对对象属性为对象的,因为基本数据类型在进行赋值操作时(也就是拷贝)是直接将值赋给了新的变量,也就是该变量是原变量的一个副本,这个时候你修改两者中的任何一个的值都不会影响另一个,而对象或者引用数据来说在进行浅拷贝时,只是将对象的引用复制了一份,也就内存地址;即两个不同的变量指向了同一个内存地址,那么在改变任一个变量的值都是改变这个内存地址所指向的值,所以两个变量的值都会改变。
上方这句话不太好理解,这样,我把他们区别出来,做两个讲解,一个是关于基本类型的拷贝;一个关于引用数据类型的拷贝,分开理解,会大大降低学习成本~

2.基本类型的拷贝

在基本类型的拷贝中,深拷贝浅拷贝的作用就没什么区别了,拷贝出来的对象都是拥有相同的数值,这里用语言文字很难描述,可以看下面的代码和图来帮助理解。

    public static void main(String[] args) throws CloneNotSupportedException {
    
    
        //原来的对象
        Person person1=new Person("张三",15);
        //克隆出来的对象
        Person person2=(Person) person1.clone();
    }

我们打印这两个对象的地址:
观察可以看出,他两个的地址是不相同的,也就是说,他两个现在是不同的对象。
在这里插入图片描述
他两个在堆、栈图中是如何表示的呢?看下方的图:
我们可以看出,p2p1中的所有数据都重新生成了一份,让给让p2指向新生成的那份数据。
在这里插入图片描述

3.引用类型的拷贝

这个就很有引用类型拷贝就会在深拷贝浅拷贝上区别很大了!
我们仍然以人来举例子,人身上一般都会带着money,我们给人添加一个新的成员变量(钱),并且我们让钱这个成员变量改为引用变量

3.1 关于引用类型的浅拷贝


class Money {
    
    
    public double value=99.99;


}

class Person implements Cloneable{
    
    
    public String name;
    public int age;
    public Money money=new Money();

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

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

    public static void main(String[] args) throws CloneNotSupportedException {
    
    
        //原来的对象
        Person person1=new Person("张三",15);
        //克隆出来的对象
        Person person2=(Person) person1.clone();
        System.out.print("p1的钱钱数:");
        System.out.println(person1.money.value);
        
        System.out.print("p2的钱钱数:");
        System.out.println(person2.money.value);
    }
}

打印出来的结果:

p1的钱钱数:99.99
p2的钱钱数:99.99

我们可以观察结果得出,p1的钱钱和p2的钱钱我们设置好的99.99。
当我们去尝试修改p1钱钱的数时:

public static void main(String[] args) throws CloneNotSupportedException {
    
    
        //原来的对象
        Person person1=new Person("张三",15);
        //克隆出来的对象
        Person person2=(Person) person1.clone();

        System.out.println("修改p1的钱钱数");
        person1.money.value=50;

        System.out.print("p1的钱钱数:");
        System.out.println(person1.money.value);

        System.out.print("p2的钱钱数:");
        System.out.println(person2.money.value);
    }

代码打印结果:
在这里插入图片描述
我去,你修改p1的钱钱数,为啥我p2的钱钱数也发送了改变,这不就相当于别人花的钱扣在了我身上?这可不是我想要的结果!
这到底怎能回事?其实很好理解,当我们在进行拷贝时,我们拷贝money这个成员变量时,我们拷贝的数值是一个引用变量,这个引用变量又指向一个对象。因此当我们修改p1的钱的数值时,我们时顺着引用变量找所指向的值将其修改了,又因为两个引用对象指向同一个值,因此修改后,再通过引用变量查看值,就是修改后的值了;上述过程就是浅拷贝了,它的结构图如下:
在这里插入图片描述

3.2 关于引用类型的深拷贝

如何解决这样的问题呢?其实很简单,当我们进行拷贝时,我们让引用变量引用的对象重新创建一个新的对象,并且让拷贝的那一份的引用变量指向创建的对象。
我们主要修改的就算Person类中的clone方法,我们先让Money类实现Cloneable接口,然后在Person类中的clone方法调用Money类中的clone方法。(这里比较绕,看不懂没关系,下面有画的结构图,图看起来会很好理解!)


class Money implements Cloneable{
    
    
    public double value=99.99;

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

class Person implements Cloneable{
    
    
    public String name;
    public int age;
    public Money money=new Money();

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
    
    
        Person person=(Person) super.clone();
        person.money=(Money) person.money.clone();
        return person;
    }
}
public class testDemo2 {
    
    

    public static void main(String[] args) throws CloneNotSupportedException {
    
    
        //原来的对象
        Person person1=new Person("张三",15);
        //克隆出来的对象
        Person person2=(Person) person1.clone();

        System.out.println("修改p1的钱钱数");
        person1.money.value=50;

        System.out.print("p1的钱钱数:");
        System.out.println(person1.money.value);

        System.out.print("p2的钱钱数:");
        System.out.println(person2.money.value);
    }
}

代码运行出来的结果:
可以看出,修改p1的钱数,不在应用p2的钱数了。
在这里插入图片描述
上方这个过程就是深拷贝,深拷贝的结构图如下:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_65228171/article/details/131004600