原型模式

原型实例指定创建对象的品种,并且通过拷贝这些原型创立新的对象,并且通过拷贝这些原型创立新的对象

调用者不须要晓得任何创立细节,不调用构造函数

其属于一种创立型模式

通用类图

长处

  • 性能好

    • 是在内存二进制流的拷贝,比间接new一个对象性能好,而且循环体内产生大量对象时,能够更好地提现长处
  • 回避构造函数的束缚

    • 间接在内存中拷贝构造函数是不会执行的

实用场景

  • 类初始化耗费资源较多
  • new 产生的一个对象须要十分繁琐的过程(数据筹备、拜访权限等)

    • 省略了本人去get,set的过程
  • 构造函数比较复杂时
  • 循环体中产生大量对象时

应用

  • 通过一个特定的办法来拷贝对应的对象

本人提供接口并且实现

应用JDK的clone办法

浅克隆

测试
@Datapublic 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;    }  }}
public static void main(String[] args) {  ConcretePrototype prototype = new ConcretePrototype();  prototype.setAge(18);  prototype.setName("zzy");  List<String> hobbies = new ArrayList<String>();  hobbies.add("music");  hobbies.add("article");  prototype.setHobbies(hobbies);  //拷贝原型对象  ConcretePrototype cloneType = prototype.clone();  cloneType.getHobbies().add("program");  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='zzy', hobbies=[music, article, program]}
克隆对象:ConcretePrototype{age=18, name='zzy', hobbies=[music, article, program]}
false
原型对象的喜好:[music, article, program]
克隆对象的喜好:[music, article, program]
true

通过后果能够看到通过clone拷贝进去对象的汇合类型的内存地址没有扭转

根本数据类型间接拷贝了,然而援用数据类型拷贝的是属性的内存地址,具体的元素并没有拷贝

这种形式会给咱们将来的应用带来危险

深克隆

序列化和反序列化的形式

手写流
这种形式对象肯定要记得序列化
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;  }
  • 毛病

    • 性能不好
    • 占用IO
    • 没有通过构造方法来生成对象

      • 会毁坏单例模式
通过json序列化和反序列化

罕用的工具

Spring 的BeanUtils

org.springframework.beans.BeanUtils#copyProperties

Apache的Beanutils

org.apache.commons.beanutils.BeanUtils

不举荐应用,性能差

对于对象拷贝加了很多的测验,包含类型的转换,甚至还会测验对象所属的类的可拜访性,非常复杂,这也造就了它的差劲的性能,

原型模式在源码中的提现

ArrayList

public class ArrayList<E> extends AbstractList<E>  implements List<E>, RandomAccess, Cloneable, java.io.Serializable{  public Object clone() {    try {      ArrayList<?> v = (ArrayList<?>) super.clone();      v.elementData = Arrays.copyOf(elementData, size);      v.modCount = 0;      return v;    } catch (CloneNotSupportedException e) {      // this shouldn't happen, since we are Cloneable      throw new InternalError(e);    }  }}

HashMap

public class HashMap<K,V> extends AbstractMap<K,V>  implements Map<K,V>, Cloneable, Serializable {  public Object clone() {    HashMap<K,V> result;    try {      result = (HashMap<K,V>)super.clone();    } catch (CloneNotSupportedException e) {      // this shouldn't happen, since we are Cloneable      throw new InternalError(e);    }    result.reinitialize();    result.putMapEntries(this, false);    return result;  }}

注意事项

构造函数不会被执行

对象拷贝时构造函数的确没有被执行,这点从原理来讲也是能够讲得通的,Object类的clone办法的原理是从内存中(具体地说就是堆内存)以二进制流的形式进行拷贝,重新分配一个内存块,那构造函数没有被执行也是十分失常的了。

深拷贝和浅拷贝

深拷贝和浅拷贝倡议不要混合应用,特地是在波及类的继承 时,父类有多个援用的状况就非常复杂,倡议的计划是深拷贝和浅拷贝

final润饰的变量是不会被拷贝的

问题

通过实现Cloneable接口怎么实现克隆原理是什么?有什么问题?代码中是如何验证是深克隆还是浅克隆的?

个别都是间接基于内存二进制流来进行拷贝,不会通过构造函数,性能可能晋升很多。

  • 留神

    • 是浅拷贝的

      • 援用数据类型是不会被拷贝的,拷贝的是内存地址,不会创立一个新的
      • final润饰的变量是不会被拷贝的
  • 验证形式

    • 通过比拟内存地址来判断
深克隆有哪些解决办法?
  • 通过序列化和反序列化
  • 通过Json工具
如果我须要单例,怎么避免克隆毁坏单例
  • 重写readResolve办法
我的笔记仓库地址gitee 快来给我点个Star吧