由于克隆并不太常见,而且有关的细节技术性很强,你可能只是想稍做了解,等真正需要时再深人学习。

先来回忆为一个包含对象引用的变量建立副本时会发生什么 。原变量和副本都是同一个对象的引用:

Employee original = new Employee ("John Public", 50000);
Employee copy = original;    // Java 中这叫做引用传递,并非 copy/clone
copy.raiseSalary(10) ; // oops-also changed original

如果希望 copy 是一个新对象, 它的初始状态与 original 相同 , 但是之后它们各自会有自己不同的状态, 这种情况下就可以使用 clone 方法 。

Employee copy = original.clone();    // 克隆,实际上不能直接使用这个方法,详情见下
copy.raiseSalary(10); / / OK original unchanged

不过并没有这么简单 。clone 方法是 Object 的一个 protected 方法 , 这说明你的代码不能直接调用这个方法 【搞不懂为什么的请参看: https://blog.csdn.net/gulang03/article/details/86728054】。 只有 Employee 类可以克隆 Employee 对象 【即 Employee copy = original.clone(); 这种代码只能出现在 Employee 类内部,除非实现  Cloneable 接口,详情往下看】。

这个限制是有原因的 。 想想看 Object 类如何实现 clone 。 它对于这个对象一无所知 , 所以只能逐个域地进行拷贝 。 如果对象中的所有数据域都是数值或其他基本类型, 拷贝这些域没有任何问题 、 但是如果对象包含子对象的引用, 拷贝域就会得到相同子对象的另一个引用, 这样一来, 原对象和克隆的对象仍然会共享一些信息【即:默认的克隆操作是 “ 浅拷贝 ” , 并没有克隆对象中引用的其他对象 。】 。

浅拷贝会有什么影响吗 ?

这要看具体情况 。 如果原对象和浅克隆对象共享的子对象是不可变的 , 那么这种共享就是安全的 。 如果子对象属于一个不可变的类, 如 String , 就是这种情况 。 或者在对象的生命期中, 子对象一直包含不变的常量 , 没有更改器方法会改变它, 也没有方法会生成它的引用, 这种情况下同样是安全的 。

不过 , 通常子对象都是可变的, 必须重新定义 clone 方法来建立一个深拷贝 , 同时克隆所有子对象 。 在这个例子中, hireDay 域是一个 Date , 这是可变的 , 所以它也需要克隆 。 (出于这个原因 , 这个例子使用 Date 类型的域而不是 LocalDate 来展示克隆过程 。 如果 hireDay是不可变的 LocalDate 类的一个实例, 就无需我们做任何处理了 。)

对于每一个类, 需要确定 :

1 ) 默 认 的 clone 方法是否满足要求 ;
2 ) 是否可以在可变的子对象上调用 clone 来修补默认的 clone 方法 ;
3 ) 是否不该使用 clone。

实际上第 3 个选项是默认选项 。 如果选择第 1 项或第 2 项, 类必须:

1 ) 实现 Cloneable 接口 ;
2 ) 重新定义 clone 方法, 并指定 public 访问修饰符 。【为了 clone() 方法能在任何地方都能使用】

关于 Cloneable 接口的说明:

Cloneable 接口只是一个标记接口(tagging interface),是Java提供的一组标记接口之一。标记接口用途是确保一个类实现一个或一组特定的方法。 标记接口不包含任何方法; 它唯一的作用就是允许在类型查询中使用 instanceof:

if ( obj instanceof Cloneable)...

Cloneable 接口源代码:

public interface Cloneable {
}
// 没有任何待实现的方法

即使 clone 的默认 (浅拷贝 ) 实现能够满足要求 , 还是需要实现 Cloneable 接口 , 将 clone重新定义为 public , 再调用 super . clone() 。

class Employee implements Cloneable{public Employee clone() throws CloneNotSupportedException {return (Employee) super.clone();}
}

与 Object.clone 提供的浅拷贝相比 , 前面看到的 clone 方法并没有为它增加任何功能 。 这里只是让这个方法是公有的 。 要建立深拷贝 , 还需要做更多工作, 克隆对象中可变的实例域 。

下面来看创建深拷贝的 clone 方法的一个例子 :

class Employee implements Cloneable{...// 注意访问修饰符改为 publicpublic Employee clone() throws CloneNotSupportedException{// call Object.clone() 完成不可变字段的拷贝Employee cloned = (Employee) super.clone();// clone mutable fileds 完成可变字段的拷贝cloned.hiredDay = (Date) hireDay.clone();return cloned;}
}

必须当心子类的克隆 。 例如, 一旦为 Employee 类定义了 clone 方法, 任何人都可以用它来克隆 Manager【PS:Manager 是 Employee 类的一个子类】对象 。 Employee 克隆方法能完成工作吗 ? 这取决于 Manager 类的域 。 在这里是没有问题的, 因为 bonus 域是基本类型 。 但是 Manager 可能会有需要深拷贝或不可克隆的域 。 不能保证子类的实现者一定会修正 clone 方法让它正常工作 。 出于这个原因, 在 Object 类中 clone 方法声明为 protected 。

《Java 核心技术卷1 第10版》学习笔记------对象克隆【对象拷贝】相关推荐

  1. Java 核心技术卷 II(第 8 版) – 读书笔记 – 第 1 章(下)

    22.一旦获得了一个 Charset,就可以在 Java 的 Unicode 和指定的编码格式之间进行转化,下面以 GBK 和 Unicode 之间做为例子. 从 Unicode 到 GBK: imp ...

  2. 《Java 核心技术卷1 第10版》学习笔记------异常

    异常处理的任务就是将控制权从错误产生的地方转移给能够处理这种情况的错误处理器 . 7.1.1 异常分类 在 Java 程序设计语言中, 异常对象都是派生于 Throwable 类的一个实例 . 稍后还 ...

  3. 《Java 核心技术卷1 第10版》学习笔记 ------ 泛型【进阶】

    这部分主要是结合 Java 虚拟机实现泛型的原理进一步研究如何更好的使用泛型. 8.5 泛型代码和虚拟机 虚拟机没有泛型类型对象---所有对象都属于普通类.所以编译器在编译的时候会进行类型擦除操作. ...

  4. 《Java 核心技术卷1 第10版》学习笔记------Object类的 equals 方法

    Object 简述 Object 类是 Java 中所有类的始祖, 在 Java 中每个类都是由它扩展而来的. 在 Java 中, 只有基本类型 ( primitive types) 不是对象, 例如 ...

  5. 《Java 核心技术卷1 第10版》学习笔记 ------ 泛型【基础】

    泛型从Java SE 5.0 中开始出现,是 Java 程序设计语言从 1.0 版本发布以来,变化最大的部分. 使用泛型机制编写的程序代码要比那些杂乱地使用 Object 变量,然后再进行强制类型转换 ...

  6. 《Java 核心技术卷1 第10版》学习笔记------调试技巧

    调试器是 Eclipse . NetBeans 这类专业集成开发环境的一部分 . 在启动调试器之前, 本节先给出一些有价值的建议 . 1 ) 可以用下面的方法打印或记录任意变量的值 : System. ...

  7. 《Java 核心技术卷1 第10版》学习笔记------日志

    日志 API 的优点: 可以很容易地取消全部日志记录, 或者仅仅取消某个级别的日志, 而且打开和关闭这个操作也很容易 . 可以很简单地禁止日志记录的输出, 因此, 将这些日志代码留在程序中的开销很小 ...

  8. 《Java 核心技术卷1 第10版》学习笔记------ 接口(Interface)

    接口技术作用:主要描述类具有什么功能,而并不给出每个功能的是的实现. Java的继承(inheritance)是不支持的多继承的,但是Java接口是支持多继承的. 一个 Java 类实现一个接口类就必 ...

  9. 《Java 核心技术卷1 第10版》学习笔记------ 对象包装器、自动装箱、拆箱

    有时, 需要将 int 这样的基本类型转换为对象. 所有的基本类型都冇一个与之对应的类.例如,Integer 类对应基本类型 int.通常, 这些类称为包装器 ( wrapper ) 这些对象包装器类 ...

最新文章

  1. 2022-2028年中国露天采矿行业调查与投资前景评估报告
  2. NSObject中的常用方法
  3. TortoiseSVN每个菜单项都表示什么意思
  4. 设计模式 (一) 初次体验
  5. 我的世界服务器启动后自动关闭,求解,服务器老师莫名其妙自动关闭,为什么会这样呢?...
  6. 数据可视化图表ECharts
  7. 如何强制gradle重新下载依赖项?
  8. 【Python】排序函数 sort、sorted 对复杂列表排序
  9. 使用Axure创建iPhone应用程序原型(二)
  10. 爱立信宣布收购FYI电视
  11. excel公式里用html,excel中value是什么函数?
  12. linux打开终端的快捷键放大,linux打开终端的快捷键是什么?
  13. c语言局部变量stu,C语言学习笔记
  14. svg图形计算、矩阵函数计算、图形点位绝对坐标计算
  15. 备忘5:爬取微博热门信息以及所有热门微博评论的用户信息
  16. CSR完全空间随机性最近邻距离分布理论(一)
  17. 这些电商运营指标你知道吗?
  18. 机器人教父预测AI未来32年!Yann LeCun留言称赞
  19. 值得纪念的一天,终于进了大厂
  20. 九州云出席全球人工智能开发者先锋大会,圆桌论道开源未来

热门文章

  1. keras从入门到放弃(四)多分类问题
  2. 微信小程序之授权登录--项目需要
  3. SIGIR 2021 | 基于不确定性正则化与迭代网络剪枝的终身情感分类方法
  4. 北京内推 | 微软亚洲互联网工程院(STCA)招聘NLP科研实习生
  5. Transformer性能被高估?DeepMind动态评估模型的时间泛化能力
  6. ACL 2019开源论文 | 句对匹配任务中的样本选择偏差与去偏方法
  7. KDD 18论文解读 | 斯坦福大学提出全新网络嵌入方法 — GraphWave
  8. 机器学习模型,能分清川菜和湘菜吗?
  9. 蓝桥备赛第三周 倍增+贪心+素数+约数
  10. Linux下用Python调用C模块