物件的克隆——原型模式(三)
7.4 帶附件的週報
通過引入原型模式,Sunny軟體公司OA系統支援工作週報的快速克隆,極大提高了工作週報的編寫效率,受到員工的一致好評。但有員工又發現一個問題,有些工作週報帶有附件,例如經理助理“小龍女”的週報通常附有本週專案進展報告彙總表、本週客戶反饋資訊彙總表等,如果使用上述原型模式來複制週報,週報雖然可以複製,但是週報的附件並不能複製,這是由於什麼原因導致的呢?如何才能實現週報和附件的同時複製呢?我們在本節將討論如何解決這些問題。
在回答這些問題之前,先介紹一下兩種不同的克隆方法,淺克隆(ShallowClone)和深克隆(DeepClone)。在Java語言中,資料型別分為值型別(基本資料型別)和引用型別,值型別包括int、double、byte、boolean、char等簡單資料型別,引用型別包括類、介面、陣列等複雜型別。淺克隆和深克隆的主要區別在於是否支援引用型別的成員變數的複製,下面將對兩者進行詳細介紹。
1.淺克隆
在淺克隆中,如果原型物件的成員變數是值型別,將複製一份給克隆物件;如果原型物件的成員變數是引用型別,則將引用物件的地址複製一份給克隆物件,也就是說原型物件和克隆物件的成員變數指向相同的記憶體地址。簡單來說,在淺克隆中,當物件被複制時只複製它本身和其中包含的值型別的成員變數,而引用型別的成員物件並沒有複製,如圖7-4所示:
圖7-4 淺克隆示意圖
在Java語言中,通過覆蓋Object類的clone()方法可以實現淺克隆。為了讓大家更好地理解淺克隆和深克隆的區別,我們首先使用淺克隆來實現工作週報和附件類的複製,其結構如圖7-5所示:
圖7-5 帶附件的週報結構圖(淺克隆)
附件類Attachment程式碼如下:
//附件類 class Attachment { private String name; //附件名 public void setName(String name) { this.name = name; } public String getName() { return this.name; } public void download() { System.out.println("下載附件,檔名為" + name); } } |
修改工作週報類WeeklyLog,修改後的程式碼如下:
//工作週報WeeklyLog class WeeklyLog implements Cloneable { //為了簡化設計和實現,假設一份工作週報中只有一個附件物件,實際情況中可以包含多個附件,可以通過List等集合物件來實現 private Attachment attachment; private String name; private String date; private String content; public void setAttachment(Attachment attachment) { this.attachment = attachment; } public void setName(String name) { this.name = name; } public void setDate(String date) { this.date = date; } public void setContent(String content) { this.content = content; } public Attachment getAttachment(){ return (this.attachment); } public String getName() { return (this.name); } public String getDate() { return (this.date); } public String getContent() { return (this.content); } //使用clone()方法實現淺克隆 public WeeklyLog clone() { Object obj = null; try { obj = super.clone(); return (WeeklyLog)obj; } catch(CloneNotSupportedException e) { System.out.println("不支援複製!"); return null; } } } |
客戶端程式碼如下所示:
class Client { public static void main(String args[]) { WeeklyLog log_previous, log_new; log_previous = new WeeklyLog(); //建立原型物件 Attachment attachment = new Attachment(); //建立附件物件 log_previous.setAttachment(attachment); //將附件新增到週報中 log_new = log_previous.clone(); //呼叫克隆方法建立克隆物件 //比較週報 System.out.println("週報是否相同? " + (log_previous == log_new)); //比較附件 System.out.println("附件是否相同? " + (log_previous.getAttachment() == log_new.getAttachment())); } } |
編譯並執行程式,輸出結果如下:
週報是否相同? false 附件是否相同? true |
由於使用的是淺克隆技術,因此工作週報物件複製成功,通過“==”比較原型物件和克隆物件的記憶體地址時輸出false;但是比較附件物件的記憶體地址時輸出true,說明它們在記憶體中是同一個物件。
2.深克隆
在深克隆中,無論原型物件的成員變數是值型別還是引用型別,都將複製一份給克隆物件,深克隆將原型物件的所有引用物件也複製一份給克隆物件。簡單來說,在深克隆中,除了物件本身被複制外,物件所包含的所有成員變數也將複製,如圖7-6所示:
圖7-6 深克隆示意圖
在Java語言中,如果需要實現深克隆,可以通過序列化(Serialization)等方式來實現。序列化就是將物件寫到流的過程,寫到流中的物件是原有物件的一個拷貝,而原物件仍然存在於記憶體中。通過序列化實現的拷貝不僅可以複製物件本身,而且可以複製其引用的成員物件,因此通過序列化將物件寫到一個流中,再從流裡將其讀出來,可以實現深克隆。需要注意的是能夠實現序列化的物件其類必須實現Serializable介面,否則無法實現序列化操作。下面我們使用深克隆技術來實現工作週報和附件物件的複製,由於要將附件物件和工作週報物件都寫入流中,因此兩個類均需要實現Serializable介面,其結構如圖7-7所示:
圖7-7 帶附件的週報結構圖(深克隆)
修改後的附件類Attachment程式碼如下:
import java.io.*; //附件類 class Attachment implements Serializable { private String name; //附件名 public void setName(String name) { this.name = name; } public String getName() { return this.name; } public void download() { System.out.println("下載附件,檔名為" + name); } } |
工作週報類WeeklyLog不再使用Java自帶的克隆機制,而是通過序列化來從頭實現物件的深克隆,我們需要重新編寫clone()方法,修改後的程式碼如下:
import java.io.*; //工作週報類 class WeeklyLog implements Serializable { private Attachment attachment; private String name; private String date; private String content; public void setAttachment(Attachment attachment) { this.attachment = attachment; } public void setName(String name) { this.name = name; } public void setDate(String date) { this.date = date; } public void setContent(String content) { this.content = content; } public Attachment getAttachment(){ return (this.attachment); } public String getName() { return (this.name); } public String getDate() { return (this.date); } public String getContent() { return (this.content); } //使用序列化技術實現深克隆 public WeeklyLog deepClone() throws IOException, ClassNotFoundException, OptionalDataException { //將物件寫入流中 ByteArrayOutputStream bao=new ByteArrayOutputStream(); ObjectOutputStream oos=new ObjectOutputStream(bao); oos.writeObject(this);
//將物件從流中取出 ByteArrayInputStream bis=new ByteArrayInputStream(bao.toByteArray()); ObjectInputStream ois=new ObjectInputStream(bis); return (WeeklyLog)ois.readObject(); } } |
客戶端程式碼如下所示:
class Client { public static void main(String args[]) { WeeklyLog log_previous, log_new = null; log_previous = new WeeklyLog(); //建立原型物件 Attachment attachment = new Attachment(); //建立附件物件 log_previous.setAttachment(attachment); //將附件新增到週報中 try { log_new = log_previous.deepClone(); //呼叫深克隆方法建立克隆物件 } catch(Exception e) { System.err.println("克隆失敗!"); } //比較週報 System.out.println("週報是否相同? " + (log_previous == log_new)); //比較附件 System.out.println("附件是否相同? " + (log_previous.getAttachment() == log_new.getAttachment())); } } |
編譯並執行程式,輸出結果如下:
週報是否相同? false 附件是否相同? false |
從輸出結果可以看出,由於使用了深克隆技術,附件物件也得以複製,因此用“==”比較原型物件的附件和克隆物件的附件時輸出結果均為false。深克隆技術實現了原型物件和克隆物件的完全獨立,對任意克隆物件的修改都不會給其他物件產生影響,是一種更為理想的克隆實現方式。
|
相關文章
- 物件的克隆——原型模式(四)物件原型模式
- 物件的克隆——原型模式(二)物件原型模式
- 物件的克隆——原型模式(一)物件原型模式
- JavaScript建立物件(三)——原型模式JavaScript物件原型模式
- JAVA設計模式 2【建立型】原型模式的理解與使用、理解淺克隆和深克隆Java設計模式原型
- 三張圖搞懂JavaScript的原型物件與原型鏈JavaScript原型物件
- 設計模式(三)——原型模式設計模式原型
- ECMAScript5中的物件,原型,原型鏈,原型的幾種繼承模式【一】物件原型繼承模式
- 3.4 PROTOTYPE(原型) — 物件建立型模式原型物件模式
- Java中的物件“克隆”Java物件
- js物件的深度克隆!JS物件
- 理解js中的原型,原型物件,原型鏈JS原型物件
- 物件導向的程式設計之原型模式物件程式設計原型模式
- Scala 與設計模式(三):Prototype 原型模式設計模式原型
- .NET物件克隆的深究 (轉)物件
- 原生js實現物件的深克隆以及淺克隆JS物件
- JS的物件原型JS物件原型
- js深度克隆物件JS物件
- 物件如何深度克隆物件
- javascript 深度克隆物件JavaScript物件
- JS物件導向程式設計(三):原型JS物件程式設計原型
- 原型物件與原型鏈原型物件
- JavaScript中的原型、原型鏈、原型模式JavaScript原型模式
- js基礎--原型物件與原型物件鏈JS原型物件
- Javascript 中實現物件原型繼承的三種方式JavaScript物件原型繼承
- 詳解 Java 中的物件克隆Java物件
- 如何實現物件的深度克隆物件
- JS的物件導向(理解物件,原型,原型鏈,繼承,類)JS物件原型繼承
- JS中的原型物件JS原型物件
- JavaScript深層克隆物件JavaScript物件
- js克隆一個物件JS物件
- 設計模式之-原型模式(三妻四妾)設計模式原型
- 函式物件、物件、原型函式物件原型
- 物件獲取原型物件物件原型
- JS型別判斷、物件克隆、陣列克隆JS型別物件陣列
- JavaScript 物件 & 原型JavaScript物件原型
- 三目運算、物件克隆、深拷貝和淺拷貝物件
- 說說JS中的原型物件和原型鏈JS原型物件