詳解java中clone方法

原文地址:http://leihuang.org/2014/11/14/java-clone/


In java, it essentially means the ability to create an object with similar state as the original object.

什麼是clone

字典中的意思就是複製(強調跟原來的一模一樣)。

*By default, java cloning is ‘field by field copy’ *.因爲Object類不知道具體類的結構,無法確定哪個clone方法將被調用。所以JVM規定clone,做如下操作。

  1. 如果這個類只有原始數據類型成員的話,那麼需要創建一個一模一樣的對象,並且返回這個新對象的引用。
  2. 如果這個類包含了任何類類型成員的話,那麼只有這些成員的對象引用被複制,且原始對象和克隆對象的成員的引用將指向同一個對象。

翻譯的不好,%>_<%

  1. If the class has only primitive data type members then a completely new copy of the object will be created and the reference to the new object copy will be returned.
  2. If the class contains members of any class type then only the object references to those members are copied and hence the member references in both the original object as well as the cloned object refer to the same object.

除了上面兩條默認的操作之外,你也可以重載clone方法,制定一套自己的clone方法。


java中clone規則

每種支持clone的語言都有它自己的規則,java也不例外。java中如果一個類需要支持clone的話,必須要做如下事情:

  1. 你必須繼承Cloneable接口---Cloneable interface is broken in java。otherwise throws java.lang.CloneNotSupportedException.
  2. 必須重寫Object類中的clone()方法
/*
Creates and returns a copy of this object. The precise meaning of "copy" may depend on the class of the object.
The general intent is that, for any object x, the expression:
1) x.clone() != x will be true
2) x.clone().getClass() == x.getClass() will be true, but these are not absolute requirements.
3) x.clone().equals(x) will be true, this is not an absolute requirement.
*/
protected native Object  [More ...] clone() throws CloneNotSupportedException;


  1. 第一條表明,clone對象分配有獨立的內存地址。
  2. 第二條表明,原始的和克隆的對象應該具有相同類型,但這不是必須的
  3. 第三條表明,原始的和克隆的對象調用equals方法的話,應該時相等的,但這不是必須的。

下面我們通過例子來分析:

class Father implements Cloneable{
    private int age ;
    private String name ;
    private Son son ;

    public Father(int age,String name,Son son){
        this.age = age ;
        this.name = name ;
        this.son = son ;
    }

    public Son getSon(){
        return this.son ;
    }

    @Override
    protected Object clone()throws CloneNotSupportedException{
        return super.clone() ;
    }
}
class Son{
    private int age ;
    private String name ;

    public Son(int age ,String name){
        this.age = age ;
        this.name = name ;
    }

    public void setName(String name){
        this.name = name ;
    }
    public String getName(){
        return this.name ;
    }
}
public class CloneDemo {
    public static void main(String args[]) throws CloneNotSupportedException{
        Son s = new Son(10,"Jack") ;
        Father fa = new Father(40,"Tom",s) ;
        Father clonedFa = (Father) fa.clone() ;

        System.out.println(fa!=clonedFa);
        System.out.println(clonedFa.getClass()==fa.getClass());
        System.out.println(clonedFa.equals(fa));

        //now we change the fa's son name by the clonedFa's son name
        clonedFa.getSon().setName("Jay");
        System.out.println(fa.getSon().getName());
    }
}
/*print:
true
true
false
Jay
*/


上面的代碼中可以看出,原始的和克隆的Father類對象,擁有指向同一對象的兩個引用,所以可以通過改變clonedFa中的Son來改變fa中Son對象。這就時所謂的淺拷貝.下面我們詳細討論一下淺拷貝和深拷貝。

淺拷貝(Shallow Cloning)

這是java中默認的實現。上面的例子中就是淺拷貝。不創建一個新的克隆拷貝對象Son,而是直接兩個引用指向同一對象。

深拷貝

If we want a clone which is independent of original and making changes in clone should not affect original.you can try Deep Cloning.

we change the clone() method in the Father class .

@Override
protected Object clone() throws CloneNotSupportedException {
        Father fa = (Father)super.clone();          
        fa.setSon((Son)fa.getSon().clone());
        return fa;
}


and we need Override the clone() method in the Son class. like this.

@Override
protected Object clone() throws CloneNotSupportedException {
        return super.clone();
}


現在我們已經實現深拷貝了。

拷貝構造函數

拷貝構造函數時一種特殊的構造器,它講自己的類類型作爲參數。我們傳遞一個類的實例給拷貝構造函數,然後它將返回一個新的類實例。lets see this in example

public class PointOne {
    private Integer x;
    private Integer y;

    public PointOne(PointOne point){
        this.x = point.x;
        this.y = point.y;
    }
}


如果要繼承它的話,則需要複製子類的參數,和傳遞參數給父類的構造器。如下:

public class PointTwo extends PointOne{
    private Integer z;

    public PointTwo(PointTwo point){
        super(point); //Call Super class constructor here
        this.z = point.z;
    }
}


下面是測試代碼:

class Test
{
    public static void main(String[] args)
    {
        PointOne one = new PointOne(1,2);
        PointTwo two = new PointTwo(1,2,3);

        PointOne clone1 = new PointOne(one);
        PointOne clone2 = new PointOne(two);

        //Let check for class types
        System.out.println(clone1.getClass());
        System.out.println(clone2.getClass());
    }
}
Output:
class corejava.cloning.PointOne
class corejava.cloning.PointOne


你也可以使用靜態工廠的方法來實現它。

public class PointOne {
    private Integer x;
    private Integer y;

    public PointOne(Integer x, Integer y)
    {
        this.x = x;
        this.y = y;
    }

    public PointOne copyPoint(PointOne point) throws CloneNotSupportedException
    {
        if(!(point instanceof Cloneable))
        {
            throw new CloneNotSupportedException("Invalid cloning");
        }
        //Can do multiple other things here
        return new PointOne(point.x, point.y);
    }
}


Cloning with serialization

這是例外一種深拷貝的方法。這裏就不多講了,詳細見:A mini guide for implementing serializable interface in java

best practices

1) When you don’t know whether you can call the clone() method of a particular class as you are not sure if it is implemented in that class, you can check with checking if the class is instance of “Cloneable” interface as below.

if(obj1 instanceof Cloneable){
    obj2 = obj1.clone();
}
//Dont do this. Cloneabe dont have any methods
obj2 = (Cloneable)obj1.clone();


2) No constructor is called on the object being cloned. As a result, it is your responsibility, to make sure all the members have been properly set. Also, if you are keeping track of number of objects in system by counting the invocation of constructors, you got a new additional place to increment the counter.

Reference:

  1. A guide to object cloning in java
  2. Effective--Java Item 11: Override clone judiciously(講的更詳細,各種clone方式的優缺點都講了)

2014-11-14 15:48:12

Brave,Happy,Thanksgiving !


發佈了142 篇原創文章 · 獲贊 104 · 訪問量 87萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章