clone方法是如何工作的


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方法,创建副本。
1 2 3 4 5 6 7 8 9 10 11 Rectangle rec = new Rectangle(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.
1 2 3 4 @Override     protected Rectangle clone() throws CloneNotSupportedException {         return (Rectangle) super.clone();     }
1  
最后调用到达java.lang.Object的clone()时,他验证相关的类是否实现Cloneable接口,如果没有实现那么抛出CloneNotSupportedException,否则他创建副本filed-by-field。 所以为了clone()方法可以正确的工作,两件事会发生:类必须实现Cloneable接口,必须重写clone方法,这是最简单的例子,对于更复杂的对象,它包含多个fileds、数组,结合不可变对象和原始类型,我们来看第二个clone教程 clone()举例 在这篇文章中,我们没有看到复杂的重写clone的方法,因为我们的Rectangle类非常简单,仅仅只是包含原生类型,只需通过Object的clone方法浅克隆就足够了。但是下面这个例子对理解对象的克隆很重要,这里是完整的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 import org.apache.log4j.Logger;   /**   * Simple example of overriding clone() method in Java to understand How Cloning of   * Object works in Java.   *   * @author  */ public class JavaCloneTest {     private static final Logger logger = Logger.getLogger(JavaCloneTest.class);       public static void main(String args[]) {           Rectangle rec = new Rectangle(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);     }   }   public class Rectangle implements Cloneable{     private int width;     private int height;       public Rectangle(int w, int h){         width = w;         height = h;     }       public void setHeight(int height) {         this.height = height;     }       public void setWidth(int width) {         this.width = width;     }       public int area(){         return widthheight;     }       @Override     public String toString(){         return String.format("Rectangle [width: %d, height: %d, area: %d]", width, height, area());     }       @Override     protected Rectangle clone() throws CloneNotSupportedException {         return (Rectangle) super.clone();     }       @Override     public boolean equals(Object obj) {         if (obj == null) {             return false;         }         if (getClass() != obj.getClass()) {             return false;         }         final Rectangle other = (Rectangle) obj;         if (this.width != other.width) {             return false;         }         if (this.height != other.height) {             return false;         }         return true;     }       @Override     public int hashCode() {         int hash = 7;         hash = 47  hash + this.width;         hash = 47  hash + this.height;         return hash;     }   }   Output: 2013-05-20 23:46:58,882 0    [main] INFO  JavaCloneTest  - Rectangle [width: 30, height: 60, area: 1800] 2013-05-20 23:46:58,882 0    [main] INFO  JavaCloneTest  - Creating Copy of this object using Clone method 2013-05-20 23:46:58,882 0    [main] INFO  JavaCloneTest  - Copy Rectangle [width: 30, height: 60, area: 1800]   2013-05-20 23:46:58,882 0    [main] INFO  JavaCloneTest  - copy != rec : true 2013-05-20 23:46:58,882 0    [main] INFO  JavaCloneTest  - copy.getClass() == rec.getClass() : true 2013-05-20 23:46:58,882 0    [main] INFO  JavaCloneTest  - copy.equals(rec) : true   2013-05-20 23:46:58,882 0    [main] INFO  JavaCloneTest  - Original object :Rectangle [width: 45, height: 100, area: 4500]   2013-05-20 23:46:58,882 0    [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(),尽管这些不是必须的
时间: 2024-10-31 15:32:15

clone方法是如何工作的的相关文章

JAVA中的clone方法剖析

原文出自:http://blog.csdn.net/shootyou/article/details/3945221 java中也有这么一个概念,它可以让我们很方便的“制造”出一个对象的副本来,下面来具体看看java中的Clone机制是如何工作的?     1. Clone&Copy     假设现在有一个Employee对象,Employee tobby =new Employee(“CMTobby”,5000),通常我们会有这样的赋值Employee cindyelf=tobby,这个时候只

Effective Java 第三版——13. 谨慎地重写 clone 方法

Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将近8年的时间,但随着Java 6,7,8,甚至9的发布,Java语言发生了深刻的变化. 在这里第一时间翻译成中文版.供大家学习分享之用. 13. 谨慎地重写 clone 方法 Cloneable接口的目的是作为一个mixin接口(条目 20),公布这样的类允许克隆.不幸的是,它没有达到这个目的.它的主要缺点是

Java高级特性:clone()方法

目录 源码 深拷贝和浅拷贝 对象串行化实现拷贝 常见面试题 源码 public class Objcet{ protected native Object clone() throws CloneNotSupportedException(); } 由源码可知. 第一:Objcet类的clone()方法是一个native方法.native方法的执行效率一般远高于Java中的非native方法(一般不是java语言所写).这也解释了为什么要用Object的clone()方法,而不是先new一个类,

java Object对象的clone方法

参考copy链接:http://blog.csdn.net/bigconvience/article/details/25025561 在看原型模式,发现要用到clone这个方法,以前和朋友聊过,没怎么看过,刚好要用,就看看了. 源码解释: /** * Creates and returns a copy of this object. The precise meaning * of "copy" may depend on the class of the object. The

JQ中的clone()方法与DOM中的cloneNode()方法

JQ中的clone()方法与DOM中的cloneNode()方法 cloneNode()定义和用法 cloneNode()方法创建节点的拷贝,并返回该副本. 语法: node.cloneNode(deep);  其接收一个可选参数"deep",为布尔类型,默认是false. 当设置为true,克隆当前节点,属性及当前节点的后代.若设置为false,仅仅克隆当前元素节点本身. 扩展:  使用cloneNode()方法,当被克隆的节点绑定了事件处理程序,事件处理程序是否会被一同克隆,这个我

谨慎重载clone方法

本文涉及到的概念 1.浅拷贝和深拷贝 2..clone方法的作用和使用方式 3.拷贝构造器和拷贝工厂 1.浅拷贝和深拷贝 浅拷贝 一个类实现Cloneable接口,然后,该类的实例调用clone方法,返回一个新的实例. 新的实例与原来的实例是不同的对象. 新的实例的各个非引用类型的成员变量值与原来的实例的成员变量值相同. 对于引用类型的成员变量,新实例的成员变量和旧实例的成员变量,都是指向相同的对象,引用值相同,这种称作浅拷贝(shallow copying) public class Car

详解Java中的clone方法

转载自:http://blog.csdn.net/zhangjg_blog/article/details/18369201 Java中对象的创建 clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象.所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象.那么在java语言中,有几种方式可以创建对象呢? 1 使用new操作符创建一个对象 2 使用clone方法复制一个对象 那么这两种方式有什么相同和不同呢? new操作符的本意是

C#关于Clone()方法的介绍

日常啪啪啪代码的时候,常常遇到浅复制与深复制的问题,下面就自己经验写写,有问题请留言! 例如我有一个简单的类: class People { public int _age; public string _name; public People(int Age,string Name) { _age = Age; _name = Name; } } 常见的赋值语句,如: People Mike = new People(12,"Mike"); People Mike2 = Mike;

Cloneable接口和Object的clone()方法

http://www.cnblogs.com/xrq730/p/4858937.html 为什么要克隆 为什么要使用克隆,这其实反映的是一个很现实的问题,假如我们有一个对象: public class SimpleObject implements Cloneable { private String str; public SimpleObject() { System.out.println("Enter SimpleObject.constructor()"); } public