薄悻不来门半掩,斜阳。负你残春泪几行。
——冯延巳《南乡子》
设计模式-原型模式-深/浅克隆 1. 原型模式在 Spring 框架中应用 1.1 实体类Student 1 2 3 4 5 6 7 8 9 10 11 @Data @AllArgsConstructor @NoArgsConstructor public class Student { private String name; private String sex; private String address; }
1.2 配置文件applicationContext.xml 1 2 3 4 5 6 7 8 9 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:p ="http://www.springframework.org/schema/p" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="student" class ="cn.justweb.pojo.Student" p:name ="涛哥" p:sex ="nan" p:address ="南京" scope ="prototype" > </bean > </beans >
以及spring的依赖。
1.3 测试 1 2 3 4 5 6 7 8 9 10 11 12 public class TestStudent { @Test public void test () { ApplicationContext ctx = new ClassPathXmlApplicationContext ( "applicationContext.xml" ); Student student = ctx.getBean( "student" , Student.class ); System.out.println( "student = " + student ); } }
1.4 通过debug调试追踪getBean()方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 @Override public <T> T getBean (String name, Class<T> requiredType) throws BeansException { assertBeanFactoryActive(); return getBeanFactory().getBean(name, requiredType); } @Override public final ConfigurableListableBeanFactory getBeanFactory () { synchronized (this .beanFactoryMonitor) { if (this .beanFactory == null ) { throw new IllegalStateException ("BeanFactory not initialized or already closed - " + "call 'refresh' before accessing beans via the ApplicationContext" ); } return this .beanFactory; } } @Override public <T> T getBean (String name, Class<T> requiredType) throws BeansException { return doGetBean(name, requiredType, null , false ); } @SuppressWarnings("unchecked") protected <T> T doGetBean ( final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException { if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, new ObjectFactory <Object>() { @Override public Object getObject () throws BeansException { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { destroySingleton(beanName); throw ex; } } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } else if (mbd.isPrototype()) { Object prototypeInstance = null ; try { beforePrototypeCreation(beanName); prototypeInstance = createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } else { String scopeName = mbd.getScope(); final Scope scope = this .scopes.get(scopeName); if (scope == null ) { throw new IllegalStateException ("No Scope registered for scope '" + scopeName + "'" ); } return (T) bean; }
2. 原型模式
原型模式(Prototype 模式)
是指:用 原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象。
原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节。
工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即 对象.clone()
。
Sheep实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 public class Sheep implements Cloneable { private String name; private Integer age; private String color; public Sheep () { } public Sheep (String name, Integer age, String color) { this .name = name; this .age = age; this .color = color; } public String getName () { return name; } public void setName (String name) { this .name = name; } public Integer getAge () { return age; } public void setAge (Integer age) { this .age = age; } public String getColor () { return color; } public void setColor (String color) { this .color = color; } @Override public String toString () { return "Sheep{" + "name='" + name + '\'' + ", age=" + age + ", color='" + color + '\'' + '}' ; } }
2.1 传统方式解决克隆羊问题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class Client { public static void main (String[] args) { Sheep sheep = new Sheep ("MM" , 3 , "yellow" ); Sheep sheep1 = new Sheep (sheep.getName(), sheep.getAge(), sheep.getColor()); Sheep sheep2 = new Sheep (sheep.getName(), sheep.getAge(), sheep.getColor()); Sheep sheep3 = new Sheep (sheep.getName(), sheep.getAge(), sheep.getColor()); System.out.println("sheep.hashCode() = " + sheep.hashCode()); System.out.println("sheep1.hashCode() = " + sheep1.hashCode()); System.out.println("sheep2.hashCode() = " + sheep2.hashCode()); System.out.println("sheep3.hashCode() = " + sheep3.hashCode()); } }
2.2 原型模式解决克隆羊问题
Java 中 Object 类 是所有类的根类,Object 类提供了一个 clone()
方法,该方法可以将一个 Java 对象复制 一份,但是需要实现 clone的Java类必须要实现一个接口Cloneable
,该接口表示该类能够复制且具有复制的能力 — 原型模式
第一步:Sheep实体类实现Cloneable接口 第二步:Sheep中重写clone()方法 1 2 3 4 5 6 7 8 9 10 11 12 13 @Override protected Object clone () { Sheep sheep = null ; try { sheep = (Sheep) super .clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return sheep; }
第三步:使用clone()方法克隆Sheep对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class Client { public static void main (String[] args) { Sheep sheep = new Sheep ("MM" , 3 , "yellow" ); Sheep sheep4 = (Sheep)sheep.clone(); Sheep sheep5 = (Sheep)sheep.clone(); System.out.println("sheep4 = " + sheep4); System.out.println("sheep5 = " + sheep5); System.out.println("sheep4.hashCode() = " + sheep4.hashCode()); System.out.println("sheep5.hashCode() = " + sheep5.hashCode()); } }
3. 深拷贝和浅拷贝 3.1 浅拷贝
对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。
对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等
,那么浅拷贝会进行
引用传递
,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成 员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。
前面我们克隆羊就是浅拷贝。
浅拷贝是使用默认的 clone()方法来实现 sheep = (Sheep) super.clone();
3.2 深拷贝
复制对象的所有基本数据类型的成员变量值
为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说, 对象进行深拷贝要对整个对象( 包括对象的引用类型) 进行拷贝。
深拷贝实现方式:
重写 clone 方法来实现深拷贝
通过对象序列化实现深拷贝(推荐)
准备工作
测试深拷贝,需要让DeepCloneTarget类作为DeepProtoType类的属性。
DeepProtoType类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class DeepProtoType implements Serializable , Cloneable{ public String name; public DeepCloneTarget deepCloneTarget; public DeepProtoType () { super (); } @Override public String toString () { return "DeepProtoType{" + "name='" + name + '\'' + ", deepCloneTarget=" + deepCloneTarget + '}' ; } }
DeepProtoType类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class DeepCloneTarget implements Serializable , Cloneable { private String cloneName; private Integer cloneAge; public DeepCloneTarget (String cloneName, Integer cloneAge) { this .cloneName = cloneName; this .cloneAge = cloneAge; } @Override protected Object clone () throws CloneNotSupportedException { return super .clone(); } @Override public String toString () { return "DeepCloneTarget{" + "cloneName='" + cloneName + '\'' + ", cloneAge=" + cloneAge + '}' ; } }
方案一:重写 clone 方法实现深拷贝
在DeepProtoType类中重写clone()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Override protected Object clone () throws CloneNotSupportedException { Object deep = null ; deep = super .clone(); DeepProtoType deepProtoType = (DeepProtoType) deep; deepProtoType.deepCloneTarget = (DeepCloneTarget) deepCloneTarget.clone(); return deepProtoType; }
测试结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class client { public static void main (String[] args) throws CloneNotSupportedException { DeepProtoType dpt = new DeepProtoType (); dpt.name = "MM" ; dpt.deepCloneTarget = new DeepCloneTarget ("DD" ,17 ); System.out.println("dpt = " + dpt); System.out.println("dpt.deepCloneTarget.hashCode() = " + dpt.deepCloneTarget.hashCode()); DeepProtoType dpt2 = (DeepProtoType)dpt.clone(); System.out.println("dpt2 = " + dpt2); System.out.println("dpt2.deepCloneTarget.hashCode() = " + dpt2.deepCloneTarget.hashCode()); } }
方案二:对象序列化实现深拷贝(推荐) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public Object deepClone () { ByteArrayInputStream bis = null ; ObjectInputStream ois = null ; ByteArrayOutputStream bos = null ; ObjectOutputStream oos = null ; try { bos = new ByteArrayOutputStream (); oos = new ObjectOutputStream (bos); oos.writeObject(this ); bis = new ByteArrayInputStream (bos.toByteArray()); ois = new ObjectInputStream (bis); DeepProtoType copydeepProtoType = (DeepProtoType)ois.readObject(); return copydeepProtoType; } catch (Exception e1) { e1.printStackTrace(); return null ; }finally { try { bos.close(); oos.close(); bis.close(); ois.close(); } catch (Exception e2) { e2.printStackTrace(); } } }
测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public class client { public static void main (String[] args) throws CloneNotSupportedException { DeepProtoType dpt = new DeepProtoType (); dpt.name = "MM" ; dpt.deepCloneTarget = new DeepCloneTarget ("DD" ,17 ); System.out.println("dpt = " + dpt); System.out.println("dpt.deepCloneTarget.hashCode() = " + dpt.deepCloneTarget.hashCode()); DeepProtoType dpt2 = (DeepProtoType)dpt.clone(); System.out.println("dpt2 = " + dpt2); System.out.println("dpt2.deepCloneTarget.hashCode() = " + dpt2.deepCloneTarget.hashCode()); DeepProtoType dpt3 = (DeepProtoType) dpt.deepClone(); System.out.println("dpt.name=" + dpt.name + " dpt.deepCloneableTarget=" + dpt.deepCloneTarget.hashCode()); System.out.println("dpt3.name=" + dpt3.name + " dpt3.deepCloneableTarget=" + dpt3.deepCloneTarget.hashCode()); } }
4. 小结
创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率。
不用重新初始化对象,而是动态地获得对象运行时的状态
如果原始对象发生变化(增加或者减少属性--这个指的是对被克隆的对象,如果修改被克隆对象里面的对象属性则需要通过深克隆才能克隆其对象属性)
,其它克隆对象的也会发生相应的变化,无需修改代码。
在实现深克隆的时候可能需要比较复杂的代码,实现深克隆的缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了 ocp 原则。