一. 类型
创建型模式
二. 定义
原型模式是指原型实例创建对象的种类, 并且通过拷贝这些原型创建新的对象.
调用者不需要知道任何创建细节, 不调用构造函数.
三. 适用场景
- 类初始化消耗资源较多
- new 产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)
- 构造函数比较复杂
- 循环体中产生大量对象时
四. 优点
- 性能优良, java 自带的原型模式是基于内存二进制流的拷贝, 比直接 new 一个对象性能上提升了许多
- 可以使用深克隆方式保存对象的状态, 使用原型模式将对象复制一份将其状态保存起来, 简化了创建过程.
五. 缺点
- 必须配备克隆(或者可拷贝)的方法
- 当对已有的类进行改造的时候, 需要修改代码, 违反了开闭原则
- 深拷贝、浅拷贝需要运用得当
六. 原型模式写法
浅拷贝
/**
* 类实现了Cloneable接口来指示Object.clone()方法,该方法可以合法地对类的实例进行字段对字段的复制。
* 在一个没有实现Cloneable接口的实例上调用Object的clone方法会导致抛出CloneNotSupportedException异常。
*/
@Data
public class ConcretePrototype implements Cloneable {
private int age;
private String name;
private List<String> hobbies;
@Override
public ConcretePrototype clone() {
try {
return (ConcretePrototype)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
@Override
public String toString() {
return "ConcretePrototype{" +
"age=" + age +
", name='" + name + '\'' +
", hobbies=" + hobbies +
'}';
}
}
使用案例
public static void main(String[] args) {
//创建原型对象
ConcretePrototype prototype = new ConcretePrototype();
prototype.setAge(18);
prototype.setName("Tom");
List<String> hobbies = new ArrayList<String>();
hobbies.add("书法");
hobbies.add("美术");
prototype.setHobbies(hobbies);
//拷贝原型对象
ConcretePrototype cloneType = prototype.clone();
cloneType.getHobbies().add("技术控");
System.out.println("原型对象:" + prototype);
System.out.println("克隆对象:" + cloneType);
System.out.println(prototype == cloneType);
System.out.println("原型对象的爱好:" + prototype.getHobbies());
System.out.println("克隆对象的爱好:" + cloneType.getHobbies());
System.out.println(prototype.getHobbies() == cloneType.getHobbies());
}
执行结果
--------------------------------------------------------------------------------------------
原型对象:ConcretePrototype{age=18, name='Tom', hobbies=[书法, 美术, 技术控]}
克隆对象:ConcretePrototype{age=18, name='Tom', hobbies=[书法, 美术, 技术控]}
false
原型对象的爱好:[书法, 美术, 技术控]
克隆对象的爱好:[书法, 美术, 技术控]
true
--------------------------------------------------------------------------------------------
*结论
浅拷贝时引用对象是通过 JDK 中的字节流去完成复制, JDK 底层有一个机制, 如果是一个类已经存在的时候不会去重新加载, 就会进行一个值与值的一个简单复制, 相当于拷贝的是引用, 就会造成以上修改克隆对象中的值时, 原对象也已经被修改
深拷贝
/**
* 深拷贝
*/
@Data
public class ConcretePrototype implements Cloneable,Serializable {
private int age;
private String name;
private List<String> hobbies;
@Override
public ConcretePrototype clone() {
try {
return (ConcretePrototype)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
/**
* 可以强转成ArrayList, ArrayList 的clone方法就是使用的深拷贝
*/
public ConcretePrototype deepCloneHobbies(){
try {
ConcretePrototype result = (ConcretePrototype)super.clone();
result.hobbies = (List)((ArrayList)result.hobbies).clone();
return result;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
/**
* 序列化和反序列化 可以破坏单例, 可以利用这一点来返回一个新对象
*/
public ConcretePrototype deepClone(){
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (ConcretePrototype)ois.readObject();
}catch (Exception e){
e.printStackTrace();
return null;
}
}
@Override
public String toString() {
return "ConcretePrototype{" +
"age=" + age +
", name='" + name + '\'' +
", hobbies=" + hobbies +
'}';
}
}
使用案例
public static void main(String[] args) {
//创建原型对象
ConcretePrototype prototype = new ConcretePrototype();
prototype.setAge(18);
prototype.setName("Tom");
List<String> hobbies = new ArrayList<String>();
hobbies.add("书法");
hobbies.add("美术");
prototype.setHobbies(hobbies);
//拷贝原型对象
ConcretePrototype cloneType = prototype.deepCloneHobbies();
// ConcretePrototype cloneType = prototype.deepClone();
cloneType.getHobbies().add("技术控");
System.out.println("原型对象:" + prototype);
System.out.println("克隆对象:" + cloneType);
System.out.println(prototype == cloneType);
System.out.println("原型对象的爱好:" + prototype.getHobbies());
System.out.println("克隆对象的爱好:" + cloneType.getHobbies());
System.out.println(prototype.getHobbies() == cloneType.getHobbies());
}
执行结果
--------------------------------------------------------------------------------------------
原型对象:ConcretePrototype{age=18, name='Tom', hobbies=[书法, 美术]}
克隆对象:ConcretePrototype{age=18, name='Tom', hobbies=[书法, 美术, 技术控]}
false
原型对象的爱好:[书法, 美术]
克隆对象的爱好:[书法, 美术, 技术控]
false
--------------------------------------------------------------------------------------------
*结论
以上深拷贝两种方式
- 参见 public ConcretePrototype deepCloneHobbies() 方法
可以强转成ArrayList, ArrayList 的clone方法就是使用的深拷贝 - 参见 public ConcretePrototype deepClone() 方法
序列化和反序列化 可以破坏单例, 可以利用这一点来返回一个新对象