前言
之前在抽象类和接口中已经简略的谈过了深拷贝,但是写的不太具体,所以就有了这篇博客。
一丶关于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再次重写。}
那么我们就需要对Student和Sport这个类的clone再次重写。
//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();
}
}
一起加油!!!