定义:根据现有的对象进行克隆产生一个新的对象。当直接进行对象创建性能消耗比较大的情形之下,即可用使用原型模式。 比如在创建一对象的时候,需要通过操作数据库或者文件才能被创建。这种情况之下我们就可以对已有的对象进行缓存,然后在下次创建对象的时候返回,从而减少数据库或者文件的操作。
具体结构图(图片摘自百度百科)
需要的角色: 客户端(Client):具体调用者类。 抽象的原型类(Prototype): 实现Cloneable接口。 方便扩展。 具体的原型类(ConcretePrototype):客服端需要的对象, 重写Object的clone()方法,进行对象的克隆。
使用场景: 1、类初始化需要消耗很大的资源。 2、对性能和安全有较高的要求。 3、创建一个对象需要大量的数据准备和访问权限。 4、对象可能被多个使用者修改。 优点: 1、对于需要产生大量对象的场景, 使用clone大大提高性能。 2、直接从内存中拷贝, 跳过了构造函数的约束。 缺点: 1、必须实现Cloneable接口。 2、对于已经封装好的类不容易实现。 3、逃避了构造函数的约束。 使用示例: 1、细胞分裂。 2、游戏中大量相同对象(打飞机游戏中的子弹)。 注意事项: 1、原型模式是通过直接在内存中进行对象克隆的,因此不会执行构造方法,而且访问权限也是无效的。原型模式和单例模式是冲突的。 2、clone()方法只会对基本数据类型(包括String)进行拷贝, 对于数组、map、自定义对象等不会进行拷贝,如果需要,则需要进行深度拷贝(对这些数据另行进行拷贝)。 3、必须实现Cloneable 接口,不然会抛出CloneNotSupportedException异常。 4、clone()的访问权限为protected, 需要修改为public,不然在客户端没法调用。
示例代码:
具体原型类(这只是简单测试, 如果需要扩展,可以定义基类)
public class Mobile implements Cloneable {
private static final String TAG = "Clone";
private String name;
private String brand;
public Mobile() {
Log.i(TAG, "Mobile: 构造方法被调用啦");
}
public void setName(String name) {
this.name = name;
}
public void setBrand(String brand) {
this.brand = brand;
}
@Override
public Mobile clone() throws CloneNotSupportedException {
return (Mobile) super.clone();
}
@Override
public String toString() {
return "name = " + name + " , brand = " + brand ;
}
}
客户端测试代码:
Mobile mobile = new Mobile();
mobile.setName("红米note4");
mobile.setBrand("小米");
try {
Mobile mobile1 = mobile.clone();
mobile1.setName("vivox9");
mobile1.setBrand("vivo");
Mobile mobile2 = mobile.clone();
mobile2.setBrand("乐视");
mobile2.setName("乐视s3");
Log.i(TAG, "cloneMobile: " + mobile.toString());
Log.i(TAG, "cloneMobile: " + mobile1.toString());
Log.i(TAG, "cloneMobile: " + mobile2.toString());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
测试结果: 从输出的结果可以看出, 构造方法值被执行一次,说明后面两个对象都是通过clone实现的,从而验证了clone不调用构造方法的说法。
上面示例代码中的对象的属性采用的是String, 下面我们换成自定义对象试试:
自定义属性类:
public class Brand {
private String name;
private String sysVersion;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSysVersion() {
return sysVersion;
}
public void setSysVersion(String sysVersion) {
this.sysVersion = sysVersion;
}
@Override
public String toString() {
return "name = " + name + ", sysVersion = " + sysVersion ;
}
}
具体原型类中代码修改:
public class Mobile implements Cloneable {
private static final String TAG = "Clone";
private String name;
private Brand brand;
public Mobile() {
Log.i(TAG, "Mobile: 构造方法被调用啦");
this.brand = new Brand();
}
public void setName(String name) {
this.name = name;
}
public void setBrand(String name, String sysVersion) {
this.brand.setName(name);
this.brand.setSysVersion(sysVersion);
}
@Override
public Mobile clone() throws CloneNotSupportedException {
return (Mobile) super.clone();
}
@Override
public String toString() {
return "name = " + name + " , brand = " + brand ;
}
}
客户端测试代码:
Mobile mobile = new Mobile();
mobile.setName("红米note4");
mobile.setBrand("小米", "6.0");
try {
Mobile mobile1 = mobile.clone();
mobile1.setName("vivox9");
mobile1.setBrand("vivo", "6.0");
Mobile mobile2 = mobile.clone();
mobile2.setBrand("乐视", "5.0");
mobile2.setName("乐视s3");
Log.i(TAG, "cloneMobile: " + mobile.toString());
Log.i(TAG, "cloneMobile: " + mobile1.toString());
Log.i(TAG, "cloneMobile: " + mobile2.toString());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
测试结果:通过测试结果可以看出, 自定义属性并没有被clone,只是后面的赋值替换前面的赋值,从而以验证了clone()方法只能对基本数据类型以及String类型进行clone,而不能对数组、map、自定义数据类型进行clone(这就是常说的浅克隆)。
那怎样能实现对自定义数据类型进行拷贝呢? 实现很简单,进行深度克隆即可,即对自定义数据类型进行单独克隆,示例代码如下:
自定义数据类: 需要实现Cloneable接口, 重写clone()方法。
public class Brand implements Cloneable {
private String name;
private String sysVersion;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSysVersion() {
return sysVersion;
}
public void setSysVersion(String sysVersion) {
this.sysVersion = sysVersion;
}
@Override
public Brand clone() throws CloneNotSupportedException {
return (Brand) super.clone();
}
@Override
public String toString() {
return "name = " + name + ", sysVersion = " + sysVersion ;
}
}
具体原型类中代码
public class Mobile implements Cloneable {
private static final String TAG = "Clone";
private String name;
private Brand brand;
public Mobile() {
Log.i(TAG, "Mobile: 构造方法被调用啦");
this.brand = new Brand();
}
public void setName(String name) {
this.name = name;
}
public void setBrand(String name, String sysVersion) {
this.brand.setName(name);
this.brand.setSysVersion(sysVersion);
}
@Override
public Mobile clone() throws CloneNotSupportedException {
Mobile mobile = (Mobile) super.clone();
mobile.brand = this.brand.clone(); //对引用对象进行深度clone
return mobile;
}
@Override
public String toString() {
return "name = " + name + " , brand = " + brand ;
}
}
客户端测试代码:
Mobile mobile = new Mobile();
mobile.setName("红米note4");
mobile.setBrand("小米", "6.0");
try {
Mobile mobile1 = mobile.clone();
mobile1.setName("vivox9");
mobile1.setBrand("vivo", "6.0");
Mobile mobile2 = mobile.clone();
mobile2.setBrand("乐视", "5.0");
mobile2.setName("乐视s3");
Log.i(TAG, "cloneMobile: " + mobile.toString());
Log.i(TAG, "cloneMobile: " + mobile1.toString());
Log.i(TAG, "cloneMobile: " + mobile2.toString());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
测试结果:从测试结果看出,我们实现了深度克隆。