Java 深拷貝與淺拷貝

1 Java 中對象的創建

clone顧名思義就是複製, 在Java語言中, clone方法被對象調用,所以會複製對象。所謂的複製對象,首先要分配一個和源對象同樣大小的空間,在這個空間中創建一個新的對象

那麼在java語言中,創建對象可以通過:
1,使用new操作符創建一個對象
2,使用clone方法複製一個對象

使用clone而不是new一個類 的原因:
clone 方法是native 方法,效率要遠高於非native的方法

關於 clone() 方法

clone 是定義在Object 類下的基本方法,定義如下:

protected native Object clone() throws CloneNotSupportedException;

可以看到
1)clone方法是 protected 修飾的native 方法
2)克隆方法返回的是一個Object對象,所以必須要經過強制類型轉換。

複製對象 or 複製引用

// ‘=’ 賦值
        Clones c0 = new Clones("abc",123);
        Clones c1 = c0;
        System.out.println(c0);
        System.out.println(c1);

輸出:

com.something.Clones@1581593
com.something.Clones@1581593

可以看出打印地址是相同的,則肯定是同一個對象。c0 c1 只是一個引用,他們指向同一個對象。
這裏寫圖片描述

而使用clone 方法時:

    Clones c0 = new Clones("abc",123);
        Clones c1 = (Clones)c0.clone();
        System.out.println(c0);
        System.out.println(c1);

打印結果:

com.something.Clones@1f06dc3
com.something.Clones@1b64e6a

兩個對象的地址是不同的,也就是說創建了新的對象, 而不是把原對象的地址賦給了一個新的引用變量。

這裏寫圖片描述

在源碼的註釋中可以看到:

/**
* 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 {@code x}, the expression:
* x.clone() != x
* will be true, and that the expression:
* x.clone().getClass() == x.getClass() 所屬類形同
* will be {@code true}, but these are not absolute requirements.
* While it is typically the case that:
* x.clone().equals(x) 對象內容相同
* will be {@code true}, this is not an absolute requirement.

x.clone() != x 必須爲真,也就是對於**基礎類型**來說,其克隆後在堆中有兩個**獨立且內容相同的內存區域**。而對於**引用類型**來說,其**引用也不相同。**也就是說克隆對象和原始對象在java 堆(heap)中是兩個獨立的對象。

2 深拷貝 淺拷貝

拷貝是複製一個對象的副本。但是一個對象中有可能有基本數據類型,也有對象數據類型。

  1. shallow clone 淺拷貝:要克隆的對象,對於基本數據類型的屬性,複製一個新的,對於非基本類型的屬性,僅複製引用給新的對象,而指向同一個對象

  2. deep clone 深拷貝: 引用的對象的值也被改變了。深複製把要複製的對象所引用的對象都複製了一遍。

1,淺拷貝:

  • 實現java.lang.Cloneable 接口。
    Cloneable 接口是一個標識接口,不包含任何方法。調用Object 類中的clone方法必須要實現Cloneable 接口,否則會拋出異常
  • 重寫java.lang.Object.clone()方法。
    將clone權限設置爲public。 clone方法返回一個Object對象的拷貝。注意:1,返回的是一個新的對象,2,返回的對象與new操作符返回的區別就是這個拷貝已經包含了一些原來對象的信息。

clone 方法產生的效果:先在內存中開闢一塊和原始對象一樣的空間,然後鴛鴦拷貝原始對象中的內容。

public class Product implements Cloneable {   
    private String name;   

    public Object clone() {   
        try {   
            return super.clone();   
        } catch (CloneNotSupportedException e) {   
            return null;   
        }   
    }   
}  

2,深拷貝

即對非基本類型的屬性也實現拷貝。

對於要拷貝的類和類中所有的非基本數據類型的屬性對應的類

  • 都實現java.lang.Cloneable接口
  • 都重寫java.lang.Object.clone()方法

深拷貝與淺拷貝:
這裏寫圖片描述

深拷貝:

public class CloneTest {

    public static void main(String[] args) throws CloneNotSupportedException{

        Head  head = new Head();
        Body  body = new Body(head);
        Body  newBody = body.clone();
        System.out.println(body.head==newBody.head);
    }

}
class Body implements Cloneable{  
    public Head head;  
    public Body() {}  
    public Body(Head head) {this.head = head;}  


    protected Body clone () throws CloneNotSupportedException{
        Body  newBody = (Body) super.clone();
        //將newBody 中的head也進行克隆
        newBody.head = (Head) head.clone();
        return  newBody;
    }


}  
class Head implements Cloneable{  
    public  int face;  

    public Head() {}  
    public Head(int face){this.face = face;}  

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

輸出結果爲false。兩個body 中的head 也指向了不同的Head 對象。

當然,如果Head 中的Face 依舊是對象的話,上面的拷貝只是完成了:
這裏寫圖片描述

如果進一步深拷貝的話需要在Head 對象中重寫clone 方法。

3 使用序列化實現深拷貝

序列化即是把對象寫到流裏面的過程;反序列化即是把對象從流中讀取出來的過程。寫在流裏的是對象的一個拷貝,而原來的對象仍然在JVM裏面。

實現過程的描述:
前提是對象以及對象內部所有用到的對象都是可序列化的,否則就需要考慮把那些不可序列化的對象標記爲transient,從而把它排除到複製範圍之外。

然後使對象實現Serializable接口。

把對象寫入到一個流裏(不用依賴於文件,直接暫存在內存中即可),在從流裏讀取出來,便得到了一個深複製的對象。

參考:
http://blog.csdn.net/zhangjg_blog/article/details/18369201
http://www.itzhai.com/java-based-notebook-the-object-of-deep-and-shallow-copy-copy-copy-implement-the-cloneable-interface-serializing-deep-deep-copy.html#read-more
http://blog.csdn.net/mazhimazh/article/details/16828505#comments
http://blog.sina.com.cn/s/blog_6145ed810100uy8b.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章