原型模式
原型模式
是利用克隆方法克隆出新的对象.
- 定义:原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
- 特点:不需要知道任何创建的细节,不调用构造函数
- 适用场景:
- 类初始化消耗较多资源
- new产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)
- 构造函数比较复杂
- 循环体中产生大量对象时
- 优点:
- 原型模式性能比直接new一个对象性能高
- 简化创建过程
- 缺点:
- 必须配备克隆方法,这个模式的核心就是克隆方法,通过这个方法进行对象的拷贝
- 对克隆复杂对象或对克隆出的对象进行复杂改造时,容易引入风险
- 深拷贝和浅拷贝要运用得当,否则很容易引入风险
浅拷贝
拷贝对象中嵌套的对象是引用,指向同一个内存地址,而非独立内存空间。
浅拷贝得到新的对象A+,A+的内存地址是独立的,但其内部嵌套对象B却不是独立的内存,其仍然指向先前A对象中的B对象内存地址。改变A+的成员变量不会影响原有的A对象,但改变A+中的B对象成员变量却会影响原有A对象的B对象成员变量。
实现方法
实现Cloneable接口,重写基类Object的clone方法。
注意:
clone是一个native方法,其要比对象之间的直接引用性能要高。
native关键字说明其修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在用其他语言(C/C++)实现的文件中。Java本身不能对操作系统底层进行访问和操作,但可以通过JNI接口调用其他语言来实现对底层的访问。
深拷贝
即对象完全克隆;不管A对象中包含了多少个对象,包含的对象又嵌套多少个对象,都完全将其进行复制。复制后新对象A+,不管对A+及其内部对象做了什么操作,都不会改变原有对象A。
实现方法
实现Serializable接口(包括嵌套对象),自定义克隆方法,利用对象的字节流进行序列化和反序列化得到新的对象。
代码
ESexType
/** * <p>性别枚举类</p> */ public enum ESexType { /** * 枚举对象 */ MALE("男",0), FEMALE("女",1); private final String name ; private final Integer value; /** * 枚举构造器 * @param name 性别名称 * @param value 性别值 */ ESexType(String name, Integer value){ this.name = name; this.value = value; } /** * 通过name获取对应的枚举对象 * @param name 类型名称 */ public static ESexType getEnum(String name){ for(ESexType sexEnum : ESexType.values()){ if(name.equals(sexEnum.getName())){ return sexEnum; } } return null; } /** * 通过value获取对应的枚举对象 * @param value 类型值 */ @JsonCreator public static ESexType getEnum(Integer value){ for(ESexType sexEnum : ESexType.values()){ if(value.equals(sexEnum.getValue())){ return sexEnum; } } return null; } public String getName() { return name; } @JsonValue public Integer getValue() { return value; } }
EWorkType
/** * <p>作业枚举类</p> */ public enum EWorkType { YU_WEN("语文",0), SHU_XUE("数学",1), YING_YU("英语",2), WU_LI("物理",3), HUA_XUE("化学",4), SHENG_WU("生物",5); private final String name ; private final Integer value; /** * 枚举构造器 * @param name 作业类型名称 * @param value 作业类型值 */ EWorkType(String name, Integer value){ this.name = name; this.value = value; } /** * 通过name获取对应的枚举对象 * @param name 类型名称 */ public static EWorkType getEnum(String name){ for(EWorkType typeEnum : EWorkType.values()){ if(name.equals(typeEnum.getName())){ return typeEnum; } } return null; } /** * 通过value获取对应的枚举对象 * @param value 类型值 */ @JsonCreator public static EWorkType getEnum(Integer value){ for(EWorkType typeEnum : EWorkType.values()){ if(value.equals(typeEnum.getValue())){ return typeEnum; } } return null; } public String getName() { return name; } @JsonValue public Integer getValue() { return value; } }
PupilStudent
/** * <p>小学生 == 如果要实现对象的深度拷贝,嵌套引用类型的对象类必须也实现Serializable接口</p> */ @Data public class PupilStudent implements Serializable { private static final long serialVersionUID = 1L; /**学号*/ private Long sNo; /**年级*/ private Integer sClass; /**姓名*/ private String name; /**年龄*/ private Integer age; /**性别*/ private ESexType sex = ESexType.MALE; }
HomeWork
/** * <p>小学生的家庭作业</p> */ @Data public class HomeWork implements Cloneable, Serializable { private static final long serialVersionUID = 1L; /** * 作业类型 */ private EWorkType type = EWorkType.YU_WEN; /** * 完成时间 */ @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") private Date finishTime; /** * 作业页码数【作业量】 */ private Integer pages = 0; /** * 完成者 */ private PupilStudent pupilStudent; /** * 对象浅拷贝 == 对象中按值类型传递部分均能完美拷贝走,但是按引用类型传递部分则拷贝不走 * * @return HomeWork * @throws CloneNotSupportedException */ @Override public HomeWork clone() throws CloneNotSupportedException { return (HomeWork) super.clone(); } /** * 深度拷贝 == 不管你对象中是值类型部分,还是引用类型部分,我全部拿走 * 对象字节流的序列与反序列化 ==> 对象完全、深度、彻彻底底的Copy!!! * * @return HomeWork */ public HomeWork deepClone() { // Anything 都是可以用字节流进行表示! HomeWork homeWork = null; try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); // 将当前的对象写入baos【输出流 -- 字节数组】里 oos.writeObject(this); // 从输出字节数组缓存区中拿到字节流 byte[] bytes = baos.toByteArray(); // 创建一个输入字节数组缓冲区 ByteArrayInputStream bais = new ByteArrayInputStream(bytes); // 创建一个对象输入流 ObjectInputStream ois = new ObjectInputStream(bais); // 下面将反序列化字节流 == 重新开辟一块空间存放反序列化后的对象 homeWork = (HomeWork) ois.readObject(); } catch (Exception e) { System.out.println(e.getClass() + ":" + e.getMessage()); } return homeWork; } @Override public String toString() { return String.format("类型:%s,页数:%s,完成时间:%s," + "完成者:%s,学号:%d,年级:%d,年龄:%d,性别:%s", this.type.getName() , this.pages, DateUtils.date2Str(this.finishTime), this.pupilStudent.getName(), this.pupilStudent.getsNo(), this.pupilStudent.getsClass(), this.pupilStudent.getAge(), this.pupilStudent.getSex().getName()); } }
测试
/** * <p>原型模式测试 == 两种方式?</p> */ public class PrototypeTest { public static void main(String[] args) throws CloneNotSupportedException{ // 原型 == 我们创建一个已经由小学生【刘晓然】完成的作业对象 HomeWork homeWork = new HomeWork(); // 设置作业信息 homeWork.setType(EWorkType.WU_LI); homeWork.setPages(12); homeWork.setFinishTime(new Date()); // 设置小学生信息 == 刘晓然 PupilStudent pupilStudent = new PupilStudent(); pupilStudent.setsNo(1001L); pupilStudent.setName("刘晓然"); pupilStudent.setAge(10); pupilStudent.setSex(ESexType.FEMALE); pupilStudent.setsClass(4); homeWork.setPupilStudent(pupilStudent); // 1、原型模式第一种 == 作业对象浅拷贝测试 HomeWork ykHomeWork = shallowCopy(homeWork); System.out.println("刘晓然的作业:\n"+homeWork); System.out.println("我的作业:\n"+ykHomeWork); /** * 以上输出结果为: * 刘晓然的作业: * 类型:物理,页数:12,完成时间:2018年11月08日 14时03分45秒,完成者:Appleyk,学号:1002,年级:4,年龄:10,性别:男 * 我的作业: * 类型:物理,页数:12,完成时间:2018年11月09日 14时03分45秒,完成者:Appleyk,学号:1002,年级:4,年龄:10,性别:男 */ // 2、原型模式第二种 == 作业对象深拷贝测试 System.out.println("=========================================分割线"); /** * 假设有第三个同学,要抄作业,这个同学假设叫"张聪明" */ HomeWork zhangHomeWork = deepCopy(ykHomeWork); System.out.println("Appleyk的作业:\n"+ykHomeWork); System.out.println("张聪明的作业:\n"+zhangHomeWork); /** * 以上输出结果为: * Appleyk的作业: * 类型:物理,页数:12,完成时间:2018年11月09日 14时16分11秒,完成者:Appleyk,学号:1002,年级:4,年龄:10,性别:男 * 张聪明的作业: * 类型:物理,页数:12,完成时间:2018年11月09日 14时16分11秒,完成者:张聪明,学号:1003,年级:4,年龄:10,性别:男 */ } /** * 对象浅拷贝 * @param homeWork * @return * @throws CloneNotSupportedException */ public static HomeWork shallowCopy(HomeWork homeWork) throws CloneNotSupportedException{ HomeWork myHomeWork = homeWork.clone(); myHomeWork.setFinishTime(DateUtils.addDays(1)); PupilStudent mySelf = myHomeWork.getPupilStudent(); mySelf.setsNo(1002L); mySelf.setName("Appleyk"); mySelf.setSex(ESexType.MALE); return myHomeWork; } /** * 对象深度拷贝 * @param homeWork * @return * @throws CloneNotSupportedException */ public static HomeWork deepCopy(HomeWork homeWork) throws CloneNotSupportedException{ HomeWork myHomeWork = homeWork.deepClone(); myHomeWork.setFinishTime(DateUtils.addDays(1)); PupilStudent mySelf = myHomeWork.getPupilStudent(); mySelf.setsNo(1003L); mySelf.setName("张聪明"); mySelf.setSex(ESexType.MALE); return myHomeWork; } }