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萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章