java克隆的节制
为消除克隆本领,各人也许认为只需将clone()要领简朴地设为private(私有)即可,但这样是行不通的,因为不能回收一个基本类要领,并使其在衍生类中更“私有”。所以工作并没有这么简朴。另外,我们有须要节制一个工具是否可以或许克隆。对付我们设计的一个类,实际有很多种方案都是可以采纳的:
(1) 保持中立,不为克隆做任何工作。也就是说,尽量不行对我们的类克隆,但从它担任的一个类却可按照实际环境抉择克隆。只有Object.clone()要对类中的字段举办某些公道的操纵时,才可以作这方面的抉择。
(2) 支持clone(),回收实现Cloneable(可克隆)本领的尺度操纵,并包围clone()。在被包围的clone()中,可挪用super.clone(),并捕捉所有违例(这样可使clone()不“掷”出任何违例)。
(3) 有条件地支持克隆。若类容纳了其他工具的句柄,而那些工具也许可以或许克隆(荟萃类即是这样的一个例子),就可试着克隆拥有对方句柄的所有工具;假如它们“掷”出了违例,只需让这些违例通过即可。举个例子来说,假设有一个非凡的Vector,它试图克隆本身容纳的所有工具。编写这样的一个Vector时,并不知道客户措施员会把什么形式的工具置入这个Vector中,所以并不知道它们是否真的可以或许克隆。
(4) 不实现Cloneable(),可是将clone()包围成protected,使任何字段都具有正确的复制行为。这样一来,从这个类担任的所有对象都能包围clone(),并挪用super.clone()来发生正确的复制行为。留意在我们实现方案里,可以并且应该挪用super.clone()——纵然谁人要领原来预期的是一个Cloneable工具(不然会掷出一个违例),因为没有人会在我们这种范例的工具上直接挪用它。它只有通过一个衍生类挪用;对谁人衍生类来说,假如要担保它正常事情,需实现Cloneable。
(5) 不实现Cloneable来试着防备克隆,并包围clone(),以发生一个违例。为使这一设想顺利实现,只有令从它衍生出来的任何类都挪用从头界说后的clone()里的suepr.clone()。
(6) 将类设为final,从而防备克隆。若clone()尚未被我们的任何一个上级类包围,这一设想便不会乐成。若已被包围,那么再一次包围它,并“掷”出一个CloneNotSupportedException(克隆不支持)违例。为包管克隆被克制,将类设为final是独一的步伐。除此以外,一旦涉及保密工具可能碰着想对建设的工具数量举办节制的其他环境,应该将所有构建器都设为private,并提供一个或更多的非凡要领来建设工具。回收这种方法,这些要领就可以限制建设的工具数量以及它们的建设条件——一种非凡环境是第16章要先容的singleton(独子)方案。
下面这个例子总结了克隆的各类实现要领,然后在条理布局中将其“封锁”:
//: CheckCloneable.java
// Checking to see if a handle can be cloned
// Can't clone this because it doesn't
// override clone():
class Ordinary {}
// Overrides clone, but doesn't implement
// Cloneable:
class WrongClone extends Ordinary {
public Object clone()
throws CloneNotSupportedException {
return super.clone(); // Throws exception
}
}
// Does all the right things for cloning:
class IsCloneable extends Ordinary
implements Cloneable {
public Object clone()
throws CloneNotSupportedException {
return super.clone();
}
}
// Turn off cloning by throwing the exception:
class NoMore extends IsCloneable {
public Object clone()
throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
}
class TryMore extends NoMore {
public Object clone()
throws CloneNotSupportedException {
// Calls NoMore.clone(), throws exception:
return super.clone();
}
}
class BackOn extends NoMore {
private BackOn duplicate(BackOn b) {
// Somehow make a copy of b
// and return that copy. This is a dummy
// copy, just to make the point:
return new BackOn();
}
public Object clone() {
// Doesn't call NoMore.clone():
return duplicate(this);
}
}
// Can't inherit from this, so can't override
// the clone method like in BackOn:
final class ReallyNoMore extends NoMore {}
public class CheckCloneable {
static Ordinary tryToClone(Ordinary ord) {
String id = ord.getClass().getName();
Ordinary x = null;
if(ord instanceof Cloneable) {
try {
System.out.println("Attempting " + id);
x = (Ordinary)((IsCloneable)ord).clone();
System.out.println("Cloned " + id);
} catch(CloneNotSupportedException e) {
System.out.println(
"Could not clone " + id);
}
}
return x;
}
public static void main(String[] args) {
// Upcasting:
Ordinary[] ord = {
new IsCloneable(),
new WrongClone(),
new NoMore(),
new TryMore(),
new BackOn(),
new ReallyNoMore(),
};
Ordinary x = new Ordinary();
// This won't compile, since clone() is
// protected in Object:
//! x = (Ordinary)x.clone();
// tryToClone() checks first to see if
// a class implements Cloneable:
for(int i = 0; i < ord.length; i++)
tryToClone(ord[i]);
}
} ///:~
#p#分页标题#e#
第一个类Ordinary代表着各人在本书遍地最常见到的类:不支持克隆,但在它正式应用今后,却也不克制对其克隆。但如果有一个指向Ordinary工具的句柄,并且谁人工具大概是从一个更深的衍生类上溯造型来的,便不能判定它到底能不能克隆。
WrongClone类展现了实现克隆的一种不正确途径。它确实包围了Object.clone(),并将谁人要领设为public,但却没有实现Cloneable。所以一旦发出对super.clone()的挪用(由于对Object.clone()的一个挪用造成的),便会无情地掷出CloneNotSupportedException违例。
在IsCloneable中,各人看到的才是举办克隆的各类正确动作:先包围clone(),并实现了Cloneable。可是,这个clone()要领以及本例的别的几个要领并不捕捉CloneNotSupportedException违例,而是任由它通过,并通报给挪用者。随后,挪用者必需用一个try-catch代码块把它困绕起来。在我们本身的clone()要领中,凡是需要在clone()内部捕捉CloneNotSupportedException违例,而不是任由它通过。正如各人今后会领略的那样,对这个例子来说,让它通过是最正确的做法。
类NoMore试图凭据Java设计者规划的那样“封锁”克隆:在衍生类clone()中,我们掷出CloneNotSupportedException违例。TryMore类中的clone()要领正确地挪用super.clone(),并理会成NoMore.clone(),后者掷出一个违例并克制克隆。
但在已被包围的clone()要领中,假使措施员不遵守挪用super.clone()的“正确”要领,又会呈现什么环境呢?在BackOn中,各人可看到实际会产生什么。这个类用一个独立的要领duplicate()建造当前工具的一个副本,并在clone()内部挪用这个要领,而不是挪用super.clone()。违例永远不会发生,并且新类是可以克隆的。因此,我们不能依赖“掷”出一个违例的要领来防备发生一个可克隆的类。独一安详的要领在ReallyNoMore中获得了演示,它设为final,所以不行担任。这意味着如果clone()在final类中掷出了一个违例,便不能通过担任来举办修改,并可有效地克制克隆(不能从一个拥有任意担任级数的类中明晰挪用Object.clone();只能挪用super.clone(),它只可会见直接基本类)。因此,只要建造一些涉及安详问题的工具,就最好把那些类设为final。
在类CheckCloneable中,我们看到的第一个类是tryToClone(),它能采取任何Ordinary工具,并用instanceof查抄它是否可以或许克隆。若谜底是必定的,就将工具造型成为一个IsCloneable,挪用clone(),并将功效造型回Ordinary,最后捕捉有大概发生的任何违例。请留意用运行期范例判断(见第11章)打印出类名,使本身看到产生的一切环境。
在main()中,我们建设了差异范例的Ordinary工具,并在数组界说中上溯造型成为Ordinary。在这之后的头两行代码建设了一个纯粹的Ordinary工具,并试图对其克隆。然而,这些代码不会获得编译,因为clone()是Object中的一个protected(受到掩护的)要领。代码剩余的部门将遍历数组,并试着克隆每个工具,别离陈诉它们的乐成或失败。输出如下:
Attempting IsCloneable Cloned IsCloneable Attempting NoMore Could not clone NoMore Attempting TryMore Could not clone TryMore Attempting BackOn Cloned BackOn Attempting ReallyNoMore Could not clone ReallyNoMore
总之,假如但愿一个类可以或许克隆,那么:
(1) 实现Cloneable接口
(2) 包围clone()
(3) 在本身的clone()中挪用super.clone()
(4) 在本身的clone()中捕捉违例
这一系列步调能到达最抱负的结果。