前言
Object类中存在这一个clone方法,调用这个方法可以创建一个对象的“拷贝”。但是想要合法调用clone
方法,必须要先实现Clonable
接口,否则就会抛出CloneNotSupportedException
异常。
1 Cloneable接口
//Cloneable接口声明
public interface Cloneable {
}
我们发现Cloneable
接口中没有任何定义字段和方法,也就是说Cloneable
接口是个空接口,既然是一个空接口那么实现这个空接口的意思是什么呢?
Cloneable
接口作为一个空接口,它的作用是用来标记当前实现类,表示当前类是可以被克隆的,也就是我们将Cloneable
接口当作一个标记接口。
1.1 clone()
前面我们提到Object
类中存在一个clone()方法
,调用这个方法可以创建一个对象的“拷贝“,我们通过clone()方法
将下文定义类克隆,观察出现的现象来学习Cloneable
接口。
//Person.java
public class Person {
//字段
public String name;
public int age;
//方法
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void print() {
System.out.println("我是" + this.name);
}
//Object类中的clone()方法被protected修饰只能在同个包或子类中使用
//想在外界使用就必须重写
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
//Main.java
public class Main {
//当方法中抛出受查异常,借助throws将异常抛给方法的调用者来处理
public static void main(String[] args) throws CloneNotSupportedException {
//实例一个Person对象
Person person1 = new person("张三",18);
}
}
- 当我们调用
clone()
克隆出张三的拷贝会出现什么现象?
Person person2 = (Person)person1.clone();
当我们在编译器运行时,编译器向我们抛出了受查异常CloneNotSupportedException
,这说明当前类不支持克隆,也就是说当前类中没有实现Cloneable
接口这个作为克隆标记的接口,当我们在Person
类中实现这个接口后:
当程序运行时,我们就会将person1引用的对象中的内容拷贝一份到person2所引用的对象中去,堆栈关系图如下:
也就是说当我们要利用Object类
中的clone()
方法去克隆一个对象时,我们需要进行以下步骤:
- 重写父类
Object
中的克隆方法 - 注意对克隆返回对象进行类型转换,
clone()
方法默认放回Object - 处理异常
CloneNotSupportedException
- 实现
Cloneable
接口,标记当前接口可克隆
2. 深拷贝和浅拷贝
我们将上文提到的Person类进行修改,以修改后的person类进行深拷贝和浅拷贝的讲解
//Money.java
public class Money {
public double m = 1999.9;
}
//Person.java
public class Person implements Cloneable{
public String name;
public Money money;
//方法
public Person(String name) {
this.name = name;
this.money = new Money();
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
2.1 浅拷贝
- #浅拷贝: 仅复制对象本身及其引用的子对象的引用,而不复制这些子对象本身。结果是新对象和原对象共享子对象。如果子对象被修改,原对象也会受到影响。
Person person1 = new Person("张三");
Person person2 = (Person)person1.clone();
System.out.println("通过person1修改前:");
System.out.println(person1.money.m);
System.out.println(person2.money.m);
person1.money.m = 1888.8;
System.out.println("通过person1修改后:");
System.out.println(person1.money.m);
System.out.println(person2.money.m);
执行后:
也就是我们可以得到通过浅拷贝的堆栈图如下:
我们的子对象引用了一个对象,也就是说在浅拷贝过程中只实现了子对象引用的拷贝,所以我们通过person1修改的money对象中的m实际上修改的是同一个。
2.2 深拷贝
- #深拷贝: 递归地复制对象及其所有子对象,创建一个完全独立的副本。新对象与原对象及其子对象完全独立,修改新对象不会影响原对象。
我们希望深拷贝是实现了所有子对象的拷贝,形成一个完全独立的副本,也就是我们希望得到以下堆栈图:
那么如何通过堆栈图所述得到我们想要的深拷贝效果呢?我们可以通过修改重写的clone()
方法和实现Money
类的Cloneable
接口来实现。
//Money.java
public class Money implements Cloneable{
public double m = 1999.9;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
//Person.java
public class Person implements {
//...
@Override
protected Object clone() throws CloneNotSupportedException {
Person tmp = (Person)super.clone();//(1)
tmp.money = (Money)this.money.clone();//(2)
return tmp;
}
}
//(2)通过将子对象本身拷贝创建出独立的副本
需要注意的是:
- 对子对象进行拷贝的时候需要相对应的子对象所对应类也实现
Cloneable
接口
实现深拷贝后再次执行代码:
Person person1 = new Person("张三");
Person person2 = (Person)person1.clone();
System.out.println("通过person1修改前:");
System.out.println(person1.money.m);
System.out.println(person2.money.m);
person1.money.m = 1888.8;
System.out.println("通过person1修改后:");
System.out.println(person1.money.m);
System.out.println(person2.money.m);
执行结果:
深拷贝过程创建一个完全独立的副本,使得新对象与原对象及其子对象完全独立,修改新对象不会影响原对象。