原型模式是创建型模式的一种,其特点在于通过“复制”一个已经存在的实例来返回新的实例,而不是新建实例。被复制的实例就是我们所称的“原型”,这个原型是可定制的。原型模式多用于创建复杂的或者耗时的实例,因为这种情况下,复制一个已经存在的实例使程序运行更高效;或者创建值相等,只是命名不一样的同类数据
为什么使用原型模式
一个我们项目中经常会遇到的场景:通过数据库获取了一个对象A,这个对象的值会展示在页面上,并会根据页面的输入改变某些值。但这个对象A的原值我们需要保存,这时候我们就需要复制一个和A一样的B对象,用来展示和接收页面的值,使得A对象的值不受影响
class Human implements Cloneable {
String humanName;
String humanSex;
String humanAge;
public Human() {
}
public Human(String humanName, String humanSex, String humanAge) {
this.humanName = humanName;
this.humanSex = humanSex;
this.humanAge = humanAge;
}
@Override
public String toString() {
return "human{" +
"humanName='" + humanName + '\'' +
", humanSex='" + humanSex + '\'' +
", humanAge='" + humanAge + '\'' +
'}';
}
public Human clone() {
Human human = null;
try {
//调用Object类的clone方法,使用这个方法的对象需要实现Cloneable接口
human = (Human) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return human;
}
}
public class PrototypeDemo {
public static void main(String[] args) {
Human human1 = new Human("John", "Man", "23");
Human human2 = human1.clone();
//确认是否获取了原对象的所有值
System.out.println(human2.toString());
//确认是否是一个新的对象
System.out.println(human2.hashCode() == human1.hashCode());
}
}
上面代码有一个潜在的问题,Object类的clone方法对于拷贝的解释是,如果属性是基本数据类型它会把值复制过去,但属性如果是一个引用数据类型,它会把对应的地址复制过去,也就是说两个不同对象的属性指向了同一个引用,这就是浅拷贝。很多时候这种浅拷贝并不能满足我我们的需求,所有就有了深拷贝:利用序列化来复制对象
public Human deepClone() {
ByteArrayOutputStream byteArrayOutputStream = null;
ObjectOutputStream objectOutputStream = null;
ByteArrayInputStream byteArrayInputStream = null;
ObjectInputStream objectInput = null;
try {
//序列化
byteArrayOutputStream = new ByteArrayOutputStream();
objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(this);
//反序列化
byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
objectInput = new ObjectInputStream(byteArrayInputStream);
Human human = (Human) objectInput.readObject();
return human;
} catch (Exception e) {
System.out.println(e);
return null;
} finally {
//错误的关闭流连接
try {
objectInput.close();
byteArrayInputStream.close();
objectOutputStream.close();
byteArrayOutputStream.close();
} catch (Exception e2) {
}
}
}
序列化反序列化的方式有很多种,使用JSON也可以完成,包括很多第三方库也提供类似的功能。感觉这个原型模式使用的不是很频繁,不过在Spring中规定类的作用范围(scope)中有原型模式。上文中对于流的关闭采取了最简单的方式,这种方式也是有风险的:关闭流出错会造成流的关闭不完全。后面会介绍关闭流的正确姿势。