天天看點

Java Clone機制 1          什麼是 Clone ,容易實作嗎? 2          Java 對 clone 的支援 3          Shallow Clone 與 Deep Clone

1          什麼是 Clone ,容易實作嗎?

簡單地說, Clone 就是對于給定的一個對象執行個體 o ,得到另一個對象執行個體 o’ : o 與 o’ 類

型相同( o.getClass() == o’.getClass() ),内容相同(對于 o/o’ 中的字段 f ,如果 f 是基本資料類型,則 o.f == o’.f ;如果 f 是對象引用,則 o.f == o’.f 或 o.f 指向的對象與 o’.f 指向的對象的内容相同)。通常稱 o’ 為 o 的克隆或副本。

       直覺上看,似乎很容易為一個類加上 clone 方法:

class A {

       private Type1 field1;

    private Type2 field2;

     …..

    public Object clone() {

              A a = new A();

        a.field1 = a.getField1();

        a.field2 = a.getField2();

        ……

        return a;

    }

}

然而,稍加推敲,就會發現這樣的實作方法有兩個問題:

1.         要想某個類有 clone 功能,必須單獨為其實作 clone() 函數,函數實作代碼與該類定義密切相關。

2.         即使基類 A 已有 clone() 函數,其子類 ExtendA 若要具備 clone 功能,則必須 override 其基類 A 的 clone() 函數。否則,對類型為 ExtendA 的對象 ea 的 clone() 方法的調用,會執行于類 A 中定義的 clone() 方法而傳回一個類型為 A 的對象,它顯然不是 ea 的克隆。

2          Java 對 clone 的支援

萬類之初的 Object 類有 clone() 方法:

protected native Object clone() throws CloneNotSupportedException;

該方法是 protected 的,顯然是留待被子類 override 的。該方法又是 native 的,必然做了

與具體平台相關的底層工作。

事實上,類 Object 的 clone() 方法首先會檢查 this.getClass() 是否實作了 Cloneable 接口。

Cloneable 隻是一個标志接口而已,用來标志該類是否有克隆功能。

public interface Cloneable {

}

       如果 this.getClass() 沒有實作 Cloneable 接口, clone() 就會抛 CloneNotSupportedException 傳回。否則就會建立一個類型為 this.getClass() 的對象 other ,并将 this 各 field 的值指派給 other 的對應 field ,然後傳回 other 。

       如此一來,我們要定義一個具有 Clone 功能的類就相當友善:

1.         在類的聲明中加入“ implements Cloneable ”,标志該類有克隆功能;

2.         Override 類 Object 的 clone() 方法,在該方法中調用 super.clone() :

class CloneableClass implements Cloneable {

       ……

public Object clone() {

       try {

              return super.clone(); // 直接讓 Object.clone() 為我們代勞一切

    } catch (CloneNotSupportedException e) {

              throw new InternalError();

       }

}

}

3          Shallow Clone 與 Deep Clone

3.1     Shallow 與 Deep 從何而來

一個具有克隆功能的類,如果有可變( Mutable )類類型的字段 field ,如何為其克隆(副

本)對象 o’ 中的 field 指派?

       方法一、如 Object 的 clone() 方法所實作:設原始對象為 o ,其克隆對象是 o’ ,執行 o’.field = o.field 。這樣, o’.field 與 o.field 指向同一個可變對象 m 。 o 與 o’ 可能會互相影響(一個對象的狀态可能會随着另一個對象的狀态的改變而改變)。這樣的 Clone 稱為 Shallow Clone 。這也是 Object 的 clone() 方法的實作方式。

       方法二、将 o.field 指向的可變對象 m 克隆,得到 m’ ,将 m’ 的引用指派給 o’.field 。這樣 o’ 與 o 内容相同,且互相之間無影響(一個對象狀态的改變不會影響另一個對象的狀态)。這樣的 Clone 稱為 Deep Clone 。

       Java Collection 類庫中具體資料結構類( ArrayList/LinkedList , HashSet/TreeSet , HashMap/TreeMap 等)都具有克隆功能,且都是 Shallow Clone ,這樣設計是合理的,因為它們不知道存放其中的每個資料對象是否也有克隆功能。 System.arrayCopy() 的實作采用的也是 Shallow Clone 。

       Deep Clone 對于實作不可變( Immutable )類很有幫助。設一個類包含可變類 M 類型的 field ,如何将其設計為不可變類呢?先為 M 實作 Deep Clone 功能,然後這樣設計類 ImmutableClass :

class ImmutableClass {

       MutableClass m;

ImmutableClass(MutableClass m) {

       this.m = m.clone(); // 将傳入的 m 的 clone 指派給内部 m

}

public MutableClass getM() {

    return this.m.clone(); // 将内部 m 的 clone 傳回給外部

}

}

3.2     如何實作 Deep Clone

檢查類有無可變類類型的字段。如果無,傳回 super.clone() 即可;

如果有,確定包含的可變類本身都實作了 Deep Clone ;

Object o = super.clone(); // 先執行淺克隆,確定類型正确和基本類型及非可變類類型字段内容正确

對于每一個可變類類型的字段 field :

       o.field = this.getField().clone();

傳回 o 。

繼續閱讀