原型模式
-
简单描述:创建对象的一种方式,相对于new方法创建对象,原型模式通过二进制流进行拷贝。说白了就是一个类实现Cloneable 接口。
-
应用场景:
- 类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等,通过原型拷贝避免这些消耗
- 通过new一个对象需要非常繁琐的数据准备或访问权限,可以使用原型模式
- 一个对象有可能被其他对象访问,而且还有可能修改类里的属性状态等,若想保持原属性不变,需要使用原型模式。
-
优点:
创建对象快啊,直接通过二进制流拷贝,多香
-
缺点:
直接在内存中拷贝,构造函数是不会执行的
-
代码
浅复制
@Data public class Car { private String color; private String plateNumber; } @Data public class Test implements Cloneable{ //引用类型 private Car car; //基本类型 private int num; //引用类型 private String status; Test(){ Car newCar = new Car(); newCar.setColor("蓝宝石"); newCar.setPlateNumber("辽A88888"); this.car = newCar; System.out.println("我创建了一个car"); } @Override public Object clone() throws CloneNotSupportedException{ return super.clone(); } public static void main(String[] args) throws CloneNotSupportedException { Test tt = new Test(); tt.setNum(100); tt.setStatus("ff"); Test tClone = (Test)tt.clone(); tClone.getCar().setColor("红宝石"); tClone.setNum(1); tClone.setStatus("s"); System.out.println(JSON.toJSONString(tt)); System.out.println(JSON.toJSONString(tClone)); } }
String 的特殊性
这里说明一下,类分为基本类型和引用类型,当浅复制的时候基本类型可以自动实现克隆,属于值传递;而引用类型则是传递对象的引用,也就是复制出来的对象还是指向原来的地址,故此改变对象引用的值,肯定会影响原来的对象。那么String也是引用类型,为何它却可以实现深复制呢?究其根本String是一个final类,它是不可变的,当我们改变复制后的String的值,会产生一片新的内存空间,此时该对象的这个属性的引用将指向这片新的内存空间,此时两个对象的String类型的属性指向的就是不同的2片内存空间,改变一个不会影响到另一个,可以当做基本类型来使用。
深复制
深复制有两种方式,一种是复制的对象内部的引用类型也实现cloneable接口;另一种是实现Serializable接口,通过字节流序列化实现深拷贝。这里贴出第二种的代码(此代码摘自https://blog.csdn.net/zyxhangiian123456789/article/details/98630429)
import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; /** * 通过字节流序列化实现深拷贝,需要深拷贝的对象必须实现Serializable接口 * * @author Administrator */ public class CloneUtils { @SuppressWarnings("unchecked") public static <T extends Serializable> T clone(T obj) { T cloneObj = null; try { // 写入字节流 ByteArrayOutputStream out = new ByteArrayOutputStream(); ObjectOutputStream obs = new ObjectOutputStream(out); obs.writeObject(obj); obs.close(); // 分配内存,写入原始对象,生成新对象 ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray()); ObjectInputStream ois = new ObjectInputStream(ios); // 返回生成的新对象 cloneObj = (T) ois.readObject(); ois.close(); } catch (Exception e) { e.printStackTrace(); } return cloneObj; } }
综上,如果在深复制过程中有些类没有实现Serializable接口,或是不想让某些类进行复制,则可以在该对象的声明前加上transient关键字
@Data public class Test implements Serializable{ //这样复制Test对象的时候这个Car就不会被复制,复制出来的对象中的car则是个null private transient Car car; private int num; private String status; }