1、Cloneable 接口的目的是表明对象时复制的
- 没达到目的的原因是:缺少clone方法
2、Object 的克隆方法是受保护的
- 不借助于反射,仅仅实现Cloneable 接口是无法调用clone 方法的
3、Cloneable 接口决定了,Object 中受保护的clone方法实现的行为
- 如果实现了Cloneable,Object 的clone 方法返回逐域拷贝
- 否则,抛出CloneNotSupportedException 异常
- 接口的极端非典型用法,不值得效仿
- 通常情况下,实现接口是为了表明它可以为类做些什么
- Cloneable 接口改变了超类中受保护的方法的行为
- 实现了该接口,无需调用构造器就可以创建对象
4、clone方法的通用约定非常弱
- 拷贝的精确含义取决于,实现类本身
- 一般意义上(但不是绝对):
- 通常情况下(但不是绝对)
- 拷贝对象需要创建累的新实例
- 而且需要拷贝内部结构
5、不调用构造器的规定太强硬
- 行为良好的构造器可以由构造器创建,再复制内部对象
- 如果是final 类,甚至可以是返回一个构造器创建的对象
6、x.clone().getClass()==x.getClass() 通常情况下是true
- 在子类中调用super.clone(),返回的是子类的实例
- 超类实现这种功能的唯一途径是返回一个super.clone()返回的对象
- 所有超类都遵守该规则,最后返回Object 的clone ,返回一个正确的实例(行为良好clone 方法)
- 如果返回一个构造器创建的类,就得到一个错误的实例
7、Object 的clone 方法是一个本地方法
- Object 的clone 方法是一个本地方法
- 执行的时候使用RTTI(run-time type identification)
- 动态得到正在调用clone方法的那个reference
- 根据它的大小申请内存空间,然后进行bitwise的复制
- 将该对象的内存空间完全复制到新的空间中去,从而达到shallowcopy的目的
- 得到当前类的副本,不是父类
- 根本没有必用调用this.clone()
8、从超类的角度,拷贝对象是对原始对象功能的完整拷贝
- 基本类型和不可变类,返回对象没有问题,不需要进一步处理
永远不要让客户去做类库可以提供的事情
- 如果带有可变类,简单的clone 实现可能带来灾难性后果
- 下述Stack 类,如果采用简单的clone ,elements仅仅是引用复制,新老引用指向同一个数组
clone 方法是类的另一个构造器,确保不要伤害原有对象,确保正确创建被克隆对象所有约束条件
- 最容易实现方法:
数组不需要 类型转换,类型在编译时与被克隆类型是相同的
9、如果可变类存在final 的域,上述方案不能正常运行:clone 方法会被禁止赋新值
- clone 架构与引用可变对象的final 域之间本身就是冲突的
- 去掉final 再clone
10、递归调用clone 有时还不够
- 例如:
- 上述类,带有内部类,需要复制每一个对象(深度拷贝):
- 递归调用式拷贝:
- 递归调用针对链表中每个元素,都消耗一段栈空间
- 链表过长可能会溢出
- 迭代式拷贝避免了上述情况(迭代器能用就用啊):
- clone 方法如下:
11、克隆复杂对象的最后一种方法
- 先调用super.clone 方法
- 把所有域置空
- 最后再把每个值都填进去
- 这是一种简单、优美、合理的克隆方法
- 运行效率比直接操作对象内部状态要差
12、clone 方法与构造器一样,clone 方法在构造过程中,不能调用新对象任何非 final 的方法
- 新对象被拷贝出来前,不能调用它的非 final 方法
13、覆盖版本的clone 方法不应该忽略声明可抛出异常 CloneNotSupportedException
- Object 的clone 方法可抛出 CloneNotSupportedException 异常
- 公有的覆盖版本clone 方法可省略
- 省略后使用更轻松
14、专门为继承而设计的类<17条>,应尽量模仿Object 的clone 方法
- (protected、可抛出上述异常)
- 不应该实现Cloneable 接口,继承类可自由选择是否实现Cloneable 接口
- 这样便直接扩展了Object
15、线程安全的类实现 Cloneable 接口,clone 方法也必须同步
- Object 的clone 没有同步
- 使用同步的clone 方法调用super.clone()
16、不可变类不要提供拷贝方法
- 俩对象没区别,拷贝没意义
17、拷贝构造器(拷贝工厂)
- 类似于静态工厂:
优势:
- 不依赖于有风险的、对象之外的对象创建机制
- 不要求遵守尚未指定好的文档规范
- 不会与 final 域的正常使用发生冲突
- 不会抛出不必要的受检异常
- 不需要进行类型转换
- 采用拷贝构造器没有放弃接口的功能特性(Cloneable 接口是一个空接口)
18、拷贝构造器(拷贝工厂)可以带有一个参数:
- 参数类型是通过该类实现的接口
- 实质就是转换构造器(转换工厂)
- 比如HashSet 转换成TreeSet:new TreeSet(s)