原型模式是一种比较简单的设计模式,也非常的容易理解,属于创建型设计模式的一种,只实现一个接口,重写一个方法即可完成原型模式。那么我们就来看看吧。
一、定义
用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。
二、适用情况
创建一些大对象,比较耗时的对象的时候,可以使用原型模式提高创建对象的效率。
三、原型模式实现
1、实现Cloneable接口,在java中有一个Cloneable接口,它的作用只有一个,就是通知虚拟机可以安全的在实现了此接口的类上使用clone()方法。如果你想看下这个接口,其实可以告诉你,并没有什么可看的,只是一个标识接口,实现此接口就是表示了这个类具有被Clone的能力,如果没有实现此接口,而去使用clone(),就会抛出CloneNotSupportedException异常。
2、重写Object类中的clone方法。Java中,所有类的父类都是Object类,Object类中有一个clone方法,作用是返回对象的一个拷贝,但是其作用域protected类型的,一般的类无法调用,因此,Prototype类需要将clone方法的作用域修改为public类型。
下面就以羊来为原型克隆多利
代码实现
package cn.wong.pattern.prototype;
import java.util.Date;
public class Sheep implements Cloneable {
private String name;
private Date birthDay;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public Sheep() {
}
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;
}
}
package cn.wong.pattern.prototype;
import java.util.Date;
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Sheep s1 = new Sheep("芬兰母羊",new Date(324324234L));
System.out.println(s1);
System.out.println(s1.getName());
System.out.println(s1.getBirthDay());
System.out.println("---------进行clone----------");
Sheep s2 = (Sheep)s1.clone();
System.out.println(s2);
System.out.println(s2.getName());
System.out.println(s2.getBirthDay());
}
}
结果:
cn.wong.pattern.prototype.Sheep@4633c1aa
芬兰母羊
Mon Jan 05 02:05:24 CST 1970
---------进行clone----------
cn.wong.pattern.prototype.Sheep@28a50395
芬兰母羊
Mon Jan 05 02:05:24 CST 1970
从结果可以看出拷贝出来的是一个新的对象,并和原始对象具有相同的属性值
使用原型模式创建对象比直接new一个对象在性能上要好很多,因为Object类的clone方法是一个本地方法,它直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显。
protected native Object clone() throws CloneNotSupportedException;
使用原型模式的注意事项
1. 使用原型模式不会调用类的构造方法,因为它是直接在内存中进行复制的。
2. 深拷贝和浅拷贝
四、浅拷贝和深拷贝
Object类的clone方法只会拷贝对象中的8中基本数据类型,对于数组、日期、容器对象、引用对象等都不会拷贝,这就是浅拷贝。如果要实现深拷贝,必须将原型模式中的数组、容器对象、引用对象等另行拷贝
浅拷贝:
package cn.wong.pattern.prototype;
import java.util.Date;
public class Sheep implements Cloneable {
private String name;
private Date birthDay;
@Override
public Object clone() throws CloneNotSupportedException {
Object obj = super.clone();
/*Sheep s = (Sheep)obj;
s.birthDay=(Date) this.birthDay.clone();*/
return obj;
}
public Sheep() {
}
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;
}
}
package cn.wong.pattern.prototype;
import java.util.Date;
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Date date = new Date(324324234L);
Sheep s1 = new Sheep("芬兰母羊",date);
System.out.println("---------打印s1的信息----------");
System.out.println(s1);
System.out.println(s1.getName());
System.out.println(s1.getBirthDay());
//进行复制
Sheep s2 = (Sheep)s1.clone();
//修改s1所指向的时间
date.setTime(23432454354L);
System.out.println("\n---------再次打印s1的信息----------");
System.out.println(s1.getName());
System.out.println(s1.getBirthDay());
System.out.println("\n---------打印s2的信息----------");
System.out.println(s2);
System.out.println(s2.getName());
System.out.println(s2.getBirthDay());
}
}
结果:
---------打印s1的信息----------
cn.wong.pattern.prototype.Sheep@5df1cc1a
芬兰母羊
Mon Jan 05 02:05:24 CST 1970
---------再次打印s1的信息----------
芬兰母羊
Tue Sep 29 13:00:54 CST 1970
---------打印s2的信息----------
cn.wong.pattern.prototype.Sheep@7a0ec850
芬兰母羊
Tue Sep 29 13:00:54 CST 1970
发现s2的信息是改变时间后的,我们知道,clone是在修改时间前进行。所以由此我们可以知道引用类型对象不会进行拷贝,s1、s2两个对象同时都是指向同一个Date,这就是浅拷贝。
深拷贝:
如果要实现深拷贝,必须将引用对象另行拷贝。而对于java的大部分的类都实现了Cloneable接口,所以深拷贝并不困难。
修改Sheep中clone方法
@Override
public Object clone() throws CloneNotSupportedException {
Object obj = super.clone();
Sheep s = (Sheep)obj;
s.birthDay=(Date) this.birthDay.clone();
return obj;
}
测试不变
package cn.wong.pattern.prototype;
import java.util.Date;
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Date date = new Date(324324234L);
Sheep s1 = new Sheep("芬兰母羊",date);
System.out.println("---------打印s1的信息----------");
System.out.println(s1);
System.out.println(s1.getName());
System.out.println(s1.getBirthDay());
//进行复制
Sheep s2 = (Sheep)s1.clone();
//修改s1所指向的时间
date.setTime(23432454354L);
System.out.println("\n---------再次打印s1的信息----------");
System.out.println(s1.getName());
System.out.println(s1.getBirthDay());
System.out.println("\n---------打印s2的信息----------");
System.out.println(s2);
System.out.println(s2.getName());
System.out.println(s2.getBirthDay());
}
}
结果
---------打印s1的信息----------
cn.wong.pattern.prototype.Sheep@2d8eef25
芬兰母羊
Mon Jan 05 02:05:24 CST 1970
---------再次打印s1的信息----------
芬兰母羊
Tue Sep 29 13:00:54 CST 1970
---------打印s2的信息----------
cn.wong.pattern.prototype.Sheep@60813aca
芬兰母羊
Mon Jan 05 02:05:24 CST 1970