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];
	}
}
A3是最复杂的

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这个方法。

如这里:主题:java 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;
使用C/C++来实现的。

翻了翻相关资料,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接口。



发布了125 篇原创文章 · 获赞 50 · 访问量 112万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章