java设计模式之原型模式
原型模式概念
该模式的思想就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。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咯
深拷贝和浅拷贝建议不要混合使用,一个类中某些引用使用深拷贝某些引用使用浅拷贝,这是一种非常差的设计,特别是是在涉及到类的继承,父类有几个引用的情况就非常的复杂,建议深拷贝和浅拷贝分开实现。