clone()方法
clone()方法将对象复制了一份并返回给调用者。一般而言,clone()方法满足:
- 对任何对象a,都有a.clone() != a,即克隆对象与源对象不同
- 对任何对象a,都有a.clone().getClass == a.getClass,即克隆对象与源对象的类型相同
clone()方法时Object类的一个方法,因此,所有的类都具有这个方法。clone()的作用在于复制对象,在复制对象的过程中,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象。
众所周知,我们可以使用new来创建一个对象,那么使用new操作符和使用clone方法复制一个对象有什么区别呢?
new操作符的本质是分配内存,程序执行到new操作符时,首先去看new操作符时,首先去看new操作符后边的类型,因为不同的类型分配的内存空间不同,知道了类型就能决定要分配的内存空间多大。然后调用构造函数,填充对象各个域,这一步才叫对象的初始化。构造方法返回后,一个对象创建完毕,可以把他的引用(地址)发布到外部,在外部就可以使用这个引用来操纵这个对象。而clone在第一步是和new相似的,都是分配内存,调用clone方法时,分配的内存和源对象相同,然后使用源对象中对应的各个域,填充新对象的域。填充完成后,clone方法返回,一个新的相同的对象被创建,同样可以把这个新对象的引用发布到外部。
复制引用与复制对象
要注意复制引用和复制对象的区别。这两者最大的差异在于有没有在堆区建立一个新的对象。
复制引用
ClassA clazz1 = new ClassA(123);
ClassA clazz2 = clazz1;
上边的代码就是一个复制引用,clazz1和clazz2都指向了同一个对象ClassA(123)。我们可以通过这张图来理解它:
复制对象
ClassA clazz1 = new ClassA(123);
ClassA clazz2 = (ClassA) clazz1.clone();
复制对象也就是通过我们本文的重点clone()方法来实现,出clazz1和clazz2都分别指向一个对象,虽然这两个对象的内容相同,但地址不同,因此是两个不同的对象。上图你就明白了:
很明显,通过复制对象我们产生了一个新的对象,当我们对克隆的对象进行修改时,并不会影响到源对象,同理,修改源对象也不会影响到克隆对象。
浅复制和深复制
浅复制
浅复制指的是被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的 引用仍然指向原来的对象。换句话说,浅复制并不关心它所引用的的对象。
例如:类A引用了类B,当我们对类A进行克隆产生一个类C,类C会使用类A原本对类B的引用,即类C直接对类B进行引用。看图说话:
深复制
被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量都指向被复制过的新对象,而不是原有的哪些被引用的对象。也就是说,深复制在浅复制的基础上,将源对象中引用的对象也都复制一遍,用于给克隆对象进行引用。
浅复制和深复制的实现
java.lang.Object类中的clone()方法是浅复制,我们要使用浅复制则直接调用类原有的clone方法即可,那么我们要如何实现深复制呢?
实现深复制的关键就在于覆盖Object中的clone方法。为了要在clone对象时进行深复制,那么就要Cloneable接口,覆盖并实现clone方法,除了调用父类中的clone方法得到新的对象,还要将该类中的引用变量也clone出来。
public class Test {
static class Body implements Cloneable {
public Head head;
public Body() {
}
public Body(Head head) {
this.head = head;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Body newBody = (Body) super.clone();
newBody.head = (Head) head.clone();
return newBody;
}
}
static class Head implements Cloneable {
public Face face;
public Head() {
}
public Head(Face face) {
this.face = face;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
static class Face implements Cloneable {
public Face() {
}
}
public static void main(String[] args) throws CloneNotSupportedException {
Body body = new Body(new Head());
Body body1 = (Body) body.clone();
System.out.println("body == body1 : " + (body == body1));
System.out.println("body.head == body1.head : " + (body.head == body1.head));
}
}
运行main函数的结果为:
由此可见,我们实现了深复制。但是完全的实现深复制是很难的,如上边的代码中,可能Face类中还存在着别的引用,上边的深复制也是一个不完全的深复制。