一、简介
原型模式是一种创建型设计模式,允许使用已有的实例对象作为原型,创建新的对象,无需知道任何如何创建的细节,也就是“克隆指定对象”。
实现原型模式的思想是让需要拷贝的原型类必须实现"java.lang.Cloneable"接口,然后重写Object类中的clone方法。
Cloneable是一个“标记接口”,所谓的标记接口就是该接口中没有任何内容,但只有实现了Cloneable接口的类才有资格调用Object类中的clone方法(该方法提供了浅拷贝的功能),否则会抛出“CloneNotSupportedException”异常。
原型模式主要有两类角色:
1.抽象原型角色((Prototype):
实现Cloneable接口重写clone方法(可定义为抽象,也可具体实现,视情况而定),来说明它有被克隆功能。
2.具体原型(Concrete Prototype)角色
继承抽象原型角色,则具体原型角色自动具有拷贝功能,这样节省代码量,当然也可以让具体原型角色直接实现实现Cloneable接口。
原型模式的类图如下:
二、使用场景
当对象的创建非常复杂,可以使用原型模式快捷的创建对象。或者在运行过程中不知道对象的具体类型,可使用原型模式创建一个相同类型的对象。
使用原型模式的优点在于可以基于原型,快速的创建一个对象,而无需知道创建的细节。另外,由于clone方法是由虚拟机直接复制内存块执行,不会执行构造方法,避免了初始化需要的时间
三、举例
public class 原型模式{
public static void main(String[] args) {
Car1ConcretePrototype car1_1=new Car1ConcretePrototype("car1",100,new ArrayList<String>(){{add("hello1");}});
Car1ConcretePrototype car1_2=(Car1ConcretePrototype)car1_1.clone();
Car2ConcretePrototype car2_1=new Car2ConcretePrototype("car2",200,new ArrayList<String>(){{add("hello2");}});
Car2ConcretePrototype car2_2=(Car2ConcretePrototype)car2_1.clone2();
car1_1.show();car1_2.show();
car2_1.show();car2_2.show();
}
}
class ICarPrototype implements Cloneable,Serializable{
private static final long serialVersionUID = 1L;
private String name;
private int price;
private ArrayList<String>otherInfo;
public ICarPrototype(String name, int price, ArrayList<String> otherInfo) {
this.name = name;
this.price = price;
this.otherInfo = otherInfo;
}
public String getName() {
return name;
}
public int getPrice() {
return price;
}
public ArrayList<String> getOtherInfo() {
return otherInfo;
}
//深度克隆方式1
@Override
protected Object clone() {
ICarPrototype cloneResult=null;
try {
//可以对基本数据类型进行复制,即Object中的clone方法实现的只是浅拷贝
cloneResult=(ICarPrototype)super.clone();
//下面对每一个复杂成员对象(如数组、容器、引用对象等)分别进行拷贝,以实现对象的深拷贝
cloneResult.otherInfo=(ArrayList<String>)this.otherInfo.clone();
} catch (CloneNotSupportedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
return cloneResult;
}
//深度克隆方式2,通过序列化对象实现深拷贝,需要实现Serializable接口
protected Object clone2() {
//声明流对象
ByteArrayOutputStream bos=null;
ByteArrayInputStream bis=null;
ObjectOutputStream oos=null;
ObjectInputStream ois=null;
try{
//创建序列化流
bos=new ByteArrayOutputStream();
oos=new ObjectOutputStream(bos);
//将当前对象以对象流的形式输出
oos.writeObject(this);
//创建反序列化流
bis=new ByteArrayInputStream(bos.toByteArray());
ois=new ObjectInputStream(bis);
//将流对象反序列化,从而实现对象的深度拷贝
return ois.readObject();
}catch(Exception e){
e.printStackTrace();
return null;
}finally{
//释放资源
try {
bos.close();bis.close();
oos.close();ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
class Car1ConcretePrototype extends ICarPrototype{
private static final long serialVersionUID = 2L;
public Car1ConcretePrototype(String name, int price,ArrayList<String> otherInfo) {
super(name, price, otherInfo);
}
public void show(){
System.out.println("**1类型车的信息:**");
System.out.println("车名:"+getName());
System.out.println("车价:"+getPrice());
System.out.println("其他信息:"+getOtherInfo().toString());
}
}
class Car2ConcretePrototype extends ICarPrototype{
private static final long serialVersionUID =3L;
public Car2ConcretePrototype(String name, int price,ArrayList<String> otherInfo) {
super(name, price, otherInfo);
}
public void show(){
System.out.println("##2类型车的信息:##");
System.out.println("车名:"+getName());
System.out.println("车价:"+getPrice());
System.out.println("其他信息:"+getOtherInfo().toString());
}
}
四、扩展
- 需要注意的是调用clone方法克隆java对象的时候不会调用构造器。
这是因为执行clone方法的时候是直接从内存中去获取数据的,在第一次创建对象的时候就会把数据在内存保留一份,克隆的时候直接使用就好了 - 访问权限对原型模式无效
由于数据从内存中直接复制的,所以克隆起来也会直接无视数据的访问权限,即使对于私有的数据也可以获取复制过来。