定义:
- 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
通用类图:
/*
* 实现Cloneable接口,复习父类中的clone方法
* */
public class Prototype implements Cloneable {
@Override
public Prototype clone(){
// TODO Auto-generated method stub
Prototype prototype = null;
try {
prototype = (Prototype) super.clone();
} catch (Exception e) {
// TODO Auto-generated catch block
// 相关的异常处理
e.printStackTrace();
}
return prototype;
}
}
原型模式的优点(通常是和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者):
- 性能优良,原型模式是对内存中二进制流的拷贝,要比直接new一个对象的性能好很多
- 逃避构造函数的约束,直接在内存中拷贝,构造函数是不会执行的。
原型模式的使用场景:
- 资源优化场景,类初始化需要消耗非常多的资源,数据、硬件资源等
- 性能和安全要求的场景,通过new产生一个对象需要非常繁琐的数据准备或访问权限
- 一个对象多个修改者
原型模式的注意事项
- 构造函数不会被执行(new 会执行构造函数中的方法,通过clone则不会执行构造函数中的方法)
- 浅拷贝和深拷贝
/*
* 浅拷贝代码
* */
public class ThingSimple implements Cloneable {
// 定义一个私有变量
private ArrayList<String> arrayList = new ArrayList();
@Override
public ThingSimple clone() {
// TODO Auto-generated method stub
ThingSimple thingSimple = null;
try {
thingSimple = (ThingSimple) super.clone();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return thingSimple;
}
public void setValue(String value) {
this.arrayList.add(value);
}
public ArrayList<String> getValue() {
return this.arrayList;
}
}
public class ThingDeep implements Cloneable {
// 定义一个私有变量
private ArrayList<String> arrayList = new ArrayList();
@Override
public ThingDeep clone() {
// TODO Auto-generated method stub
ThingDeep thingDeep = null;
try {
thingDeep = (ThingDeep) super.clone();
thingDeep.arrayList = (ArrayList<String>) this.arrayList.clone();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return thingDeep;
}
public void setValue(String value) {
this.arrayList.add(value);
}
public ArrayList<String> getValue() {
return this.arrayList;
}
}
public class Client {
public static void main(String[] args) {
// 测试浅拷贝
ThingSimple thingSimple = new ThingSimple();
thingSimple.setValue("1");
ThingSimple copySimple = thingSimple.clone();
copySimple.setValue("2");
System.out.println("原数据-->" + thingSimple.getValue());
System.out.println("拷贝数据-->" + copySimple.getValue());
System.out.println(">>>>>>>>>>>>>>>");
// 测试深拷贝
ThingDeep thingDeep = new ThingDeep();
thingDeep.setValue("3");
ThingDeep copyDeep = thingDeep.clone();
copyDeep.setValue("4");
System.out.println("原数据-->" + thingDeep.getValue());
System.out.println("拷贝数据-->" + copyDeep.getValue());
}
}
原数据-->[1, 2]
拷贝数据-->[1, 2]
>>>>>>>>>>>>>>>
原数据-->[3]
拷贝数据-->[3, 4]
解析:java做了一个偷懒的拷贝动作,Object类提供的方法clone只是拷贝对象本身,其对象内部的数组、
引用对象等都不拷贝,还是指向原生对象的内部元素地址,这种拷贝叫做浅拷贝,
多个对象共享一个变量,是一种非常不安全的方式。内部的数组和引用对象不拷贝,
其他原始类型,比如int、long、char等都会被拷贝,但是对于string类型,
java希望你认为它是基本类型,它是没有clone方法的,并且处理机制也比较特殊,
通过字符串池(stringpool)在需要的时候才在内存中创建新的字符串。
注:在使用原型模式的时候,引用的成员变量必须满足,一是类的成员变量,
不是方法内变量,二必须是可变的引用对象,而不是一个原始的类型或不可变对象。
对于深拷贝不能使用final进行修饰,因为要重新赋值,对于浅拷贝因为指向的是
内存中同一个地址,所以无影响。