原型设计模式是用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。简单来说就是对象的拷贝过程。所以其优点也是很突出的:使用原型模式创建对象比直接new一个对象在性能上要好的多,因为Object类的clone方法是一个本地方法,它直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显
所要具备的条件:
实现Cloneable接口。在java语言有一个Cloneable接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法。在java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedException异常。重写Object类中的clone方法。Java中,所有类的父类都是Object类,Object类中有一个clone方法,作用是返回对象的一个拷贝,但是其作用域protected类型的,一般的类无法调用。原型模式的注意事项:
使用原型模式复制对象不会调用类的构造方法。因为对象的复制是通过调用Object类的clone方法来完成的,它直接在内存中复制数据,因此不会调用到类的构造方法。不但构造方法中的代码不会执行,甚至连访问权限都对原型模式无效。单例模式中,只要将构造方法的访问权限设置为private型,就可以实现单例。但是clone方法直接无视构造方法的权限,所以,单例模式与原型模式是冲突的,在使用时要特别注意。深拷贝与浅拷贝。Object类的clone方法只会拷贝对象中的基本的数据类型(8种基本数据类型byte,char,short,int,long,float,double,boolean),对于数组、容器对象、引用对象等都不会拷贝,这就是浅拷贝。如果要实现深拷贝,必须将原型模式中的数组、容器对象、引用对象等另行拷贝。 引用的复制: Person p = new Person(23, "zhang"); Person p1 = p; System.out.println(p); System.out.println(p1); 当Person p1 = p;执行之后, 是创建了一个新的对象吗? com.pansoft.zhangjg.testclone.Person@2f9ee1ac com.pansoft.zhangjg.testclone.Person@2f9ee1ac 对象的复制: Person p = new Person(23, "zhang"); Person p1 = (Person) p.clone(); System.out.println(p); System.out.println(p1); 从打印结果可以看出,两个对象的地址是不同的,也就是说创建了新的对象, 而不是把原对象的地址赋给了一个新的引用变量: com.pansoft.zhangjg.testclone.Person@2f9ee1ac com.pansoft.zhangjg.testclone.Person@67f1fba0
言归正传,我们可以看出,该模式的核心就在于clone方法的使用,举个例子:
package com.zndroid.dm.PrototypeModel; /** * Created by luzhenyu on 2016/9/28. */ public class Resume implements Cloneable {//简历类实现可clone接口 private String name;//同一个人的简历中该属性应该是不会变化的 private String age; private String sex; private String workTimeArea; private String company; private Work mWork;//引用类型 需要深复制 private Apartment mApartment; public Resume(String name) { this.name = name; this.mWork = new Work(); this.mApartment = new Apartment(); } public void setPersonInfo(String age, String sex) { this.age = age; this.sex = sex; } public void setApartment(String place) { this.mApartment.setPlace(place); } public void setWorkExperience(String workTimeArea, String company) { this.workTimeArea = workTimeArea; this.company = company; mWork.setWorkTimeArea(workTimeArea); mWork.setCompany(company); } public void display() { System.out.println("name=" + name + " sex=" + sex + " age=" + age + " work=" + mWork.getWorkTimeArea() + " company=" + mWork.getCompany() + " place=" + mApartment.getPlace()); System.out.println("【" + mWork.toString() + " " + mApartment.toString() + "】"); } /** * 1、由于clone方法是由虚拟机直接复制内存块执行,所以在速度上比使用new的方式创建对象要快。 * 2、可以基于原型,快速的创建一个对象,而无需知道创建的细节。 * 3、可以在运行时动态的获取对象的类型以及状态,从而创建一个对象。 * 4、主要的缺点就是实现深度拷贝比较困难,需要很多额外的代码量 * * 对于值类型潜拷贝没有问题,对于引用类型需要进行深拷贝 * */ @Override public Object clone() {//该模式 关键在于此 //潜复制 /*try { return super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); }*/ //深复制 //有几个引用类型就需要分别对该引用类型复制,所引用类型要实现Cloneable接口 Object o = null; try { o = super.clone(); ((Resume)o).mWork = this.mWork.clone(); ((Resume)o).mApartment = this.mApartment.clone(); return o; } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } class Work implements Cloneable { private String workTimeArea; private String company; public String getWorkTimeArea() { return workTimeArea; } public void setWorkTimeArea(String workTimeArea) { this.workTimeArea = workTimeArea; } public String getCompany() { return company; } public void setCompany(String company) { this.company = company; } @Override public Work clone() { try { return (Work) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } } class Apartment implements Cloneable { private String place; public String getPlace() { return place; } public void setPlace(String place) { this.place = place; } @Override protected Apartment clone() { try { return (Apartment)super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } } }使用方式:
//案例 一个人在不同阶段的简历 Resume mResume = new Resume("hy"); mResume.display(); log("1111 = " + mResume.toString()); // Resume mResume2 = (Resume) mResume.clone(); mResume2.setWorkExperience("xuexiao", "null"); mResume2.setmApartment("xuzhou"); mResume2.setPersonInfo("22", "nv"); mResume2.display(); log("2222 = " + mResume2.toString()); // Resume mResume3 = (Resume) mResume2.clone(); mResume3.setWorkExperience("suzhou", "alibaba"); mResume3.setApartment("suzhou"); mResume3.setPersonInfo("24", "nv"); mResume3.display(); log("3333 = " + mResume3.toString()); // log("----------------我是分割线-----------------");【欢迎上码】
【微信公众号搜索 h2o2s2】