原型模式是23种设计模式之一,很多类的拷贝都使用到了此模式。
例如Spring框架中bean的作用域prototype模式。
意义:当我们想要去复制一个对象的时候,使用原型模式,就可以无需知道该对象的内部细,快速高效的去拷贝出一个对象。
类型:创建型模式
未使用原型模式
场景:构建一个细胞类,然后创建一个红细胞,复制该红细胞
//1.细胞类 public class Cell { /** * 属性:细胞名称 * 这里属性目前只有一个方便编写、实际这里属性越多,越能体现原型模式重要性 */ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public Cell() { } public Cell(String name) { this.name = name; } }
public class Test { public static void main(String[] args) { //先创建一个细胞对象 Cell cell = new Cell(); cell.setName("红细胞"); //根据上面创建的在复制一个红细胞 Cell cell1 = new Cell(cell.getName()); //比较是相同的红细胞吗,,结果是false System.out.println(cell == cell1); } }
到这里小伙伴肯定有个疑惑,直接new不行吗,在这里确实直接new很方便,但是实际工作中,一个对象往往有几十个属性,那么在一个个填充就会非常麻烦,假如说对象嵌套,那更会麻烦,接下来我们用原型模式构建。
使用原型模式
原型模式实现思路:类本身实现Cloneable接口,该接口是Object类的方法。
在拷贝对象的时候直接调用clone()方法就可以了,但是要注意该方法返回值为Object,需要进行强制类型转换。
//1.细胞类 public class Cell implements Cloneable { /** * 属性:细胞名称 * 这里属性目前只有一个方便编写、实际这里属性越多,越能体现原型模式重要性 */ private String name; /** * 这里我们实现clone接口,默认使用父类的实现方式 */ @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public Cell() { } public Cell(String name) { this.name = name; } }
public class Test { public static void main(String[] args) throws CloneNotSupportedException { //先创建一个细胞对象 Cell cell = new Cell(); cell.setName("红细胞"); //直接使用clone方法克隆对象 Cell cell1 = (Cell)cell.clone(); //结果为: //红细胞 //false System.out.println(cell1.getName()); System.out.println(cell == cell1); } }
浅拷贝和深拷贝
到这里,原型模式已经学习完了。那么你肯定有一种感觉,这么简单。其实使用clone接口的父类方法只是一种浅拷贝,那么浅拷贝是什么呢?
浅拷贝其实把我们对象中的基本数据类型和String类型进行了拷贝,如果是对象类型,那么直接把内存地址进行拷贝,实际上还是原来的对象。
我们来测试一下clone是否是浅拷贝?
//1.细胞类 public class Cell implements Cloneable { private String name; //这里又添加了一个细胞对象细胞中含有细胞 private Cell cell; /** * 这里我们实现clone接口,默认使用父类的实现方式 */ @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public Cell getCell() { return cell; } public void setCell(Cell cell) { this.cell = cell; } public Cell() { } public Cell(String name, Cell cell) { this.name = name; this.cell = cell; } public Cell(String name) { this.name = name; } }
public class Test { public static void main(String[] args) throws CloneNotSupportedException { //先创建一个细胞对象 Cell cell = new Cell("卵细胞",new Cell("精子")); //直接使用clone方法克隆对象 Cell cell1 = (Cell)cell.clone(); //结果为true System.out.println(cell.getCell() == cell1.getCell()); } }
发现结果为true,果然是同一个精子,也就是两个不同的卵细胞用了同一个精子,这不麻烦了吗。
那么如何实现深拷贝呢?
首先我们了解什么是深拷贝:深拷贝其实就是在对象拷贝过程中,如果有引用类型的属性,那么也会重新创建一个新的对象。
而实现深拷贝,其实我们修改clone接口就可以了。
自定义clone接口
//1.细胞类 public class Cell implements Cloneable { private String name; //这里又添加了一个细胞对象细胞中含有细胞 private Cell cell; /** * 这里我们实现clone接口,默认使用父类的实现方式 */ @Override protected Object clone() throws CloneNotSupportedException { Cell clone = (Cell) super.clone(); if (this.cell != null){ clone.setCell((Cell) this.cell.clone()); } return clone; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Cell getCell() { return cell; } public void setCell(Cell cell) { this.cell = cell; } public Cell() { } public Cell(String name, Cell cell) { this.name = name; this.cell = cell; } public Cell(String name) { this.name = name; } }
public class Test { public static void main(String[] args) throws CloneNotSupportedException { //先创建一个细胞对象 Cell cell = new Cell("卵细胞",new Cell("精子")); //直接使用clone方法克隆对象 Cell cell1 = (Cell)cell.clone(); //结果为false System.out.println(cell.getCell() == cell1.getCell()); } }
最终实现了深拷贝,但是这种方式编码比较复杂,这里一个属性还行,但是二三十个属性后就很吃力了。
序列化
其实序列化可以实现深拷贝
//1.细胞类 public class Cell implements Serializable { private String name; //这里又添加了一个细胞对象细胞中含有细胞 private Cell cell; public String getName() { return name; } public void setName(String name) { this.name = name; } public Cell getCell() { return cell; } public void setCell(Cell cell) { this.cell = cell; } public Cell() { } public Cell(String name, Cell cell) { this.name = name; this.cell = cell; } public Cell(String name) { this.name = name; } }
public class Test { public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException { //先创建一个细胞对象 Cell cell = new Cell("卵细胞",new Cell("精子")); //把对象进行序列化输出 ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(byteOut); out.writeObject(cell); //把输出的对像进行反序列化 ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray()); ObjectInputStream in = new ObjectInputStream(byteIn); Cell cell1 = (Cell) in.readObject(); //false System.out.println(cell.getCell().getName()); System.out.println(cell1.getCell().getName()); System.out.println(cell.getCell() == cell1.getCell()); } }
实际操作中,我们只需要把序列化和反序列化抽出一个工具类就可以反复调用了。