原型模式顾名思义,就是指以某个实例为原型,copy出一个新的实例,该实例属性与原型相同或者是类似。
很多时候,我们需要创建大量的相同或者相似的对象,如果一个个用new 构造函数的形式去创建的话比较繁琐,就像孙悟空要想变出成千上万个猴子猴孙总不能一个个去生吧,拔一根猴毛一吹就copy出了成千上万个猴子猴孙,我们原型模式原理也是如此,那就是clone克隆。
我们都知道JAVA中有个祖宗级别的类Object,每个类都默认继承它,它提供了一个本地方法clone(),代码如下:
protected native Object clone() throws CloneNotSupportedException;
可以看到它抛出了一个CloneNotSupportedException异常,不支持克隆,其实这个本地方法中有做一件事情,那就是判断原型类有没有实现CloneAble接口,如果没有,那就直接抛出该异常,我随便建了个实例调用clone方法,确实该抛异常了。
这就是原型模式一个典型的特点,规定了具体原型对象必须实现的接口。
原型模式包含以下主要角色。
(1)抽象原型类:规定了具体原型对象必须实现的接口。
(2)具体原型类:实现抽象原型类的clone()方法,它是可被复制的对象。
(3)访问类:使用具体原型类中的clone()方法来复制新的对象。
在上面的例子中Object就是抽象原型类,他规定了原型类必须实现CloneAble接口。
原型模式的结构图如下:
原型模式的克隆又分为浅克隆跟深克隆
Java中Object类中的clone()的实现是浅克隆,浅克隆指复制值类型,引用类型的地址不复制,也就是说克隆出来的对象中的引用类型的成员变量的地址跟原型中的地址是同一个,经常会一不注意就掉这坑里了,这也是原型模式的一个缺点吧。
深克隆就正好填上了那个坑,一般采用序列化+流的方式,在clone()方法中将原型对象序列化并写入流中,再从流中将字节流取出来反序列化成原型对象,此时,对象地址已经改变。代码如下:
public SubAccountInfo clone(){
SubAccountInfo subAccountInfo = null;
try {
//将对象序列化到流里
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(this);
//将流反序列化成对象
ByteArrayInputStream is = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream ois = new ObjectInputStream(is);
subAccountInfo = (SubAccountInfo) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return subAccountInfo;
}
这里要注意原型类需要实现Serializable接口,否则无法序列化,抛出异常如下:
java.io.NotSerializableException: GamePlusCom.GamePlus.game.entity.subAccount.SubAccountInfo
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at GamePlusCom.GamePlus.game.entity.subAccount.SubAccountInfo.clone(SubAccountInfo.java:78)
at GamePlusCom.GamePlus.game.constant.GlobalConstant.main(GlobalConstant.java:36)
原型模式就讲到这,原型模式跟单例模式都属于创建型模式,创建型模式还有工厂方法模式、抽象工厂模式、建造者模式,有时间我会一一分享。
分享是种乐趣~~~修BUG也是