原型模式概念
該模式的思想就是將一個對象作為原型,對其進行復制、克隆,產生一個和原對象類似的新對象。java中復制通過clone()實現的。clone中涉及深、淺復制。深、淺復制的概念如下:
⑴淺復制(淺克隆)
被復制對象的所有變量都含有與原來的對象相同的值,而所有的對其他對象的引用仍然指向原來的對象。換言之,淺復制僅僅復制所考慮的對象,而不復制它所引用的對象。 Object類提供的方法clone只是拷貝本對象,其對象內部的數組、引用對象等都不拷貝,還是指向原生對象的內部元素地址
⑵深復制(深克隆)
被復制對象的所有變量都含有與原來的對象相同的值,除去那些引用其他對象的變量。那些引用其他對象的變量將指向被復制過的新對象,而不再是原有的那些被引用的對象。換言之,深復制把要復制的對象所引用的對象都復制了一遍。
原型模式代碼
package com.roc.prototype; import java.io.Serializable; /** * 原型模式 程序員 * @author liaowp * */ public class Programmer implements Serializable,Cloneable{ /** * */ private static final long serialVersionUID = 3078949912404836178L; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public Programmer clone() throws CloneNotSupportedException { Programmer proto = (Programmer) super.clone(); return proto; } }
下面寫一個淺復制的例子
package com.roc.prototype; /** * 地址 * @author liaowp * */ public class Address{ private String province;//省 private String city;//市 public Address(String province,String city){ this.province=province; this.city=city; } public String getProvince() { return province; } public void setProvince(String province) { this.province = province; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } }
package com.roc.prototype; /** * 原型模式 程序員 * @author liaowp * */ public class Programmer implements Cloneable{ private String name;//名字 private Address address; public Programmer(String name,Address address){ this.name=name; this.address=address; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Object clone() throws CloneNotSupportedException { Programmer proto = (Programmer) super.clone(); return proto; } }
package com.roc.prototype; /** * 原型模式 * @author liaowp * */ public class Client { public static void main(String[] args) throws CloneNotSupportedException { //淺復制復制 Address address=new Address("jx","gz"); Programmer a=new Programmer("liaowp",address); a.setAddress(new Address("jx", "gz")); a.setName("liaowp"); Programmer b=(Programmer) a.clone(); b.setName("pwl"); b.getAddress().setProvince("bj"); System.err.println(b.getName()+b.getAddress().getProvince()); System.err.println(a.getName()+a.getAddress().getProvince()); } }
輸出結果:pwlbj
liaowpbj
可以看出來對象並復制,依然使用的是同一個引用。其對象內部的數組、引用對象等都不拷貝,還是指向原生對象的內部元素地址。下面看深復制的寫法,深復制有2種寫法,一種是對象實現Cloneable,另外一種是二進制流。我都一起寫了。
package com.roc.prototype; /** * 原型模式 程序員 * @author liaowp * */ public class Programmer implements Cloneable{ private String name;//名字 private Address address; public Programmer(String name,Address address){ this.name=name; this.address=address; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Object clone() throws CloneNotSupportedException { Programmer proto = (Programmer) super.clone(); proto.address=(Address) address.clone();
return proto; } }
package com.roc.prototype; /** * 地址 * @author liaowp * */ public class Address implements Cloneable{ private String province;//省 private String city;//市 public Object clone(){ Address address = null; try { address = (Address) super.clone(); } catch (CloneNotSupportedException e) { // TODO Auto-generated catch block e.printStackTrace(); } return address; }
public Address(String province,String city){ this.province=province; this.city=city; } public String getProvince() { return province; } public void setProvince(String province) { this.province = province; } public String getCity() { return city; } public void setCity(String city) { this.city = city; }
/* 深復制 */ 二進制的寫法,需要類序列化
public Object deepClone() throws IOException, ClassNotFoundException {
/* 寫入當前對象的二進制流 */
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
/* 讀出二進制流產生的新對象 */
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
}
package com.roc.prototype; /** * 原型模式 * @author liaowp * */ public class Client { public static void main(String[] args) throws CloneNotSupportedException { //淺復制復制 Address address=new Address("jx","gz"); Programmer a=new Programmer("liaowp",address); a.setAddress(new Address("jx", "gz")); a.setName("liaowp"); Programmer b=(Programmer) a.clone(); b.setName("pwl"); b.getAddress().setProvince("bj"); System.err.println(b.getName()+b.getAddress().getProvince()); System.err.println(a.getName()+a.getAddress().getProvince()); }
}
結果:pwlbj
liaowpjx
拷貝還有2個知識點,對象拷貝時,類的構造函數是不會被執行的。一個實現了 Cloneable 並重寫了 clone 方法的類 Programmer,有一個無參構造或有參構造 ,通過 new 關鍵字產生了一個對象 A,再然后通過 A.clone()方式產生了一個新的對象 T,那么在對象拷貝時構造函數是不會被執行的。即拷貝的過程中只執行一次構造方法。
Clone 與 final 兩對冤家。對象的 clone 與對象內的 final 屬性是由沖突.在上面的Programmer類中修改為private final Address address;去掉get,set方法, proto.address=(Address) address.clone();這一句就會報錯: proto.address=(Address) address.clone();final類型不能重新設置值。解決辦法就是刪除掉fina咯
深拷貝和淺拷貝建議不要混合使用,一個類中某些引用使用深拷貝某些引用使用淺拷貝,這是一種非常差的設計,特別是是在涉及到類的繼承,父類有幾個引用的情況就非常的復雜,建議深拷貝和淺拷貝分開實現。