兼職網(wǎng)網(wǎng)站建設(shè)方案建議書娃哈哈軟文推廣
目錄
基本介紹
傳統(tǒng)方式克隆
原型模式改進
淺拷貝和深拷貝
淺拷貝的介紹
深拷貝的介紹
原型模式的注意事項和細節(jié)
基本介紹
(1) 原型模式(prototype模式): 用原型實例指定創(chuàng)建對象的種類 并且通過拷貝這些原型 創(chuàng)建新的對象
(2) 原型模式是一種創(chuàng)建型設(shè)計模式 允許一個對象再創(chuàng)建另一個可定制的對象 而不需要知道如何創(chuàng)建的細節(jié)
(3) 工作原理: 通過將一個原型對象傳給那個要發(fā)動創(chuàng)建的對象 這個要發(fā)動創(chuàng)建的對象通過請求原型對象拷貝它們自己來實施創(chuàng)建 即對象.clone()
傳統(tǒng)方式克隆
(1) 優(yōu)點是簡單易操作 比較好理解
(2) 在創(chuàng)建新的對象時 總是需要重新獲取原始對象的屬性 如果創(chuàng)建的對象比較復(fù)雜時 效率較低
(3) 總是需要重新初始化對象 而不是動態(tài)地獲得對象運行時的狀態(tài) 不夠靈活
(4)改進思路分析
? ? java中Object類是所有類的基類 Object類提供了一個clone()方法 該方法可以將一個java對象復(fù)制一份 但是需要實現(xiàn)clone的java類必須要實現(xiàn)一個接口Cloneable 該接口表示該類能夠復(fù)制且具有復(fù)制的能力-->原型模式
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Monkery {private String name;private Integer age;private String color;}
測試結(jié)果:
?
原型模式改進
要克隆的實例實現(xiàn)Cloneable接口并實現(xiàn)默認的clone方法
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MonkeyClone implements Cloneable{private String name;private Integer age;private String color;//克隆該實例 使用默認的clone方法實現(xiàn)@Overrideprotected Object clone() {MonkeyClone monkeyClone = null;try {monkeyClone = (MonkeyClone)super.clone();}catch (Exception e){System.out.println(e.getMessage());}return monkeyClone;}
}
?測試結(jié)果:
淺拷貝和深拷貝
淺拷貝的介紹
(1) 對于數(shù)據(jù)類型是基本數(shù)據(jù)類型的成員變量 淺拷貝會直接進行值傳遞 也就是將該屬性值復(fù)制一份給新的對象
(2) 對于數(shù)據(jù)類型是引用數(shù)據(jù)類型的成員變量 比如說成員變量是某個數(shù)組 某個類的對象等 那么淺拷貝會進行引用傳遞 也就是只是將該成員變量的引用值(內(nèi)存地址)復(fù)制一份給新的對象 因為實際上兩個對象的該成員變量都指向同一個實例 在這種情況下 在一個對象中修改該成員變量的話會影響其他對象的該成員變量值
(3) 前面的克隆monkey就是淺拷貝
(4) 淺拷貝是使用默認的clone()方法來實現(xiàn)? monkey=(MonkeyClone) super.clone();
為MonkeyClone 添加一個MonkeryClone類型的child屬性 此時這個數(shù)據(jù)類型即為引用類型
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MonkeyClone implements Cloneable{private String name;private Integer age;private String color;private MonkeyClone child;public MonkeyClone(String name, Integer age, String color) {this.name = name;this.age = age;this.color = color;}//克隆該實例 使用默認的clone方法實現(xiàn)@Overrideprotected Object clone() {MonkeyClone monkeyClone = null;try {monkeyClone = (MonkeyClone)super.clone();}catch (Exception e){System.out.println(e.getMessage());}return monkeyClone;}
}
我們接下來看引用數(shù)據(jù)類型的clone特性:
測試
?
我們這里可以看到,后面復(fù)制的monke它們的hashCode都是一致的,說明它們的內(nèi)存地址指向的都是同一個地址
深拷貝的介紹
(1) 復(fù)制對象的所有基本數(shù)據(jù)類型的成員變量值
(2) 為所有引用數(shù)據(jù)類型的成員變量申請儲存空間 并復(fù)制每個引用數(shù)據(jù)類型成員變量所引用的對象 直到該對象可達的所有對象 也就是說 對象進行深拷貝要對整個對象進行拷貝
(3) 深拷貝的實現(xiàn)方式1: 重寫clone方法來實現(xiàn)深拷貝
(4) 深拷貝的實現(xiàn)方式2: 通過對象序列化來實現(xiàn)深拷貝
public class DeepCloneTarget implements Serializable,Cloneable {private String cloneName;private String cloneClass;public DeepCloneTarget(String cloneName, String cloneClass) {this.cloneName = cloneName;this.cloneClass = cloneClass;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}
@Data
@Accessors(chain = true)
public class DeepProtoType implements Serializable,Cloneable {String name; //String 屬性DeepCloneTarget deepCloneTarget; //引用類型public DeepProtoType() {super();}//深拷貝 方式1 使用clone方法@Overrideprotected Object clone() throws CloneNotSupportedException {Object deep = null;//這里完成對基本數(shù)據(jù)類型(屬性)和String的克隆deep = super.clone();//對引用類型的屬性 進行單獨處理DeepProtoType deepProtoType =(DeepProtoType) deep;deepProtoType.deepCloneTarget = (DeepCloneTarget) deepCloneTarget.clone();return deepProtoType;}
}
方式二:
@Data
@Accessors(chain = true)
public class DeepProtoType implements Serializable,Cloneable {String name; //String 屬性DeepCloneTarget deepCloneTarget; //引用類型public DeepProtoType() {super();}//深拷貝 方式1 使用clone方法@Overrideprotected Object clone() throws CloneNotSupportedException {Object deep = null;//這里完成對基本數(shù)據(jù)類型(屬性)和String的克隆deep = super.clone();//對引用類型的屬性 進行單獨處理DeepProtoType deepProtoType =(DeepProtoType) deep;deepProtoType.deepCloneTarget = (DeepCloneTarget) deepCloneTarget.clone();return deepProtoType;}//深拷貝 方式2 通過對象序列化實現(xiàn)(推薦)public Object deepClone(){//創(chuàng)建流對象ByteArrayOutputStream bos = null;ObjectOutputStream oos = null;ByteArrayInputStream bis = null;ObjectInputStream ois = null;try{//序列化bos = new ByteArrayOutputStream();oos = new ObjectOutputStream(bos);oos.writeObject(this); //當前這個對象以對象流的方式輸出//反序列化bis = new ByteArrayInputStream(bos.toByteArray());ois = new ObjectInputStream(bis);DeepProtoType copyObj = (DeepProtoType) ois.readObject();return copyObj;}catch (Exception e){e.printStackTrace();return null;}finally {//關(guān)閉流try {bos.close();oos.close();ois.close();bis.close();}catch (Exception ee){System.out.println(ee.getMessage());}}}
}
測試
原型模式的注意事項和細節(jié)
(1) 創(chuàng)建新的對象比較復(fù)雜時 可以利用原型模式簡化對象的創(chuàng)建過程 同時也能夠提高效率
(2) 不用初始化對象 而是動態(tài)地獲得對象運行時的狀態(tài)
(3) 如果原始對象發(fā)生變化(增加或者減少屬性) 其他克隆對象的也會發(fā)生相應(yīng)的變化 無需修改代碼
(4) 在實現(xiàn)深克隆的時候可能需要比較復(fù)雜的代碼
(5) 缺點: 需要為每一個類配備一個克隆方法 這對全新的類來說不是很難 但對已有的類進行改造時 需要修改其源碼 違背了ocp原則