指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新 的对象,属于创建型模式。 说白了就是拷贝创建对象,不通过调用构造方法.
优点:
- 性能优良 ,Java 自带的 原型模式 是基于内存二进制流的拷贝,比直接 new 一个对象性能上提 升了许多
- 可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,简化 了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。
缺点:
- 需要为每一个类配置一个克隆方法
- 克隆方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违反了开闭原则。
- 在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克 隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深拷贝、浅拷贝需要运用得 当
使用场景
为什么需要原型模式?我们直接通过new不好了吗?
比如如果一个类的属性有30个,这时候你怎么办? 通过构造方法创建?,那万一有些是可选字段,有些是必选呢?,这时候就要创建很多构造方法。
- 类初始化消耗资源较多
- new产生一个对象需要非常繁琐的过程(比如数据准备,访问权限)
- 构造函数比较复杂
- 循环体中产生大量对象
在 Spring 中,原型模式应用得非常广泛。例如 scope=“prototype”,在我们经常用的 JSON.parseObject()也是一种原型模式。
浅克隆
Demo
JDK 已经帮我们实现了一个现 成的 API,我们只需要实现 Cloneable 接口即可。
public class Parkin implements Cloneable{
private String name;
private int age;
private List<String> hobbies = new ArrayList<>();
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setHobbies(List<String> hobbies) {
this.hobbies = hobbies;
}
public List<String> getHobbies() {
return hobbies;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Parkin{" +
"name='" + name + '\'' +
", age=" + age +
", hobbies=" + hobbies +
'}';
}
}
// 测试类
public class ShadowClone {
public static void main(String[] args) throws CloneNotSupportedException {
Parkin parkin1 = new Parkin();
Parkin parkin2 = (Parkin) parkin1.clone();
System.out.println(parkin1 == parkin2); // false 证明不是同一个对象
parkin1.setAge(23);
parkin1.setName("Parking1");
parkin2.setAge(12);
parkin2.setName("Parking2");
/**
* 上面值改了,还是改的自己的
*/
System.out.println(parkin1);
System.out.println(parkin2);
/**
* 结果为true
* 引用的是同一个对象
*
*/
System.out.println(parkin1.getHobbies() == parkin2.getHobbies());
/**
* 发现基本类型可以改变,引用类型的还是同一个对象
* 那String也是引用类型啊,为什么他也变了呢?
* String是不可变对象,因为是final修饰的,所以他会重新创建一个
*
*/
}
}
深克隆
如何解决上面那个引用的对象没变呢?这就需要用到深克隆了。
深克隆有2种实现方式
- 序列化
- JSON
Demo
public class Parkin implements Serializable {
private String name;
private int age;
private List<String> hobbies = new ArrayList<>();
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setHobbies(List<String> hobbies) {
this.hobbies = hobbies;
}
public List<String> getHobbies() {
return hobbies;
}
@Override
public String toString() {
return "Parkin{" +
"name='" + name + '\'' +
", age=" + age +
", hobbies=" + hobbies +
'}';
}
}
// 测试类
public static void main(String[] args) throws IOException, ClassNotFoundException {
Parkin parkin1 = new Parkin();
// 1.第一种方式:序列化 也可以使用工具类
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(parkin1);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
Parkin parkin2 = (Parkin) objectInputStream.readObject();
System.out.println(parkin1 == parkin2);
System.out.println(parkin1.getHobbies() == parkin2.getHobbies());
// 2. 第二种方式:使用JSON
Parkin parkin3 = JSON.parseObject(JSON.toJSONString(parkin1), Parkin.class);
System.out.println(parkin1 == parkin3);
System.out.println(parkin1.getHobbies() == parkin3.getHobbies());
}
克隆破坏单例
Demo
public class Singleton implements Cloneable{
private static final Singleton SINGLETON = new Singleton();
private Singleton(){}
public static Singleton getInstance() {
return SINGLETON;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
//测试类
public static void main(String[] args) throws CloneNotSupportedException {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = (Singleton) singleton1.clone();
System.out.println(singleton1 == singleton2);
/**
* 运行结果为false,违背了单例
* 解决方法:
* 1. 不实现Cloneable接口
* 2. 在clone方法里不调用父类的clone方法,直接返回SINGLETON实例
*/
}