一.原型模式介紹
A.優點:
當創建新的對象實例較爲複雜時,使用原型模式可以簡化的對象的創建過程,通過複製一個已有實例可以提高新實例的創建效率。
原型模式允許動態增加或減少產品類。
原型模式具有給一個應用軟件動態加載新功能的能力。
產品類不需要非得有任何事先確定的等級結構 。
B.缺點:
原型模式的最主要缺點就是每一個類必須配備一個克隆方法。而且這個克隆方法需要對類的功能進行通盤考慮,這對全新的類來說不是很難,但對已有的類進行改造時,不一定是件容易的事。
原型模式的另一個缺點是在實現深克隆時需要編寫較爲複雜的代碼
C.應用情況:
創建新對象成本較大(CPU,初始化)。
系統要保存對象的狀態,對象狀態變化很小。
當一個類的實例只有幾個不同狀態組合時,建立相應數目的原型並克隆它們可能比每次用合適的狀態手工實例化更爲方便。
二.java實現深克隆的方法:
1). 實現Cloneable接口並重寫Object類中的clone()方法;
2). 實現Serializable接口,通過對象的序列化和反序列化實現克隆,可以實現真正的深度克隆,主要用來解決多層克隆問題
A.Card類
public class Card implements Cloneable{
private int cardId;
private String name;
public int getCardId() {
return cardId;
}
public void setCardId(int cardId) {
this.cardId = cardId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Card(int cardId, String name) {
super();
this.cardId = cardId;
this.name = name;
}
public Object colne()
{
Card c=null;
try {
c=(Card) super.clone();
}
catch(CloneNotSupportedException e)
{
e.printStackTrace();
}
return c;
}
}
B.Student類
public class Student implements Cloneable {
private String name;
private String sex;
private String major;
private String phoneNumber;
private Card card;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getMajor() {
return major;
}
public void setMajor(String major) {
this.major = major;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public Object clone()
{
Student s=null;
try
{
s=(Student) super.clone();
}
catch(CloneNotSupportedException e)
{
e.printStackTrace();
}
s.setCard((Card) card.colne());
return s;
}
public Card getCard() {
return card;
}
public void setCard(Card card) {
this.card = card;
}
}
2.2通過序列話實現深克隆
A.Card類(只需實現Serializable接口,並定義序列號)
public class Card implements Serializable{
private static final long serialVersionUID = 872390113109L;
}
B.Student類
public class Student implements Serializable{
private static final long serialVersionUID = 369285298572941L;
private String name;
private String sex;
private String major;
private String phoneNumber;
private Card card;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getMajor() {
return major;
}
public void setMajor(String major) {
this.major = major;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public Object clone()
{
Student s=null;
try
{
//字節數組輸出流在內存中創建一個字節數組緩衝區,所有發送到輸出流的數據保存在該字節數組緩衝區中。創建字節數組緩衝區 無參數傳入時爲32字節
ByteArrayOutputStream baos=new ByteArrayOutputStream();
//創建對象輸出流
ObjectOutputStream oo=new ObjectOutputStream(baos);
//將對象寫入內存
oo.writeObject(this);
//字節數組輸入流在內存中創建一個字節數組緩衝區,所有發送到輸入流中的數據保存再該字節數組緩衝區
ByteArrayInputStream bais=new ByteArrayInputStream(baos.toByteArray());
//創建對象輸入流
ObjectInputStream oi=new ObjectInputStream(bais);
//將對象從內存的輸出流中讀回對象,完成深克隆 反序列化
s=(Student) oi.readObject();
oi.close();
oo.close();
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return s;
}
public Card getCard() {
return card;
}
public void setCard(Card card) {
this.card = card;
}
}
三.序列化的詳情
(來自於各種大佬的博客)
A. 只有實現Serializable接口才能實現對象的序列化。
B. serialVersionUID 是用於記錄class文件的版本信息的,serialVersionUID這個數字是JVM(JAVA虛擬界)通過一個類的類名、成員、包名、工程名算出的一個數字。
C. 如果序列化與反序列化的時候可能會修改類的成員,那麼最好一開始就給這個類指定一個serialVersionUID,如果一類已經指定的serialVersionUID,然後在序列化與反序列化的時候,jvm都不會再自己算這個 class的serialVersionUID了。
D. 對象的反序列化創建對象的時候並不會調用到構造方法的。
E. 如果一個對象某個數據不想被序列化到硬盤上,可以使用關鍵字transient修飾。
F. 如果一個類維護了另外一個類的引用,則另外一個類也需要實現Serializable接口
G. 序列化只將對象的狀態信息轉化成可以傳輸或者儲存的二進制文件(對象寫到流的過程,寫到流中的對象是原有對象的一個拷貝,而原對象仍然存在於內存中。)。在序列化期間,對象將其當前狀態寫入到臨時存儲區或持久性存儲區,之後,便可以通過從存儲區中讀取或反序列化對象的狀態信息,來重新創建該對象
H. Serializable主要用來支持兩種主要的特性:
1、Java的RMI(remote method invocation),RMI允許像在本機上一樣操作遠程機器上的對象,當發送消息給遠程對象時,就需要用到序列化機制來發送參數和接受返回值
2、Java的JavaBean,Bean的狀態信息通常是在設計時配置的,Bean的狀態信息必須被保存下來,以便當程序運行時能恢復這些狀態信息,這也需要序Serializable機制。