
前言
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);
执行结果:
 
 深拷贝过程创建一个完全独立的副本,使得新对象与原对象及其子对象完全独立,修改新对象不会影响原对象。



















