第十一条:谨慎的覆盖clone()方法

一个类要想实现克隆,需要实现Cloneable接口,表明这个类的对象具有克隆的功能。

Cloneable接口是一个mixin接口,它里面并没有任何的抽象方法,类似的接口有Serializable接口,表明该类的对象可以序列化。

首先应该明确通过一个对象克隆出另一个对象的概念:通过一个对象克隆出另一个对象,简称对象1和对象2,这两个对象就成为两个独立的对象,

那么对对象1做任何的修改,对象2应该不受影响;对对象2做任何的修改的,对象1就不应该受影响。这样就要求对象1与对象2的实例域各自是独立的。

那么我们知道一个对象的实例域,包括基本类型(int,long..等等)的域,引用类型的域(分为可变的域和final不可变的域)。

现在了解Object类的clone()方法:

protected native Object clone() throws CloneNotSupportedException;

clone()方法是一个受保护的本地方法。

clone()方法的通用约束如下:

x.clone() != x        应该返回true

x.clone().getClass() == x.getClass()        应该返回true

x.clone().equals(x)           应该返回true

对于实现了Cloneable()接口的类,也就是具有克隆能力的类,我们总是期望它提供一个功能适当的公有的clone()方法。

如果我们在一个类的clone()方法只调用super.clone(),然后返回,它仅仅是返回了一个新的对象,这个对象具有和原对象一样的基本类型域,

但是这个对象的引用类型域引用的对象还是原对象的引用类型域所引用的对象。这样就导致一个问题,对原对象引用类型的域所做的修改会影响

到新对象的引用类型域。     也就是说调用Object类的克隆方法,仅仅完成了新对象的创建,和基本类型域的克隆,但是引用类型域的引用还是

指向了旧对象的引用类型域的实例。如下图:

但是有一种情况允许出现这样的克隆,这个类引用类型域只有final不可变类型。 因为对象1的final引用类型域只要引用到了实例,就不允许再改变。

这样对象1的域要改变也只有基本类型域了,而基本类型域我们已经完成了克隆。

简而言之,所有实现了Cloneable接口的类都应该有一个公有的方法覆盖从Object类继承而来的clone()方法,此公有方法先调用super.clone(),

得到一个“浅拷贝”的对象,然后根据原对象的实例域的情况,修正任何需要修正的域。一般情况下,这意味这要拷贝任何包含内部“深层结构”

的可变对象。并将新对象的引用代替原来指向这些对象的引用。虽然,这些内部拷贝操作往往可以通过层层递进的调用clone()来完成(要求图中的

Apple类和Dog类也必须正确的提供公有的clone()方法),但这通常不是最佳的方法。如果该类只包含基本类型的域,和不可变类型的引用域,

那么多半情况下是没有域需要修正的。

时间: 2024-10-10 10:19:01

第十一条:谨慎的覆盖clone()方法的相关文章

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

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

第11条:谨慎地覆盖clone

Cloneable接口表明这样的对象时允许克隆的,但这个接口并没有成功达到这个目的,主要是因为它缺少一个clone方法,Object的clone方法是受保护的.如果不借助反射,就不能仅仅因为一个对象实现了Colneable就可以钓鱼clone方法,即使是反射调用也不能保证这个对象一定具有可访问clone方法. 既然Cloneable并没有包含任何方法,那么它到底有什么用呢?它其实觉得了Object中受保护的clone方法实现的行为,如果一个类实现了Cloneable那么Object的clone方

[Effective Java 读书笔记] 第三章 对所有对象都通用的方法 第十---十一条

第十条 始终覆盖toString() toString的实现可以使类使用起来更加舒适,在执行println等方法时打印出定制信息. 一单实现了自己的toString,指定输出的固定格式,在方法的文档说明中应该做好注释说明! 第十一条 谨慎覆盖clone

Effective Java - 谨慎覆盖clone

覆盖clone时需要实现Cloneable接口,Cloneable并没有定义任何方法. 那Cloneable的意义是什么? 如果一个类实现了Clonable,Object的clone方法就可以返回该对象的逐域拷贝,否则会抛出CloneNotSupportedException. 通常,实现接口是为了表明类的行为. 而Cloneable接口改变了超类中protected方法的行为. 这是种非典型用法,不值得仿效. 好了,既然覆盖了clone方法,我们需要遵守一些约定: x.clone() != x

谨慎重载clone方法

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

[Effective Java 读书笔记] 第三章类和接口 第二十-二十一条

第二十条 用函数对象表示策略 函数指针(JAVA的函数指针,是指使用对象的引用来作为参数,传递给另一个对象的方法)主要用来实现策略模式,为了在JAVA中实现这种模式,要申明一个接口来表示该策略,并为每个具体策略申明一个实现了该接口的类. 如果这个策略只被执行一次,使用匿名类,如果重复使用,则通常实现为私有的静态成员类,并通过共有的静态final域导出(最后一个例子),其类型为该策略接口. 第二十一条 优先考虑静态成员类 嵌套类主要有四种:静态成员类,非静态成员类,匿名类,局部类 静态成员类,一般

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

(转)搞定DC/DC电源转换方案设计,必看金律十一条

[导读] 搞嵌入式的工程师们往往把单片机.ARM.DSP.FPGA搞的得心应手,而一旦进行系统设计,到了给电源系统供电,虽然也能让其精心设计的程序运行起来,但对于新手来说,有时可能效率低下,往往还有供电电流不足或过大引起这样那样的问题,本文十大金律轻松搞定DCDC电源转换电路设计. 关键词:DC/DC 搞嵌入式的工程师们往往把单片机.ARM.DSP.FPGA搞的得心应手,而一旦进行系统设计,到了给电源系统供电,虽然也能让其精心设计的程序运行起来,但对于新手来说,有时可能效率低下,往往还有供电电流

Java clone方法(下)

1.最终调用的是一个JNI方法,即java本地方法,加快速度 2.使用clone方法,分为浅复制.深复制,这里直接使用网上抄来的案例来说明吧: 说明: 1)为什么我们在派生类中覆盖Object的clone()方法时,一定要调用super.clone()呢?在运行时刻,Object中的clone()识别你要复制的是哪一个对象,然后为此对象分配空间,并进行对象的复制,将原始对象的内容一一复制到新对象的存储空间中. 2)继承自java.lang.Object.clone()方法是浅层复制.一下代码可以