概述
- 是一种对象创建型模式,使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象,主要用于创建重复的对象,同时又能保证性能
- 工作原理是将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝自己来实现创建过程
- 应该是最简单的设计模式了,实现一个接口,重写一个方法即完成了原型模式
核心组成
- Prototype: 声明克隆方法的接口,是所有具体原型类的公共父类,Cloneable接口
- ConcretePrototype : 具体原型类
- Client: 让一个原型对象克隆自身从而创建一个新的对象
应用场景
- 创建新对象成本较大,新的对象可以通过原型模式对已有对象进行复制来获得
- 如果系统要保存对象的状态,做备份使用
编码实现
定义一个对象,实现Cloneable
接口,重写clone
方法
public class Person implements Cloneable{
private String name;
public Person() {
System.out.println("构造方法被调用了");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected Person clone() throws CloneNotSupportedException {
return (Person)super.clone();
}
}
使用:
public static void main(String[] args) throws CloneNotSupportedException {
Person person = new Person();
person.setName("张三");
Person person1 = person.clone();
person.setName("李四");
Person person1 = person.clone();
person.setName("王五");
}
观察控制台,只有一句话:构造方法被调用了,证明构造方法只被调用了一次。
场景说明:如果Person对象里面有几十种属性,但只需要就改变其中的几个属性,使用原型设计模式会方便很多。
问题剖析——深拷贝与浅拷贝
看起来是不是很简单?别着急还没完呢,如果里面是一个复杂的数据类型呢?
例如对象为List,通过DEBUG模式发现,拷贝后的内存地址是一样的,因此当一个对象修改了List内容,其他对象都会同步修改,如果我们不想同步修改该怎么办呢?
- 浅拷贝:如果原型对象的成员变量是基本数据类型(int、double、byte、boolean、char等),将复制一份给克隆对象;
如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址
通过覆盖Object类的clone()方法可以实现浅克隆 - 深拷贝:无论原型对象的成员变量是基本数据类型还是引用类型,都将复制一份给克隆对象,如果需要实现深克隆,可以通过序列化(Serializable)等方式来实现
- 原型模式是内存二进制流的拷贝,比new对象性能高很多,使用的时候记得注意是选择浅拷贝还是深拷贝
我们让拷贝的对象进行一次序列化与反序列化,这样内存地址就改变了。既然涉及到了序列化就需要实现Serializable
接口
public class Person implements Cloneable, Serializable {
private String name;
private List<String> myList;
public Person() {
System.out.println("构造方法被调用了");
}
//get&&set
@Override
protected Person clone() throws CloneNotSupportedException {
return (Person)super.clone();
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", myList=" + myList +
'}';
}
/**
* 深拷贝
* 原理:经过了序列化与反序列化,内存地址会发送变更
*/
public Object deepClone() {
try {
//输出 序列化
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
//输入 反序列化
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
Person copyObj = (Person) ois.readObject();
return copyObj;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
测试:
public static void main(String[] args) throws CloneNotSupportedException {
ArrayList<String> myList = new ArrayList<String>() {{
add("张三丰");
add("赵六");
}};
Person person = new Person();
person.setName("张三");
person.setMyList(myList);
// 浅拷贝
Person person1 = person.clone();
person1.setName("李四");
person1.getMyList().add("测试1");
// 深拷贝
Person person2 = (Person) person.deepClone();
person.setName("赵六");
person2.getMyList().add("测试2");
System.out.println(person);
System.out.println(person1);
System.out.println(person2);
}
结果
构造方法被调用了
Person{name='赵六', myList=[张三丰, 赵六, 测试1]}
Person{name='李四', myList=[张三丰, 赵六, 测试1]}
Person{name='张三', myList=[张三丰, 赵六, 测试1, 测试2]}