23种设计模式之原型模式
23种设计模式之原型模式一、定义: 通过复制现有的对象实例来创建新的对象实例。二、实现:实现Cloneable接口: Cloneable接口的作用是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法。在java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedException异常。...
23种设计模式之原型模式
一、定义:
通过复制现有的对象实例来创建新的对象实例。
二、实现:
实现Cloneable接口:
Cloneable接口的作用是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法。在java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedException异常。
重写Object类中的clone方法:
Java中,所有类的父类都是Object类,Object类中有一个clone方法,作用是返回对象的一个拷贝,但是其作用域protected类型的,一般的类无法调用,因此,原型类需要将clone方法的作用域修改为public类型。
示例:
例如,对于拿邮件发邀请函,邮件类大部分内容都是一样的:邀请原由、相邀地点,相聚时间等等,但对于被邀请者的名称和发送的邮件地址是不同的。
定义Mail类:
public class Mail implements Cloneable {
private String receiver;
private String subject;
private String content;
private String tail;
public Mail(EventTemplate et) {
this.tail = et.geteventContent();
this.subject = et.geteventSubject();
}
@Override
public Mail clone() {
Mail mail = null;
try {
mail = (Mail) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
} return mail;
}
//get、set.....
}
测试方法:
public static void main(String[] args) {
int i = 0;
int MAX_COUNT = 10;
EventTemplate et =
new EventTemplate("邀请函(不变)", "婚嫁生日啥的....(不变部分)");
Mail mail = new Mail(et);
while (i < MAX_COUNT) {
Mail cloneMail = mail.clone();
cloneMail.setContent("XXX先生(女士)(变化部分)"
+ mail.getTail());
cloneMail.setReceiver("每个人的邮箱地址...com(变化部分)");
sendMail(cloneMail);
i++;
}
}
三、优点:
1,使用原型模型创建一个对象比直接new一个对象更有效率,因为它直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显。
2,隐藏了制造新实例的复杂性,使得创建对象就像我们在编辑文档时的复制粘贴一样简单。
四、缺点:
1,由于使用原型模式复制对象时不会调用类的构造方法,所以原型模式无法和单例模式组合使用,因为原型类需要将clone方法的作用域修改为public类型,那么单例模式的条件就无法满足了。
2,使用原型模式时不能有final对象。
3,Object类的clone方法只会拷贝对象中的基本数据类型,对于数组,引用对象等只能另行拷贝。这里涉及到深复制和浅复制的概念。
五、深复制与浅复制:
浅复制:
如果原型对象的成员变量是值类型,则将该值复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的内存地址复制一份给克隆对象,也就是说,原型对象和克隆对象的成员变量指向相同的内存地址。简单来说,在浅克隆中,当对象复制时指复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。(这样不安全)
深复制:
在深复制中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象。深克隆将原型对象的所有引用对象也复制一份给克隆对象。简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制。
那么深拷贝如何具体实现呢?
继续上面的例子,增加了一个ArrayList属性。
private String receiver;
private String subject;
private String content;
private String tail;
private ArrayList<String> ars;
此时,单mail = (Mail) super.clone();无法将ars指向的地址区域改变,必须另行拷贝:
try {
mail = (Mail) super.clone();
mail.ars = (ArrayList<String>)this.ars.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
六、适用场景:
1,复制对象的结构和数据。
2,希望对目标对象的修改不影响既有的原型对象。
3,创建一个对象的成本比较大。
更多推荐
所有评论(0)