模式定义:
指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
- 举个例子:对象A初始化好很多实例,如果要得到实例的拷贝如何得到?
传统的方式通过new的方式
现在我们可以在实例类中提供一个clone方法,通过这个方法拿到实例的数据。
做一个实验室项目,实验室中对一个数据可能会有n多个样品数据,他们之间的差异会非常小,传统:创建100个实验项目,new 100次
将实例类实现cloneable接口(它是一个标记接口,里面没有任何方法);
重写Object中的clone()方法,返回Computer类型,super.clone()调用的是native方法,是由JVM 实现的,完成我们数据的拷贝。
如何拿到当前实例的拷贝 :实例名.clone();
克隆方法是如何工作的呢?
在克隆的这个步骤中,我们需要注意的是当初始化的对象中包含可变引用数据的话,对于这个可变的引用数据也需要实现cloneable接口,并在该对象中重写clone()方法,否则的话,我们克隆后的数据的引用是指向原数据的,当改变原数据后,克隆对象的数据也会改变,这就不符合我们的需求了。
对于不可变属性,我们在修改原数据后,clone的数据不会发生变化。
实现如下:
public class prototypeTest { public static void main(String[] args) throws CloneNotSupportedException { ComputerInfo xiosad = new ComputerInfo("xiosad"); Computer computer = new Computer("sada", "sd", "dsa", "sa", "ssss", "sssaa",xiosad); System.out.println("computer = " + computer); Computer clone = computer.clone(); System.out.println("clone = " + clone); //修改不可变属性CompanyName--->clone的数据中CompanyName不会发生变化 computer.setCompanyName("gaibian"); //修改可变引用类型ComputerInfo computer.getComputerInfo().setComputerName("修改的"); System.out.println("computer = " + computer); System.out.println("clone = " + clone); }}class ComputerInfo{ private String computerName; public ComputerInfo(String computerName) { this.computerName = computerName; } public String getComputerName() { return computerName; } public void setComputerName(String computerName) { this.computerName = computerName; } @Override public String toString() { return super.hashCode()+" ] ComputerInfo{" + "computerName='" + computerName + ''' + '}'; }}class Computer implements Cloneable { //实假设需要构建的对象的字段如下 private String computerName; private String companyName; private String cpu; private String part2; private String part3; private String part4; private ComputerInfo computerInfo; public Computer() { } public Computer(String computerName, String companyName, String cpu, String part2, String part3, String part4,ComputerInfo computerInfo) { this.computerName = computerName; this.companyName = companyName; this.cpu = cpu; this.part2 = part2; this.part3 = part3; this.part4 = part4; this.computerInfo = computerInfo; } public String getComputerName() { return computerName; } public void setComputerName(String computerName) { this.computerName = computerName; } public String getCompanyName() { return companyName; } public void setCompanyName(String companyName) { this.companyName = companyName; } public String getCpu() { return cpu; } public void setCpu(String cpu) { this.cpu = cpu; } public String getPart2() { return part2; } public void setPart2(String part2) { this.part2 = part2; } public String getPart3() { return part3; } public void setPart3(String part3) { this.part3 = part3; } public String getPart4() { return part4; } public void setPart4(String part4) { this.part4 = part4; } public ComputerInfo getComputerInfo() { return computerInfo; } public void setComputerInfo(ComputerInfo computerInfo) { this.computerInfo = computerInfo; } @Override protected Computer clone() throws CloneNotSupportedException { return ((Computer) super.clone()); } @Override public String toString() { return super.hashCode()+" ] Computer{" + "computerName='" + computerName + ''' + ", companyName='" + companyName + ''' + ", cpu='" + cpu + ''' + ", part2='" + part2 + ''' + ", part3='" + part3 + ''' + ", part4='" + part4 + ''' + ", computerInfo=" + computerInfo + '}'; }}
运行结果:
如何解决这个问题呢,这就需要深拷贝来实现,对于可变引用数据类型,我们也要clone它,因此对于ComputerInfo类,我们要实现Cloneable接口,修改后的ComputerInfo类:
class ComputerInfo implements Cloneable{ private String computerName; public ComputerInfo(String computerName) { this.computerName = computerName; } public String getComputerName() { return computerName; } public void setComputerName(String computerName) { this.computerName = computerName; } @Override protected ComputerInfo clone() throws CloneNotSupportedException { return ((ComputerInfo) super.clone()); } @Override public String toString() { return super.hashCode()+" ] ComputerInfo{" + "computerName='" + computerName + ''' + '}'; }}
Computer类中的clone()方法需要设置ComputerInfo的克隆:
@Overrideprotected Computer clone() throws CloneNotSupportedException { Computer clone = (Computer)super.clone(); ComputerInfo clone1 = this.computerInfo.clone(); clone.setComputerInfo(clone1); return clone;}
测试运行结果:
- 如果可变引用特别多的话,可以利用java的序列化机制来实现深拷贝。
1)修改重写的clone()函数如下。
@Override protected Computer clone() throws CloneNotSupportedException {// v1 --深复制版本一// Computer clone = (Computer)super.clone();// ComputerInfo clone1 = this.computerInfo.clone();// clone.setComputerInfo(clone1);// return clone;// v2 --深复制-序列化// 1.定义一下输出流 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();// 2.把JVM中的数据写到流中 try { ObjectOutputStream oos = new ObjectOutputStream(byteArrayOutputStream); oos.writeObject(this); } catch (IOException e) { e.printStackTrace(); } // 3.将流读到对象中 ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); try { ObjectInputStream ois = new ObjectInputStream(byteArrayInputStream); Computer object = ((Computer) ois.readObject()); return object; } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; }
2)序列化Computer类、ComputerInfo类(实现Serializable接口即可)
执行结果:
当然深拷贝的操作不建议利用序列化,因为序列化的方式的代码实现是CPU密集型的,解析流消耗性能,速度会变慢,实际使用时需要平衡性能
应用场景:
当代码不应该依赖于需要复制的对象的具体类时,使用原型模式
优点:
1)可以不耦合具体类的情况下克隆对象
2)避免重复的初始化对象
3)更方便构建复杂对象
Spring中的源码实现:
java.util.ArrayList
org.springframework.beans.factory.support.AbstractBeanDefinition