原型模式
用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。
其实就是从一个对象在创建一个另外一个可定制的对象,并且不需要知道任何创建的细节。
原型类:
package com.hy.prototype;
public class Prototype implements Cloneable {
private String name;
public Prototype(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// 关键是这个克隆方法
public Prototype clone() {
Prototype prototype = null;
try {
prototype = (Prototype) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return prototype;
}
}
具体原型类:
package com.hy.prototype;
public class ConcretePrototype extends Prototype {
public ConcretePrototype(String name) {
super(name);
//System.out.println("构造方法");
}
public void show() {
System.out.println(this.getName());
}
}
测试类:
package com.hy.prototype;
public class Test {
public static void main(String[] args) {
ConcretePrototype cp1 = new ConcretePrototype("jame");
cp1.show();
for(int i = 0;i<2;i++){
ConcretePrototype clonecp = (ConcretePrototype) cp1.clone();
clonecp.show();
}
}
}
原型类需要具备两个条件:
实现cloneable接口。作用是运行时通知虚拟机可以在实现了此接口的类上使用clone方法。在java虚拟机中,只有实现这个接口可以实现拷贝,否则抛出CloneNotSupportedException异常。
重写object类中的clone方法。Object中的clone方法作用是返回一个对象的拷贝,但是其作用域是protected类型,一般的类无法调用,因此,Prototype类需要将clone方法的作用域修改为public类型。
原型模式的优点:
使用原型模式创建对象简化了对象的创建过程,而却比直接new一个对象在性能上好的多。因为object类的clone是一个本地方法,直接操作内存中的二进制流,特别是复制大对象时,原型模式优势更加明显。
应用场景:
需要重复的创建相似的对象时可以考虑使用原型模式。例如对象的创建过程比较复杂或者对象的创建的循环次数很多。
原型模式的注意事项:
原型模式复制对象的时候不会调用类的构造方法,因为object类的clone方法是在内存中复制数据,因此不会调用到构造方法。而且clone方法直接无视构造方法的权限,所以原型模式和单例模式是冲突的。
深拷贝和浅拷贝。
*1. Object类中的clone方法只会拷贝对象中的基本数据类型,对于数组、容器对象、引用对象等都不会拷贝,这就是浅拷贝。
- 将原型模式的中的数组、容器对象、引用对象另行拷贝,即为深拷贝。*
PS:会发生深拷贝的有java中的8种基本类型以及他们的封装类型,另外还有String类型。其余的都是浅拷贝。
java提供的大部分的容器类都实现了Cloneable接口。所以实现深拷贝并不是特别困难。
例如----简历对象中添加工作经历的对象
工作经历类:
package com.hy.prototype;
public class Experience implements Cloneable{
private String company;
private int year;
public Experience(String com, int year) {
this.company = com;
this.year = year;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
Get()set()略…
}
简历类:
package com.hy.prototype;
public class Resume implements Cloneable{
private String name;
private int age;
private Experience exp;
public Resume(String name, int age, Experience exp) {
super();
this.name = name;
this.age = age;
this.exp = exp;
}
@Override
public String toString() {
return "Resume [name=" + name + ", age=" + age + ", 经验: 在" + exp.getCompany()+"工作了"+exp.getYear()
+ "年 ]";
}
@Override
public Object clone() throws CloneNotSupportedException {
Resume resume = (Resume) super.clone();
resume.exp = (Experience) this.exp.clone();
return resume;
}
Get()set()略…
}
测试类:
package com.hy.prototype;
public class Test {
public static void main(String[] args) {
try {
Resume a = new Resume("aaa",12,new Experience("hyd",1));
Resume b = (Resume) a.clone();
System.out.println(a==b);
System.out.println(a.getExp()==b.getExp());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
输出结果均为false。如果Experience类不实现Cloneable接口,并且不在Resume类中clone方法实现Experience对象的克隆,则结果为false,ture。因为Experience并未实现深拷贝,导致引用对象相同,此时如果对克隆对象的Experience进行修改,则会影响原Resume对象。