java 编码实现内存拷贝_java提高篇(六)-----使用序列化实现对象的拷贝
我們知道在Java中存在這個接口Cloneable,實現該接口的類都會具備被拷貝的能力,同時拷貝是在內存中進行,在性能方面比我們直接通過new生成對象來的快,特別是在大對象的生成上,使得性能的提升非常明顯。然而我們知道拷貝分為深拷貝和淺拷貝之分,但是淺拷貝存在對象屬性拷貝不徹底問題。關于深拷貝、淺拷貝的請參考這里:漸析java的淺拷貝和深拷貝
一、淺拷貝問題
我們先看如下代碼:
public class Person implementsCloneable{/**姓名 **/
privateString name;/**電子郵件 **/
privateEmail email;publicString getName() {returnname;
}public voidsetName(String name) {this.name =name;
}publicEmail getEmail() {returnemail;
}public voidsetEmail(Email email) {this.email =email;
}publicPerson(String name,Email email){this.name =name;this.email =email;
}publicPerson(String name){this.name =name;
}protectedPerson clone() {
Person person= null;try{
person= (Person) super.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}returnperson;
}
}public classClient {public static voidmain(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分鐘到,即把郵件的內容修改下:
public classClient {public static voidmain(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到二會議室參加會議...。但是結果是:
張三的郵件內容是:請與今天12:00到二會議室參加會議...
李四的郵件內容是:請與今天12:00到二會議室參加會議...
王五的郵件內容是:請與今天12:00到二會議室參加會議...
這里我們就疑惑了為什么李四和王五的郵件內容也發送了改變呢?讓他們提前30分鐘到人家會有意見的!
其實出現問題的關鍵就在于clone()方法上,我們知道該clone()方法是使用Object類的clone()方法,但是該方法存在一個缺陷,它并不會將對象的所有屬性全部拷貝過來,而是有選擇性的拷貝,基本規則如下:
1、 基本類型
如果變量是基本很類型,則拷貝其值,比如int、float等。
2、 對象
如果變量是一個實例對象,則拷貝其地址引用,也就是說此時新對象與原來對象是公用該實例變量。
3、 String字符串
若變量為String字符串,則拷貝其地址引用。但是在修改時,它會從字符串池中重新生成一個新的字符串,原有紫都城對象保持不變。
基于上面上面的規則,我們很容易發現問題的所在,他們三者公用一個對象,張三修改了該郵件內容,則李四和王五也會修改,所以才會出現上面的情況。對于這種情況我們還是可以解決的,只需要在clone()方法里面新建一個對象,然后張三引用該對象即可:
protectedPerson clone() {
Person person= null;try{
person= (Person) super.clone();
person.setEmail(newEmail(person.getEmail().getObject(),person.getEmail().getContent()));
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}returnperson;
}
所以:淺拷貝只是Java提供的一種簡單的拷貝機制,不便于直接使用。
對于上面的解決方案還是存在一個問題,若我們系統中存在大量的對象是通過拷貝生成的,如果我們每一個類都寫一個clone()方法,并將還需要進行深拷貝,新建大量的對象,這個工程是非常大的,這里我們可以利用序列化來實現對象的拷貝。
二、利用序列化實現對象的拷貝
如何利用序列化來完成對象的拷貝呢?在內存中通過字節流的拷貝是比較容易實現的。把母對象寫入到一個字節流中,再從字節流中將其讀出來,這樣就可以創建一個新的對象了,并且該新對象與母對象之間并不存在引用共享的問題,真正實現對象的深拷貝。
public classCloneUtils {
@SuppressWarnings("unchecked")public static T clone(T obj){
T cloneObj= null;try{//寫入字節流
ByteArrayOutputStream out = newByteArrayOutputStream();
ObjectOutputStream obs= newObjectOutputStream(out);
obs.writeObject(obj);
obs.close();//分配內存,寫入原始對象,生成新對象
ByteArrayInputStream ios = newByteArrayInputStream(out.toByteArray());
ObjectInputStream ois= newObjectInputStream(ios);//返回生成的新對象
cloneObj =(T) ois.readObject();
ois.close();
}catch(Exception e) {
e.printStackTrace();
}returncloneObj;
}
}
使用該工具類的對象必須要實現Serializable接口,否則是沒有辦法實現克隆的。
public class Person implementsSerializable{private static final long serialVersionUID = 2631590509760908280L;
..................//去除clone()方法
}public class Email implementsSerializable{private static final long serialVersionUID = 1267293988171991494L;
....................
}
所以使用該工具類的對象只要實現Serializable接口就可實現對象的克隆,無須繼承Cloneable接口實現clone()方法。
public classClient {public static voidmain(String[] args) {//寫封郵件
Email email = new Email("請參加會議","請與今天12:30到二會議室參加會議...");
Person person1= new Person("張三",email);
Person person2=CloneUtils.clone(person1);
person2.setName("李四");
Person person3=CloneUtils.clone(person1);
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());
}
}-------------------Output:
張三的郵件內容是:請與今天12:00到二會議室參加會議...
李四的郵件內容是:請與今天12:30到二會議室參加會議...
王五的郵件內容是:請與今天12:30到二會議室參加會議...
鞏固基礎,提高技術,不懼困難,攀登高峰!!!!!!
參考文獻《編寫高質量代碼 改善Java程序的151個建議》----秦小波
總結
以上是生活随笔為你收集整理的java 编码实现内存拷贝_java提高篇(六)-----使用序列化实现对象的拷贝的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python字符串填充_填充函数(Pyt
- 下一篇: python 执行文件内容_python