原型模式
模式说明
需要获取一个类的多个实例的场景下,若该类的构造器需要做较多的初始化工作,则创建多个实例,会因为每次创建都需要执行构造器中的初始化过程,耗时耗资源。如果在创建第一个实例后,后续实例都直接拷贝该实例,就可以避免每次获取实例时的初始化开销,对于需要修改的字段针对性修改即可,这就是原型模式。根据对拷贝对象中的引用类型字段的拷贝程度分为浅拷贝和深拷贝。
浅拷贝
对原型P的实例p1执行实例p1.clone()方法拷贝给p2时(P p2 = p1.clone()),对p1内的引用类型字段(准确地说是可变引用类型)q(类型Q),只拷贝其引用,这就是浅拷贝。Java中重写clone方法时若只调用super.clone(),则该clone方法在使用时即为浅拷贝,即上述p1.q == p2.q。注意clone方法在Cloneable接口中是native方法,返回类型是Object,赋值给p2时要向下转型。
// 第一个实例通过new创建
P p1 = new P();
// 此后通过拷贝创建
P p2 = (P)p1.clone();可以这样描述:p1.clone()在堆中生成一个新的实例,p2是这个新实例的引用(地址),新实例中有p1的基本数据和不变类型(final修饰的引用类型如String)的独立的完整拷贝,但对于可变的引用类型,只拷贝其引用。即如果p1中有一可变引用字段Q,那么p1.q == p2.q,p1和p2中的q指向同一个实例,即原本p1中的q实例。
深拷贝
以上述浅拷贝中的描述为例,深拷贝对p中的可变引用类型q,也拷贝一个完整的q到新实例p2中(与q并列的独立的Q类型新实例),此时p1.q !=p2.q。深拷贝要求Q自身也要实现Cloneable接口并重写clone方法。并在原型P的重写clone方法内针对q要有类似如下的语句。
p.q = (Q)p.getQ().clone();
本示例定义一个Person类,使其实现Cloneable接口并通过重写clone方法使其支持浅拷贝。在Person类中定义一个不变引用类型Country和一个可变引用类型WorkExperience,展示浅拷贝Person时,对不变类型Country拷贝出一个新实例,对可变引用类型WorkExperience拷贝的是它的引用(地址)。作为对比,本示例定义一个PersonDeep类,该类内有一个可变引用类型WorkExperienceDeep,使WorkExperienceDeep也实现Cloneable接口并重写clone方法,且在PersonDeep类的clone方法中,针对可变引用l类型WorkExperienceDeep,执行一条对它的clone方法来得到它的一个实例,从而实现对PersonDeep的深拷贝。
结构
原型接口: 通常为Cloneable。 具体原型类: 实现Cloneable接口并重写clone方法,分为深拷贝和浅拷贝。
代码演示
Last updated