Item 11: Override clone judiciously (审慎的)

fantasy0126 2010-12-29 02:00:59
1) 一个类实现了Cloneable接口,才可能在它的实例上,或通过反射调用clone()方法,否则会抛出
CloneNotSupportedException
2) 某个类要实现clone()方法,那么所有它的超类必须服从 a fairly complex,unenforceable,and thinly
documented protocol(一个相当复杂的,不能强制实行的的并且很小文档的协议?) 由此得到一个语言之
外的机制:无需调用构造函数便可创建对象
3) java se 6.0 上的api说明(不解释)
Creates and returns a copy of this object. The precise meaning of “copy” may
depend on the class of the object. The general intent is that, for any object x,
the expression
x.clone() != x
will be true, and the expression
x.clone().getClass() == x.getClass()
will be true, but these are not absolute requirements. While it is typically the
case that
x.clone().equals(x)
will be true, this is not an absolute requirement. Copying an object will typi-
cally entail creating a new instance of its class, but it may require copying of
internal data structures as well. No constructors are called.
4) No constructors are called这个要求太强了,一个行为良好的clone方法可以在其内部数据成员上调用构造
函数,来完成clone的功能,如果实现clone的这个类是final的,则可以直接调用构造函数来创建本类的对象.
这里解释一下,如果是非final的类,如果也这么搞的话,那么子类如果也提供一个clone()方法,在调用super
.clone()是就会返回一个超类的实例.这样x.clone().getClass() == x.getClass()就不是true了
5) if you override the clone method in a nonfinal class, you should
return an object obtained by invoking super.clone. If all of a class’s super-
classes obey this rule, then invoking super.clone will eventually invoke
Object’s clone method, creating an instance of the right class.
5) 作为1.6的发行版,并没有讲实现Cloneable接口应该承担的责任.
在实践中,一个实现了Cloneable的类应该提供一个合适的公有clone方法
但这必须在它的所有超类都提供了一个公有的,或者保护的.well behaved clone implementation的基础上才可能完成
6) 如果超类就是Object并且数据成员只有不变类型或primitive类型这样写就OK
注意以下用了5.0的新特性covariant return type
@Override public PhoneNumber clone() {
try {
return (PhoneNumber) super.clone();
} catch(CloneNotSupportedException e) {
throw new AssertionError(); // Can't happen
}
}

Object.clone()方法会返回正确的类型,所有数据成员的值会和原对象一样. 对于非primitive非不变类型的类型会和原对象共享同一引用(事实上不变类型也在共享同一引用,但那是没关系的)
有一些例外,如果一个字段代表序列号或其他unique ID,或者代表对象的创建时间,这就需要在clone()方法中做出修正,即使这些字段是primitive或不变类型
7) Stack是你自己写的一个普通类,elements是一个Object 数组
@Override public Stack clone() {
try {
Stack result = (Stack) super.clone();
result.elements = elements.clone();
return result;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
As of release 1.5, calling clone on an array returns an array whose compile-time
type is the same as that of the array being cloned.
如果 elements是final,那么这段代码就有问题.事实上,如果想要实现clone有必要移除某些final修饰符

8) 一个HashTable,内部用数组实现,数组的每个元素要么是null,要指向一个单向链表,这个单向链表的每个元素是一个键值 对和指向下一个的引用.出于性能的考虑,它并不是java.util.LinkedList
public class HashTable implements Cloneable {
private Entry[] buckets = ...;
private static class Entry {
final Object key;
Object value;
Entry next;
Entry(Object key, Object value, Entry next) {
this.key = key;
this.value = value;
this.next = next;
}
// Recursively copy the linked list headed by this Entry //这里写得很帅气
Entry deepCopy() {
return new Entry(key, value,
next == null ? null : next.deepCopy());
}
}
@Override public HashTable clone() {
try {
HashTable result = (HashTable) super.clone();
result.buckets = new Entry[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) {
throw new AssertionError();
}
}
... // Remainder omitted
}

这里因为那个deepCopy()方法是递归调用,对链表的每一个元素要消耗一个stack frame, 如果这个链表过长,可能会引起Stack overflow
将递归改成循环
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;
}

8)7的一个替代办法是
@Override public HashTable clone() {
try {
HashTable result = (HashTable) super.clone();
return result;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
然后先初始化buckets数组,再对于这个hashtable的所有元素,再put进去一次
9)clone()方法中不应调用任何非final的,并且会被子类覆写的方法.这是由于java的多态机制决定的,类似于构造函数的情况.因为子类的数据成员此时还没有初始化.所以8)中的put方法或许应该是私有的.
这是为了解决子类的clone方法调用super.clone()可能会出现的问题,如果本类是final的就不用这样担心了

10)如果本类覆写clone方法是为了便于在子类中决定是否子类中也覆写
it should be declared protected, it should be declared to throw CloneNotSupportedExcep-
tion, and the class should not implement Cloneable.

11)如果想写一个线程安全的clone()方法,加入synchronized关键字吧,防止正clone的对象正处于某种内部不一致状态,和此对象的其他改变状态的方法共享同一个锁

12)对于clone()方法的替代方案
提供一个用于复制的构造函数
public Yum(Yum yum);
或者一个静态工厂方法
public static Yum newInstance(Yum yum);
或者叫做conversion constructors and conversion factories有新的功能,即新对象可以和原对象的类型不同,但数据是一样的
allow the client to choose the implementation type of the copy rather than forcing the client
to accept the implementation type of the original. Suppose you have a HashSet s,
and you want to copy it as a TreeSet. The clone method can’t offer this function-
ality, but it’s easy with a conversion constructor: new TreeSet(s).
...全文
227 2 打赏 收藏 转发到动态 举报
写回复
用AI写文章
2 条回复
切换为时间正序
请发表友善的回复…
发表回复
fhuibo212 2010-12-29
  • 打赏
  • 举报
回复
haoduo啊
小莫M 2010-12-29
  • 打赏
  • 举报
回复
学习了,不过.....你想说什么.....
源码链接: https://pan.quark.cn/s/0f7c75cb3aad ### MIPI Video Mode 与 Command Mode 的差异 #### 一、引言 MIPI (Mobile Industry Processor Interface) 是一种用于连接移动设备中处理器及其外围设备的标准化接口。MIPI 接口支持多种协议,其中包含 DSI (Display Serial Interface) 和 DCS (Display Control Interface) 等协议。在 MIPI 接口的应用中,主要涉及两种工作模式:Video Mode(视频模式)和 Command Mode(命令模式)。本文旨在系统性地阐述这两种模式的工作机制、特性以及实际应用环境。 #### 二、LCD RAM 概念说明 在进一步探讨 MIPI 的两种模式之前,有必要对文中提及的“LCD RAM”概念进行明确。实际上,“LCD RAM”并非一个通用术语,而是本文作者用来描述 LCD 控制器中用于存储显示数据的内存区域。LCD(Liquid Crystal Display,液晶显示屏)通常配备一个控制 IC(Integrated Circuit,集成电路),该控制 IC 可能内置 RAM 以缓存显示数据。 #### 三、MIPI Video Mode(视频模式) **定义:** - 视频模式是一种类似于传统 RGB 接口的工作模式,它要求主机持续不断地向显示器传输刷新数据。 - 在这种模式下,数据和控制信号以报文的形式通过 MIPI 总线进行传输。 - 显示器本身无需配备帧缓冲器,因为主机会周期性地刷新屏幕。 **特点:** 1. **实时性高:** 主机需要不...
源码下载地址: https://pan.quark.cn/s/a4b39357ea24 在电磁模拟技术中,CST(Computer Simulation Technology)是一种被广泛采纳的软件工具,它主要用于电磁场、微波、天线以及射频系统的设计工作。本资料将详细分析CST软件中离散端口的具体配置方法,这些方法对于提升仿真结果的精确度和专业水准具有决定性作用。离散端口在CST软件中扮演着模拟信号输入或输出的重要角色,它们构成了仿真模型不可或缺的部分。在配置离散端口时,一个核心的原则是保证端口的方向与网格线保持一致,这是因为这样做能够有效降低计算过程中产生的误差,并确保仿真数据的有效性。如果未能遵循这一指导原则,可能会引发未知的计算问题,进而导致仿真结果失去可靠性。 在CST软件中配置离散端口,通常需要借助“Pick Points”这一功能。通过选择“Pick Edge Center”选项,端口将被设定在模型边缘的中心位置上。然而,这种做法并不总是能够确保端口与网格线保持平行。在某些特定情形下,模型的几何构造可能不允许直接选取一个与网格线平行的边作为端口的安装位置。 为了克服这一挑战,可以采用多种不同的策略。如果模型本身已经包含一条与馈电口平行的边,那么可以直接利用这条边来建立端口,此时CST软件会自动调整端口使其与网格线对齐。另一种可选的方法是,当模型不具备现成的平行边时,用户可以手动构建一个几何结构,比如一个立方体,并使其边缘与馈电口平行。通过这种方式,新建立的几何结构的边缘就可以作为端口的位置,从而确保端口与网格线的平行关系。 在实施上述操作时,必须关注端口尺寸的合理性和物理意义的一致性。端口的尺寸应当依据实际天线馈电部分的尺寸进行适当调整,过大的端口或...

62,622

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧