java的深度克隆
先做个标记
http://www.iteye.com/topic/182772
http://www.blogjava.net/jerry-zhaoj/archive/2009/10/14/298141.html
关于super.clone的理解
http://hi.baidu.com/%BB%AA%CF%C4%D1%A7%C9%FA%C1%AA%C3%CB/blog/item/7d70a43842622832b8998f86.html
http://tieba.baidu.com/p/938267457
http://topic.csdn.net/u/20080326/09/a48470f3-f2c2-43c4-af56-fa8ffc12b629.html
http://www.iteye.com/topic/1113790
如何查看JDK源码
http://hi.baidu.com/koflance/blog/item/07809915d6f70e5cf3de32dd.html
http://www.iteye.com/topic/1113790
java对象的克隆步骤:
1.类需要实现Cloneable接口
2 覆盖clone方法,调用super.clone即可。如果是复合类型,如数组,集合类,还需要继续clon下去。
类A1只含有原生类型的属性,A2、A3类含有复合类型的属性,实现起来要clone到底,不然只会拷贝一个引用,从而实现浅拷贝(影子拷贝),实际上只有一个堆对象。两个引用实际上指向了一个对象。
A1的实现
- public class A1 implements Cloneable {
- public int age;
- public Object clone() {
- A1 o = null;
- try {
- o = (A1) super.clone();
- } catch (CloneNotSupportedException e) {
- e.printStackTrace();
- }
- return o;
- }
- Object o = new Object();
- }
A2的实现:
- public class A2 implements Cloneable {
- public String[] name;
- public Object clone() {
- A2 o = null;
- try {
- o = (A2) super.clone();
- o.name = (String[]) name.clone();
- } catch (CloneNotSupportedException e) {
- e.printStackTrace();
- }
- return o;
- }
- public A2() {
- name = new String[2];
- }
- }
- package com.tmall;
- import java.util.Vector;
- public class A3 implements Cloneable {
- public String name[];
- public Vector<B> claB;
- public A3() {
- name = new String[2];
- claB = new Vector<B>();
- }
- public Object clone() {
- A3 o = null;
- try {
- o = (A3) super.clone();
- o.name = (String[]) name.clone();// 深度clone
- o.claB = new Vector<B>();// 将clone进行到底
- for (int i = 0; i < claB.size(); i++) {
- B temp = (B) claB.get(i).clone();// 当然Class B也要实现相应clone方法
- o.claB.add(temp);
- }
- } catch (CloneNotSupportedException e) {
- e.printStackTrace();
- }
- return o;
- }
- }
其中B类的定义:
- public class B {
- public int age;
- public Object clone() {
- B o = null;
- try {
- o = (B) super.clone();
- } catch (CloneNotSupportedException e) {
- e.printStackTrace();
- }
- return o;
- }
- }
两个测试方法:
- public static void testA1(){
- A1 a11 = new A1();
- A1 a12 = (A1)a11.clone();
- a11.age = 10;
- a12.age = 20;
- System.out.println(a11.age);
- System.out.println(a12.age);
- }
- public static void testA2(){
- A2 a1=new A2();
- a1.name[0]="a";
- a1.name[1]="1";
- A2 a2=(A2)a1.clone();
- a2.name[0]="b";
- a2.name[1]="1";
- System.out.println("a1.name="+a1.name);
- System.out.println("a1.name="+a1.name[0]+a1.name[1]);
- System.out.println("a2.name="+a2.name);
- System.out.println("a2.name="+a2.name[0]+a2.name[1]);
- }
运行结果,可以知道成功实现了深拷贝。
进一步研究:
1 . 翻阅cloneable接口的源码,发现是一个空接口,啥都没有。
- package java.lang;
- /**
- * A class implements the <code>Cloneable</code> interface to
- * indicate to the {@link java.lang.Object#clone()} method that it
- * is legal for that method to make a
- * field-for-field copy of instances of that class.
- * <p>
- * Invoking Object's clone method on an instance that does not implement the
- * <code>Cloneable</code> interface results in the exception
- * <code>CloneNotSupportedException</code> being thrown.
- * <p>
- * By convention, classes that implement this interface should override
- * <tt>Object.clone</tt> (which is protected) with a public method.
- * See {@link java.lang.Object#clone()} for details on overriding this
- * method.
- * <p>
- * Note that this interface does <i>not</i> contain the <tt>clone</tt> method.
- * Therefore, it is not possible to clone an object merely by virtue of the
- * fact that it implements this interface. Even if the clone method is invoked
- * reflectively, there is no guarantee that it will succeed.
- *
- * @author unascribed
- * @version 1.17, 11/17/05
- * @see java.lang.CloneNotSupportedException
- * @see java.lang.Object#clone()
- * @since JDK1.0
- */
- public interface Cloneable {
- }
2.
clone方法的错误使用。
在不止一篇帖子里面,错误地使用了clone这个方法。
作者是这样调用clone方法的:
- A a1=new A();
- A a2=new A(); **
- a2=(A)a1.clone();
- A1 a11 = new A1();
- A1 a12 = (A1) a11.clone();
- A1 a13 = new A1();
- System.out.println("a11的地址是" + a11);
- System.out.println("a12的地址是" + a12);
- System.out.println("a13的地址是" + a13);
- a13 = (A1) a11.clone();
- System.out.println("a13的地址是" + a13);
我的电脑运行结果是
a11的地址是com.tmall.A1@c17164
a12的地址是com.tmall.A1@1fb8ee3
a13的地址是com.tmall.A1@61de33
a13的地址是com.tmall.A1@14318bb
说明a12克隆成功;
- A1 a12 = (A1) a11.clone();
反而实现new 一个对象如a13,在调用clone()方法会多此一举:
- A1 a13 = new A1();
- a13 = (A1) a11.clone();
可以看到运行结果里面,a13的地址换了两次。第一次是new出来的新对象,第二次是克隆出来的新对象,两次地址都不等于那个被clone的对象a11。
我翻阅了《effectice java》的条款11,“谨慎地覆盖clone”。作者Joshua Bloch(可是google的首席java工程师)第三段写道:
“如果实现cloneable接口是对某个类起到作用,类和它的所有超类都必须遵守一个想当复杂的、不可实施的,并且基本上没有文档说明的协议。由此得到一种语言之外的机制:
无需要调用构造器就可以创建对象”,红色部分也说明了**行new 一个类是多余的。
3. Object类里面的clone定义。
翻看JDK源码,Object类里面的clone方法定义如下
- protected native Object clone() throws CloneNotSupportedException;
翻了翻相关资料,java clone 克隆 super.clone
是“bitwise(逐位)的复制, 将该对象的内存空间完全复制到新的空间中去”这样实现的。
另外这个方法是protected的,不同的包是不能用的。这就可以解释为什么我们不能显示地调用Object.clone()这个方法。因为Object这个类是放在java,lang这个包里面的,而clone()方法是protected的,是不可见的。如果这样写
- Object o1 = new Object();
- Object o2 = o1.clone();
之前我不理解A类中为什么用super.clone()这样子的写法,现在明白了。
4. 如果一个类实现了Cloneable接口,Object的clone方法就会返回该对象的逐域拷贝,否则就会抛出CloneNotSupportedException异常。
来自《effective java》条款11的第二段话。所以要实现对象的clone,必须实现cloneable接口。