博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
谨慎的覆盖clone(11)
阅读量:6693 次
发布时间:2019-06-25

本文共 2174 字,大约阅读时间需要 7 分钟。

hot3.png

1、Cloneable 接口的目的是表明对象时复制的

  • 没达到目的的原因是:缺少clone方法

2、Object 的克隆方法是受保护的

  • 不借助于反射,仅仅实现Cloneable 接口是无法调用clone 方法的
  •  

3、Cloneable 接口决定了,Object 中受保护的clone方法实现的行为

  • 如果实现了Cloneable,Object 的clone 方法返回逐域拷贝
  • 否则,抛出CloneNotSupportedException 异常
  • 接口的极端非典型用法,不值得效仿
  • 通常情况下,实现接口是为了表明它可以为类做些什么
  • Cloneable 接口改变了超类中受保护的方法的行为
  • 实现了该接口,无需调用构造器就可以创建对象

4、clone方法的通用约定非常弱

  • 拷贝的精确含义取决于,实现类本身
  • 一般意义上(但不是绝对):

075104_njR6_3847203.png

075115_Ngc3_3847203.png

  • 通常情况下(但不是绝对)

075135_UMtq_3847203.png

  • 拷贝对象需要创建累的新实例
  • 而且需要拷贝内部结构

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、从超类的角度,拷贝对象是对原始对象功能的完整拷贝

  • 基本类型和不可变类,返回对象没有问题,不需要进一步处理

084943_c8Bz_3847203.png

永远不要让客户去做类库可以提供的事情

  • 如果带有可变类,简单的clone 实现可能带来灾难性后果
  • 下述Stack 类,如果采用简单的clone ,elements仅仅是引用复制,新老引用指向同一个数组

085618_xih5_3847203.png

clone 方法是类的另一个构造器,确保不要伤害原有对象,确保正确创建被克隆对象所有约束条件

  • 最容易实现方法:

085848_lcDw_3847203.png

数组不需要 类型转换,类型在编译时与被克隆类型是相同的

9、如果可变类存在final 的域,上述方案不能正常运行:clone 方法会被禁止赋新值

  • clone 架构与引用可变对象的final 域之间本身就是冲突的
  • 去掉final 再clone

10、递归调用clone 有时还不够

  • 例如:

090919_nzQy_3847203.png

  • 上述类,带有内部类,需要复制每一个对象(深度拷贝):
  • 递归调用式拷贝:

091237_ZgBt_3847203.png

  • 递归调用针对链表中每个元素,都消耗一段栈空间
  • 链表过长可能会溢出
  • 迭代式拷贝避免了上述情况(迭代器能用就用啊):

092050_Q6vQ_3847203.png

  • clone 方法如下:

091040_cMSN_3847203.png

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、拷贝构造器(拷贝工厂)

  • 类似于静态工厂:

095641_W50q_3847203.png

优势:

  • 不依赖于有风险的、对象之外的对象创建机制
  • 不要求遵守尚未指定好的文档规范
  • 不会与 final 域的正常使用发生冲突
  • 不会抛出不必要的受检异常
  • 不需要进行类型转换
  • 采用拷贝构造器没有放弃接口的功能特性(Cloneable 接口是一个空接口

18、拷贝构造器(拷贝工厂)可以带有一个参数:

  • 参数类型是通过该类实现的接口
  • 实质就是转换构造器(转换工厂)
  • 比如HashSet 转换成TreeSet:new TreeSet(s)

转载于:https://my.oschina.net/u/3847203/blog/1820344

你可能感兴趣的文章
前端下载 图片 总结
查看>>
Vue表单输入绑定
查看>>
LINUX下进程打开的文件怎么和底层磁盘关联的?
查看>>
Java 设计模式之命令模式
查看>>
JavaScript六种非常经典的对象继承方式
查看>>
可能是把Java内存区域讲的最清楚的一篇文章
查看>>
PHP中的几个随机数生成函数
查看>>
Anaconda不同envs的pip和python的版本
查看>>
深度学习与神经网络:最值得关注的6大趋势
查看>>
给SUBVERSION-EDGE和GITLAB-CE增加多LDAP域认证支持的经历
查看>>
SQLServer之创建全文索引
查看>>
如何以并发方式在同一个流上执行多种操作?--复制流
查看>>
Spring Boot 参考指南(开发Web应用程序)
查看>>
策略模式总结
查看>>
关于作用域插槽的理解
查看>>
javascript块级作用域处理闭包和释放内存的垃圾回收
查看>>
快速入门React
查看>>
正则表达式语法入门
查看>>
关于顶级、一级、二级域名如何理解?
查看>>
为什么点积等价于投影后的乘积?
查看>>