2019独角兽企业重金招聘Python工程师标准>>>

说在前面

有些专家级程序员干脆从来不去覆盖clone方法,也从来不去调用它,除非拷贝数组。

其他方式

可以提供一个构造函数或者工厂去实现clone功能。

相比于clone,它们有如下优势:

  1. 不依赖于某一种很有风险的、语言之外的对象创建机制;
  2. 不要求遵守尚未定制好的文档规范;
  3. 不会与final域发生冲突;
  4. 不会抛出不必要的受检异常;
  5. 不需要进行类型转换;

例如,通用集合的实现都提供了一个拷贝构造函数,它的参数类型为Collection或Map。

假如要把一个HashSet拷贝成一个TreeSet:

HashSet s = ...
new TreeSet(s)

如果一定要覆盖clone方法,那么则需要了解以下它的注意事项了。

Clone规范

x.clone() != x //true
x.clone().getClass() == x.getClass() //true
x.clone.equals(x) // true

行为良好的clone方法可以调用构造器来创建对象,构造之后再复制内部数据。

Clone做法

  1. 所有实现了Cloneable接口的类都应该用一个公有方法覆盖clone;
  2. 此公有方法首先要调用super.clone,然后在修正需要修正的域;
// 伪代码
class User implements Cloneable {@Overridepublic User clone() {User user = (User)super.clone(); // 1.先调用super.cloneuser.set ...               // 2.在修正}}

Clone要点

如果覆盖了非final类中的clone方法,则应该返回一个通过调用super.clone而得到的对象,如果类的所有父类都遵守这条规则,那么调用super.clone最终会调用Object的clone方法,从而创建出正确类的实例。这种机制大体上类似于自动的构造器调用链。

简单Clone

如果类中包含的每个域是一个基本类型的值,或者包含的是一个指向不可变对象的引用,那么调用clone被返回的对象则可能正是所需要的对象,在这种情况下不需要在做进一步的处理。

复杂Clone

如果类中包含的域是指向一个可变对象的引用,那么就要小心的对其进行clone。

例如,若类中存在一个Object[]数组,则可以参考一下做法:

// 伪代码
class Stack {private Object[] elements;private int size = 0;@Overridepublic Stack clone() {Stack result = (Stack) super.clone();result.elements = this.elements.clone();}
}

还有一种情况,若类中存在一个对象或者集合(自定义对象、List、Map等),那么光调用这些对象的clone还不够,例如编写一个散列表的clone方法,它的内部数据包含一个散列桶数组:

// 伪代码
class HashTable implements Cloneable {private Entry[] buckets = ...private static class Entry {final Object key;Object value;Entry next;Entry(key, value, next) ...}
}

如果只调用了buckets.clone,其实克隆出来的buckets和被克隆的buckets内的entry是引用着同一对象的。

这种情况下,必须单独拷贝并组成每个桶的链表,例如:

// 伪代码
class HashTable implements Cloneable {private Entry[] buckets = ...private static class Entry {final Object key;Object value;Entry next;Entry(key, value, next) ...}// 提供一个深拷贝函数Entry deepCopy() {return new Entry(key, value, next == null ? null : next.deepCopy());}@Overridepublic HashTable clone() {try ...HashTable result = (HashTable) super.clone();result.buckets = new Enrty[buckets.length];for(int i=0;i<buckets.length;i++) {if(buckets[i] != null) result.buckets[i] = buckets[i].deepCopy();}return result;catch CloneNotSupportedException e ...}}

提供一个深拷贝方法,遍历源对象的buckets,将它拷贝到新对象中。

这种做法有一个确定,如果散列桶很长,很容易导致栈溢出,因为递归的层级太多!

解决这种问题,可以采用迭代(iteration)来代替递归(recursion),修改一下deepCopy方法:

Entry deepCopy() {Entry result = new Entry(key, value, next);for (Entry p = result; p.next != null; p = p.next) {p.next = new Entry(p.next.key, p.next.value, p.next.next);}return result;
}

最好还做到

Object的clone方法被声明为可跑出CloneNotSupportedException异常,但是,覆盖版本的clone方法可能会忽略这个声明。公有的clone方法应该省略这个声明,因为不会跑出受检异常的方法用起来更轻松。

如果专门为了继承而设计的类覆盖类clone方法,覆盖版本的clone方法就应该模拟Object.clone的行为:

  1. 声明为protected;
  2. 抛出CloneNotSupportedException;
  3. 不实现CloneableJiekou ;

总结

以上就是对Effective Java第十一条的摘要。

转载于:https://my.oschina.net/u/2450666/blog/2209179

谨慎的覆盖clone方法相关推荐

  1. Effective Java之谨慎地覆盖clone(十一)

    Clone提供一种语言之外的机制:无需调用构造器就可以创建对象. 它的通用约定非常弱: 创建和返回该对象的一个拷贝.这个拷贝的精确含义取决于该对象的类.一般含义是,对于任何对象x,表达式x.clone ...

  2. 【克隆】——Object类clone方法彻底剖析

    目录 一.什么是克隆 二.为什么要克隆 三.如何克隆 四.深克隆和浅克隆 浅克隆 深克隆 一.什么是克隆 克隆就是依据已经有的数据,创造一份新的完全一样的数据拷贝. 在Java中对象的克隆有深克隆和浅 ...

  3. 详解Java中的clone方法 -- 原型模式

    Java中对象的创建 clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象.所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象.那 ...

  4. Java基础篇:对象拷贝:clone方法 以及 序列化

    我们知道在Java中存在这个接口Cloneable,实现该接口的类都会具备被拷贝的能力,同时拷贝是在内存中进行,在性能方面比我们直接通过new生成对象来的快,特别是在大对象的生成上,使得性能的提升非常 ...

  5. java中clone方法_Java Object clone()方法– Java中的克隆

    java中clone方法 Cloning is the process of creating a copy of an Object. Java Object class comes with na ...

  6. Java基础深度总结:Object类-clone方法

    凡心所向,素履所往,生如逆旅,一苇以航. 内容 1.clone概述 2.Cloneable接口 3.clone与new的区别 4.浅拷贝与深拷贝(重点) 5.Object.clone 6.浅拷贝存在的 ...

  7. 详解Java中的clone方法 -- 深拷贝和浅拷贝

    Java中对象的创建 clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象.所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象.那 ...

  8. 禁止 Python 子类覆盖父类方法

    当实现我们自己的父类Animal的时候,由于meta.has_base为 False,所以不会触发检查逻辑.但当我们基于Animal实现Dog子类的时候,由于meta.has_base是True,所以 ...

  9. arraycopy用法_Java复制(拷贝)数组的4种方法:arraycopy()方法、clone() 方法、copyOf()和copyOfRan...

    所谓复制数组,是指将一个数组中的元素在另一个数组中进行复制.本文主要介绍关于 Java 里面的数组复制(拷贝)的几种方式和用法.在 Java 中实现数组复制分别有以下 4 种方法: Arrays 类的 ...

最新文章

  1. The next Industry Standard in IT Monitoring, a python implementation Nagios like tool --- Shinken
  2. 不是你无法入门自然语言处理(NLP),而是你没找到正确的打开方式
  3. Linux线程时间片如何修改,请教如何修改线程时间片
  4. sql server java类型_使用基本 JDBC 数据类型 - SQL Server | Microsoft Docs
  5. 一份关于kaggle特征构建技巧和心得 1
  6. notes from《classification and regression trees》
  7. wampServer配置WWW根目录遇到的坑
  8. XP下,文件夹添加右键命令行
  9. user32.dll 函数说明
  10. 计算机桌面美化软件,电脑桌面软件哪个好 桌面美化管理软件推荐
  11. mpush 搭建消息服务器,mpush学习笔记windows服务器部署(一)
  12. 计算机小写换大写函数,Excel函数公式应用:小写数字转换成人民币大写9种方法-excel技巧-电脑技巧收藏家...
  13. 「2019纪中集训Day12」解题报告
  14. python根据日期生成动态密码
  15. 详解色彩模型、色域以及颜色空间转换
  16. 【人工智能】— 逻辑Agent、一般逻辑、Entailment 蕴涵、命题逻辑、前向链接、反向链接、Resolution归结
  17. 魔百盒之小型家庭NAS
  18. Google Pixel手机解锁 bootloader
  19. 【Linux】之 Linux 性能监控工具
  20. png.h缺失 - 安装

热门文章

  1. windows下安装cygwin及配置
  2. 据lovecherry的一步一步学Remoting序列文章学习.net Remoting日记(2)
  3. KeyValueTextInputFormat使用案例
  4. (How to) Call somatic mutations using GATK4 Mutect2
  5. 车辆贷款违约预测挑战赛
  6. 部署篇01:Linux 安装配置JDK
  7. WPF以Clickonce方式发布后使用管理员身份运行
  8. Silverlight游戏设计(Game Design):(四)从零开始搭建游戏主体框架
  9. 基于Spark ALS算法的个性化推荐
  10. 谷歌浏览器安卓版_谷歌翻译(在线翻译)下载-谷歌翻译下载安装安卓版v5.12.0...