原型设计模式是用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。简单来说就是对象的拷贝过程。所以其优点也是很突出的:使用原型模式创建对象比直接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
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
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】