原型模式
当我们需要构建一个与已经存在对象相似时,再通过new去产生一个新对象需要非常繁琐的数据准备或访问权限,而这时就可以用到原型模式,原型模式实现就是Java中的克隆技术,以某个对象为原型,复制出新的对象,显然新的对象具备原型对象相同的特点。原型模式优点是,效率高,可以直接复制,避免了重新执行构造方法过程步骤。
原型模式中的复制类似于new,但不同于new。new创建新的对象属性采用的是默认值,而复制出的对象的属性和原型对象属性完全相同。并且复制出的新对象的改变不会影响原型对象。然后再根据需要修改复制出的对象的值。
原型模式实现
原型模式主要用于对象的复制,它的核心是就是类图中的原型类Prototype。Prototype类需要具备以下两个条件:
- 实现Cloneable接口。在java语言有一个Cloneable接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法。在java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedException异常。
- 重写Object类中的clone方法。Java中,所有类的父类都是Object类,Object类中有一个clone方法,作用是返回对象的一个拷贝,但是其作用域protected类型的,一般的类无法调用,因此,Prototype类需要将clone方法的作用域修改为public类型。
下面以克隆羊的例子,来写一个示例代码:
原型模式最核心的便是原型类ProtoType:
public class Sheep implements Cloneable{
private String name;
private Date birthday;
@Override
public Object clone() throws CloneNotSupportedException {
Object obj=super.clone();//直接调用Object对象的clone方法
return obj;
}
public Sheep(String name, Date birthday) {
this.name = name;
this.birthday = birthday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
进行复制:
public class Client {
public static void main(String[] args) throws Exception {
Sheep s1=new Sheep("多利",new Date(134635126));
Sheep s2=(Sheep) s1.clone();
System.out.println(s1);
System.out.println(s1.getName());
System.out.println(s1.getBirthday());
System.out.println(s2);
System.out.println(s2.getName());
System.out.println(s2.getBirthday());
}
}
运行结果:
prototype.Sheep@15db9742
多利
Fri Jan 02 21:23:55 GMT+08:00 1970
prototype.Sheep@5c647e05
多利
Fri Jan 02 21:23:55 GMT+08:00 1970
从运行结果可看出复制出的是一个新对象并且和原型对象有相同属性值。
深复制和浅复制
Object类的clone方法只会拷贝对象中的基本的数据类型(8种基本数据类型 byte,char,short,int,long,float,double,boolean ),对于数组、容器对象、引用对象等都不会拷贝,这就是浅拷贝。如果要实现深拷贝,必须将原型模式中的数组、容器对象、引用对象等另行拷贝。
1.浅复制
如上完成了对象的复制,但实际上还是有问题的,这两个对象都有一个birthday的Date对象属性,所以s1,s2对象属性引用指向的是同一个Date对象,即如图:
这就是所谓的浅复制,即原型对象与复制对象间仍然存在联系,不能完全独立。
2.深复制
深复制 即指它们的属性也能够完全复制一份独立出来,即:
具体的实现其实也简单,只要重写一下原型类的clone()方法即可,
@Override
public Object clone() throws CloneNotSupportedException {
Object obj=super.clone(); //直接调用Object对象的clone方法
//添加如下代码实现深复制(deep Clone)
Sheep s =(Sheep)obj ;
s.birthday=(Date)this.birthday.clone();//把属性也进行克隆!
return obj ;
}
这样便完成了深复制,深复制之后s1中Date数据的改变不会再影响到s2中的Date数据。