原型模式
类型:创建型
通过复制现有实例来创建新的实例,无需知道相应类的信息。
关键字:Clone
深拷贝和浅拷贝
浅拷贝:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。
深拷贝:将一个对象复制后,不论是基本数据类型还是引用类型,都是重新创建的。
简单来说,就是深拷贝进行了完全彻底的拷贝,而浅拷贝不彻底。
clone明显是深拷贝,clone出来的对象是是不能去影响原型对象的
优点
先说优点是因为怕有人觉得为什么不直接new
原因:
- 使用原型模式创建对象比直接new一个对象在性能上要好的多,因为Object类的clone方法是一个本地方法,它直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显。
- 使用原型模式的另一个好处是简化对象的创建,使得创建对象就像我们在编辑文档时的复制粘贴一样简单。
角色
Client:客户端
Prototype:接口(抽象类),声明具备clone能力,例如java中得Cloneable接口
ConcretePrototype:具体的原型类
UML图
原型模式的具体实现:一个原型类,只需要实现Cloneable接口,覆写clone方法,此处clone方法可以改成任意的名称,因为Cloneable接口是个空接口,你可以任意定义实现类的方法名,如cloneA或者cloneB,因为此处的重点是super.clone()这句话,super.clone()调用的是Object的clone()方法。
包结构
原型类
package 原型模式.prototype;
public class PrototypeEmail implements Cloneable{
@Override
public PrototypeEmail clone() throws CloneNotSupportedException {
return (PrototypeEmail) super.clone();
}
}
具体类
package 原型模式.concretePrototype;
import 原型模式.prototype.PrototypeEmail;
public class Email extends PrototypeEmail {
public void print(int i){
System.out.println("制造出了第"+i+"封信");
}
}
客户端
package 原型模式.client;
import 原型模式.concretePrototype.Email;
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Email em = new Email();
for(int i=1;i<=10;i++){
Email email = (Email)em.clone();
email.print(i);
}
}
}
输出
制造出了第1封信
制造出了第2封信
制造出了第3封信
制造出了第4封信
制造出了第5封信
制造出了第6封信
制造出了第7封信
制造出了第8封信
制造出了第9封信
制造出了第10封信
Process finished with exit code 0
在需要重复地创建相似对象时可以考虑使用原型模式。比如需要在一个循环体内创建对象,假如对象创建过程比较复杂或者循环次数很多的话,使用原型模式不但可以简化创建过程,而且可以使系统的整体性能提高很多。
原型模式的注意事项
1. 使用原型模式复制对象不会调用类的构造方法
因为对象的复制是通过调用Object类的clone方法来完成的,它直接在内存中复制数据。不但构造方法中的代码不会执行,甚至连访问权限都对原型模式无效。
而在单例模式中,只要将构造方法的访问权限设置为private类型,就可以实现单例。但是clone方法直接无视构造方法的权限.
所以,单例模式与原型模式是冲突的,在使用时要特别注意。
2. 深拷贝与浅拷贝。
Object类的clone方法只会拷贝对象中的基本的数据类型,对于数组、容器对象、引用对象等都不会拷贝,这就是浅拷贝。
如果要实现深拷贝,必须将原型模式中的数组、容器对象、引用对象等另行拷贝。
package 原型模式.prototype;
import java.util.ArrayList;
public class PrototypeEmail implements Cloneable{
private ArrayList arrayList = new ArrayList();
@Override
public PrototypeEmail clone() throws CloneNotSupportedException {
PrototypeEmail pe=(PrototypeEmail)super.clone();
//这里对引用对象另行拷贝了
pe.arrayList = (ArrayList)this.arrayList.clone();
return pe;
}
}
java提供的大部分的容器类都实现了Cloneable接口
会发生深拷贝的有java中的8种基本类型以及他们的封装类型,另外还有String类型。其余的都是浅拷贝。