再谈深拷贝

前言

之前在抽象类和接口中已经简略的谈过了深拷贝,但是写的不太具体,所以就有了这篇博客。

一丶关于clone()方法

所谓的深拷贝,这里的话还是用一个题来贯穿所有的知识点。

首先,我们定义一个Student类,用这个类来进行测试。

public class Student {
    String name;//每个学生都有自己的名字
    int age;//每个学生都有自己的年龄
    Sport spo;//每个学生都有自己喜欢的运动

}

class Sport{//定义一个运动类作为内部的引用类型
    String name;
}

那么接下来,我们既然想要深拷贝,那么就直接进行拷贝,然后需要什么,我们就给他添加什么:
在这里插入图片描述
那么很容易的可以看出,这里的clone是报错的,那么接下来我们看看。一个成功的clone方法需要那些东西。
在这里插入图片描述
这里说克隆不支持异常,然后下面给出了clone这个方法的具体出处。因为出自于Object类,所以我们不需要继承,直接重写就可以。

重写clone方法

按住ctrl + o,直接给出Object类中的clone方法。
在这里插入图片描述

那么我们接下来进行重写就可以。放出代码。

	//这里是Object类中的clone方法,接下来重写
	@Override 
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
	
	//这里是重写后的
	@Override
    public Student clone() throws CloneNotSupportedException {
        return (Student) super.clone();
    }

重写完之后:
在这里插入图片描述我们可以看到这里还是错的,那么接下里有两种解决办法,第二种就是我之前博客里用的增加try/catch,而这里我们用第一种。

给main方法增加throws CloneNotSupportedException

运行之后是这个样子的:
在这里插入图片描述
这里可以看出抛出了异常:克隆不支持异常。

实现Clonable接口

那么为了解决这个异常,我们需要实现Cloneable接口,在这个接口里面有什么东西呢?

在这里插入图片描述
可以看到,这个接口里面啥没有,所以不需要重写任何方法。
接着我们以DeBug模式运行。

在这里插入图片描述

发现问题了没有,这里的S1和S2对象地址不一样,但是里面的spo的引用地址完全一致,并且其中一个的修改也会引起另外一个的改变。但是对于基础数据类型的改变其中一个改变另外一个不会改变。那么这里给出结论:
c l o n e ( ) 方 法 底 层 其 实 就 是 另 类 的 一 种 浅 拷 贝 \color{red}{clone()方法底层其实就是另类的一种浅拷贝} clone()

那么这里,我们给出具体图示:

在这里插入图片描述

二丶关于深拷贝

写到这里有一个问题,我们需要变换这个Sport中的类成员,因为如果说是String
类型的话,字符串常量池中不会存在相同的字符串,所以没办法比较,这里的话临
时改变成int。但是结论是一样的

改变之后的Sport类
在这里插入图片描述
改变了Sport类成员之后的运行结果:

在这里插入图片描述

可以看出,对结论没有任何影响。

那么在这里我们需要什么呢?我们需要一个真正意义上的深拷贝,不仅仅单纯拷贝一个地址。
那 么 我 们 就 需 要 对 S t u d e n t 和 S p o r t 这 个 类 的 c l o n e 再 次 重 写 。 \color{red}{那么我们就需要对Student和Sport这个类的clone再次重写。} StudentSportclone

//Student类中的重写方法
@Override
    public Student clone() throws CloneNotSupportedException {
        Student key = (Student) super.clone();//首先克隆当前对象
        key.spo = spo.clone();//在接着克隆原来对象当中的Sport成员
        return key;
    }

//Sport类中的重写方法
 @Override
    public Sport clone() throws CloneNotSupportedException {
        return (Sport) super.clone();
    }

然后具体的运行结果如下:
在这里插入图片描述
可以看到,不论是对象的地址,还是引用类型的成员其都有了一个新的地址。并且

对于其中一个的改变也不会引起另外一个的变化。这就是真正意义上的深拷贝。

以下是目前这一章节所有的代码:

public class Student implements Cloneable{
    String name;//每个学生都有自己的名字
    int age;//每个学生都有自己的年龄
    Sport spo;//每个学生都有自己喜欢的运动

    public Student(String name, int age, Sport spo) {
        this.name = name;
        this.age = age;
        this.spo = spo;
    }

    @Override
    public Student clone() throws CloneNotSupportedException {
        Student key = (Student) super.clone();//首先克隆当前对象
        key.spo = spo.clone();//在接着克隆原来对象当中的Sport成员
        return key;
    }

    public static void main(String[] args) throws CloneNotSupportedException{
        Student s1 = new Student("李明",3,new Sport(1));
        Student s2 = s1.clone();
        s2.spo.sum = 22;
        System.out.println(s1.spo.sum);
    }
}

class Sport implements Cloneable{
    int sum;

    public Sport(int sum) {
        this.sum = sum;
    }

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

三丶再深入一下吧

这里的话来三个类吧。也就顺便当做练习了。

1.让第一个类和第二个类中的成员变量有一个引用类型。
2.重写三个类当中的clone()方法
3.重写三个方法当中的toString方法。

以下是运行结果:
在这里插入图片描述

接下来给出具体代码,我三个类分别用First,Second,Third来易于区分。

public class First implements Cloneable{
    String name;
    int age;
    Second se;

    public First(String name, int age, Second se) {//第一个类的构造器
        this.name = name;
        this.age = age;
        this.se = se;
    }

    @Override
    public First clone() throws CloneNotSupportedException {//重写第一个类的clone方法
        First t1 = (First)super.clone();
        t1.se = se.clone();
        return t1;
    }

    @Override
    public String toString() {//重写toString方法,不然是一个地址值
        String s = "[";
        s += this.name + "," + this.age + "," + this.se + "]";
        return s;
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        First f1 = new First("实验体一",1,new Second(new Third(1)));
        First f2 = f1.clone();
        f2.se.tir.num = 10;//修改第二个对象的值看会不会影响第一个对象
        System.out.println(f1);
        System.out.println(f2);
    }
}

class Second implements Cloneable{
    Third tir;

    public Second(Third tir) {//第二个类的构造器
        this.tir = tir;
    }

    public String toString() {//重写第二个类的toString方法
        String s = this.tir.toString();
        return s;
    }
    @Override
    public Second clone() throws CloneNotSupportedException {//重写第二个类的clone方法
        Second sec = (Second) super.clone();
        sec.tir = tir.clone();
        return sec;
    }
}

class Third implements Cloneable{
    int num;

    public Third(int num) {//第三个类的构造器
        this.num = num;
    }

    @Override
    public String toString() {//重写第三个类的toString方法
        String s = String.valueOf(num);
        return s;
    }

    @Override
    public Third clone() throws CloneNotSupportedException {//重写第三个类的clone
        return (Third)super.clone();
    }
}

一起加油!!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值