Java设计模式之原型模式与深浅拷贝

概述

原型模式是一种创建型模式,允许用户从一个样板实例中复制出一个内部属性一致的对象,俗称为克隆.被复制出来的实例就是我们所称的原型. 多用在创建实例比较复杂或者耗时的情况下,因为复制一个已经存在的实例可以使程序运行更高效.

定义

用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。

使用场景

  • 类初始化需要消化非常多的资源,通过原型拷贝避免这些消耗

  • 通过 new 产生一个对象需要非常繁琐的数据准备或访问权限

  • 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,保护原始对象.

UML类图

 

  • Client : 客户端用户。

  • Prototype : 抽象类或者接口,声明具备clone能力。

  • ConcretePrototype : 具体的原型类.

原型模式主要用于对象的复制,其原型类(ConcretePrototype)想要被复制,需要满足如下条件

  • 实现Cloneable接口,此接口相当于Prototype角色.

  • 复写 clone方法.并将其包访问权限修改为public的.

Object中的clone 方法是受保护的,如果一个类实现了Cloneable接口,clone方法就会返回改对象的逐域拷贝,否则会抛出CloneNotSupportedException异常

Cloneable接口中没有任何方法,它仅仅是一个标记接口,只有实现此接口的对象才能被拷贝。

对象的拷贝

在Java中,如果我们将原始对象的值赋给另一个对象,就是值的传递,如

  int a = 1;
  int b = a;12

如果将引用类型的值赋给另一个对象,则是引用的传递,如

  String[] a = new String[5];
  String[] b = a;//这里b只是指向了a的引用12

浅拷贝

如果我们只是实现了Cloneable接口,并简单的使用super.clone()来复写clone方法的话,那么基本上就是浅拷贝,浅拷贝对于引用类型就如上一样,并不是真正的拷贝.

  public class Clone implements Cloneable{
        private int mInt;
        private ArrayList mList;
  ​
        @Override protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
        //....
    }123456789
      CloneImpl cloneImpl = new CloneImpl();
      //...
      try {
        Object clone = cloneImpl.clone();
  ​
        CloneImpl cloneImpl1 = (CloneImpl) clone;
        //...
      } catch (CloneNotSupportedException e) {
        e.printStackTrace();
      }12345678910

通过日志打印,可以看到源mList的值会因为clone实例的改变而改变.而mInt值不变.

浅拷贝的特点:

  • 对于引用类型,只拷贝其引用

  • 效率高

  • 不能完全做到保护性拷贝

浅拷贝适用于 对象只包含原始数据域或者不可变对象域的时候,提高效率

深拷贝

深拷贝可以做到保护性拷贝,看ArrayList的源码,我们发现其实现了Cloneable接口,并重写了clone()方法.因此,将上述代码修改为如下形式

  @Override protected Object clone() throws CloneNotSupportedException {
      CloneImpl2 clone = (CloneImpl2) super.clone();
      clone.mList = (ArrayList) mList.clone();
  ​
      return clone;
    }123456

之后再次执行clone()后,这是clone实例的改变就不会影响到源对象了

深拷贝的特点:

  • 复写clone方法的时候,需要调用属性的clone方法

  • 做到 100% 保护性拷贝

  • 比浅拷贝耗时

当对象存在引用类型的属性,要使用深拷贝.为了防止错误的发生,在使用对象拷贝的时候,建议尽量使用深拷贝

注意事项

  • Effective Java 第11条,要谨慎的覆盖clone 中有这样的描述,

  对于任何对象x,表达式
  ​
  x.clone() != x 将会是 true 
  x.clone().getClass() == x.getClass() 将会是 true,但不是绝对要求 x.clone().equals(x) 将会是 true,但也不是绝对的要求
  ​
  拷贝对象会创建一个类新的实例,并会拷贝其内部结构,但是不会调用构造器
  1234567
  • 原型模式中对象的拷贝是二进制流的拷贝,并不会执行构造函数,因此要在构造函数中做一些额外操作的对象需要注意此问题

  • 因为其忽略了构造函数(包括其访问权限),所以和单例模式是冲突的,因为单例模式将构造函数设为private的.

  • 尽量使用深拷贝类防止错误的发生,对于只有值域类型的对象使用浅拷贝

  • 原型模式中的 原型clone实例通过 equals==比较返回值都是 false

  • 有些对象的clone()方法是直接new一个对象,如Intent,需要根据创建对象的复杂程度来决定.

  • 对于对象中包含final关键字的拷贝无法编译通过

  • Object中的clone()方法是线程不同步的.在需要线程安全的场景,需要做好同步工作.

  • 对象的拷贝还有其他方式,如序列化接口Serializable

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值