1、原型模式的使用和本质、以及优势:
a、通过 new 产生一个对象需要非常繁琐的数据准备或者访问权限,则可以使用原型模式。
b、原型模式的使用就是 java 中的克隆技术,以某个对象为原型,复制出新的对象。显然,新的对象具备原型对象的特点。
c、其优势有:效率高(直接克隆,避免了重新执行构造过程步骤)。
d、克隆类似于 new, 但是不同于 new 。new 创建新的对象属性采用的是默认值。克隆出的对象的属性值完全和原型对象相同。并且克隆出来的新对象改变不会影响原型对象。(可以对克隆对象修改属性值)
2、原型模式的实现:
- Clone 接口 和 clone 方法
- 简单的代码演示:
a、可以看到,在 Sheep 这个类中实现了 Cloneable 接口,并且覆盖了 父类 Object 的 clone() 方法。(这样就实现了浅克隆)
package com.geeklicreed.prototype; import java.util.Date; public class Sheep implements Cloneable{ private Date birthday; private String name; public Date getBirthday(){ return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override protected Object clone() throws CloneNotSupportedException { Object obj = super.clone(); return obj; } public Sheep(Date birthday, String name) { super(); this.birthday = birthday; this.name = name; } public Sheep() { // TODO Auto-generated constructor stub } }
b、在 Client06 测试类中可以看到,克隆出来的 Sheep 对象和原来的 Sheep 对象是两个不同的对象,但是它们两的属性值相同。
package com.geeklicreed.prototype; import java.util.Date; public class Client { public static void main(String[] args) throws Exception { Date date = new Date(1320948723000L); Sheep s1 = new Sheep(date,"多利"); Sheep s2 = (Sheep)s1.clone(); System.out.println(s1); System.out.println(s1.getName()); System.out.println(s2); System.out.println(s2.getName()); /*输出结果为: com.geeklicreed.prototype.Sheep@2a139a55 多利 com.geeklicreed.prototype.Sheep@15db9742 多利*/ } }
c、上面提到了浅克隆的问题,现在看看在 Client06 测试类修改一些代码后的输出结果。
package com.geeklicreed.prototype; import java.util.Date; public class Client { public static void main(String[] args) throws Exception { Date date = new Date(1320928712391L); Sheep s1 = new Sheep(date, "多利"); Sheep s2 = (Sheep)s1.clone(); System.out.println(s1.getBirthday()); date.setTime(3302428929382983L); System.out.println(s1.getBirthday()); System.out.println(s2.getBirthday()); /*输出结果为: Thu Nov 10 20:38:32 CST 2011 Sat Oct 23 09:16:22 CST 106619 Sat Oct 23 09:16:22 CST 106619*/ } }
- 注:可以看到,在测试结果中, Sheep 类的 对象 s1 和 s2 是共同使用 Date 对象,所以当 Date 对象的值发生修改时,它们两个的 brithday 属性值也就发生修改。(浅克隆只对对象进行克隆,深克隆是将对象的属性值也进行克隆操作。)
d、将 Sheep 类中的 clone 方法进行如下修改,就可以实现深克隆。
@Override protected Object clone() throws CloneNotSupportedException { Object obj = super.clone(); Sheep sheep = (Sheep) obj; sheep.birthday = (Date) this.birthday.clone();//即将对象的属性也复制一遍 return obj; }
package com.geeklicreed.prototype; import java.util.Date; public class Client { public static void main(String[] args) throws Exception { Date date = new Date(1320928712391L); Sheep s1 = new Sheep(date, "多利"); Sheep s2 = (Sheep)s1.clone(); System.out.println(s1.getBirthday()); date.setTime(3302428929382983L); System.out.println(s1.getBirthday()); System.out.println(s2.getBirthday()); /*输出结果为: Thu Nov 10 20:38:32 CST 2011 Sat Oct 23 09:16:22 CST 106619 Thu Nov 10 20:38:32 CST 2011*/ } }
3、使用序列化和反序列化实现深克隆:
package com.geeklicreed.prototype; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Date; public class Client { public static void main(String[] args) throws Exception { Date date = new Date(1320928712391L); Sheep s1 = new Sheep(date, "多利"); //使用序列化和反序列化实现深克隆 ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(s1); byte[] by = bos.toByteArray(); ByteArrayInputStream bis = new ByteArrayInputStream(by); ObjectInputStream ois = new ObjectInputStream(bis); Sheep s2 = (Sheep) ois.readObject(); date.setTime(222324131028374983L); System.out.println(s1.getBirthday()); System.out.println(s2.getBirthday()); /* 输出结果为: Thu Apr 03 07:26:14 CST 7047141 Thu Nov 10 20:38:32 CST 2011 */ } }
- 注:序列化和反序列化需要在在 Sheep 类中 实现 Serializable 接口。
4、开发中的应用场景:
- 原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。
- spring 中 bean 的创建实际上就是两种:单例模式和原型模式。(原型模式一般会和工厂模式搭配使用。)