原型模式通过给出一个原型对象来指定所有创建对象的类型,然后复制原型对象创建出更多同类型的对象,其主要用于对象的复制。
原型模式核心是原型类Prototype,一个原型类通常要:
- 实现Cloneable接口(在JVM中只有实现了这个接口才可以被拷贝,否则运行时会抛出CloneNotSupportedException异常);
- 重写Object类的clone方法;
示例:
//原型
interface Prototype {
void setSize(int x);
void printSize();
}
// 一个具体类
class A implements Prototype, Cloneable {
private int size;
public A(int x) {
this.size = x;
}
@Override
public void setSize(int x) {
this.size = x;
}
@Override
public void printSize() {
System.out.println("Size: " + size);
}
@Override
public A clone() throws CloneNotSupportedException {
return (A) super.clone();
}
}
//需要很多类似的对象进行测试
public class PrototypeTest {
public static void main(String args[]) throws CloneNotSupportedException {
A a = new A(1);
for (int i = 2; i < 10; i++) {
Prototype temp = a.clone();
temp.setSize(i);
temp.printSize();
}
}
}
深拷贝和浅拷贝:
- 浅拷贝:Object类的clone方法只会拷贝对象中的基本类型和String类型变量,拷贝的对象的引用与原来对象引用指向同一个堆对象;
- 深拷贝:把拷贝对象所引用的对象都复制一遍;
- 利用序列化进行深拷贝的前提是,对象以及对象内部所有引用到的对象都是可序列化的,否则就需要考察哪些不可序列化的对象是否设置成transient将其排除序列化之外;有一些对象,比如Thread或Socket是不能简单复制和共享的,不管是浅度克隆还是深度克隆都要将其设成transient!或者有程序仔细创建出相当的同种对象,权当做复制件使用。
原型模式的优点及使用场景:
- 使用原型模式创建对象比直接new一个对象在性能上要好得多,特别是在复制大对象,因为Object类的clone方法是一个本地方法,它直接操作内存中的二进制流;
- 简化对象的创建;
- 当需要重复创建相似对象时可以使用原型模型,比如在创建过程比较复杂或者在一个循环体内创建对象;
使用原型模式复制对象不会调用类的构造方法,因为对象的复制是通过Object类的clone方法来完成的——它是直接在内存中复制数据。
注意单例模式和原型模式一起使用会产生问题!
按照约定,实例的克隆应该通过调用super.clone方法获取,这样有助于克隆对象的不变性(不是必须)。