原型模式:以现有对象为原型,克隆新的一模一样的对象。减少通过构造方法新建对象时分配属性、方法的权限时间。
原型方法实现方式:实现Cloneable接口(标记接口)和Object.clone()方法、序列化实现复制。
一、实现Cloneable接口(标记接口)和Object.clone()方法(在破解单例模式中使用过)
通过java提供的Cloneable接口方式实现原型模式,分为浅克隆和深克隆。
1.浅克隆
package prototype;
import java.util.Date;
public class Sheep implements Cloneable{
//基本类型
private String name;
//对象
private Date birthday;
/**
* 此方法在object方法中 Cloneable接口是一个空接口 调用的是本地方法
* @return
* @throws CloneNotSupportedException
*/
@Override
protected Object clone() throws CloneNotSupportedException {
Sheep obj = (Sheep) super.clone();
return obj;
}
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 prototype;
import java.util.Date;
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Sheep sheep = new Sheep();
sheep.setName("小羊");
sheep.setBirthday(new Date(123213));
Sheep sheep2 = (Sheep) sheep.clone();
Date birthday = sheep.getBirthday();
birthday.setTime(123312323);
sheep.setName("大羊");
System.out.println(sheep.getName());
System.out.println(sheep.getBirthday().toString());
System.out.println(sheep2.getName());
System.out.println(sheep2.getBirthday().toString());
}
}
结果
总结:通过object的clone方法克隆的对象,当对象的属性为非基本类型时,克隆对象和被克隆对象中是同一个对象。内存示意图如下克隆是克隆属性的值(在java中全是值传递,当属性为非基本类型时属性存储的是 对象内存地址的值)
2.深克隆
为了解决上一问题,我们需要对象也为新克隆出来的对象。 那么我们需要重写clone方法。
@Override
protected Object clone() throws CloneNotSupportedException {
Sheep obj = (Sheep) super.clone();
obj.birthday = (Date) this.birthday.clone();
return obj;
}
增加了属性对象的单独克隆obj.birthday = (Date) this.birthday.clone(); 实现深克隆
二、通过序列化实现深克隆
序列化对象实现Serializable接口 ,其他不用修改
测试代码
package prototype;
import java.io.*;
import java.util.Date;
public class Client2 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Sheep sheep = new Sheep();
sheep.setName("小羊");
sheep.setBirthday(new Date(123213));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(sheep);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
Sheep sheep2 = (Sheep) ois.readObject();
sheep.getBirthday().setTime(11111111);
System.out.println(sheep.getName());
System.out.println(sheep.getBirthday().toString());
System.out.println(sheep2.getName());
System.out.println(sheep2.getBirthday().toString());
}
}
测试结果
三、性能对比,测试代码都是根据实际使用代码,如你有差异,请指出共同勉进,谢谢
package prototype;
import java.io.*;
import java.util.Date;
public class Client3 {
static int MAXCYCLIC = 100000;
public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
Sheep sheep = new Sheep();
sheep.setName("小羊");
sheep.setBirthday(new Date(123213));
Long start = System.currentTimeMillis();
for(int i=0; i<MAXCYCLIC;i++){
Sheep sheep1 = new Sheep();
sheep.setName("小羊");
sheep.setBirthday(new Date(123213));
}
Long end = System.currentTimeMillis();
System.out.println("构造方法创建对象所需时间"+(end-start));
start = System.currentTimeMillis();
for(int i=0; i<MAXCYCLIC;i++){
Sheep sheep1 = (Sheep) sheep.clone();
}
end = System.currentTimeMillis();
System.out.println("克隆接口方式创建对象所需时间"+(end-start));
start = System.currentTimeMillis();
for(int i=0; i<MAXCYCLIC;i++){
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(sheep);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
Sheep sheep2 = (Sheep) ois.readObject();
ois.close();
bais.close();
oos.close();
baos.close();
}
end = System.currentTimeMillis();
System.out.println("序列化方式创建对象所需时间"+(end-start));
}
}
测试结果(每次结果根据电脑资源情况 会不一致):序列化方式因为存在 输入输出流的创建和关闭非常耗资源和时间。
总结:接口克隆方式性能高但是我们需要更关注代码的实现,序列化方式简单粗暴,但是性能相对低, 看具体情况择优选择。