原型模式(Prototype Pattern)
原型模式就是为了对象拷贝的,省掉了堆内存一系列的复杂流程。
对象拷贝分为深拷贝和浅拷贝
浅拷贝:将对象中的简单类型和String类型的值进行复制,引用类型复制的只是对象的引用地址
深拷贝:所有的类型都是直接复制的值,包括引用对象;如果是引用对象,会新创建一个对象,并且引用地址改为该对象。
原型模式的优点:
1、根据客户端要求实现动态创建对象,客户端不需要知道对象的创建细节,便于代码的维护和发展
2、使用原型模式创建对象比直接new一个对象在性能上要好得多,因为Object类的clone方法是一个本地方法,他直接操作内存中的二进制流,特别是复制大对象时,性能差别非常明显。
使用原型模式时需要注意的点:
1、使用原型模式复制对象不会调用类的构造方法
2、在使用时要注意深拷贝和浅拷贝的问题
浅拷贝代码示例:
package com.lcl.galaxy.design.pattern.prototype; import java.io.Serializable; public class Prototype implements Cloneable, Serializable { private static final long serialVersion = 1L; private String name; private int age; private Order order; public Prototype shallowClone() throws CloneNotSupportedException { Prototype prototype = (Prototype) super.clone(); return prototype; } }
浅复制主要的点就是需要对象实现Cloneable接口,该接口没有方法,只是做一个标识,JVM虚拟机看到该标识后,就可以做对象的浅拷贝。至于具体哪个方法做对象复制,自己定义方法名即可,在方法中直接调用super的clone方法即可
深拷贝代码示例:
package com.lcl.galaxy.design.pattern.prototype; import java.io.*; public class Prototype implements Cloneable, Serializable { private static final long serialVersion = 1L; private String name; private Order order; public void setName(String name) { this.name = name; } public void setOrder(Order order) { this.order = order; } public String getName() { return name; } public Order getOrder() { return order; }public Prototype deepClone() throws IOException, ClassNotFoundException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(this); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); return (Prototype) ois.readObject(); } }
深拷贝的代码主要是使用字节流来创建一个新的对象,那么无论是该对象本身还是该对象中的引用对象,都需要实现Serializable序列化接口。
针对上述浅拷贝和深拷贝,测试代码如下:
package com.lcl.galaxy.design.pattern; import com.alibaba.fastjson.JSON; import com.lcl.galaxy.design.pattern.builder.Animal; import com.lcl.galaxy.design.pattern.builder.AnimalBuilder; import com.lcl.galaxy.design.pattern.prototype.Order; import com.lcl.galaxy.design.pattern.prototype.Prototype; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import java.io.IOException; @Slf4j @SpringBootTest public class PrototypeTest { @Test public void prototypeShallowCloneTest() throws CloneNotSupportedException { Prototype prototype = getPrototype(); Prototype copyPrototype = prototype.shallowClone(); print(prototype, copyPrototype); } @Test public void prototypeDeepCloneTest() throws IOException, ClassNotFoundException { Prototype prototype = getPrototype(); Prototype copyPrototype = prototype.deepClone(); print(prototype, copyPrototype); } private Prototype getPrototype(){ Order order = new Order(); order.setOrderId("1234"); Prototype prototype = new Prototype(); prototype.setOrder(order); prototype.setName("lcl"); return prototype; } private void print(Prototype prototype, Prototype copyPrototype){ log.info("prototype=================={}=================", prototype); log.info("copyPrototype=================={}=================", copyPrototype); log.info("prototype=================={}=================", JSON.toJSONString(prototype)); log.info("copyPrototype=================={}=================", JSON.toJSONString(copyPrototype)); log.info("prototype.getOrder()=================={}=================", prototype.getOrder()); log.info("copyPrototype.getOrder()=================={}=================", copyPrototype.getOrder()); log.info("prototype.equals(copyPrototype)=================={}=================", prototype.equals(copyPrototype)); log.info("prototype.getName().equals(copyPrototype.getName())=================={}=================", prototype.getName().equals(copyPrototype.getName())); log.info("prototype.getOrder().equals(copyPrototype.getOrder())=================={}=================", prototype.getOrder().equals(copyPrototype.getOrder())); log.info("prototype.getOrder().getOrderId().equals(copyPrototype.getOrder().getOrderId())=================={}=================", prototype.getOrder().getOrderId().equals(copyPrototype.getOrder().getOrderId())); } }
最终的输出结构,虽然取到的值多一样,但是如果对比Prototype中的Order对象,浅拷贝和深拷贝则是两种结果:浅拷贝为true,即引用对象为同一个对象;深拷贝为false,即引用对象不是同一个对象。
------------------------------------------------------------------
-----------------------------------------------------------
---------------------------------------------
朦胧的夜 留笔~~
-----------------------------------------------------------
---------------------------------------------
朦胧的夜 留笔~~