作为一种另类的对象创建模式,原型模式是指:
使用原型实例来指定创建对象的种类,并通过拷贝这些原型创建新的对象。
- 使用原型模式创建对象比直接new一个对象在性能上要好的多,因为Object类的clone方法是一个本地方法,它直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显。
- 使用原型模式的另一个好处是简化对象的创建,使得创建对象就像我们在编辑文档时的复制粘贴一样简单。
- 显然,核心是如何在原型类中实现拷贝方法,那么作为核心的原型类,需要满足:
- 实现Cloneable接口。在java语言有一个Cloneable接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法。在java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedException异常。
- 重写Object类中的clone方法。Java中,所有类的父类都是Object类,Object类中有一个clone方法,作用是返回对象的一个拷贝,但是其作用域protected类型的,一般的类无法调用,因此,Prototype类需要将clone方法的作用域修改为public类型。
1. 结构
- ProtoType抽象原型类:所有具体原型类的父类,可以是接口,抽象类,甚至是实现类;
- ConcreteProtoType具体原型类:包含拷贝方法,该拷贝方法需要返回一个拷贝自己的对象;
- Client客户类:创建原型对象,调用其拷贝方法。注意:客户端通常针对抽象原型类编程;
2. 拷贝方法的实现:
通用实现方法,和Java语言提供的clone()方法
1.首先来看通用实现方法:
class ConcreteProtoType implements ProtoType{
private String attr;
public void setAttr(String attr){
this.attr = attr;
}
public String getAttr(){
return this.attr;
}
//拷贝方法
public ProtoType clone(){
ProtoType protoType = new ConcreteProtoType();
protoType.setAttr(this.attr);
return protoType;
}
}
class Client{
public void init(){
//面向抽象原型类编程
ProtoType productA = new ConcreteProtoType();
productA.setAttr("This is for productB.");
ProtoType productB = productA.clone();
}
}
2.java.lang.Object类提供了clone()方法,可以直接使用:
class ConcreteProtoType implements Cloneable,ProtoType{
public ProtoType clone(){
Object obj = null;
try{
obj = super.clone()
} catch (CloneNotSupportedException e){
System.err.println("Cloneable is not supported!");
}
return (ProtoType)obj;
}
}
class Client{
ProtoType obj1 = new ConcreteProtoType();
ProtoType obj2 = obj1.clone();
}
java提供的clone()方法产生的对象有如下特点:
1. x.clone()!=x;
2. x.clone().getClass()==x.getClass();
3. x.clone.equals(x)==true;(equals()方法不被重写)
3. 浅拷贝和深拷贝
深拷贝和浅拷贝的区别就在于:对其他对象的引用如何处理。
java中Object类的clone()方法其实做的是“偷懒的”浅拷贝动作,只拷贝对象本身(包括八种基本数据类型和它们的封装类,String类型),不拷贝对象内部的数组,引用对象。
public class Student implements Cloneable{
private ArrayList<String> al = new ArrayList<String>();
@Override
public Student clone(){
Student student = null;
try{
student = (Student) super.clone();
} catch (CloneNotSupportedException e){
System.err.println("This cloneable is not supported!");
}
return student;
}
public void setList(String name){
this.al.add(name);
}
public ArrayList<String> getList(){
return this.al;
}
}
public class Client{
Student studentA = new Student();
studentA.setList("BJTShang");
//拷贝对象
Student studentB = studentA.clone();
studentB.setList("FETShang");
System.out.println(studentB.getList());
}
输出:”BJTShang”,”FETShang”
因为调用的super.clone()方法是Object类的,只复制对象,不复制其中的对象引用al!
这种共享私有变量的方法其实是不安全的,实际项目中使用较少。
那么如何进行深拷贝呢?将需要的数组或者引用在clone()方法中手动clone()!
public class Student implements Cloneable{
private ArrayList<String> al = new ArrayList<String>();
@Override
public Student clone(){
Student student = null;
try{
student = (Student) super.clone();
student.al = (ArrayList<String>)this.al.clone();//将拷贝对象的私有变量也拷贝一份
} catch (CloneNotSupportedException e){
System.err.println("This cloneable is not supported!");
}
return student;
}
public void setList(String name){
this.al.add(name);
}
public ArrayList<String> getList(){
return this.al;
}
}
public class Client{
Student studentA = new Student();
studentA.setList("BJTShang");
//拷贝对象
Student studentB = studentA.clone();
studentB.setList("FETShang");
System.out.println(studentB.getList());
}