定义:
指原型实例指定创建对象的种类, 并且通过拷贝这些原型创建新的对象. 不需要知道任何创建的细节, 不调用构造函数
适用场景:
类初始化的时候消耗较多资源
new 产生的对象需要非常繁琐的过程
构造函数比较复杂
循环体中产生大量对象
详解:
接下来我们分下面几部分讲解:
原型模式的核心
深克隆和浅克隆
JDK 源码分析
1. 原型模式的核心
其实很简单, 就是实现 Cloneable 接口, 然后重写 clone()方法. 上面我们已经说过 , 当你在上面的适用场景中的时候, 按照我们平常的办法来说肯定是直接 new 对象出来, 但是 new 对象特别多的时候就会消耗很多资源, 并且效率也是比较缓慢的. 所以我们引入原型模式的情况, 其实我们只需要创建出一个原型来 , 剩下的完全可以通过克隆来达到创建新对象的目的. 克隆是底层直接拿二进制流来克隆出新对象, 然后对新对象进行特别的操作.
2. 深克隆和浅克隆
这时候你心里可能会有疑惑, 克隆不就克隆就行了吗? 怎么还分浅克隆和深克隆. 事实上在一个类中如果有另外的类的实例作为属性的话, 正常使用 Object.clone()方法, 这个对象成员是无法被克隆的, 也就是浅克隆. 所以你怎么改原型中的对象成员, 后面克隆的版本中这个对象成员就会一直跟原型一样. 但是我们的目标是创建新对象来进行特定的操作, 也就是希望每个对象里面的值不会跟他人共享. 新对象是新对象, 原型是原型. 所以我们需要深克隆. 下面我举几个例子来看看
- public class Student implements Cloneable{
- private int age;
- private Date date;
- private String name;
- public void setAge(int age) {
- this.age = age;
- }
- public void setDate(Date date) {
- this.date = date;
- }
- public void setName(String name) {
- this.name = name;
- }
- @Override
- public String toString() {
- return "Student{" +
- "age=" + age +
- ", date=" + date +
- ", name='" + name + '\'' +
- '
- }';
- }
- @Override
- protected Object clone() throws CloneNotSupportedException {
- return super.clone();
- }
- }
然后我们写一下测试类
- class Test {
- public static void main(String[] args) throws CloneNotSupportedException {
- Student stu=new Student();
- Student stu1= (Student) stu.clone();
- Date date=new Date(0L);
- int i=10;
- stu.setDate(date);
- stu1.setDate(date);
- stu.setAge(i);
- stu1.setAge(i);
- System.out.println(stu);
- System.out.println(stu1);
- date.setTime(666666666L);
- stu.setDate(date);
- i=11;
- stu.setAge(i);
- System.out.println(stu);
- System.out.println(stu1);
- }
- }
最终输出结果就是
分析一下
注意看这 4 行, 我们上面是两个都调用了 set 方法, 下面只有 stu 原型调用了 set 方法, 但是最终却两个对象中的值一起改了. 可能细心的的注意到 Age 只有 stu 改了, 这是因为 int 类型基本数据类型. 而 Date 类型的对象成员就不行了, 实际上只有 Student 这个类进行了克隆, 但是 Student 里面的对象成员变量没有进行克隆. 所以那个对象还是那个对象.
上面就是浅克隆. 要想做到深克隆, 可以在 clone 方法里面 clone 出对应的对象成员结果及代码如下
上面我重写了 clone()方法, 实现了深克隆
3.JDK 源码解析
其实我们主要理解拷贝原型来创建新的对象, 拷贝是比 new 更快的一个创建对象的方法, 当你需要大批量创建新对象而且都是同一个类的对象的时候可以考虑用原型模式. 但是千万千万注意就是一般的克隆只是浅克隆 (浅克隆: 只是对象的 hash 值不一样, 但是对象里面的对象成员变量的 hash 值是一样的) 有些场景可能是需要我们深克隆的, 这时候就需要我们重写 Object.clone()方法. 就拿 ArrayList 中的 clone()方法来看
- public Object clone() {
- try {
- ArrayList<?> v = (ArrayList<?>) super.clone();// 先克隆出一个 ArrayList
- v.elementData = Arrays.copyOf(elementData, size);// 将原型中的数据拷贝到新的 ArrayList 中
- v.modCount = 0;// 把修改次数改为 0
- return v;
- } catch (CloneNotSupportedException e) {
- // this shouldn't happen, since we are Cloneable
- throw new InternalError(e);
- }
- }
相信看懂了上面的实例, 你也能理解 ArrayList 中这段代码到底是做什么
总结:
尽管我们会用了原型, 知道拷贝比 new 快, 知道深克隆, 但是具体的还是看业务场景的需求, 希望能多理解适用场景的那几种情况.
来源: https://www.cnblogs.com/Cubemen/p/10650177.html