原型模式是一种创建型设计模式,Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。原型模式包含以下角色:
原型模式的克隆分为浅克隆和深克隆
Java中的Object类中提供了clone()方法来实现浅克隆。Cloneable接口是Java提供的抽象原型类而实现了Cloneable接口的子实现类就是具体原型类
【案例】
同一个学校的三好学生奖状除了获奖姓名不同,其他都相同,可以使用原型模式复制出多个三好学生奖状,然后修改奖状上的学生姓名即可
public class Citation implements Cloneable {
private String name; //三好学生的姓名
public String getName(){return name;}
public void setName(String name) {this.name = name;}
public void show(){System.out.println(name+"同学,在2022学年第一学期中表现优异,被评为三好学生,特颁此状");}
public Citation clone() throws CloneNotSupportedException {
return (Citation)super.clone();
}
}
public class Test{
public static void main(String[] args) throws CloneNotSupportedException {
Citation citation = new Citation();//创建原型对象
Citation clone = citation.clone();//复制原型对象
citation.setName("张三");
clone.setName("李四");
citation.show();//张三同学,在2022学年第一学期中表现优异,被评为三好学生,特颁此状
clone.show();//李四同学,在2022学年第一学期中表现优异,被评为三好学生,特颁此状
}
}
对于浅拷贝来说,具体原型对象的属性只能是基本数据类型和String类型,如果换成其他数据类型会发生什么呢
public class Citation implements Cloneable{
private Student student;
public void setStudent(Student student) {
this.student = student;
}
public Student getStudent() {
return student;
}
public void show(){
System.out.println(student.getName()+"同学,在2022学年第一学期中表现优异,被评为三好学生,特颁此状");
}
protected Citation clone() throws CloneNotSupportedException {
return (Citation)super.clone();
}
}
class CitationTest{
public static void main(String[] args) throws Exception {
Citation citation = new Citation();//创建原型对象
Student student = new Student();
student.setName("张三");
citation.setStudent(student);
Citation clone = citation.clone();//拷贝原型对象
clone.getStudent().setName("李四");
citation.show();//李四同学,在2022学年第一学期中表现优异,被评为三好学生,特颁此状
clone.show();//李四同学,在2022学年第一学期中表现优异,被评为三好学生,特颁此状
}
}
class Student{
private String name;
public String getName() {return name;}
public void setName(String name) {this.name = name;}
}
为什么两个输出都是李四同学?
因为Object类中提供了clone()方法实现的是浅克隆,对于非基本类型属性仍指向原有属性所指向的内存地址,原型类中的student对象和克隆类中的对象是同一个对象,当修改克隆类的student对象时也会修改原型类中的student对象
深拷贝就能解决以上问题
深拷贝的两种实现方式
以上面浅拷贝存在问题代码为例,我们只需要重写clone()方法和给Student类实现Cloneable接口并重写clone()方法即可完成深拷贝
//重写clone()方法
public Citation clone() throws CloneNotSupportedException {
Citation deepClone = (Citation )super.clone();
deepClone.student = (Student) student.clone();
return deepClone;
}
//重写clone()方法后Student要实现Cloneable接口
class Student implements Cloneable{
//其他代码...(和上面一样)
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
以上面浅拷贝存在问题代码为例,在主方法中序列化具体原型对象然后反序列化即可
public static void main(String[] args) throws Exception {
Citation citation = new Citation();
Student student = new Student();
student.setName("张三");
citation.setStudent(student);
/*序列化对象*/
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("b.txt"));//开启对象输出流
oos.writeObject(citation);//序列化对象
oos.close();//关闭对象输出流
/*反序列化对象*/
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("b.txt"));//开启对象输入流
Citation clone = (Citation ) ois.readObject();//反序列化对象
ois.close();//关闭对象输入流
clone.getStudent().setName("李四");
citation.show();//张三同学,在2022学年第一学期中表现优异,被评为三好学生,特颁此状
clone.show();//李四同学,在2022学年第一学期中表现优异,被评为三好学生,特颁此状
}
//因为要序列化对象所以Student类和Citation类
class Citation implements Cloneable,Serializable{//代码...}
class Student implements Serializable{//代码...}