基础面试题:深拷贝和浅拷贝详解以及实例

深拷贝 vs 浅拷贝

浅拷贝

概念

复制基本类型的属性;引用类型的属性复制,复制栈中的变量 和 变量指向堆内存中的对象的指针,不复制堆内存中的对象。

如图:

在这里插入图片描述

特点

​ 1.对于基本数据类型的成员对象,因为基础数据类型是值传递的,所以是直接将属性值赋值给新的对象。基础类型的拷贝,其中一个对象修改该值,不会影响另外一个。
2.对于引用类型,比如数组或者类对象,因为引用类型是引用传递,所以浅拷贝只是把内存地址赋值给了成员变量,它们指向了同一内存空间。改变其中一个,会对另外一个也产生影响。

实现

​ 实现 Cloneable 接口,重写 clone() 方法

/**
 * @ClassName ShallowCopyDemo
 * @Description: 实现 Cloneable 接口,重写 clone() 方法。
 * @Author fking
 * @Date 2020/4/30 0030
 * @Version V1.0
 **/
public class ShallowCopyDemo {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person p1 = new Person(1, "fking01");//创建对象 Person p1
        Person p2 = (Person)p1.clone();//克隆对象 p1
        p2.setName("fking02");//修改 p2的name属性,p1的name未变
        System.out.println(p1);
        System.out.println(p2);

        p2 = p1;
        p2.setName("fking02");//修改 p2的name属性,p1的也跟着变
        System.out.println(p1);
        System.out.println(p2);
    }
}

class Person implements Cloneable {

    private int pid;

    private String name;

    public Person(int pid, String name) {
        this.pid = pid;
        this.name = name;
        System.out.println("Person constructor call");
    }

    public int getPid() {
        return pid;
    }

    public void setPid(int pid) {
        this.pid = pid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

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

    @Override
    public String toString() {
        return "Person [pid:" + pid + ", name:" + name + "]";
    }
}

深拷贝

概念

​ 复制基本类型的属性;引用类型的属性复制,复制栈中的变量 和 变量指向堆内存中的对象的指针和堆内存中的对象。

在这里插入图片描述

特点

​ 1.对于基本数据类型的成员对象,因为基础数据类型是值传递的,所以是直接将属性值赋值给新的对象。基础类型的拷贝,其中一个对象修改该值,不会影响另外一个(和浅拷贝一样)。

​ 2.对于引用类型,比如数组或者类对象,深拷贝会新建一个对象空间,然后拷贝里面的内容,所以它们指向了不同的内存空间。改变其中一个,不会对另外一个也产生影响。

​ 3.对于有多层对象的,每个对象都需要实现 Cloneable 并重写 clone() 方法,进而实现了对象的串行层层拷贝。

​ 4.深拷贝相比于浅拷贝速度较慢并且花销较大。

实现方式

​ 1.对象的属性的Class 也实现 Cloneable 接口,在克隆对象时也手动克隆属性

/**
 * @ClassName DeepCopyDemo01
 * @Description: 对象的属性的Class 也实现 Cloneable 接口,在克隆对象时也手动克隆属性。
 * @Author fking
 * @Date 2020/4/30 0030
 * @Version V1.0
 **/
public class DeepCopyDemo01 {
    public static void main(String[] args) throws CloneNotSupportedException {
        DPerson01 p1 = new DPerson01(1, "fking01", new DFood01("food01"));//创建Person 对象 p1
        DPerson01 p2 = (DPerson01)p1.clone();//克隆p1
        p2.setName("fking02");//修改p2的name属性
        p2.getFood().setName("food02");//修改p2的自定义引用类型 food 属性
        System.out.println(p1);//修改p2的自定义引用类型 food 属性被改变,p1的自定义引用类型 food 属性也随之改变,说明p2的food属性,只拷贝了引用,没有拷贝food对象
        System.out.println(p2);
    }
}

class DPerson01 implements Cloneable {

    private int pid;

    private String name;

    private DFood01 food;

    public DPerson01(int pid, String name, DFood01 food) {
        this.pid = pid;
        this.name = name;
        this.food = food;
        System.out.println("DPerson01 constructor call");
    }

    public int getPid() {
        return pid;
    }

    public void setPid(int pid) {
        this.pid = pid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    /**
     * 对象的属性的Class 也实现 Cloneable 接口,在克隆对象时也手动克隆属性。
     * @return
     * @throws CloneNotSupportedException
     */
    @Override
    public Object clone() throws CloneNotSupportedException {
        DPerson01 p = (DPerson01)super.clone();
        p.setFood((DFood01)p.getFood().clone());
        return p;
    }

    @Override
    public String toString() {
        return "DPerson01 [pid:"+pid+", name:"+name+", food:"+food.getName()+"]";
    }

    public DFood01 getFood() {
        return food;
    }

    public void setFood(DFood01 food) {
        this.food = food;
    }

}

class DFood01 implements Cloneable {

    private String name;

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

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

​ 2.结合序列化(JDK java.io.Serializable 接口、JSON格式、XML格式等),完成深拷贝

/**
 * @ClassName DeepCopyDemo02
 * @Description: 结合序列化(JDK java.io.Serializable 接口、JSON格式、XML格式等),完成深拷贝
 * @Author fking
 * @Date 2020/4/30 0030
 * @Version V1.0
 **/
public class DeepCopyDemo02 {
    public static void main(String[] args) throws CloneNotSupportedException {
        DPerson02 p1 = new DPerson02(1, "fking01", new SFood02("food01"));//创建 DPerson02 对象 p1
        DPerson02 p2 = (DPerson02)p1.cloneBySerializable();//克隆 p1
        p2.setName("fking02");//修改 p2 的 name 属性
        p2.getFood().setName("food02");//修改 p2 的自定义引用类型 food 属性
        System.out.println(p1);//修改 p2 的自定义引用类型 food 属性被改变,p1的自定义引用类型 food 属性未随之改变,说明p2的food属性,只拷贝了引用和 food 对象
        System.out.println(p2);
    }
}

class DPerson02 implements Cloneable, Serializable {

    private static final long serialVersionUID = -7710144514831611031L;

    private int pid;

    private String name;

    private SFood02 food;

    public DPerson02(int pid, String name, SFood02 food) {
        this.pid = pid;
        this.name = name;
        this.food = food;
        System.out.println("DPerson02 constructor call");
    }

    public int getPid() {
        return pid;
    }

    public void setPid(int pid) {
        this.pid = pid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    /**
     * 通过序列化完成克隆
     * @return
     */
    public Object cloneBySerializable() {
        Object obj = null;
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(this);
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            obj = ois.readObject();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return obj;
    }

    @Override
    public String toString() {
        return "DPerson02 [pid:"+pid+", name:"+name+", food:"+food.getName()+"]";
    }

    public SFood02 getFood() {
        return food;
    }

    public void setFood(SFood02 food) {
        this.food = food;
    }

}

class SFood02 implements Serializable {

    private static final long serialVersionUID = -3443815804346831432L;

    private String name;

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

如果大家对java架构相关感兴趣,可以关注下面公众号,会持续更新java基础面试题, netty, spring boot,spring cloud等系列文章,一系列干货随时送达, 超神之路从此展开, BTAJ不再是梦想!

架构殿堂

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章