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 深拷貝 淺拷貝
拷貝是複製一個對象的副本。但是一個對象中有可能有基本數據類型,也有對象數據類型。
shallow clone 淺拷貝:要克隆的對象,對於基本數據類型的屬性,複製一個新的,對於非基本類型的屬性,僅複製引用給新的對象,而指向同一個對象
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