Loading

java的深度克隆

原文:http://blog.csdn.net/randyjiawenjie/article/details/7563323

先做个标记

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的实现

 

[java] view plaincopy
 
  1. public class A1 implements Cloneable {  
  2.     public int age;  
  3.   
  4.     public Object clone() {  
  5.         A1 o = null;  
  6.         try {  
  7.             o = (A1) super.clone();  
  8.         } catch (CloneNotSupportedException e) {  
  9.             e.printStackTrace();  
  10.         }  
  11.         return o;  
  12.     }  
  13.     Object o = new Object();  
  14. }  

A2的实现:

 

 

[java] view plaincopy
 
  1. public class A2 implements Cloneable {  
  2.     public String[] name;     
  3.     public Object clone() {  
  4.         A2 o = null;  
  5.         try {  
  6.             o = (A2) super.clone();  
  7.             o.name = (String[]) name.clone();  
  8.         } catch (CloneNotSupportedException e) {  
  9.             e.printStackTrace();  
  10.         }  
  11.         return o;  
  12.     }  
  13.     public A2() {  
  14.         name = new String[2];  
  15.     }  
  16. }  
A3是最复杂的

 

 

[java] view plaincopy
 
  1. package com.tmall;  
  2.   
  3. import java.util.Vector;  
  4.   
  5. public class A3 implements Cloneable {  
  6.     public String name[];  
  7.     public Vector<B> claB;  
  8.   
  9.     public A3() {  
  10.         name = new String[2];  
  11.         claB = new Vector<B>();  
  12.     }  
  13.   
  14.     public Object clone() {  
  15.         A3 o = null;  
  16.         try {  
  17.             o = (A3) super.clone();  
  18.             o.name = (String[]) name.clone();// 深度clone  
  19.             o.claB = new Vector<B>();// 将clone进行到底  
  20.             for (int i = 0; i < claB.size(); i++) {  
  21.                 B temp = (B) claB.get(i).clone();// 当然Class B也要实现相应clone方法  
  22.                 o.claB.add(temp);  
  23.             }  
  24.         } catch (CloneNotSupportedException e) {  
  25.             e.printStackTrace();  
  26.         }  
  27.         return o;  
  28.     }  
  29. }  

其中B类的定义:

 

 

[java] view plaincopy
 
  1. public class B {  
  2.     public int age;  
  3.   
  4.     public Object clone() {  
  5.         B o = null;  
  6.         try {  
  7.             o = (B) super.clone();  
  8.         } catch (CloneNotSupportedException e) {  
  9.             e.printStackTrace();  
  10.         }  
  11.         return o;  
  12.     }  
  13. }  

两个测试方法:

 

 

[java] view plaincopy
 
  1. public static void testA1(){  
  2.         A1 a11 = new A1();  
  3.         A1 a12 = (A1)a11.clone();  
  4.         a11.age = 10;  
  5.         a12.age = 20;  
  6.         System.out.println(a11.age);  
  7.         System.out.println(a12.age);  
  8.     }  
  9.     public static void testA2(){  
  10.         A2 a1=new A2();      
  11.         a1.name[0]="a";    
  12.         a1.name[1]="1";    
  13.         A2 a2=(A2)a1.clone();    
  14.         a2.name[0]="b";    
  15.         a2.name[1]="1";    
  16.         System.out.println("a1.name="+a1.name);    
  17.         System.out.println("a1.name="+a1.name[0]+a1.name[1]);    
  18.         System.out.println("a2.name="+a2.name);    
  19.         System.out.println("a2.name="+a2.name[0]+a2.name[1]);   
  20.     }  

运行结果,可以知道成功实现了深拷贝。

 

进一步研究:

1  . 翻阅cloneable接口的源码,发现是一个空接口,啥都没有。

 

[java] view plaincopy
 
  1. package java.lang;  
  2.   
  3. /** 
  4.  * A class implements the <code>Cloneable</code> interface to  
  5.  * indicate to the {@link java.lang.Object#clone()} method that it  
  6.  * is legal for that method to make a  
  7.  * field-for-field copy of instances of that class.  
  8.  * <p> 
  9.  * Invoking Object's clone method on an instance that does not implement the  
  10.  * <code>Cloneable</code> interface results in the exception  
  11.  * <code>CloneNotSupportedException</code> being thrown. 
  12.  * <p> 
  13.  * By convention, classes that implement this interface should override  
  14.  * <tt>Object.clone</tt> (which is protected) with a public method. 
  15.  * See {@link java.lang.Object#clone()} for details on overriding this 
  16.  * method. 
  17.  * <p> 
  18.  * Note that this interface does <i>not</i> contain the <tt>clone</tt> method. 
  19.  * Therefore, it is not possible to clone an object merely by virtue of the 
  20.  * fact that it implements this interface.  Even if the clone method is invoked 
  21.  * reflectively, there is no guarantee that it will succeed. 
  22.  * 
  23.  * @author  unascribed 
  24.  * @version 1.17, 11/17/05 
  25.  * @see     java.lang.CloneNotSupportedException 
  26.  * @see     java.lang.Object#clone() 
  27.  * @since   JDK1.0 
  28.  */  
  29. public interface Cloneable {   
  30. }  
在这篇帖子中对这个问题就行了讨论

 

主题:我发现了一个悖论

2.

clone方法的错误使用。

在不止一篇帖子里面,错误地使用了clone这个方法。

如这里:主题:java clone方法使用详解

作者是这样调用clone方法的:

 

[java] view plaincopy
 
  1. A a1=new A();    
  2. A a2=new A();     **  
  3. a2=(A)a1.clone();    
其实做一次实验就可以知道,**行是多余的。

 

 

[java] view plaincopy
 
  1.               A1 a11 = new A1();  
  2. A1 a12 = (A1) a11.clone();  
  3. A1 a13 = new A1();  
  4. System.out.println("a11的地址是" + a11);  
  5. System.out.println("a12的地址是" + a12);  
  6. System.out.println("a13的地址是" + a13);  
  7. a13 = (A1) a11.clone();  
  8. 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克隆成功;

 

[java] view plaincopy
 
  1. A1 a12 = (A1) a11.clone();  
是没有问题的。

 

反而实现new 一个对象如a13,在调用clone()方法会多此一举:

 

[java] view plaincopy
 
  1. A1 a13 = new A1();  
[java] view plaincopy
 
  1. a13 = (A1) a11.clone();  

可以看到运行结果里面,a13的地址换了两次。第一次是new出来的新对象,第二次是克隆出来的新对象,两次地址都不等于那个被clone的对象a11。

我翻阅了《effectice java》的条款11,“谨慎地覆盖clone”。作者Joshua Bloch(可是google的首席java工程师)第三段写道:

“如果实现cloneable接口是对某个类起到作用,类和它的所有超类都必须遵守一个想当复杂的、不可实施的,并且基本上没有文档说明的协议。由此得到一种语言之外的机制:

无需要调用构造器就可以创建对象”,红色部分也说明了**行new 一个类是多余的。

 

3. Object类里面的clone定义。

翻看JDK源码,Object类里面的clone方法定义如下

 

[java] view plaincopy
 
  1. protected native Object clone() throws CloneNotSupportedException;  
使用C/C++来实现的。

 

翻了翻相关资料,java clone 克隆 super.clone

是“bitwise(逐位)的复制, 将该对象的内存空间完全复制到新的空间中去”这样实现的。

另外这个方法是protected的,不同的包是不能用的。这就可以解释为什么我们不能显示地调用Object.clone()这个方法。因为Object这个类是放在java,lang这个包里面的,而clone()方法是protected的,是不可见的。如果这样写

 

[java] view plaincopy
 
  1. Object o1 = new Object();  
  2. Object o2 = o1.clone();  
编译器肯定会报错的:

 

之前我不理解A类中为什么用super.clone()这样子的写法,现在明白了。

4.  如果一个类实现了Cloneable接口,Object的clone方法就会返回该对象的逐域拷贝,否则就会抛出CloneNotSupportedException异常。

来自《effective java》条款11的第二段话。所以要实现对象的clone,必须实现cloneable接口。

 

posted @ 2014-03-26 22:59  dai.sp  阅读(318)  评论(0编辑  收藏  举报