日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > java >内容正文

java

《克隆人的进攻》面向对象Java版

發(fā)布時(shí)間:2024/3/12 java 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 《克隆人的进攻》面向对象Java版 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
Java Q&A - Java 問(wèn)答 - Attack of the clones - 克隆人的進(jìn)攻 之 面向?qū)ο驤ava版

Java Q&A
Java 問(wèn)答

Attack of the clones

克隆人的進(jìn)攻

之 面向?qū)ο驤ava版

Time and space considerations in four different approaches to implementing deep clone() methods

權(quán)衡時(shí)間和空間的得失,有四種不同的方案來(lái)實(shí)現(xiàn) deep clone() 方法。

By Vladimir Roubtsov
K ][ N G of A R K? 編譯 [Revision 0.1]


January 24, 2003
二零零三年一月二十四日

Q:What are the advantages and disadvantages of implementing deep cloning via Java serialization and a built-in Object.clone() method from a performance point of view?
問(wèn):從性能的角度觀之, 以 Java serialization(次第讀寫(xiě))或者內(nèi)建的 Object.clone() 方法(method)來(lái)實(shí)現(xiàn) deep cloning(深度克隆),各有哪些優(yōu)劣之處?

A:Equipping classes in your application with correct clone() implementation is essential to many defensive programming patterns. Common examples include defensive copying of method parameters, cloning internal fields before returning them in getters, implementing immutability patterns, implementing containers with deep cloning semantics, and so on.
答:在您的應(yīng)用程序中為各個(gè)類別搭載正確實(shí)現(xiàn)了的 clone() 方法,這對(duì)于許多防御式編程模式而言是至關(guān)重要的。常見(jiàn)的防御式編程模式包括:防御式的對(duì)方法接收的參數(shù)進(jìn)行拷貝;從getters返回內(nèi)部字段(field)之前先對(duì)該內(nèi)部字段進(jìn)行克隆;實(shí)現(xiàn)提供不可變功能的模式(immutability patterns);以 deep cloning(深度克隆)語(yǔ)義實(shí)現(xiàn) containers(容器);等等。

Even though the question mentions just two possibilities, there are at least four distinct approaches to clone() implementation. In this Java Q&A installment, I consider design and performance tradeoffs involved in all of them.
盡管問(wèn)題中只提到了兩個(gè)可能的方案,其實(shí)至少有四種不同的方案來(lái)實(shí)現(xiàn) clone() 方法。在本期的Java 問(wèn)答中,我就針對(duì)這四種方案來(lái)進(jìn)行設(shè)計(jì)和性能兩方面的權(quán)衡。

Because cloning is so customizable, this article's examples will not necessarily translate directly to your own code; however, the general conclusions we will reach should provide useful guidelines in any application design.
由于克隆實(shí)現(xiàn)代碼的可定制性很強(qiáng),因此本文的示例代碼不一定就適合直接轉(zhuǎn)化到您自己的代碼中;然而,我們得出的普適結(jié)論應(yīng)該能為任何應(yīng)用設(shè)計(jì)提供有用的指導(dǎo)。

Note the following disclaimer: What exactly constitutes a deep clone is debatable. Even though two objects can safely share the same String reference viewed as data, they cannot if the same field is used as an instance-scoped object monitor (such as when calling Object.wait()/notify() on it) or if field instance identity (such as when using the == operator) is significant to the design. In the end, whether or not a field is shareable depends on the class design. For simplicity, I assume below that all fields are used as pure data.
請(qǐng)注意這面這句不作承諾的聲明:deep clone(深度克隆)究竟有哪些具體的實(shí)現(xiàn)要素,這個(gè)問(wèn)題本身就具有爭(zhēng)議性。盡管一個(gè)被視為數(shù)據(jù)的“String 引用”可以被兩個(gè)對(duì)象安全的共享,但如果該 String 字段是被用作實(shí)體生存空間范圍內(nèi)(instance-scoped)的對(duì)象監(jiān)視器(object monitor,比如對(duì)其調(diào)用 Object.wait()/notify() 的情形),或者字段實(shí)體的身份(identity)對(duì)于設(shè)計(jì)而言至關(guān)重要(比如使用 == operator 的情形),那么它就無(wú)法被安全的共享了。一言以蔽之,字段是否可被共享取決于類別的設(shè)計(jì)。為了簡(jiǎn)單起見(jiàn),我假設(shè)本文所述的所有字段都被視為純粹的數(shù)據(jù)來(lái)使用。

Performance measurements setup
用于性能度量的范例設(shè)定
Let's jump right into some code. I use the following simple hierarchy of classes as my cloning guinea pig:
讓我們直接來(lái)看些代碼。我使用如下簡(jiǎn)單的類別階層體系來(lái)作為克隆“實(shí)驗(yàn)鼠”:

public class TestBaseClass
???????????? implements Cloneable, Serializable
{
????public TestBaseClass (String dummy)
????{
????????m_byte = (byte) 1;
????????m_short = (short) 2;
????????m_long = 3L;
????????m_float = 4.0F;
????????m_double = 5.0;
????????m_char = '6';
????????m_boolean = true;

????????m_int = 16;
????????m_string = "some string in TestBaseClass";
????????
????????m_ints = new int [m_int];
????????for (int i = 0; i < m_ints.length; ++ i) m_ints [i] = m_int;
????????
????????m_strings = new String [m_int];
????????m_strings [0] = m_string; // invariant: m_strings [0] == m_string
????????for (int i = 1; i < m_strings.length; ++ i)
????????????m_strings [i] = new String (m_string);
????}

????public TestBaseClass (final TestBaseClass obj)
????{
????????if (obj == null) throw new IllegalArgumentException ("null input: obj");
????????
????????// Copy all fields:
????????
????????m_byte = obj.m_byte;
????????m_short = obj.m_short;
????????m_long = obj.m_long;
????????m_float = obj.m_float;
????????m_double = obj.m_double;
????????m_char = obj.m_char;
????????m_boolean = obj.m_boolean;
????????
????????m_int = obj.m_int;
????????m_string = obj.m_string;
????????
????????if (obj.m_ints != null) m_ints = (int []) obj.m_ints.clone ();
????????if (obj.m_strings != null) m_strings = (String []) obj.m_strings.clone ();
????}
????
????// Cloneable:
????public Object clone ()
????{
????????if (Main.OBJECT_CLONE)
????????{
????????????try
????????????{
????????????????// Chain shallow field work to Object.clone():
????????????????final TestBaseClass clone = (TestBaseClass) super.clone ();
????????????????
????????????????// Set deep fields:
????????????????if (m_ints != null)
????????????????????clone.m_ints = (int []) m_ints.clone ();
????????????????if (m_strings != null)
????????????????????clone.m_strings = (String []) m_strings.clone ();
????????????????
????????????????return clone;
????????????}
????????????catch (CloneNotSupportedException e)
????????????{
????????????????throw new InternalError (e.toString ());
????????????}
????????}
????????else if (Main.COPY_CONSTRUCTOR)
????????????return new TestBaseClass (this);
????????else if (Main.SERIALIZATION)
????????????return SerializableClone.clone (this);
????????else if (Main.REFLECTION)
????????????return ReflectiveClone.clone (this);
????????else
????????????throw new RuntimeException ("select cloning method");
????}
????
????protected TestBaseClass () {} // accessible to subclasses only
????

????private byte m_byte;
????private short m_short;
????private long m_long;
????private float m_float;
????private double m_double;
????private char m_char;
????private boolean m_boolean;

????private int m_int;
????private int [] m_ints;
????private String m_string;
????private String [] m_strings; // invariant: m_strings [0] == m_string????
} // end of class


public final class TestClass extends TestBaseClass
???????????? implements Cloneable, Serializable
{
????public TestClass (String dummy)
????{
????????super (dummy);
????????
????????m_int = 4;
????????
????????m_object1 = new TestBaseClass (dummy);
????????m_object2 = m_object1; // invariant: m_object1 == m_object2
????
????????m_objects = new Object [m_int];
????????for (int i = 0; i < m_objects.length; ++ i)
????????????m_objects [i] = new TestBaseClass (dummy);
????}

????public TestClass (final TestClass obj)
????{
????????// Chain to super copy constructor:
????????super (obj);
????????
????????// Copy all fields declared by this class:
????????
????????m_int = obj.m_int;
????????
????????if (obj.m_object1 != null)
????????????m_object1 = ((TestBaseClass) obj.m_object1).clone ();
????????m_object2 = m_object1; // preserve the invariant
????????
????????if (obj.m_objects != null)
????????{
????????????m_objects = new Object [obj.m_objects.length];
????????????for (int i = 0; i < m_objects.length; ++ i)
????????????????m_objects [i] = ((TestBaseClass) obj.m_objects [i]).clone ();
????????}
????}
????
????// Cloneable:
????public Object clone ()
????{
????????if (Main.OBJECT_CLONE)
????????{
????????????// Chain shallow field work to Object.clone():
????????????final TestClass clone = (TestClass) super.clone ();
????????????
????????????// Set only deep fields declared by this class:
????????????
????????????if (m_object1 != null)
????????????????clone.m_object1 = ((TestBaseClass) m_object1).clone ();
????????????clone.m_object2 = clone.m_object1; // preserve the invariant
????????????
????????????if (m_objects != null)
????????????{
????????????????clone.m_objects = (Object []) m_objects.clone ();
????????????????for (int i = 0; i < m_objects.length; ++ i)
????????????????????clone.m_objects [i] = ((TestBaseClass) m_objects [i]).clone ();
????????????}

????????????return clone;
????????}
????????else if (Main.COPY_CONSTRUCTOR)
????????????return new TestClass (this);
????????else if (Main.SERIALIZATION)
????????????return SerializableClone.clone (this);
????????else if (Main.REFLECTION)
????????????return ReflectiveClone.clone (this);
????????else
????????????throw new RuntimeException ("select cloning method");
????}
????????
????protected TestClass () {} // accessible to subclasses only

????private int m_int;????????
????private Object m_object1, m_object2; // invariant: m_object1 == m_object2
????private Object [] m_objects;
} // End of class

TestBaseClass has several fields of primitive types as well as a String and a couple of array fields. TestClass both extends TestBaseClass and aggregates several instances of it. This setup allows us to see how inheritance, member object ownership, and data types can affect cloning design and performance.
TestBaseClass 擁有幾個(gè)基本型別(primitive types)的字段(fields),還有一個(gè) String 以及兩個(gè)數(shù)組。 TestClass 繼承自 TestBaseClass ,還聚合了幾個(gè) TestBaseClass 實(shí)體。這種范例設(shè)定可以讓我們看到繼承、成員對(duì)象所有權(quán)(ownership)以及數(shù)據(jù)類型如何會(huì)影響克隆方法的設(shè)計(jì)與性能。

In a previous Java Q&A article, I developed a simple timing library that comes in handy now. This code in class Main measures the cost of TestClass.clone():
在 上一期 Java 問(wèn)答 中,我開(kāi)發(fā)了一個(gè)簡(jiǎn)單的計(jì)時(shí)程序庫(kù),現(xiàn)在可以信手拈來(lái)使用。在 class Main 中的如下代碼測(cè)量了 TestClass.clone() 的時(shí)間消耗:

????????// Create an ITimer:
????????final ITimer timer = TimerFactory.newTimer ();
????????
????????// JIT/hotspot warmup:
????????// ...

????????TestClass obj = new TestClass ();
????????
????????// Warm up clone():
????????// ...

????????final int repeats = 1000;
????????
????????timer.start ();
????????// Note: the loop is unrolled 10 times
????????for (int i = 0; i < repeats / 10; ++ i)
????????{
????????????obj = (TestClass) obj.clone ();
????????????... repeated 10 times ...
????????}
????????timer.stop ();
????????
????????final DecimalFormat format = new DecimalFormat ();
????????format.setMinimumFractionDigits (3);
????????format.setMaximumFractionDigits (3);
????????
????????System.out.println ("method duration: " +
????????????format.format (timer.getDuration () / repeats) + " ms");

I use the high-resolution timer supplied by TimerFactory with a loop that creates a moderate number of cloned objects. The elapsed time reading is reliable, and there is little interference from the garbage collector. Note how the obj variable continuously updates to avoid memory caching effects.
我使用了由 TimerFactory 提供的高解析度的計(jì)時(shí)器(high-resolution timer),利用一個(gè)循環(huán)創(chuàng)建了相當(dāng)數(shù)量的克隆出來(lái)的對(duì)象。表示流逝時(shí)間的數(shù)據(jù)是可靠的,受垃圾收集器的影響很小。請(qǐng)注意 obj 變量被持續(xù)更新,以避免內(nèi)存緩沖效應(yīng)(memory caching effects)。

Also note how clone() is implemented in both classes. The implementation in each class is in fact four, selected one at a time using four conditional compilation constants in Main: OBJECT_CLONE, COPY_CONSTRUCTOR, SERIALIZATION, and REFLECTION. Recompile the entire object when changing the cloning approach.
還請(qǐng)注意,在兩個(gè)類別中都實(shí)現(xiàn)了 clone() 方法。實(shí)際上每個(gè)類別中都有四種克隆動(dòng)作的實(shí)現(xiàn),可以通過(guò) Main 里面的條件編譯常量(conditional compilation constants)來(lái)選擇施行其中之一,這些常量分別是: OBJECT_CLONE,COPY_CONSTRUCTOR,SERIALIZATION 以及 REFLECTION 。要改變克隆動(dòng)作的實(shí)現(xiàn)方案,需要重新編譯整個(gè)類別。

Let's now examine each approach in detail.
現(xiàn)在我們就分別詳細(xì)的審視前述的四個(gè)方案。

Approach 1: Cloning by chaining to Object.clone()

方案 1:通過(guò)串鏈 Object.clone() 實(shí)現(xiàn)克隆

This is perhaps the most classical approach. The steps involved are:
這或許就是最經(jīng)典型的方案了。該方案涉及的實(shí)現(xiàn)步驟為:

  • Declare your class to implement the Cloneable marker interface.
  • 令您的類別實(shí)現(xiàn) Cloneable 標(biāo)記接口(marker interface)。
  • Provide a public clone override that always begins with a call to super.clone() followed by manual copying of all deep fields (i.e., mutable fields that are object references and cannot be shared between several instances of the parent class).
  • 提供一個(gè)覆寫(xiě)(override)版本的 public clone 方法,其內(nèi)以調(diào)用 super.clone() 開(kāi)頭,后面再接續(xù)拷貝所有深層字段(deep fields,即為對(duì)象引用,且不能共享于多個(gè)父輩類別實(shí)體之間的可變字段(mutable fields))的代碼。
  • Declare your clone override not to throw any exceptions, including CloneNotSupportedException. To this effect, the clone() method in your hierarchy's first class that subclasses a non-Cloneable class will catch CloneNotSupportedException and wrap it into an InternalError.
  • 聲明該覆寫(xiě)(override)版本的 clone 方法不拋出任何異常,包括不能拋出 CloneNotSupportedException 異常。 意思就是說(shuō):在您的類別階層體系中,對(duì)于第一個(gè)派生自 non-Cloneable 類別的那個(gè)類別,其 clone() 方法能夠捕獲 CloneNotSupportedException 異常并將該異常包入 InternalError 中。

Correct implementation of Cloneable easily deserves a separate article. Because my focus is on measuring performance, I will repeat the relevant points here and direct readers to existing references for further details (see Resources).
光是 Cloneable 的正確實(shí)現(xiàn)方法就可以很容易的需要占用另外一整篇文章的篇幅來(lái)進(jìn)行闡述。鑒于我在這里關(guān)注的是性能的測(cè)量,因而我也就只復(fù)述一些相關(guān)的要點(diǎn),并為讀者您提供更多細(xì)節(jié)的參考信息(詳見(jiàn)參考資源)。

This traditional approach is particularly well suited to the presence of inheritance because the chain of super.clone() eventually calls the native java.lang.Object.clone() implementation. This is good for two reasons. First, this native method has the magic ability to always create an instance of the most derived class for the current object. That is, the result of super.clone() in TestBaseClass is an instance of TestClass when TestBaseClass.clone() is part of the chain of methods originating from TestClass.clone(). This makes it easy to implement the desirable x.clone().getClass() == x.getClass() invariant even in the presence of inheritance.
這個(gè)經(jīng)典型的方案特別適用于有繼承體系的地方,因?yàn)?super.clone() 串鏈最終會(huì)導(dǎo)致調(diào)用原生的 java.lang.Object.clone() 方法。說(shuō)這樣做很妥當(dāng)有兩個(gè)原因。其一,該原生方法(native method)具有神奇的能力,總是能夠?yàn)楫?dāng)前對(duì)象創(chuàng)建繼承體系最末端的類別實(shí)體。這就是說(shuō),TestBaseClass 中 super.clone() 的執(zhí)行結(jié)果得到 TestClass 實(shí)體,因?yàn)?TestBaseClass.clone() 是起源自 TestClass.clone() 的一系列串鏈起來(lái)的方法之一。這樣一來(lái),即使是在繼承體系之中也很容易實(shí)現(xiàn)我們想要的 x.clone().getClass() == x.getClass() 不變式(invariant)。

Second, if you examine the JVM sources, you will see that at the heart of java.lang.Object.clone() is the memcpy C function, usually implemented in very efficient assembly on a given platform; so I expect the method to act as a fast "bit-blasting" shallow clone implementation, replicating all shallow fields in one fell swoop. In many cases, the only remaining manual coding is done to deeply clone object reference fields that point to unshareable mutable objects.
其二,如果您查看JVM源代碼的話,您會(huì)看到 java.lang.Object.clone() 的核心部分是C函數(shù) memcpy ,這個(gè)函數(shù)是用目標(biāo)平臺(tái)上非常高效的匯編代碼實(shí)現(xiàn)的;因此可以期望這個(gè) java.lang.Object.clone() 方法的實(shí)現(xiàn)是以快速的“按比特狂做(bit-blasting)”之方式進(jìn)行的淺度克隆(shallow clone),能夠迅捷的復(fù)制所有淺層字段(shallow fields)。這樣一來(lái)在許多情況下,所剩的唯一需要手工編寫(xiě)的代碼就只用負(fù)責(zé)對(duì)“指向非共享、可易變對(duì)象(unshareable mutable objects)之引用”進(jìn)行深度克隆。

Running the test with the OBJECT_CLONE variable set to true on a Windows 550-MHz machine with Sun Microsystems' JDK 1.4.1 produces:
將 OBJECT_CLONE 變量設(shè)為 true ,在一臺(tái)安裝了 Sun Microsystems JDK 1.4.1 的 Windows 550-MHz 機(jī)器上面運(yùn)行測(cè)試程序就產(chǎn)生出如下結(jié)果:

clone implementation: Object.clone()
method duration: 0.033 ms

This is not bad for a class with multiple primitive and object reference fields. But for better insight, I must compare the result with other approaches below.
對(duì)于擁有多個(gè)基本型別字段和對(duì)象引用字段的類別而言,這不算壞。然而為了更好的考究問(wèn)題,我須將此結(jié)果與下面其它方案進(jìn)行比較才對(duì)。

Despite its advantages, this approach is plagued with problems due to poor java.lang.Object.clone() design. It cannot be used for cloning final fields unless they can be copied shallowly. Creating smart, deeply cloning container classes is complicated by the fact that Cloneable is just a marker interface, and java.lang.Object.clone() is not public. Finally, cloning inner classes does not work due to problems with outer references. See articles by Mark Davis and Steve Ball in Resources for some of the earliest discussions about this topic.
盡管該方案有自己的優(yōu)勢(shì),但設(shè)計(jì)欠佳的 java.lang.Object.clone() 方法使其備受折磨。除非 final 字段能被淺層拷貝,否則該方案就不能用于對(duì) final 字段進(jìn)行克隆的情形。由于 Cloneable 只是一個(gè)標(biāo)記接口(marker interface),而 java.lang.Object.clone() 方法又不是 public ,因此創(chuàng)建既聰明又具有 deeply cloning(深度克隆)能力的 container classes(容器類別)變得復(fù)雜起來(lái)。最后,由于外圍引用(outer references)亦招致問(wèn)題,因此該方案也無(wú)法運(yùn)用于克隆內(nèi)隱類別(inner classes)的情形。關(guān)于此議題的最早的討論,參見(jiàn) 參考資源 中 Mark Davis 和 Steve Ball 的文章。

Approach 2: Cloning via copy construction

方案 2: 通過(guò)拷貝構(gòu)造動(dòng)作進(jìn)行克隆

This approach complements Approach 1. It involves these steps:
這是對(duì)方案1的增強(qiáng)補(bǔ)足方案,實(shí)現(xiàn)起來(lái)包含下列步驟:

  • For every class X, provide a copy constructor with signature X(X x).
  • 對(duì)于每個(gè) class X ,以標(biāo)記式(signature) X(X x) 來(lái)提供一個(gè) copy constructor 。

  • Chain to the base class's copy constructor in all but the first class in your hierarchy. You can chain to clone() or directly to the base copy constructor. The former choice is more polymorphic and works when the base copy constructor is private, and the latter sometimes avoids the small cost of casting clone()'s return value to a specific type.
  • 將基類的拷貝構(gòu)造函數(shù)(copy constructor)串鏈到類別階層體系的所有類別中,階層體系最頂端的第一個(gè)類除外。您可以將其串鏈到這些類的 clone() 方法中,或者直接串鏈到它們的基類的拷貝構(gòu)造函數(shù)(copy constructor)中。前一種做法更具多態(tài)特性,在基類的拷貝構(gòu)造函數(shù)(copy constructor)為private時(shí)即可湊效;后一種做法有時(shí)候能夠避免“將 clone() 方法的返回值轉(zhuǎn)型(cast)到某個(gè)特定型別”所帶來(lái)的微小性能消耗。

  • Following the chaining call, set all class fields by copying them from the input parameter. For every object reference field, you decide individually whether to clone it deeply.
  • 將上述調(diào)用串鏈起來(lái)之后,將輸入?yún)?shù)拷貝給所有的類別字段(fields)。接著由您自己來(lái)決定是否對(duì)各個(gè)對(duì)象引用字段進(jìn)行深度克隆。

Setting COPY_CONSTRUCTOR to true and rerunning the test produces:
將 COPY_CONSTRUCTOR 設(shè)為 true ,再重新運(yùn)行測(cè)試程序,產(chǎn)生如下結(jié)果:

clone implementation: copy construction
method duration: 0.024 ms

This beats Approach 1. The result might not be surprising because the overhead of native method calls has increased and the cost of new object creation has decreased with increasing JDK versions. If you rerun the same tests in Sun's JDK 1.2.2, the situation favors Approach 1. Of course, performance depends on the relative mix of shallow and deep fields in the class hierarchy. Classes with many primitive type fields benefit more from Approach 1. Classes with a few mostly immutable fields work very efficiently with Approach 2, with a speed advantage at least 10 times greater than Approach 1.
這次的結(jié)果意味方案2勝過(guò)方案1?;蛟S這結(jié)果并不令人吃驚,因?yàn)樵黾恿藢?duì)原生方法的調(diào)用,而創(chuàng)建新對(duì)象的消耗伴隨著 JDK 版本的升高而減小。如果您在 Sun 公司的 JDK 1.2.2 之下重新運(yùn)行相同的測(cè)試,方案1就會(huì)勝出。當(dāng)然,性能依賴于類別階層體系中淺層字段(shallow fields)和深層字段(deep fields)的混雜方式。擁有很多基本型別之字段的類別會(huì)更多的得益于方案1。而對(duì)于只擁有少數(shù)字段且多為不可變字段的類別,方案2運(yùn)作得非常高效,其速度上的優(yōu)勢(shì)至少為快過(guò)方案1十倍。

Approach 2 is more error prone than Approach 1 because it is easy to forget to override clone() and accidentally inherit a superclass's version that will return an object of the wrong type. If you make the same mistake in Approach 1, the result will be less disastrous. Additionally, it is harder to maintain the implementation in Approach 2 when fields are added and removed (compare the OBJECT_CLONE branch in TestBaseClass.clone() with similar code in the copy constructor). Also, Approach 1 requires less class cooperation in some cases: for a base class with only shallow fields, you don't need to implement Cloneable or even provide a clone() override if you do not intend to clone at the base class level.
方案2比方案1更容易出錯(cuò),因?yàn)楹苋菀淄浉矊?xiě)(override) clone() 方法,并由此意外的繼承了父輩類別(superclass)的 clone() 版本,其返回一個(gè)錯(cuò)誤型別的對(duì)象。但若您在方案1中犯下同樣的錯(cuò)誤,后果就不會(huì)那么慘重。另外,當(dāng)類別的字段被添加或者刪除時(shí),方案2的實(shí)現(xiàn)代碼更難于維護(hù)(將 TestBaseClass.clone() 中的 OBJECT_CLONE 分支與拷貝構(gòu)造函數(shù)中的相應(yīng)代碼進(jìn)行比較即可知)。再有就是,方案1在某些情況下對(duì)類別之間的合作需求更少:對(duì)于只擁有淺層字段的基類,您不需要實(shí)現(xiàn) Cloneable 方法;如果您無(wú)意在基類的層級(jí)上進(jìn)行克隆動(dòng)作,您甚至不需要提供覆寫(xiě)版本的 clone() 方法。

However, an undeniable advantage of cloning via copy construction is that it can handle both final fields and inner classes. But due to dangers present when inheritance is involved, I recommend using this sparingly and preferably simultaneously with making the relevant classes final.
然而,通過(guò)拷貝構(gòu)造動(dòng)作進(jìn)行克隆(譯注:即方案2)有個(gè)不可否認(rèn)的優(yōu)勢(shì),此即:該方案既可以處理 final 字段,也可以處理內(nèi)隱類別(inner classes)。鑒于該方案在涉及繼承時(shí)所具有的危險(xiǎn)性,我建議保守的采用之,且采用該方案時(shí)最好同時(shí)將有關(guān)的類別聲明為final 。

Approach 3: Cloning via Java serialization

方案 3:通過(guò) Java serialization(次第讀寫(xiě))進(jìn)行克隆

Java serialization is convenient. Many classes are made serializable by simply declaring them to implement java.io.Serializable. Thus, a whole hierarchy of classes can be made cloneable by deriving them from a base Serializable class whose clone() is implemented as a simple, yet extremely generic method:
Java serialization(次第讀寫(xiě))方便好用。許多類別只要被簡(jiǎn)單的聲明為“實(shí)現(xiàn) java.io.Serializable” 就能具備 serializable 性質(zhì)。于是,若令整個(gè)階層體系派生自基類 Serializable ,那么階層體系的所有類別就都能具備 cloneable 性質(zhì),欲使然只要求基類 Serializable 實(shí)現(xiàn)出一個(gè)簡(jiǎn)單,同時(shí)又極為通用的 clone() 方法:

????public Object clone (Object obj)
????{
????????try
????????{
????????????ByteArrayOutputStream out = new ByteArrayOutputStream ();
????????????ObjectOutputStream oout = new ObjectOutputStream (out);
????????????oout.writeObject (obj);
????????????
????????????ObjectInputStream in = new ObjectInputStream (
????????????????new ByteArrayInputStream (out.toByteArray ()));
????????????return in.readObject ();
????????}
????????catch (Exception e)
????????{
????????????throw new RuntimeException ("cannot clone class [" +
????????????????obj.getClass ().getName () + "] via serialization: " +
????????????????e.toString ());
????????}
????}

This is so generic it can be used for cloning classes that can be written and added to your application by someone else long after you provide the base classes. But this convenience comes at a price. After switching TestBaseClass.clone() and TestClass.clone() to the SERIALIZATION branch I get:
這個(gè)實(shí)現(xiàn)是如此之通用,在您寫(xiě)好基類很久以后,別人要將新編寫(xiě)的類別加入您的應(yīng)用程序時(shí),還可以利用該方法來(lái)克隆那些新編寫(xiě)的類別。然而這種便利性得來(lái)有代價(jià)。將 TestBaseClass.clone() 和 TestClass.clone() 之實(shí)現(xiàn)代碼切換到 SERIALIZATION 分支的情況下,我得到如下的結(jié)果:

clone implementation: serialization
method duration: 2.724 ms

This is roughly 100 times slower than Approaches 1 and 2. You probably would not want this option for defensive cloning of parameters of otherwise fast intra-JVM methods. Even though this method can be used for generic containers with deep cloning semantics, cloning a few hundred objects would make you see times in the one-second range: a doubtful prospect.
這比方案1和方案2慢了有100倍左右。如果您是在為本該很快的 intra-JVM 之 方法的參數(shù)作防御性的克隆,您大概不會(huì)希望采用這種方案。盡管該方法可被運(yùn)用于帶有深度克隆語(yǔ)義的通用containers(容器),但像這樣克隆幾百個(gè)對(duì)象的話,您會(huì)得到1秒鐘范圍內(nèi)的時(shí)間消耗——其應(yīng)用前景令人生疑。

There are several reasons why this approach is so slow. Serialization depends on reflective discovery of class metadata, known to be much slower than normal method calls. Furthermore, because a temporary input/output (I/0) stream is used to flatten the entire object, the process involves UTF (Universal Transformation Format) 8-encoding and writing out every character of, say, TestBaseClass.m_string. Compared to that, Approaches 1 and 2 only copy String references; each copy step has the same small fixed cost.
該方案如此緩慢有幾個(gè)原因。首先,serialization(次第讀寫(xiě))機(jī)制系依靠類別元數(shù)據(jù)(metadata)的映像式探知?jiǎng)幼?#xff08;reflective discovery),已知它比普通的函數(shù)調(diào)用慢得多。更為甚之,由于serialization(次第讀寫(xiě))使用一個(gè)臨時(shí)的 輸入/輸出(I/0)串流(stream)來(lái)攤開(kāi)(flatten)整個(gè)對(duì)象,因而整個(gè)過(guò)程涉及到 UTF8 編碼動(dòng)作(UTF8-encoding,Universal Transformation Format)以及向外寫(xiě)入被攤開(kāi)的對(duì)象成分的每個(gè)字符(比如 TestBaseClass.m_string)。相比之下(再以 TestBaseClass.m_string 為例),方案1和方案2只需要拷貝 String 引用,且每次拷貝具有相同的固定的時(shí)間消耗。

What's even worse, ObjectOutputStream and ObjectInputStream perform a lot of unnecessary work. For example, writing out class metadata (class name, field names, metadata checksum, etc.) that may need to be reconciled with a different class version on the receiving end is pure overhead when you serialize a class within the same ClassLoader namespace.
更糟糕的是,ObjectOutputStream 和 ObjectInputStream 做了諸多不必要的工作。例如向外寫(xiě)入類別元數(shù)據(jù)(metadata,這包括類別名稱、字段名稱、元數(shù)據(jù)校驗(yàn)和,等等),只為與寫(xiě)入操作之接收端的不同版本類別相配合,而這對(duì)于您在同一個(gè) ClassLoader 命名空間(namespace)里面次第讀寫(xiě)(serialize)類別的情況下,純粹就是額外負(fù)荷。

On the plus side, serialization imposes fairly light constructor requirements (the first non-Serializable superclass must have an accessible no-arg constructor) and correctly handles final fields and inner classes. This is because native code constructs the clone and populates its fields without using any constructors (something that can't be done in pure Java).
從好的一面來(lái)說(shuō),次第讀寫(xiě)(serialization)對(duì)構(gòu)造函數(shù)的特定需求相當(dāng)小(第一個(gè) non-Serializable 基類必須擁有一個(gè)可訪問(wèn)的無(wú)參數(shù)構(gòu)造函數(shù)),并能正確妥當(dāng)?shù)奶幚韋inal字段和內(nèi)隱類別的情形。這是因?yàn)樵a能在不使用構(gòu)造函數(shù)的情況下構(gòu)造克隆對(duì)象并轉(zhuǎn)存(populates)對(duì)象的字段(這是單純依靠Java所無(wú)法做到的)。

One more interesting advantage of Approach 3 is that it can preserve the structure of object graph rooted at the source object. Examine the dummy TestBaseClass constructor. It fills the entire m_strings array with the same m_string reference. Without any special effort on our part, the invariant m_strings[0] == m_string is preserved in the cloned object. In Approaches 1 and 2, the same effect is either purely incidental (such as when immutable objects remain shared by reference) or requires explicit coding (as with m_object1 and m_object2 in TestClass). The latter could be hard to get right in general, especially when object identities are established at runtime and not compile time (as is the case with TestClass).
方案3還有一個(gè)優(yōu)勢(shì):它可以保持根基于次第讀寫(xiě)源對(duì)象的“對(duì)象圖面(object graph)”結(jié)構(gòu)。來(lái)觀察一下 dummy TestBaseClass 構(gòu)造函數(shù)。該構(gòu)造函數(shù)以相同的 m_string 引用填充整個(gè) m_strings 數(shù)組。在我們的代碼中,不用借助任何特殊動(dòng)作就可以在克隆出來(lái)的對(duì)象內(nèi)保持 m_strings[0] == m_string 不變式(invariant)。而要在方案1和方案2中達(dá)到同樣的效果,則要么純粹靠巧合(比如不可變對(duì)象通過(guò)引用保持被共享),要么就需要額外的編碼(如同 TestClass 中 m_object1 和 m_object2 的情形)。要把后一種情況做到正確無(wú)誤通常是困難的,特別是在對(duì)象的身份在運(yùn)行期(而非編譯期)才建立之情形下(如 TestClass 中的情形)。

Approach 4: Cloning via Java reflection

方案 4:通過(guò) Java reflection(映像)進(jìn)行克隆

Approach 4 draws inspiration from Approach 3. Anything that uses reflection can work on a variety of classes in a generic way. If I require the class in question to have a (not necessarily public) no-arg constructor, I can easily create an empty instance using reflection. It is especially efficient when the no-arg constructor doesn't do anything. Then it is a straightforward matter to walk the class's inheritance chain all the way to Object.class and set all (not just public) declared instance fields for each superclass in the chain. For each field, I check whether it contains a primitive value, an immutable object reference, or an object reference that needs to be cloned recursively. The idea is straightforward but getting it to work well requires handling a few details. My full demo implementation is in class ReflectiveClone, available as a separate download. Here is the pseudo-code of the full implementation, with some details and all error handling omitted for simplicity:
方案4從方案3吸取了一些要領(lǐng)。針對(duì)各種類別,任何動(dòng)用映像(reflection)者都能以通用的方式處理之。如果我希望手中的類別能擁有一個(gè)無(wú)參數(shù)構(gòu)造函數(shù)(并非需要為 public),我用映像(reflection)簡(jiǎn)單的創(chuàng)建一個(gè)空白實(shí)體即可。在無(wú)參數(shù)構(gòu)造函數(shù)并不做任何事情的情況下,使用映像(reflection)就特別有效率。于是,我們可以直截了當(dāng)?shù)淖弑轭悇e的繼承鏈,一路直至 Object.class ,并在其間為繼承鏈中每一個(gè)基類設(shè)置所有聲明的實(shí)體字段(不僅只含 public 的字段)。我針對(duì)其中每一個(gè)字段做檢查,看其包含的是否為:基本型別的值,或不可變對(duì)象之引用,或是需要被遞歸克隆的對(duì)象引用。整個(gè)想法是直截了當(dāng)?shù)?#xff0c;但欲令其正確運(yùn)作,我們需要處理幾個(gè)細(xì)節(jié)。我撰寫(xiě)的完整范例實(shí)現(xiàn)在 ReflectiveClone 類別中,被作為一個(gè)單獨(dú)的 下載 供您查看。該完整實(shí)現(xiàn)的偽碼如下,為了簡(jiǎn)單起見(jiàn)忽略了某些細(xì)節(jié)以及所有錯(cuò)誤處理:

public abstract class ReflectiveClone
{
????/**
???? * Makes a??reflection-based deep clone of 'obj'. This method is mutually
???? * recursive with {@link #setFields}.
???? *
???? * @param obj current source object being cloned
???? * @return obj's deep clone [never null; can be == to 'obj']
???? */
????public static Object clone (final Object obj)
????{????????
????????final Class objClass = obj.getClass ();
????????final Object result;
????????????????
????????if (objClass.isArray ())
????????{??????????
????????????final int arrayLength = Array.getLength (obj);
????????????
????????????if (arrayLength == 0) // empty arrays are immutable
????????????????return obj;
????????????else
????????????{??????????????????????
????????????????final Class componentType = objClass.getComponentType ();
????????????????
????????????????// Even though arrays implicitly have a public clone(), it
????????????????// cannot be invoked reflectively, so need to do copy construction:
????????????????
????????????????result = Array.newInstance (componentType, arrayLength);
????????????????
????????????????if (componentType.isPrimitive () ||
????????????????????FINAL_IMMUTABLE_CLASSES.contains (componentType))
????????????????{
????????????????????System.arraycopy (obj, 0, result, 0, arrayLength);
????????????????}
????????????????else
????????????????{
????????????????????for (int i = 0; i < arrayLength; ++ i)
????????????????????{
????????????????????????// Recursively clone each array slot:
????????????????????????final Object slot = Array.get (obj, i);
????????????????????????if (slot != null)
????????????????????????{
????????????????????????????final Object slotClone = clone (slot);
????????????????????????????Array.set (result, i, slotClone);
????????????????????????}
????????????????????}
????????????????}
????????????????
????????????????return result;
????????????}
????????}
????????else if (FINAL_IMMUTABLE_CLASSES.contains (objClass))
????????{
????????????return obj;
????????}
????????
????????// Fall through to reflectively populating an instance created
????????// via a no-arg constructor:

????????// clone = objClass.newInstance () can't handle private constructors:
????????????
????????Constructor noarg = objClass.getDeclaredConstructor (EMPTY_CLASS_ARRAY);
????????if ((Modifier.PUBLIC & noarg.getModifiers ()) == 0)
????????{
????????????noarg.setAccessible (true);
????????}

????????result = noarg.newInstance (EMPTY_OBJECT_ARRAY);
????????
????????for (Class c = objClass; c != Object.class; c = c.getSuperclass ())
????????{
????????????setFields (obj, result, c.getDeclaredFields ());
????????}
????????
????????return result;
????}????

????/**
???? * This method copies all declared 'fields' from 'src' to 'dest'.
???? *
???? * @param src source object
???? * @param dest src's clone [not fully populated yet]
???? * @param fields fields to be populated
???? */
????private static void setFields (final Object src, final Object dest,
?????????????????????????????????? final Field [] fields)
????{
????????for (int f = 0, fieldsLength = fields.length; f < fieldsLength; ++ f)
????????{????????????
????????????final Field field = fields [f];
????????????final int modifiers = field.getModifiers ();
????????????
????????????if ((Modifier.STATIC & modifiers) != 0) continue;
????????????
????????????// Can also skip transient fields here if you want reflective
????????????// cloning to be more like serialization.
????????????
????????????if ((Modifier.FINAL & modifiers) != 0)
????????????????throw new RuntimeException ("cannot set final field" +
????????????????field.getName () + " of class " + src.getClass ().getName ());
????????????
????????????if ((Modifier.PUBLIC & modifiers) == 0) field.setAccessible (true);
????????????
????????????Object value = field.get (src);
????????????
????????????if (value == null)
????????????????field.set (dest, null);
????????????else
????????????{
????????????????final Class valueType = value.getClass ();
????????????????
????????????????if (! valueType.isPrimitive () &&
????????????????????! FINAL_IMMUTABLE_CLASSES.contains (valueType))
????????????????{
????????????????????// Value is an object reference, and it could be either an
????????????????????// array or of some mutable type: try to clone it deeply
????????????????????// to be on the safe side.
????????????????????????
????????????????????value = clone (value);
????????????????}
????????????????
????????????????field.set (dest, value);
????????????}
????????}
????}

????private static final Set FINAL_IMMUTABLE_CLASSES; // Set in
????private static final Object [] EMPTY_OBJECT_ARRAY = new Object [0];
????private static final Class [] EMPTY_CLASS_ARRAY = new Class [0];
????
????static
????{
????????FINAL_IMMUTABLE_CLASSES = new HashSet (17);
????????
????????// Add some common final/immutable classes:
????????FINAL_IMMUTABLE_CLASSES.add (String.class);
????????FINAL_IMMUTABLE_CLASSES.add (Byte.class);
????????...
????????FINAL_IMMUTABLE_CLASSES.add (Boolean.class);
????}
} // End of class

Note the use of java.lang.reflect.AccessibleObject.setAccessible() to gain access to nonpublic fields. Of course, this requires sufficient security privileges.
請(qǐng)注意,使用了 java.lang.reflect.AccessibleObject.setAccessible() 來(lái)獲得對(duì) non-public 字段的訪問(wèn)。當(dāng)然,這也需要有足夠安全級(jí)別的權(quán)限才能為之。

Since the introduction of JDK 1.3, setting final fields via reflection is no longer possible (see Note 1 in Resources); so, this approach resembles Approach 1 because it can't handle final fields. Note also that inner classes cannot have no-arg constructors by definition (see Note 2 in Resources), so this approach will not work for them either.
自從 JDK 1.3 以來(lái),通過(guò)映像(reflection)設(shè)置 final 字段就不再被允許了。(詳見(jiàn)參考資源中的注釋1);因此,本方案類似方案1,它無(wú)法處理 final 字段的情形。還請(qǐng)注意,內(nèi)隱類別(inner classes)不能在其定義中含有無(wú)參數(shù)構(gòu)造含數(shù)(詳見(jiàn) 參考資源中的注釋2),故本方案也無(wú)法處理內(nèi)隱類別(inner classes)情形。

Coupled with the no-arg constructor requirement, this approach restricts the type of classes it can handle. But you would be surprised how far it can go. The full implementation adds a few useful features. While traversing the object graph rooted at the source object, it keeps an internal objMap parameter that maps values in source object graphs to their respective clones in the cloned graphs. This restores the ability to preserve object graphs that I had in Approach 3. Also, the metadataMap parameter caches class metadata for all classes that it encounters while cloning an object and improves performance by avoiding slow reflection. The relevant data structures are scoped to a single call to clone(), and the overall idea is very similar to Java serialization revamped to just do object cloning. Similar to the previous section, a whole hierarchy of suitable classes can be made cloneable by equipping the base class with one generic method:
該方案除了有“需要無(wú)參數(shù)構(gòu)造函數(shù)”之要求,還對(duì)能夠處理的類別有所限制。但您也許會(huì)驚訝于其能夠做到什么程度。完整的 實(shí)現(xiàn) 中增加了幾個(gè)有用的功能。在遍歷根基于克隆源對(duì)象的對(duì)象圖面(object graph)過(guò)程中,該實(shí)現(xiàn)會(huì)保留一個(gè)內(nèi)部的 objMap 參數(shù),用來(lái)將克隆源對(duì)象之圖面中的值對(duì)應(yīng)到其克隆目標(biāo)對(duì)象的圖面中去。這樣做就回復(fù)了方案3中的那種“保持對(duì)象圖面”的能力。另外, metadataMap 參數(shù)用來(lái)緩存(caches)克隆過(guò)程中遇到的所有類別之元數(shù)據(jù)(metadata),以此盡量避免緩慢的影像(reflection)動(dòng)作從而提高性能。相關(guān)數(shù)據(jù)結(jié)構(gòu)的生存空間被限定在單獨(dú)的 clone() 調(diào)用之中,其總體想法非常類似于“為了讓其專做對(duì)象克隆而對(duì) Java serialization(次第讀寫(xiě)) 進(jìn)行修補(bǔ)”。這里的情形類同前面的小節(jié):為基類搭載一個(gè)通用的方法,就可讓整個(gè)相互搭配的類別階層體系具有 cloneable 性質(zhì):

????public Object clone ()
????{
????????return ReflectiveClone.clone (this);
????}

What is this method's performance? Rerunning the test with the REFLECTION branch selected produces:
這個(gè)方法的性能如何呢?以 REFLECTION 分支重新運(yùn)行測(cè)試程序產(chǎn)生出如下結(jié)果:

clone implementation: reflection
method duration: 0.537 ms

This is roughly five times faster than straightforward serialization—not too bad for another generic approach. In terms of its performance and capabilities, it represents a compromise between the other three solutions. It can work very well for JavaBean-like classes and other types that usually do not have final fields.
這比直截了當(dāng)型的次第讀寫(xiě)方案大約快了5倍——作為一個(gè)通用的方案還不算太壞。從其性能和處理能力來(lái)考量,該方案代表了對(duì)另外三個(gè)解決方案的折衷. 對(duì)于 JavaBean 形式的類別以及其它通常沒(méi)有 final 字段的型別,該方案非常湊效。

Resource considerations

對(duì)資源的考量

Measuring memory overhead is more difficult than measuring performance. It should be obvious that the first two approaches shine in this area, as they instantiate only the data that will populate the cloned fields.
度量?jī)?nèi)存負(fù)荷比度量性能更為困難。在內(nèi)存負(fù)荷方面,前兩個(gè)方案應(yīng)該具有很明顯的優(yōu)勢(shì),因?yàn)槠渲兄挥杏脕?lái)轉(zhuǎn)存(populate)克隆字段的數(shù)據(jù)才會(huì)被具現(xiàn)化(instantiated)。

Cloning via serialization has an extra drawback that may have escaped your attention above. Even though serializing an object preserves the structure of the object graph rooted at that instance, immutable values will get duplicated across disjoint calls to clone(). As an example, you can verify for yourself that
您或許剛才還沒(méi)留意,通過(guò)次第讀寫(xiě)(serialization)進(jìn)行克隆還另有一個(gè)缺點(diǎn)。盡管次第讀寫(xiě)對(duì)象時(shí)能夠保持根基于該實(shí)體的對(duì)象圖面(object graph)結(jié)構(gòu),不可變的值卻會(huì)在對(duì) clone() 方法的單個(gè)調(diào)用過(guò)程中被復(fù)制。作為例證,您可以自行驗(yàn)證:

????TestClass obj = new TestClass ("dummy");
????System.out.println (obj.m_string == ((TestClass) obj.clone ()).m_string);

will print false for Approach 3 only. Thus, cloning via serialization will have a tendency to pollute heap with redundant copies of immutable objects like Strings. Approaches 1 and 2 are completely free from this problem, and Approach 3 is mostly free from it.
其結(jié)果僅在采用方案3時(shí)列印出 false 。如此看來(lái),通過(guò)次第讀寫(xiě)(serialization)進(jìn)行克隆就具有傾向性,容易產(chǎn)生冗余的諸如 Strings 這樣的不可變對(duì)象,從而污染堆(heap)空間。方案1和方案2中完全不存在這個(gè)問(wèn)題,而方案3中則是幾乎不存在這個(gè)問(wèn)題。

A quick and dirty proof of these observations can be seen by changing the body of Main.main() to keep the clones in memory and track the object count when a given heap size is reached:
有個(gè)蹩腳又便宜的辦法來(lái)證實(shí)上面的發(fā)現(xiàn),只要改變 Main.main() 函數(shù)體,令其在內(nèi)存中保留克隆體,并在堆空間增長(zhǎng)到一定大小時(shí)追蹤對(duì)象計(jì)數(shù)即可:

????????int count = 0;
????????List list = new LinkedList ();
????????try
????????{
????????????while (true)
????????????{
????????????????list.add (obj.clone ());
????????????????++ count;
????????????}
????????}
????????catch (Throwable t)
????????{
????????????System.out.println ("count = " + count);
????????}

Run this in a JVM with a -Xmx8m setting and you will see something similar to this:
若在 JVM 中以 -Xmx8m 設(shè)置來(lái)運(yùn)行上述代碼,您將看到類似如下的結(jié)果:

>java -Xmx8m Main
clone implementation: Object.clone()
count = 5978 Exception in thread "main" java.lang.OutOfMemoryError
...
clone implementation: copy construction
count = 5978
...
clone implementation: serialization
count = 747
...
clone implementation: reflection
count = 5952

Approach 3's overhead increases with the number of immutable fields in a class. Removing this overhead is nontrivial.
方案3的負(fù)荷隨著類別中不可變字段(immutable fields)數(shù)量的增加而增加。消除該負(fù)荷則需要一些心力。

The recap

摘要列表

The following table recaps the properties of all cloning approaches in this article from several perspectives: speed, resource utilization, class design constraints, object graph handling.
下面的表格從幾個(gè)方面整理了本文中所有克隆方案,這些方面包括:速度;資源利用率;類別設(shè)計(jì)上的約束;對(duì)象圖面掌控情況。

Object.clone()
SpeedHigh
Resource utilizationLow
Class design constraintsDoes not work with deep final fields; does not work with inner classes; must implement Cloneable; medium amount of manual class maintenance
Object graphsDoes not handle object graphs transparently
Copy construction
SpeedHigh
Resource utilizationLow
Class design constraintsSuperclasses and subclasses must cooperate; copy constructor required; a lot of manual class maintenance
Object graphsDoes not handle object graphs transparently
Serialization
SpeedLow
Resource utilizationHigh; creates redundant immutable fields
Class design constraintsMust implement Serializable; first non-Serializable class needs an accessible no-arg constuctor
Object graphsHandles object graphs
Reflection
SpeedMedium
Resource utilizationMedium
Class design constraintsDoes not work with final fields; does not work with inner classes; each class must provide no-arg constructor
Object graphsHandles object graphs

Object.clone()
速度
資源利用率
類別設(shè)計(jì)上的約束不能應(yīng)用于深層final字段的情形;不能應(yīng)用于內(nèi)隱類別(inner class)的情形;必須實(shí)現(xiàn) Cloneable 接口;類別的手動(dòng)維護(hù)所需工作量適中。
對(duì)象圖面不能透明的掌控對(duì)象圖面。
拷貝構(gòu)造動(dòng)作
速度
資源利用率
類別設(shè)計(jì)上的約束基類和子類必須相互協(xié)作配合;需要拷貝構(gòu)造函數(shù);類別的維護(hù)所需工作量大。
對(duì)象圖面不能透明的掌控對(duì)象圖面。
Serialization
速度
資源利用率高;創(chuàng)建冗余的不可變(immutable)字段
類別設(shè)計(jì)上的約束必須實(shí)現(xiàn) Serializable 接口;第一個(gè) non-Serializable 類別需要一個(gè)可訪問(wèn)的無(wú)參數(shù)構(gòu)造函數(shù)。
對(duì)象圖面掌控對(duì)象圖面。
Reflection
速度適中
資源利用率適中
類別設(shè)計(jì)上的約束不能應(yīng)用于final字段的情形;不能應(yīng)用于內(nèi)隱類別(inner class)的情形;類別必須提供無(wú)參數(shù)構(gòu)造函數(shù)。
對(duì)象圖面掌控對(duì)象圖面。

This article discussed implementing a single method, Object.clone(). It is amazing that a single method can have so many implementation choices and subtle points. I hope this article provided you with some food for thought and useful guidelines for your application class design.
本文討論了 Object.clone() 這單獨(dú)一個(gè)方法的實(shí)現(xiàn)。令人驚異的是,一個(gè)方法竟然可以有這么多種實(shí)現(xiàn)方案和這么多微妙的細(xì)節(jié)要點(diǎn)。我希望本文帶給您一些思考的素材,并為您的應(yīng)用程序之類別設(shè)計(jì)提供了有用指導(dǎo)。


About the author
關(guān)于作者
Vladimir Roubtsov has programmed in a variety of languages for more than 12 years, including Java since 1995. Currently, he develops enterprise software as a senior developer for Trilogy in Austin, Texas.
Vladimir Roubtsov 具有超過(guò)十二年的多語(yǔ)言編程經(jīng)驗(yàn),掌握的語(yǔ)言包括從1995就年開(kāi)始使用的Java。目前他任職于德克薩斯州奧斯汀的 Trilogy 公司,作為高級(jí)開(kāi)發(fā)人員進(jìn)行企業(yè)級(jí)軟件的開(kāi)發(fā)。

  • Resources
    相關(guān)資源
  • Download the complete source code that accompanies this article:
    http://www.javaworld.com/javaworld/javaqa/2003-01/clone/02-qa-0124-clone.zip
  • 在這里下載本文配套的源代碼:
    http://www.javaworld.com/javaworld/javaqa/2003-01/clone/02-qa-0124-clone.zip
  • The high-resolution library used for measurements in this article was developed in another Java Q&A installment:
    http://www.javaworld.com/javaworld/javaqa/2003-01/01-qa-0110-timing.html
  • 在本文用于測(cè)量時(shí)間的高解析度程序庫(kù)是在另一期Java 問(wèn)答中開(kāi)發(fā)出來(lái)的。
    http://www.javaworld.com/javaworld/javaqa/2003-01/01-qa-0110-timing.html
  • For more on cloning see "Hashing and Cloning," Mark Davis (Java Report, April 2000) pp. 60-66; "Effective Cloning," Steve Ball (Java Report, January 2000) pp. 60-67; "Solutions for Implementing Dependable Clone Methods," Steve Ball (Java Report, April 2000) pp. 68-82
  • 欲了解更多關(guān)于克隆技術(shù)的內(nèi)容,請(qǐng)參見(jiàn) "Hashing and Cloning," Mark Davis (Java Report, April 2000) pp. 60-66; "Effective Cloning," Steve Ball (Java Report, January 2000) pp. 60-67; "Solutions for Implementing Dependable Clone Methods," Steve Ball (Java Report, April 2000) pp. 68-82
  • Note 1: In Sun JDK 1.2 you could set even final fields using reflection as long as you used setAccessible(), but this changed in later Sun JDK versions.
  • 注釋 1:在 Sun JDK 1.2 中,只要您使用了 setAccessible() ,您甚至可以使用 reflection 機(jī)制來(lái)設(shè)置 final fields, 但是在后續(xù)的 Sun JDK 版本中,這個(gè)細(xì)節(jié)發(fā)生了變化。
  • Note 2: Syntactically an inner class may appear to have a no-arg constructor. However, in bytecode every constructor of an inner class takes at least one parameter that is a reference to the outer object. Note that by inner classes, I specifically mean non-static nested classes. Static nested classes do not have this problem.
  • 注釋 2:從語(yǔ)法的角度來(lái)說(shuō),內(nèi)隱類別(inner class)可以擁有一個(gè)無(wú)參數(shù)的構(gòu)造函數(shù)。然而在最終的 bytecode 里,內(nèi)隱類別的每一個(gè)構(gòu)造函數(shù)至少具有一個(gè)參數(shù),即指向外層對(duì)象的一個(gè)引用(reference)。要注意,我這里談及的 inner classes 特別意指非靜態(tài)的內(nèi)嵌類別(non-static nested classes)。靜態(tài)的內(nèi)嵌類別(static nested classes)沒(méi)有上述問(wèn)題。
  • Java 101's "Object-oriented language basics, Part 5" by Jeff Friesen (JavaWorld, August 2001) contains a section about cloning:
    http://www.javaworld.com/javaworld/jw-08-2001/jw-0803-java101.html
  • Java 101's "Object-oriented language basics, Part 5" by Jeff Friesen (JavaWorld, August 2001) 里面有一節(jié)關(guān)于克隆技術(shù)的內(nèi)容:
    http://www.javaworld.com/javaworld/jw-08-2001/jw-0803-java101.html
  • Want more? See the Java Q&A index page for the full Q&A catalog:
    http://www.javaworld.com/columns/jw-qna-index.shtml
  • 還想看更多內(nèi)容嗎?請(qǐng)瀏覽 Java 問(wèn)答 的索引頁(yè)查看完整的問(wèn)答集分類:
    http://www.javaworld.com/columns/jw-qna-index.shtml
  • For more than 100 insightful Java tips, visit JavaWorld's Java Tips index page:
    http://www.javaworld.com/columns/jw-tips-index.shtml
  • 欲學(xué)習(xí)超過(guò)100條的Java專家技巧,請(qǐng)?jiān)L問(wèn) JavaWorld's Java Tips 索引頁(yè):
    http://www.javaworld.com/columns/jw-tips-index.shtml
  • Browse the Core Java section of JavaWorld's Topical Index:
    http://www.javaworld.com/channel_content/jw-core-index.shtml
  • 請(qǐng)瀏覽至 JavaWorld's Topical Index 的 Core Java 小節(jié):
    http://www.javaworld.com/channel_content/jw-core-index.shtml
  • Get more of your questions answered in our Java Beginner discussion:
    http://forums.devworld.com/webx?50@@.ee6b804
  • 請(qǐng)到我們的 Java Beginner 論壇獲得更多自己?jiǎn)栴}的答案:
    http://forums.devworld.com/webx?50@@.ee6b804
  • Sign up for JavaWorld's free weekly email newsletters:
    http://www.javaworld.com/subscribe
  • 欲注冊(cè)訂閱 JavaWorld 免費(fèi)的新聞郵件周刊請(qǐng)至:
    http://www.javaworld.com/subscribe
  • You'll find a wealth of IT-related articles from our sister publications at IDG.net
  • 您可以在我們的兄弟出版品 IDG.net 中找到豐富的IT相關(guān)文章。

  • 主要術(shù)語(yǔ)英漢對(duì)照表
    • base class, 基類
    • cast/casting, 轉(zhuǎn)型
    • chain/chaining, 串鏈
    • class, 類別
    • clone/cloning, 克隆
    • copy constructor, 拷貝構(gòu)造函數(shù)
    • field, 字段
    • immutable, 不可變的
    • mutable, 可變的/易變的
    • override, 覆寫(xiě)
    • parameter, 參數(shù)
    • reflection, 映像
    • reflective discovery, 映像式探知
    • serialization, 次第讀寫(xiě)
    • superclass, 父輩類別/超類
    • subclass, 子輩類別/子類
    • type, 型別




總結(jié)

以上是生活随笔為你收集整理的《克隆人的进攻》面向对象Java版的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。