Prototype原型模式是一种创建型设计模式,Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。—百度百科。
原型模式又称为克隆模式,可以分为浅克隆和深克隆,下面分别用代码来实现这两种不同类型的克隆方式。
浅克隆:
package cn.zzit.prototype;
import java.util.Date;
public class Sheep implements Cloneable {
private String name;
private Date birthday;
public Sheep(String name, Date birthday) {
super();
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;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
测试代码:
package cn.zzit.prototype;
import java.util.Date;
/**
* 测试浅复制
* @author yufu
*
*/
public class Client {
public static void main(String[] args) throws Exception {
Date date=new Date(12366521155L);
Sheep s1=new Sheep("多利羊",date);
Sheep s2=(Sheep) s1.clone();
System.out.println("s1对象:"+s1);
System.out.println("s1对象的名字:"+s1.getName());
System.out.println("s1对象的生日:"+s1.getBirthday());
System.out.println("克隆的s2对象:"+s2);
System.out.println("克隆的s2对象:"+s2.getName());
System.out.println("克隆的s2对象:"+s2.getBirthday());
}
}
输出结果:
s1对象:cn.zzit.prototype.Sheep@15db9742
s1对象的名字:多利羊
s1对象的生日:Sun May 24 11:08:41 CST 1970
克隆的s2对象:cn.zzit.prototype.Sheep@5c647e05
克隆的s2对象:多利羊
克隆的s2对象:Sun May 24 11:08:41 CST 1970
由输出结果我们可以看出,s1和s2虽然是不同的对象,但是他们的属性值是一样的,这样也就实现了s1对象的克隆。
深克隆,深克隆有两种实现方式
实现方式一:重写clone()方法,在方法内对属性进行克隆
package cn.zzit.prototype;
import java.util.Date;
public class Sheep2 implements Cloneable {
private String name;
private Date birthday;
public Sheep2(String name, Date birthday) {
super();
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;
}
@Override
protected Object clone() throws CloneNotSupportedException {
// 深复制,复制了对象最初的值,如果对最初的值做了修改,则不会影响复制对象
Object obj = super.clone();
Sheep2 s2 = (Sheep2) obj;
s2.birthday = (Date) this.birthday.clone();
return obj;
}
}
测试代码:
package cn.zzit.prototype;
import java.util.Date;
/**
* 测试深复制
* @author yufu
*
*/
public class Client2 {
public static void main(String[] args) throws Exception {
Date date=new Date(12366521155L);
Sheep2 s1=new Sheep2("多利羊",date);
Sheep2 s2=(Sheep2) s1.clone();
System.out.println("s1对象:"+s1);
System.out.println("s1对象的名字:"+s1.getName());
System.out.println("s1对象的生日:"+s1.getBirthday());
date.setTime(55521452236L);
System.out.println("修改s1对象的生日:"+s1.getBirthday());
System.out.println("克隆的s2对象:"+s2);
System.out.println("克隆的s2对象:"+s2.getName());
System.out.println("克隆的s2对象:"+s2.getBirthday());
}
}
输出结果:
s1对象:cn.zzit.prototype.Sheep2@15db9742
s1对象的名字:多利羊
s1对象的生日:Sun May 24 11:08:41 CST 1970
修改s1对象的生日:Tue Oct 05 22:37:32 CST 1971
克隆的s2对象:cn.zzit.prototype.Sheep2@5c647e05
克隆的s2对象:多利羊
克隆的s2对象:Sun May 24 11:08:41 CST 1970
由输出结果可以看出:虽然s1对象的生日属性值已经修改但是,克隆的得到的对象s2仍然是s1修改以前的值。
实现方式二:使用序列化和反序列化,需要克隆对象实现序列化接口
package cn.zzit.prototype;
import java.io.Serializable;
import java.util.Date;
public class Sheep3 implements Cloneable, Serializable {
/**
*
*/
private static final long serialVersionUID = 1745954590009044884L;
private String name;
private Date birthday;
public Sheep3(String name, Date birthday) {
super();
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;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Object obj = super.clone();
return obj;
}
}
测试代码:
package cn.zzit.prototype;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Date;
/**
* 测试深复制 使用序列化和反序列化实现
*
* @author yufu
*
*/
public class Client3 {
public static void main(String[] args) throws Exception {
Date date = new Date(12366521155L);
Sheep3 s1 = new Sheep3("多利羊", date);
System.out.println("s1对象:" + s1);
System.out.println("s1对象的名字:" + s1.getName());
System.out.println("s1对象的生日:" + s1.getBirthday());
// 序列化将s1对象写入流中
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(s1);
byte[] b = bos.toByteArray();
oos.close();
bos.close();
date.setTime(55521452236L);
System.out.println("修改s1对象的生日:" + s1.getBirthday());
// 反序列化从流中读出之前写入流中的数据
ByteArrayInputStream bis = new ByteArrayInputStream(b);
ObjectInputStream ois = new ObjectInputStream(bis);
Sheep3 s2 = (Sheep3) ois.readObject();
ois.close();
bis.close();
System.out.println("克隆的s2对象:" + s2);
System.out.println("克隆的s2对象:" + s2.getName());
System.out.println("克隆的s2对象:" + s2.getBirthday());
}
}
输出结果:
s1对象:cn.zzit.prototype.Sheep3@15db9742
s1对象的名字:多利羊
s1对象的生日:Sun May 24 11:08:41 CST 1970
修改s1对象的生日:Tue Oct 05 22:37:32 CST 1971
克隆的s2对象:cn.zzit.prototype.Sheep3@6d311334
克隆的s2对象:多利羊
克隆的s2对象:Sun May 24 11:08:41 CST 1970
由输出结果同样可以看到,即使s1对象发生了变化,但是由于对象实现了深复制,所以克隆得到的对象s2并未随着s1的变化而变化。
比较深克隆与浅克隆:
浅克隆相当于复制了对象地址的引用,如果引用的值发生了改变,克隆得到的对象也随之发生变化;深克隆相当于复制了最先存储在那个内存地址上的值,如果克隆发生在那个值改变之前,克隆得到的对象并不会随着原有值的改变而改变。
最后,咱们简单的说一下克隆方式的应用场景,一般情况下克隆方式会用在对象的创建比较频繁,而且创建一次对象比较耗时的情况下。
实现代码:
package cn.zzit.prototype;
public class CloneSheep implements Cloneable {
public CloneSheep() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
测试代码:
package cn.zzit.prototype;
public class Client4 {
public static void testNew(int size) {
long start = System.currentTimeMillis();
for (int i = 0; i < size; i++) {
CloneSheep cs = new CloneSheep();
}
long end = System.currentTimeMillis();
System.out.println("使用new创建对象耗时:" + (end - start) + "毫秒");
}
public static void testClone(int size) throws Exception {
long start = System.currentTimeMillis();
CloneSheep cs = new CloneSheep();
for (int i = 0; i < size; i++) {
CloneSheep cs2 = (CloneSheep) cs.clone();
}
long end = System.currentTimeMillis();
System.out.println("使用克隆方式创建对象耗时:" + (end - start) + "毫秒");
}
public static void main(String[] args) throws Exception {
testNew(1000);
testClone(1000);
}
}
输出结果:
使用new创建对象耗时:10334毫秒
使用克隆方式创建对象耗时:10毫秒