原型模式
1. 使用场景
- 当一个对象的创建过程非常复杂,而我们需要不断复制一个已存在对象的状态时。
- 为了防止可变对象被其它方法误修改,有必要进行保护性拷贝时
2. 实现举例
希望根据一个已有的Student对象,复制(克隆)另一个新的Student对象:
深拷贝
class Student implements Serializable {
private static final long serialVersionUID = 1L;
int id;
String name;
Date birthday;
public Student(int id, String name, Date birthday) {
super();
this.id = id;
this.name = name;
this.birthday = birthday;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + ", birthday=" + birthday + "]";
}
public Student clone() {
try {
ByteArrayOutputStream os = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(this);
ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
ObjectInputStream ois = new ObjectInputStream(is);
return (Student) ois.readObject();
} catch (ClassNotFoundException | IOException e) {
throw new RuntimeException(e);
}
}
}
使用:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Student s1 = new Student(1, "张三", sdf.parse("1995-05-21"));
Student s2 = s1.clone();
System.out.println(s1);
System.out.println(s2);
System.out.println(s2 == s1);
System.out.println(s2.birthday == s1.birthday);
输出:
Student [id=1, name=张三, birthday=Sun May 21 00:00:00 CST 1995]
Student [id=1, name=张三, birthday=Sun May 21 00:00:00 CST 1995]
false
false
要点
- 利用了对象流和数组流进行了byte级别的复制,效率较高
- 因为是byte级别的复制,因此各个field都成功复制产生了新的对象
- 需要实现Serializable接口,类中的各个field 也需要实现Serializable
浅拷贝
class Student implements Cloneable {
int id;
String name;
Date birthday;
public Student(int id, String name, Date birthday) {
super();
this.id = id;
this.name = name;
this.birthday = birthday;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + ", birthday=" + birthday + "]";
}
public Student clone() {
try {
return (Student) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
}
使用:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Student s1 = new Student(1, "张三", sdf.parse("1995-05-21"));
Student s2 = s1.clone();
System.out.println(s1);
System.out.println(s2);
System.out.println(s2 == s1);
System.out.println(s2.birthday == s1.birthday);
输出:
Student [id=1, name=张三, birthday=Sun May 21 00:00:00 CST 1995]
Student [id=1, name=张三, birthday=Sun May 21 00:00:00 CST 1995]
false
true
要点
- 利用了native clone方法进行了复制,要求类实现Cloneable接口
- native clone 的特点是
- 对于String,因为String属于不可变对象,clone时虽然也是复制了String的引用,但实际使用时不会影响
- 对于Date则有问题,因为Date是可变对象,clone时仅仅复制了它的引用,克隆后的对象如果Date的内容发生了修改,会影响原来的