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

clone()是java.lang.Object类下面的一个很难处理的方法,clone()的作用很简单,提供一种克隆机制创建对象的副本,对于如何实现它已成为一个棘手的事,同时还长期被广受批评。不管怎样,我们不去争论历史,现在我们将尝试学习clone方法是怎样工作的。说实在的,想理解克隆机制并不简单,甚至有经验的java程序员也很难解释可变对象的克隆是如何运作的、深克隆(deep copy)与浅克隆(shallow copy)的区别。这文章分为三部分,我们首先看clone方法是如何工作的,第二部分将学习如何重写(override)clone方法,最后我们讨论深度克隆与浅克隆。之所以选择把它作为三部分,是为了一次专注在一件事上,clone()本身就很让人困惑的,因此最好是一个一个理解概念。在这篇文章中将学习到什么是clone方法,以及怎么工作的,顺便说一句,clone是定义一个Object类下基本方法之一,与之类似的还有hashcode(),toString(),wait和notify。

什么是克隆对象

clone()方法返回的对象叫做原始对象的克隆体。一个克隆对象的基本特性必须是:a.clone()!=a,这也就意味着克隆对象和原始对象在java 堆(heap)中是两个独立的对象。a.clone().getClass == a.getClass() 以及 clone.equals(a),也就是说克隆对象完完全全是原始对象的一个拷贝。这些特征来自于一种良好的行为—正确地重写(overriedden)clone方法。但是这些并不是克隆机制强制要求的。意味着clone()返回的对象可能会违反这些约定(通过调用super.clone()方法返回的对象),当重写clone()方法时,你可以遵循前面两条(a.clone()!=a和a.clone().getClass()==a.getClass())。为了遵循第三个特性(clone.equals(a)),你必须重写equals方法。例如:Rectangle类的克隆对象就有这三个特性,但是如果你注释equals()再来运行相同的程序,你将看到第三个约束条件clone.equlas(a)返回false,顺便说一下,在《Effective Java》中有条目提到如何有效使用clone方法的知识点,我强烈建议你在读完本文后去翻一翻这本书。

clone方法是如何工作的

java.lang.Object提供了默认的clone方法实现,它声明为protected和native。因此它的实现是取决于本地代码,因为它约定返回对象是通过调用super.clone()方法,任何克隆的过程最终都将到达java.lang.Object 的clone()方法,它首先检查这个相关的类是否实现了Cloneable接口,这个接口是一个标记接口,如果这个实例没有实现cloneable接口,那么就会抛出CloneNotSupported异常,这个异常是一个checked异常,也就是说他在克隆对象的时候总需要被处理。如果没有异常抛出,然后java.lang.Object的clone()方法将创建一个拷贝返回给调用者。因为Object类的clone()方法通过创建新对象来生成这个副本的,然后逐个域拷贝(field-by-filed),类似于赋值操作,这种操作对于原始类型(primitives)和不可变类型(immutable)来说是没问题的,但是如果你的类包含一些可变的数据结构如:ArrayList或数组就不合适了,这种情况原始对象和副本对象将指向相同的堆,你可以通过一种叫深度克隆的技术防止这种事情发生。他的每一个可变的域被独立的克隆,简而言之,下面总结就是一个clone方法是如何工作的。

1. 任何类在实例上调用clone(),他将实现cloneable重写clone方法,创建副本。

Rectangle rec =newRectangle(30,60);
logger.info(rec);
try{logger.info("Creating Copy of this object using Clone method");Rectangle copy = rec.clone();logger.info("Copy "+ copy);
}catch(CloneNotSupportedException ex) {logger.debug("Cloning is not supported for this object");
}

2. 在Rectangle中调用clone方法被委派给super.clone(),它可以是自定义的super class或者是默认的java.lang.Object.

@Override
protected Rectangle clone()throws CloneNotSupportedException {
return(Rectangle)super.clone();
}

最后调用到达java.lang.Object的clone()时,他验证相关的类是否实现Cloneable接口,如果没有实现那么抛出CloneNotSupportedException,否则他创建副本filed-by-field。

所以为了clone()方法可以正确的工作,两件事会发生:类必须实现Cloneable接口,必须重写clone方法,这是最简单的例子,对于更复杂的对象,它包含多个fileds、数组,结合不可变对象和原始类型,我们来看第二个clone教程

clone()举例

在这篇文章中,我们没有看到复杂的重写clone的方法,因为我们的Rectangle类非常简单,仅仅只是包含原生类型,只需通过Object的clone方法浅克隆就足够了。但是下面这个例子对理解对象的克隆很重要,这里是完整的代码:

importorg.apache.log4j.Logger;
/**
* Simple example of overriding clone() method in Java to understand How Cloning of
* Object works in Java.
*
* @author
*/
publicclassJavaCloneTest {
privatestaticfinalLogger logger = Logger.getLogger(JavaCloneTest.class);
publicstaticvoidmain(String args[]) {
Rectangle rec =newRectangle(30,60);
logger.info(rec);
Rectangle copy =null;
try{
logger.info("Creating Copy of this object using Clone method");
copy = rec.clone();
logger.info("Copy "+ copy);
}catch(CloneNotSupportedException ex) {
logger.debug("Cloning is not supported for this object");
}
//testing properties of object returned by clone method in Java
logger.info("copy != rec : "+ (copy != rec));
logger.info("copy.getClass() == rec.getClass() : "+ (copy.getClass() == rec.getClass()));
logger.info("copy.equals(rec) : "+ copy.equals(rec));
//Updating fields in original object
rec.setHeight(100);
rec.setWidth(45);
logger.info("Original object :"+ rec);
logger.info("Clonned object :"+ copy);
}
}
publicclassRectangleimplementsCloneable{
privateintwidth;
privateintheight;
publicRectangle(intw,inth){
width = w;
height = h;
}
publicvoidsetHeight(intheight) {
this.height = height;
}
publicvoidsetWidth(intwidth) {
this.width = width;
}
publicintarea(){
returnwidthheight;
}
@Override
publicString toString(){
returnString.format("Rectangle [width: %d, height: %d, area: %d]", width, height, area());
}
@Override
protectedRectangle clone()throwsCloneNotSupportedException {
return(Rectangle)super.clone();
}
@Override
publicbooleanequals(Object obj) {
if(obj ==null) {
returnfalse;
}
if(getClass() != obj.getClass()) {
returnfalse;
}
finalRectangle other = (Rectangle) obj;
if(this.width != other.width) {
returnfalse;
}
if(this.height != other.height) {
returnfalse;
}
returntrue;
}
@Override
publicinthashCode() {
inthash =7;
hash =47hash +this.width;
hash =47hash +this.height;
returnhash;
}
}
Output:
2013-05-2023:46:58,8820[main] INFO JavaCloneTest - Rectangle [width:30, height:60, area:1800]
2013-05-2023:46:58,8820[main] INFO JavaCloneTest - Creating Copy ofthisobject using Clone method
2013-05-2023:46:58,8820[main] INFO JavaCloneTest - Copy Rectangle [width:30, height:60, area:1800]
2013-05-2023:46:58,8820[main] INFO JavaCloneTest - copy != rec :true
2013-05-2023:46:58,8820[main] INFO JavaCloneTest - copy.getClass() == rec.getClass() :true
2013-05-2023:46:58,8820[main] INFO JavaCloneTest - copy.equals(rec) :true
2013-05-2023:46:58,8820[main] INFO JavaCloneTest - Original object :Rectangle [width:45, height:100, area:4500]
2013-05-2023:46:58,8820[main] INFO JavaCloneTest - Cloned object :Rectangle [width:30, height:60, area:1800]

从输出结果,你可以清晰的看到克隆对象和原始对象有相同的属性,同样改变原始对象的属性并不影响拷贝对象的状态。因为他们仅仅包含原生字段,包含任何可变对象将影响两者,你同样还可以看到标准的克隆对象的属性如:clone!=original,clone.getClass()==original.getClass(),clone.equals(original).

要记住的事情

  1. 克隆方法用于创建对象的拷贝,为了使用clone方法,类必须实现java.lang.Cloneable接口重写protected方法clone,如果没有实现Clonebale接口会抛出CloneNotSupportedException.
  2. 在克隆java对象的时候不会调用构造器
  3. java提供一种叫浅拷贝(shallow copy)的默认方式实现clone,创建好对象的副本后然后通过赋值拷贝内容,意味着如果你的类包含可变对象,那么原始对象和克隆都将指向相同的内部对象,这是很危险的,因为发生在可变的字段上任何改变将反应到原始对象和副本对象上。为了避免这种情况,重写clone()方法。
  4. 按照约定,实例的克隆应该通过调用super.clone()获取,这样有助克隆对象的不变性建如:clone!=original和clone.getClass()==original.getClass(),尽管这些不是必须的

转载于:https://my.oschina.net/Chaos777/blog/214291

clone方法是如何工作的相关推荐

  1. java深度克隆_浅析Java中clone()方法浅克隆与深度克隆

    现在Clone已经不是一个新鲜词语了,伴随着"多莉"的产生这个词语确实很"火"过一阵子,在Java中也有这么一个概念,它可以让我们很方便的"制造&qu ...

  2. Java高级特性:clone()方法

    标签:ringbuf   his   硬件   throws   port   protect   序列化   ext   this 目录 源码 深拷贝和浅拷贝 对象串行化实现拷贝 常见面试题 源码 ...

  3. java clone方法_干货满满:Java中创建对象的五种方式详解

    通常来说,对象具有状态和行为,变量用来表明对象的状态,方法表明对象所具有的行为. 作为Java开发者,我们通常都是使用依赖管理系统,比如Spring去创建Java对象,但使用管理系统创建对象并不是唯一 ...

  4. 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 ...

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

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

  6. 从自监督学习主流方法、最新工作进展,看未来前景研究方向

    来源:机器学习算法与自然语言处理 本文约6000字,建议阅读10分钟. 找到合适的辅助任务,对于自监督学习是最需要解决的问题. 本文作者来自东北大学,他通过整理自监督学习的一系列工作,把主流方法分成三 ...

  7. (二十三)原型模式详解(clone方法源码的简单剖析)

    作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 原型模式算是JAVA中最简单 ...

  8. 谨慎的覆盖clone方法

    2019独角兽企业重金招聘Python工程师标准>>> 说在前面 有些专家级程序员干脆从来不去覆盖clone方法,也从来不去调用它,除非拷贝数组. 其他方式 可以提供一个构造函数或者 ...

  9. java数组的clone方法_深入浅出,如何更彻底地理解Java数组的clone方法

    说在前面 在进入理解clone前,我们需要对"基本数据类型"和"引用数据类型"的存储模式有一个清晰的认识. 基本数据类型,变量的内容保存的是实际的值:引用数据类 ...

最新文章

  1. 第02章 PyTorch基础知识
  2. java为啥要捕捉异常_java – 为什么在捕获时使用IOexception而不是Exception?
  3. Java对象表示方式2:XStream实现对对象的XML化
  4. 程序从命令行接收多个数字,求和之后输出结果。
  5. Leetcode 127. 单词接龙 解题思路及C++实现
  6. Spring Boot 整合 Netty(附源码)
  7. linux mysql dns_Linux下搭建DNS服务器及踩坑
  8. Bootstrap 3: 图标转换事件 Change icons when toggle
  9. Python分析热门话题“不生孩子的人后来都怎么了”,看看丁克家庭最后都怎么样了...
  10. 为什么优秀和听话总是难两全?
  11. 百度网盘客户端(java)版本
  12. 不要拿你的认知来评价别人
  13. oracle 数据页,常见数据库分页实现方案-Oracle
  14. 老男孩教育33期周末班-决心书
  15. There was a problem confirming the ssl certificate: HTTPSConnectionPool(host=‘pypi.org‘,port=443)
  16. HNU2022夏季小学期大数据并行处理MapReduce任务
  17. 蓝桥杯 C++ 算法训练 藏匿的刺客 贪心
  18. linux中lockf的例子,小何讲Linux: 文件锁及其实例
  19. [DDR]2 - Initialization, Training and Calibration
  20. 《设计模式》——里氏替换原则

热门文章

  1. YiShaAdmin_项目的默认 XML 命名空间必须为 MSBuild XML 命名空间。如果项目是用 MSBuild 2003 格式---.Net_C#_若依.Net版Web框架使用及改造001
  2. 大数据_MapperReduce_Hbase的优化和Hbase相关面试题_以及hbase的javaapi的一部分源码---Hbase工作笔记0029
  3. k8s高可用集群_搭建高可用集群(初始化和部署keepalived)---K8S_Google工作笔记0055
  4. Seata多微服务互相调用_全局分布式事物使用案例_Storage-Module 仓储微服务说明---微服务升级_SpringCloud Alibaba工作笔记0063
  5. STM32工作笔记0049---JLINK在线调试__软件调试方法与技巧
  6. 工作资讯003---甘特图
  7. VB.NET工作笔记003---使用ASP_vbs脚本_或vb.net调用Sqlserver DTS文件
  8. 局域网如何通过SSH 2连接上VMware 10虚拟机新装的centOS系统远程登录
  9. 什么是php渲染,php数据渲染输出
  10. java数据链表 有什么用_链表(linked list)这一数据结构具体有哪些实际应用?