原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
原型模式其实就是从一个对象在创建另外一个可定制的对象,而且不需要知道任何创建的细节,
实现Cloneable接口:
Cloneable接口的作用是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法。在java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedException异常。
重写Object类中的clone方法:
Java中,所有类的父类都是Object类,Object类中有一个clone方法,作用是返回对象的一个拷贝,但是其作用域protected类型的,一般的类无法调用,因此,原型类需要将clone方法的作用域修改为public类型。
创建一个简历,并复制简历
public class Resume implements Cloneable { private String name, sex, age, timeArea, company; public Resume(String name) { this.name = name; } public void setPersonInfo(String sex,String age){ this.sex = sex; this.age = age; } public void setWorkExperience(String timeArea,String company){ this.timeArea = timeArea; this.company = company; } public void display(){ System.out.println(name + " " + sex + " " + age); System.out.println("工作经历:" + timeArea + " " + company); } @Override public Resume clone(){ Resume resume = null; try{ resume = (Resume) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return resume; } } //test //原型模式 Resume resume = new Resume("小菜"); resume.setPersonInfo("男","27"); resume.setWorkExperience("1998 - 2000","XX公司"); Resume resumeB = resume.clone(); resumeB.setWorkExperience("1998 - 2000","XX企业"); Resume resumeC = resume.clone(); resumeC.setPersonInfo("男","24"); resume.display(); resumeB.display(); resumeC.display();
输出结果:
小菜 男 27
工作经历:1998 - 2000 XX公司
小菜 男 27
工作经历:1998 - 2000 XX企业
小菜 男 24
工作经历:1998 - 2000 XX公司
如果字段值是值类型的,则对该字段执行逐位复制,如果字段是引用类型,则复制引用但不复制引用的对象,因此,原始对象及其复本引用同一对象。
浅复制:
浅复制,被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。
public class WorkExperience { //工作经历 public String workDate,company; } public class Resume implements Cloneable { private String name, sex, age, timeArea, company; private WorkExperience workExperience; public Resume(String name) { this.name = name; workExperience = new WorkExperience(); } public void setPersonInfo(String sex,String age){ this.sex = sex; this.age = age; } public void setWorkExperience(String timeArea,String company){ this.timeArea = timeArea; this.company = company; workExperience.workDate = timeArea; workExperience.company = company; } public void display(){ System.out.println(name + " " + sex + " " + age); // System.out.println("工作经历:" + timeArea + " " + company); System.out.println("工作经历:" + workExperience.workDate + " " + workExperience.company); } @Override public Resume clone(){ Resume resume = null; try{ resume = (Resume) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return resume; } } //test Resume qresume = new Resume("小菜"); qresume.setPersonInfo("男","27"); qresume.setWorkExperience("1998 - 2000","XX公司"); Resume qresumeB = qresume.clone(); resumeB.setWorkExperience("1998 - 2000","XX企业"); Resume qresumeC = qresume.clone(); resumeC.setPersonInfo("男","24"); qresume.display(); qresumeB.display(); qresumeC.display();
输入结果:
小菜 男 27
工作经历:1998 - 2000 XX公司
小菜 男 27
工作经历:1998 - 2000 XX公司
小菜 男 27
工作经历:1998 - 2000 XX公司
我们对于包含对象的复制,然后重新赋值,并没有成功。
我们无法对对象进行复制,所以对象需要单独复制。
深复制:
深复制,把引用对象的变量指向复制过的新对象,而不是原有的被引用的对象。
public class WorkExperience implements Cloneable{ public String workDate,company; @Override public WorkExperience clone(){ WorkExperience workExperience = null; try{ workExperience = (WorkExperience) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return workExperience; } } @Override public Resume clone(){ Resume resume = null; try{ resume = (Resume) super.clone(); resume.workExperience = this.workExperience.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return resume; } //test 同样是浅复制的代码
输出结果:
小菜 男 27
工作经历:1998 - 2000 XX公司
小菜 男 27
工作经历:1998 - 2000 XX企业
小菜 男 24
工作经历:1998 - 2000 XX公司
主要就是重写两个clone方法
优点:
- 1,使用原型模型创建一个对象比直接new一个对象更有效率,因为它直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显。
- 2,隐藏了制造新实例的复杂性,使得创建对象就像我们在编辑文档时的复制粘贴一样简单。
缺点:
- 1,由于使用原型模式复制对象时不会调用类的构造方法,所以原型模式无法和单例模式组合使用,因为原型类需要将clone方法的作用域修改为public类型,那么单例模式的条件就无法满足了。
- 2,使用原型模式时不能有final对象。
- 3,Object类的clone方法只会拷贝对象中的基本数据类型,对于数组,引用对象等只能另行拷贝。这里涉及到深拷贝和浅拷贝的概念。
适用场景:
- 1,复制对象的结构和数据。
- 2,希望对目标对象的修改不影响既有的原型对象。
- 3,创建一个对象的成本比较大。
参考:《大话设计模式》 阿木侠 Java知音