文章目录
1.Clonable 接口
java中提供了Clonable接口,这个单词翻译过来就是“可克隆的”的意思,显然这是用来帮助“类”进行拷贝的。我们先来看看源码:

我去,怎么一个方法都没有?这左看右看就只是一个空接口,这有什么用呢?按照常规思路的话这里面是不是应该有一个clone()方法等着我们去重写,然后直接类.clone()就完事了呀(回想我们拷贝数组的时候使用的是数组.clone())。
确实是类.clone()这样用,但是没这么简单,这种接口在java中叫“空接口”或者“标记接口”,是用来表示一个类可以被克隆,即这个类有克隆这种功能。
那么clone()这个方法在哪呢?这个方法在Object中:

这个native是什么意思?只要是被native修饰的方法,它的底层是用C/C++语言实现的,我们这里看不到里面的源码。我们只要调用这个方法它会自己帮我们实现克隆。
那么Object中的clone()与Clonable接口有什么关系呢?既然clone()在Object中,我们为什么不直接重写呢,这与Clonable接口就是八竿子打不着呀?我们先试一试直接重写不用Clonable接口的情况:
现在有一个“学生类”,我们重写了Object中的clone(),注意这里的返回值是Object。我们不需要自己重新写一个逻辑,直接return super.clone();就行了,因为在Object中已经帮我们实现了。(ps:throws … 这叫抛异常,不熟悉没关系,直接跟着写不影响。)
class Student{
public String name;
public int age;
public Student(String name,int age){
this.name = name;
this.age = age;
}
//重写Object中的toString(),方便打印。
public String toString() {
return "Student: " +
"name=" + name +
", age=" + age ;
}
//重写Object中的clone()方法。
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Student student1 = new Student("张三",20);
Student s = (Student)student1.clone();
System.out.println(student1);
System.out.println(s);
}
}
(ps:可能有人会问:Student不是继承了Object了吗,clone() 不就继承到Student里了?在Student中不是就可以不用重写clone()了吗?,直接在main中student1.clone()就行了呀。这其实是不行的,因为源码中clone()是被protected修饰的,不重写是访问不到的。如果实在想不清楚可以看看这篇文章->Java protected 关键字详解 | 菜鸟教程 (runoob.com))
上面代码表面上看是没有什么问题的,我们开始运行:

发现抛了一个异常CloneNotSupportedException,大概意思为“不支持克隆异常”,这其实就是该类不支持克隆,或者可以理解为这个类没有克隆这种功能。如果想要实现这个功能,就必须得实现Clonable接口,你可以认为Clonable就是起到一个标记的作用。
我们修改后的代码:
class Student implements Cloneable {
public String name;
public int age;
public Student(String name,int age){
this.name = name;
this.age = age;
}
//重写Object中的toString(),方便打印。
public String toString() {
return "Student: " +
"name=" + name +
", age=" + age ;
}
//重写Object中的clone()方法。
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Student student1 = new Student("张三",20);
Student s = (Student)student1.clone();
System.out.println(student1);
System.out.println(s);
}
}
结果:

可以看到已经成功了。总结:对类克隆(拷贝)的步骤有,第一实现Cloneable接口;第二重写Object中的clone()方法;第三是要throw异常。
2.深拷贝
在了解 深拷贝 之前,我们要先了解一下什么是 浅拷贝。我在上面例子的基础上增加一个新的类Money,并在Student类中new一个Money:
public class Money {
public double m = 99.99;
}
class Student implements Cloneable {
public String name;
public int age;
//Money实例
public Money money = new Money();
public Student(String name,int age){
this.name = name;
this.age = age;
}
//重写Object中的toString(),方便打印。
public String toString() {
return "Student: " +
"name=" + name +
", age=" + age ;
}
//重写Object中的clone()方法。
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
下面是main方法:
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Student student1 = new Student("张三",20);
Student s = (Student) student1.clone();
//拷贝之后,更改m的值。
student1.money.m = 30.5;
//student1中m的值
System.out.println(student1.money.m);
//s中m的值
System.out.println(s.money.m);
}
}
结果:

这两个值是相等的,为什么?是因为student1与s中的money成员是同一个引用!
这就导致修改student1中的money后,s中的money的值也会跟着发生变化。拷贝的过程如下:
拷贝前:

拷贝后:

这里只是把money的地址拷贝过去了,像上面图片这样的拷贝就是浅拷贝。
我们想要达到效果是在拷贝的时候让money重新引用一个新的实例:

如何实现呢?细心想一下,我实现深拷贝的关键 是克隆student1中的money,然后让s中的money引用它。说明money也要实现Cloneable接口并重写Object中的clone()方法,下面给出最终的代码(修改最多的是Student中的clone()方法):
class Money implements Cloneable{
public double m = 100.00;
//重写Object中的clone()方法。
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Student implements Cloneable {
public String name;
public int age;
//Money实例
public Money money = new Money();
public Student(String name,int age){
this.name = name;
this.age = age;
}
//重写Object中的toString(),方便打印。
public String toString() {
return "Student: " +
"name=" + name +
", age=" + age ;
}
//重写Object中的clone()方法。
protected Object clone() throws CloneNotSupportedException {
Student s = (Student) super.clone();
//在当前例子来看:克隆`student1`中的`money`,然后让`s`中的`money`引用它。
s.money = (Money) this.money.clone();
return s;
}
}
clone()方法的返回值可以是Student类型,只要程序能通过随你怎么写都行。
Main还是原来的Main:
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Student student1 = new Student("张三",20);
Student s = (Student) student1.clone();
//拷贝之后
student1.money.m = 30.5;
//student1中m的值
System.out.println(student1.money.m);
//s中m的值
System.out.println(s.money.m);
}
}
结果:

成功!
文章详细介绍了Java中的Clonable接口及其作用,解释了为何在实现类克隆时需要实现此接口。同时,文章通过示例展示了如何进行浅拷贝和深拷贝,强调了深拷贝在处理包含引用的对象时的必要性,并给出了实现深拷贝的具体步骤。
1009

被折叠的 条评论
为什么被折叠?



