當前位置:首頁 » 編程語言 » 深度克隆java

深度克隆java

發布時間: 2022-02-27 15:37:19

java中深克隆與淺克隆的區別

深克隆與淺克隆

大家知道,對象是互相引用的,即對象中可能包含了另一個對象的引用,舉例如:有一個Order對象,Order對象中又包含了LineItems對象,然後LineItems對象又包含了Item對象。

好了,現在我有一個Order對象order1,它包含了一個LineItems對象items,這表示的是有一個訂單order1,訂單的內容是items。

好的,現在有另一個客戶想要一份訂單,內容跟order1完全一樣,那麼在系統的邏輯層我們怎麼做呢?很簡單,order2=order1.clone(). 我們知道clone方法是在內存中生成一個新的對象,而不是只得到原對象的引用。這時候,有人說話了:「哦,明白了我們對order2的成員變數進行修改,是不會影響order1的。」 很可惜,這句話只對了一半。

假設order類有一個成員變數name,當然改變order2.name不會影響order1.name,因為他們在不同的內存區域。但是如果改變 order1.items呢?很遺憾,簡單地使用order1.clone,是會影響到order2.items的。原因很簡單,就是因為clone方法默認的是淺克隆,即不會克隆對象引用的對象,而只是簡單地復制這個引用。所以在上例中,items對象在內存中只有一個,order1和order2都指向它,任何一個對象對它的修改都會影響另一個對象。

那相對淺克隆,深克隆自然就是會克隆對象引用的對象了。也就是說,在上例中,改變order1.items並不會影響order2.items了。因為內存中有兩個一樣的items。

如果實現深克隆?

一個方法自然是重寫clone方法,添加如order.items=(LineItems)items.clone()的語句,也就是人為地添加對引用對象的復制。這個方法的缺點是如果引用對象有很多,或者說引用套引用很多重,那麼太麻煩了。業界常用的方法是使用串列化然後反串列化的方法來實現深克隆。由於串列化後,對象寫到流中,所有引用的對象都包含進來了,所以反串列化後,對等於生成了一個完全克隆的對象。絕!

這個方法的要求是對象(包括被引用對象)必須事先了Serializable介面,否則就要用transient關鍵字將其排除在復制過程中。

㈡ 如何使Java中的InputStream的深層復制

我們知道在Java中存在這個介面Cloneable,實現該介面的類都會具備被拷貝的能力,同時拷貝是在內存中進行,在性能方面比我們直接通過new生成對象來的快,特別是在大對象的生成上,使得性能的提升非常明顯。然而我們知道拷貝分為深拷貝和淺拷貝之分,但是淺拷貝存在對象屬性拷貝不徹底問題。關於深拷貝、淺拷貝的請參考這里:漸析java的淺拷貝和深拷貝

一、淺拷貝問題

我們先看如下代碼:

[java] view plain
public class Person implements Cloneable{
/** 姓名 **/
private String name;

/** 電子郵件 **/
private Email email;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Email getEmail() {
return email;
}

public void setEmail(Email email) {
this.email = email;
}

public Person(String name,Email email){
this.name = name;
this.email = email;
}

public Person(String name){
this.name = name;
}

protected Person clone() {
Person person = null;
try {
person = (Person) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}

return person;
}
}

public class Client {
public static void main(String[] args) {
//寫封郵件
Email email = new Email("請參加會議","請與今天12:30到二會議室參加會議...");

Person person1 = new Person("張三",email);

Person person2 = person1.clone();
person2.setName("李四");
Person person3 = person1.clone();
person3.setName("王五");

System.out.println(person1.getName() + "的郵件內容是:" + person1.getEmail().getContent());
System.out.println(person2.getName() + "的郵件內容是:" + person2.getEmail().getContent());
System.out.println(person3.getName() + "的郵件內容是:" + person3.getEmail().getContent());
}
}
--------------------
Output:
張三的郵件內容是:請與今天12:30到二會議室參加會議...
李四的郵件內容是:請與今天12:30到二會議室參加會議...
王五的郵件內容是:請與今天12:30到二會議室參加會議...

在該應用程序中,首先定義一封郵件,然後將該郵件發給張三、李四、王五三個人,由於他們是使用相同的郵件,並且僅有名字不同,所以使用張三該對象類拷貝李四、王五對象然後更改下名字即可。程序一直到這里都沒有錯,但是如果我們需要張三提前30分鍾到,即把郵件的內容修改下:

[java] view plain
public class Client {
public static void main(String[] args) {
//寫封郵件
Email email = new Email("請參加會議","請與今天12:30到二會議室參加會議...");

Person person1 = new Person("張三",email);

Person person2 = person1.clone();
person2.setName("李四");
Person person3 = person1.clone();
person3.setName("王五");

person1.getEmail().setContent("請與今天12:00到二會議室參加會議...");

System.out.println(person1.getName() + "的郵件內容是:" + person1.getEmail().getContent());
System.out.println(person2.getName() + "的郵件內容是:" + person2.getEmail().getContent());
System.out.println(person3.getName() + "的郵件內容是:" + person3.getEmail().getContent());
}
}
在這里同樣是使用張三該對象實現對李四、王五拷貝,最後將張三的郵件內容改變為:請與今天12:00到二會議室參加會議...。但是結果是:

[java] view plain
張三的郵件內容是:請與今天12:00到二會議室參加會議...
李四的郵件內容是:請與今天12:00到二會議室參加會議...
王五的郵件內容是:請與今天12:00到二會議室參加會議...

這里我們就疑惑了為什麼李四和王五的郵件內容也發送了改變呢?讓他們提前30分鍾到人家會有意見的!

其實出現問題的關鍵就在於clone()方法上,我們知道該clone()方法是使用Object類的clone()方法,但是該方法存在一個缺陷,它並不會將對象的所有屬性全部拷貝過來,而是有選擇性的拷貝,基本規則如下:

1、 基本類型

如果變數是基本很類型,則拷貝其值,比如int、float等。

2、 對象

如果變數是一個實例對象,則拷貝其地址引用,也就是說此時新對象與原來對象是公用該實例變數。

3、 String字元串

若變數為String字元串,則拷貝其地址引用。但是在修改時,它會從字元串池中重新生成一個新的字元串,原有紫都城對象保持不變。

基於上面上面的規則,我們很容易發現問題的所在,他們三者公用一個對象,張三修改了該郵件內容,則李四和王五也會修改,所以才會出現上面的情況。對於這種情況我們還是可以解決的,只需要在clone()方法裡面新建一個對象,然後張三引用該對象即可:

[java] view plain
protected Person clone() {
Person person = null;
try {
person = (Person) super.clone();
person.setEmail(new Email(person.getEmail().getObject(),person.getEmail().getContent()));
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}

return person;
}

所以:淺拷貝只是Java提供的一種簡單的拷貝機制,不便於直接使用。

對於上面的解決方案還是存在一個問題,若我們系統中存在大量的對象是通過拷貝生成的,如果我們每一個類都寫一個clone()方法,並將還需要進行深拷貝,新建大量的對象,這個工程是非常大的,這里我們可以利用序列化來實現對象的拷貝。

二、利用序列化實現對象的拷貝

如何利用序列化來完成對象的拷貝呢?在內存中通過位元組流的拷貝是比較容易實現的。把母對象寫入到一個位元組流中,再從位元組流中將其讀出來,這樣就可以創建一個新的對象了,並且該新對象與母對象之間並不存在引用共享的問題,真正實現對象的深拷貝。

[java] view plain
public class CloneUtils {
@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T obj){
T cloneObj = null;
try {
//寫入位元組流
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream obs = new ObjectOutputStream(out);
obs.writeObject(obj);
obs.close();

//分配內存,寫入原始對象,生成新對象
ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray());
ObjectInputStream ois = new ObjectInputStream(ios);
//返回生成的新對象
cloneObj = (T) ois.readObject();
ois.close();
} catch (Exception e) {
e.printStackTrace();
}
return cloneObj;
}
}

使用該工具類的對象必須要實現Serializable介面,否則是沒有辦法實現克隆的。

[html] view plain
public class Person implements Serializable{
private static final long serialVersionUID = 2631590509760908280L;

..................
//去除clone()方法

}

public class Email implements Serializable{
private static final long serialVersionUID = 1267293988171991494L;

....................
}

所以使用該工具類的對象只要實現Serializable介面就可實現對象的克隆,無須繼承Cloneable介面實現clone()方法。

㈢ java對象深層復制有什麼好處

你說的是不是深拷貝和淺拷貝,深拷貝是把一個原變數的值賦給新的變數,而淺拷貝只是把原變數在計算機的內存地址賦給新的變數,所以深拷貝之後可以獨立操作原變數和新變數,互不幹擾,而淺拷貝只要新變數或者原變數的值改變了,另一個也改變

㈣ 請問java中深度一個二維數組是什麼意思怎麼用代碼實現

一個二維數組可以看成一個一維數組,每個元素存儲一個一維數組首地址的引用,這個沒問題吧!
也就是說對於a[][],直接用b[][]=a,或者b[][]=a.clone() 都只是復制了一個引用(包括上面的array等方法),無法保證數據獨立性,就是說a數組值改變會影響到b,反之亦然,這就是淺層復制。
如果二維數組存放類型為基本類型,則只需要b的每一行進行復制(Object.clone()可以保證對基本類型做深層復制api上有寫):
b[][]=a.clone();//先利用淺層復制分配新的引用存放地址
for(int i=0;i<a.length;i++){
b[i]=a[i].clone();//a[i]指向數組的內容為基本類型,可以深層復制生成新引用對象
}
如果二維數組表示的是引用類型,則要對每一個元素調用clone(),並且保證所表示的引用類型遵循clone()復寫原則。
b[][]=a.clone();//先利用淺層復制分配新的引用存放地址
for(int i=0;i<a.length;i++){
for(int j=0;j<a[0].length;j++){
b[i][j]=a[i][j].clone()//為每個元素進行深層復制
}
}

以上是規范寫法,實現方法有很多,但一定要記住,單純的對引用的COPY是沒有意義的,編程中要避免。

㈤ java bean 如何過濾某些屬性進行深度克隆

序列化bean,先寫入,後讀出。這兩個bean 就毫無關系了。但內容一模一樣。

publicSerializableObject(Serializablesobj){
try{
ByteArrayOutputStreambos=newByteArrayOutputStream();

ObjectOutputStreamoos=newObjectOutputStream(bos);

oos.writeObject(sobj);

ByteArrayInputStreambis=newByteArrayInputStream(bos.toByteArray());

ObjectInputStreamois=newObjectInputStream(bis);

return(Serializable)ois.readObject();
}catch(Exceptione){
returnnull;
}

}


可以參考一下。不過 要求實現 Serializable介面。


原文:

http://tieba..com/p/3205322191

㈥ Java中的淺克隆與深克隆 ,有可以區別開的實例嗎求大神指點

淺克隆 就是只克隆類的屬性,子對象的屬性就不管了。

深克隆,就當前類屬性、子對象、子子對象屬性、、、、、、全克隆了

㈦ java如何深度一個object

方法一:把對象序列化之後再反序列化,之後得到的對象就是深度克隆的對象;
方法二:自己重寫方法,不過有點麻煩。

㈧ java中的淺克隆和深克隆是什麼

淺克隆:僅僅復制所克隆的對象,而不復制它所引用的對象。

被復制對象的所有變數都含有與原來的對象相同的值,而所有的對其他對象的引用仍然指向原來的對象。


深克隆:把要復制的對象所引用的對象都復制了一遍。

那些引用其他對象的變數將指向被復制過的新對象,而不再是原有的那些被引用的對象。


很明顯二者時間消耗有差距,在不影響的情況下盡量用淺克隆

注意區分與C語言的淺克隆深克隆 那個是引用地址與重新分配地址構建的區別,詳細可以參見:

http://www.cnblogs.com/yxnchinahlj/archive/2010/09/20/1831615.html

㈨ java深度拷貝和淺度拷貝的問題

看來你並沒理解錯克隆,反而是改變東西的方法不對。
先還是講講克隆吧。
是的,上面的clone方法也就是Object的Clone方法是淺表復制,看看他的API說的:
「此方法會創建此對象的類的一個新實例,並像通過分配那樣,嚴格使用此對象相應欄位的內容初始化該對象的所有欄位;……。所以,此方法執行的是該對象的「淺表復制」,而不「深層復制」操作。 」
這你沒理解錯。
但你測試的方法貌似有問題,讓人不知你想干什麼。
本來sb1、sb2的a欄位內容相同的。這兩個欄位代表兩個對象,所謂倆對象,說白了實際上都是一個4位元組空間,存的是StringBuffer對象地址,也就是所謂a對象,實際上包含兩個東西,一個是a這個指針(我是在說JAVA),另一個指的是這個指針指向的堆棧裡面的空間(再復雜就不說了,參閱《JAVA虛擬機》)。相同也就意味著兩個變數指向的對象空間一樣。
這樣的話,如果你調用sb1.a.put...類似的方法朝a中放東西,那sb2也會變。

可你直接把sb1的a換了,也就是讓a指向另一個你new的空間。這樣和sb2有什麼關系,而且你以後再改sb1的a,sb2也不會變。
不說了,累。不懂就問我。

㈩ java,,"淺克隆,只復制一個對象 ,深克隆 對象和引用一起復制"有java實例嗎不理解啊

對只包含進本類型的類來說,無所謂深克隆。但是一個類包含集合類或者復雜的類,就要考慮二者的區別了。

class Student {
private int id;
private Address address;
private ArrayList<Book> bookList;
}
對於以上類的一個對象a和克隆類 o來說:
淺克隆:
a !=o 但是 a.address == o.address, a.bookList == o.bookList
深克隆:
a !=o 但是 a.address != o.address, a.bookList != o.bookList
並且 bookList中的每個元素不是相同對象。
簡言之, 淺克隆只復制本對象,深克隆除了本對象,其兒子、孫子、....都要克隆。
以上純手敲,有錯請包涵。

熱點內容
個人小程序怎麼購買雲伺服器 發布:2025-01-11 06:33:08 瀏覽:908
手機mc怎麼玩伺服器國際服 發布:2025-01-11 06:18:33 瀏覽:156
win2008ftp中文亂碼 發布:2025-01-11 06:10:03 瀏覽:867
平板配置為什麼這么低 發布:2025-01-11 06:05:30 瀏覽:621
可編程視頻 發布:2025-01-11 06:03:24 瀏覽:785
java多線程編程實戰 發布:2025-01-11 06:03:17 瀏覽:631
圖的演算法java 發布:2025-01-11 05:57:07 瀏覽:482
梯形圖編譯器 發布:2025-01-11 05:56:26 瀏覽:260
安卓framework編譯 發布:2025-01-11 05:55:00 瀏覽:696
加密學原理 發布:2025-01-11 05:54:20 瀏覽:788