Clonable 接口 深拷贝与浅拷贝(超详细!!!代码附注释带图)

发布于:2022-12-31 ⋅ 阅读:(377) ⋅ 点赞:(0)

Clonable又叫克隆接口,我们在使用Clonable之前,先来了解一下什么叫深拷贝,什么叫浅拷贝

目录

1.浅拷贝

2.深拷贝

3.Clonable接口


1.浅拷贝

定义:

浅拷贝拷贝就是拷贝指向对象的指针,意思就是说:拷贝出来的目标对象的指针和源对象的指针指向的内存空间是同一块空间,浅拷贝只是一种简单的拷贝,让几个对象公用一个内存.

这里我们用一张图配合代码来理解

首先我们先写一段伪代码

String a = new String();
String b = a;

在上述代码中,a和b指向的是同一块地址空间,当a中的内容发生改变的时候,b也会随之改变

就像这个图一样,当对a中的数据进行操作的时候,b也会随之改变,我们称这种拷贝叫做浅拷贝

2.深拷贝

定义:

深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响

 还是一样,先看代码

String a = new String("abcdef");
        String b = new String();
        b += a;
        System.out.println(a);
        System.out.println(b);

这里面就是给a和b同时开了两个空间,然后把a中的数值拷贝到b中去,这种当a中的值发生改变的时候,不会影响到b,就叫做深拷贝

这张图中就比较好理解深拷贝了

知道了深浅拷贝之后,我们再去认识Clonable接口

3.Clonable接口

Object 类中存在一个 clone 方法 , 调用这个方法可以创建一个对象的 " 拷贝 ". 但是要想合法调用 clone 方法 , 必须要 先实现 Clonable 接口 , 否则就会抛出 CloneNotSupportedException 异常 .
下面我们一起看看这个接口
//这里实现这个接口的作用是表明这个类可以被克隆,实际上这个接口里面什么都没有
class Student implements Cloneable{
    public String name;
    public int Id;
    public String major;
    public Student(String name,int id,String major){
        this.name = name;
        this.Id = id;
        this.major = major;
    }
    @Override
//这里重写的是Objec类中的clone方法
//这里可以用try catch
    public Object clone() throws CloneNotSupportedException {
        Student o = null;
//这里的super其实是再调用Object中的clone方法
        o = (Student) super.clone();
        return o;
    }

}

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {

        Student s1 = new Student("张三",123,"生物制药");
        Student s2 = (Student)s1.clone();
        System.out.println("s1 " + s1.name + s1.Id + s1.major);
        s1.Id = 5;
        System.out.println("s2 " + s2.name + s2.Id + s2.major);
    }
}

 其实这个clone接口做的事情,就是在内存中克隆一份,传给s2

借助图来理解

 这里再s1调用clone方法之后,便会克隆出来一个0X777并且返回给s2

这样就做到深拷贝了吗?

不一定哦,我们再看下面的代码

class Student implements Cloneable{
    public String name;
    public int Id;
    public String major;
    public func A;
    public Student(String name,int id,String major,func A){
        this.name = name;
        this.Id = id;
        this.major = major;
        this.A = A;
    }
    @Override
    public Object clone() throws CloneNotSupportedException {
        Student o = null;
        o = (Student) super.clone();
        return o;
    }

}
//这里加了个类,让Student里面有个引用变量

class func{
    int x = 10;
}

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {

        Student s1 = new Student("张三",123,"生物制药",new func());
        Student s2 = (Student) s1.clone();
        System.out.println("s1 " + s1.name + s1.Id + s1.major + s1.A.x);
//这里是对s1进行的修改
        s1.A.x = 20;
        System.out.println("s2 " + s2.name + s2.Id + s2.major + s2.A.x);
    }
}

在上述代码中,加了一个func类,从而让Student有了引用变量,最后修改s1中的A的x

上述代码的执行结果是

最后修改s1中的A的x,导致s2中的A的x发生了改变,这种就不符合我们的预期了,成了浅拷贝了

为什么会这样呢?

一张图就能解释

这里可以看到,s1中的func和s2中的func是一样的,都是一块地址,所以指向同一块空间,所以当s1中的func中的数据改变,s2中的数据也会随之改变

那我们怎么做才能再让他进行深拷贝呢?

看代码


class Student implements Cloneable{
    public String name;
    public int Id;
    public String major;
    public func A;
    public Student(String name,int id,String major,func A){
        this.name = name;
        this.Id = id;
        this.major = major;
        this.A = A;
    }
    @Override
    public Object clone() throws CloneNotSupportedException {
//这里先克隆出一个student,但是克隆完之后,func仍然指向的是同一个地址
        Student tmp =(Student) super.clone();
//这里对func进行克隆,然后把克隆出来的结果复制给tmp.A
        tmp.A = (func)this.A.clone();
//最后再进行返回,就能得到一个全新的深拷贝对象了
        return tmp;
    }

}
//因为上面的clone需要调用this.a.clone,所以这个func也必须实现克隆,并且重写克隆方法
class func implements Cloneable{
    int x = 10;

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

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {

        Student s1 = new Student("张三",123,"生物制药",new func());
        Student s2 = (Student) s1.clone();
        System.out.println("s1 " + s1.name + s1.Id + s1.major + s1.A.x);
        s1.A.x = 20;
        System.out.println("s2 " + s2.name + s2.Id + s2.major + s2.A.x);
    }
}

代码中有详细解析

运行结果为

所以,这样我们就做到了深拷贝,但是问题又来了,如果func中又有一个引用类型呢?

所以所谓的深拷贝,浅拷贝,需要根据业务需求进行操作,并没有绝对的深拷贝 

温馨提示:当你想要复制一个集合类的对象的时候,比如队列,栈之类的,不妨试一下clone

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到