原型模式
1.经典案例
- 克隆羊多利。有姓名,身高,体重,年龄等属性。可以用来当作模板,克隆10只属性完全相同的羊。
- 三好学生奖状。有姓名,学校,获奖时间等属性。奖状可以当作模板,打印多份,使用时只需要修改姓名,学校,获奖时间。
常规解法
1.定义sheep实体类,生成构造方法、getter和setter方法、toString方法
2.实例化10个对象
缺点:
效率低,每次创建对象都需要获取原始对象的属性,不能动态获取对象运行时的状态。
新思路-原型模式
2.原型模式定义
【创建型】设计模式。允许另外一个对象再创建另外一个可定制的对象
用原型实例指定创建的对象,并通过拷贝原型,创建新对象,同时保证性能
3.工作原理
- 原型类声明克隆自己的接口
- Client类让具体的原型类对象克隆自己,创建一个新的对象
- Prototype:声明一个克隆自己的接口
- client:客户端让一个原型对象克隆自己
- ConcretePrototype:实现一个克隆自己的操作
4.原型模式分类
- 浅拷贝
代码思路:
在实体类的基础上实现Cloneable接口,重写clone方法
Demo中调用克隆对象
代码演示:
🐏的实体类
package com.henji.clone; /** * Sheep类基础上实现Cloneable接口,重写clone方法。 * * @author henji * @date 2023/3/6 19:08 */ public class Sheep implements Cloneable { private String name; private int age; @Override protected Object clone () throws CloneNotSupportedException { Sheep sheep = null; sheep = (Sheep) super.clone (); return sheep; } /*******省略构造方法、setter、getter、toString方法*******/ }
🐏的实现类
package com.henji.clone; /** * @author henji * @date 2023/3/6 20:06 */ public class Demo { public static void main (String[] args) throws CloneNotSupportedException { Sheep sheep = new Sheep ("喜羊羊", 2); Object c1 = sheep.clone (); Object c2 = sheep.clone (); Object c3 = sheep.clone (); System.out.println (c1 + "===" + c1.hashCode ()); System.out.println (c2 + "===" + c2.hashCode ()); System.out.println (c3 + "===" + c3.hashCode ()); } }
运行结果
总结:
- 三个对象的哈希值不同,说明拷贝进行是值传递,而不是引用传递
继续往下看:
此时慢羊羊也来凑热闹了,再新建一个🐏的实体类,并再Sheep类添加一个引用属性。再拷贝一遍
新建的慢羊羊实体类
package com.henji.clone; /** * 慢羊羊的实体类 * * @author henji * @date 2023/3/6 19:08 */ public class Sheep2 { private String name; private int age; /*******省略构造方法、setter、getter、toString方法*******/ }
添加了慢羊羊的引用属性的🐏实体类
package com.henji.clone; /** * Sheep类基础上实现Cloneable接口,重写clone方法。 * * @author henji * @date 2023/3/6 19:08 */ public class Sheep implements Cloneable { private String name; private int age; public Sheep2 sheep2;//引用慢羊羊 @Override protected Object clone () throws CloneNotSupportedException { Sheep sheep = null; sheep = (Sheep) super.clone (); return sheep; } /*******省略构造方法、setter、getter、toString方法*******/ }
实例化慢羊羊
package com.henji.clone; /** * @author henji * @date 2023/3/6 20:06 */ public class Demo { public static void main (String[] args) throws CloneNotSupportedException { Sheep sheep = new Sheep ("喜羊羊", 2); sheep.sheep2 = new Sheep2 ("慢羊羊", 8); Sheep c1 = (Sheep) sheep.clone (); Sheep c2 = (Sheep) sheep.clone (); Sheep c3 = (Sheep) sheep.clone (); System.out.println (c1 + "===" + c1.hashCode () + ":::" + c1.sheep2 + "===" + c1.sheep2.hashCode ()); System.out.println (c2 + "===" + c2.hashCode () + ":::" + c2.sheep2 + "===" + c2.sheep2.hashCode ()); System.out.println (c3 + "===" + c3.hashCode () + ":::" + c3.sheep2 + "===" + c3.sheep2.hashCode ()); } }
运行结果
总结:
- 拷贝的三个喜羊羊对象哈希地址各不相同,依旧发生了值传递,但三个慢羊羊的地址完全相同,没有发生拷贝动作,地址指向克隆前的同一个对象。(如何让慢羊羊的拷贝对象也发生值传递?)
特征:
- 浅拷贝使用默认的clone()方法实现
- 基本数据类型的成员变量,浅拷贝会直接进行值传递
- 引用数据类型的成员变量,浅拷贝会进行引用传递
- 深拷贝
代码思路:
慢羊羊的实体类也实现Cloneable接口
实体类实现序列化接口,不实现Cloneable接口
实现Cloneable接口,重写clone
/** * 慢羊羊的实体类 * * @author henji * @date 2023/3/6 19:08 */ public class Sheep2 implements Cloneable { private String name; private int age; /* 使用默认clone方法 */ @Override protected Object clone () throws CloneNotSupportedException { return super.clone (); } }
在喜羊羊的实体类clone方法中调用慢羊羊的clone方法
package com.henji.clone; /** * Sheep类基础上实现Cloneable接口,重写clone方法。 * * @author henji * @date 2023/3/6 19:08 */ public class Sheep implements Cloneable { private String name; private int age; public Sheep2 sheep2; @Override protected Object clone () throws CloneNotSupportedException { Object deep = null; //对基本数据类型拷贝 deep = super.clone (); //对引用数据类型拷贝 Sheep sheep = (Sheep) deep; sheep.sheep2 = (Sheep2) sheep2.clone (); return sheep; } }
再次运行Demo类
运行结果:
总结:
- 两个类(基本数据类型,引用数据类型)的拷贝,都实现了值传递,达成了深拷贝。
特征:
- 基本数据类型和引用数据类型,深拷贝都会对其进行值传递【复制】
通过序列化实现深拷贝
慢羊羊的实体类实现序列化接口,不需要再实现Cloneable接口
package com.henji.clone; import java.io.Serializable; /** * 慢羊羊的实体类 * * @author henji * @date 2023/3/6 19:08 */ public class Sheep2 implements Serializable { private String name; private int age; }
喜羊羊的实体类也实现序列化接口,并使用流
package com.henji.clone; import java.io.*; /** * Sheep类基础上实现Cloneable接口,重写clone方法。 * * @author henji * @date 2023/3/6 19:08 */ public class Sheep implements Serializable { private String name; private int age; public Sheep2 sheep2; /** * 深拷贝 * * @return */ Object deepClone () { //创建流对象 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); Sheep sheep = (Sheep) ois.readObject (); return sheep; } catch (IOException e) { throw new RuntimeException (e); } catch (ClassNotFoundException e) { throw new RuntimeException (e); } finally { try { bos.close (); oos.close (); bis.close (); ois.close (); } catch (IOException e) { throw new RuntimeException (e); } } } /*******省略构造方法、setter、getter、toString方法*******/
客户端调用
package com.henji.clone; /** * @author henji * @date 2023/3/6 20:06 */ public class Demo { public static void main (String[] args) throws CloneNotSupportedException { Sheep sheep = new Sheep ("喜羊羊", 2); sheep.sheep2 = new Sheep2 ("慢羊羊", 8); Sheep c1 = (Sheep) sheep.deepClone (); Sheep c2 = (Sheep) sheep.deepClone (); Sheep c3 = (Sheep) sheep.deepClone (); System.out.println (c1 + "===" + c1.hashCode () + ":::" + c1.sheep2 + "===" + c1.sheep2.hashCode ()); System.out.println (c2 + "===" + c2.hashCode () + ":::" + c2.sheep2 + "===" + c2.sheep2.hashCode ()); System.out.println (c3 + "===" + c3.hashCode () + ":::" + c3.sheep2 + "===" + c3.sheep2.hashCode ()); } }
- 深拷贝和浅拷贝的区别
在于对引用数据类型的成员变量的拷贝
- 浅拷贝对引用数据类型进行引用传递
- 深拷贝对引用数据类型进行值传递
5.特点:
优点
- 动态获取对象运行时的状态,不需要重新初始化对象
- 原始对象发生增加或者减少属性,其他克隆对象也会发生变化,无须修改代码
- 创建的新对象比较复杂时,可以使用原型模式简化对象创建过程,提高效率
缺点
- 需要为每一个类重写一个克隆方法
- 对已存在的类进行改造需要修改源码,违背了开闭原则
- 对象存在多层引用,每一层对象都必须支持深拷贝,并且需要编写复杂的代码