"Clone's Offense" is an object-oriented Java version

zhaozj2021-02-08  377

Java Q & A Java Q & A Attack of the Clones, the attack, the object, the object, the Time and Space Consideration, the Time () Methods, the gains and spaces of Methods, there are four different scenarios to implement Deep Clone. Method.

BY VLADIMIR RouBTSOV

K] [N g of a r kTM compile [Revision 0.1]

January 24, 2003 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? Q: From the perspective of performance, implement DEEP CLONG (depth cloning) in Java Serialization or built-in object.clone () method (depth cloning), what are the advantages and disadvantages? 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. A: Method for properly implementing the correctly implemented clone () method for each category, which is critical to many defense programming modes. Common default programming modes include: Defense-based parameters to copy; clone the internal field before returning to the internal field (Field); Immutability Patterns); ImmutAbility Patterns; Cloning (depth cloning) Semantic Conterat (container); etc. 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. Although the problems mentioned only two possible Solution, there are at least four different solutions to implement clone () methods. In this period of Java question and answer, I have a trade-off against these four programs. 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 since clones implementation code highly customizable, thus herein. The sample code is not necessarily for direct transformation into your own code; however, the general conclusions we have given should be able to provide useful guidance for any application design.

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 can not if the same field is used as an instance-scoped object monitor (such as when calling Object .wait () / notify () 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. Please note that this section does not commit to a promise: Deep Clone has any specific factors, this problem itself is controversial. Although a "String reference" that is considered data can be shared by two objects, if the String field is an object monitor that is used as an instance-scope (Object Monitor, for example Call Object.wait () / notify () situation), or the identity of the field entity (Identity) is critical to the design (such as using == Operator), then it cannot be shared by security. As soon as it is, the field can be shared depends on the design of the category. For the sake of simplicity, I assume that all fields described herein are considered as pure data to use. Performance Measurements SETUP Example for Performance Measure Sets Let's Jump Right Into Some Code. I Use The Following Simple Hierarchy of classes as my cloning guinea Pig: Let's take some code directly.

I use the following simple category class system as a cloning "experimental rat": public class testbaseclass imports 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

} // Cloneable: Public Object Clone () {if (main.object_clone) {Try {// chalone (): 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); ELS e 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

i) clone.m_objects [i] = ((TestBaseClass) m_objects [i]). Clone ();} return;} else if (main.copy_constructor) Return New TestClass (this); else if (main.Serization) 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 seeh how inheritance, Member Object Ownership, And Data Types CAN Affect Cloning Design And Performance. TestBaseClass has several fields (Fields), and a string and two arrays. TestClass inherits from TestBaseClass, also aggregated several TestBaseClass entities. This example setting allows us to see inheritance, Ownership and how the data type affects the design and performance of the cloning method. In a Previous Java Q & A Article, I Developed A Simple Timing Library That Comes in Handy Now. This CODE IN COSS Main MeasureS The cost of testclass.clone (): In the previous Java question and answer, I developed a simple timer The library can now be used by hand.

The following code is measured by TestClass.clone () in the Class Main: // crete an itimer: final itimer timer = TimerFactory.Newtimer (); // jit / hotspot warmup: // ... testclass obj = new TestClass (); // Warm Up Clone (): // ... final int references = 1000; timer.Start (); // Note: The loop is unrolled 10 Times for (INT i = 0; I

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 Please note that a Clone () method is implemented in both categories. In fact, there are four cloning motions in each category, which can be selected by the Conditional Compilation Constants in Main, these constants are: Object_clone, Copy_Constructor, Serialization, and Reflection. To change the implementation of the cloning action, you need to recompile the entire category. Let's now Examine Each Approach in Detail. Now we have detailed the four programs. Approach 1: Cloning by Chaining To Object.clone () Scheme 1: Causes By Bracel Object.clone () Implementation Clone THIS Perhaps The Most Classical Approach. The Steps Involved Are: This may be the most typical solution. The implementation step involved in this program is:

Declare Your Class To Implement The Cloneable Marker Interface. Make the category to implement the MARKER INTERFACE.

Provide a public clone override that always begins with a call to super.clone () followed by manual copying of all deep fields (ie, mutable fields that are object references and can not be shared between several instances of the parent class). To provide a coating Override version of the Public Clone method, which is within the beginning of the super.clone (), followed by copying all deep fields (Deep Fields, which is an object reference, and cannot be shared between multiple parent categories) Code of the 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. Declare the override (override ) The version of the Clone method does not throw any exceptions, including the unusual clonenotsupportedException. Means say: In your class class system, for the first category derived from the Non-Cloneable class, its Clone () method can capture CloneNotSupportedException exceptions and enclose the exception into the 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 light correct implementation of the method can be easily The need to occupy the space for another article to explain. Given that I am concerned here that performance measurements, I also repeat some related points, and provide readers with more details (see Resources for details). 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 makess it is easy to us. Getclass () == x.getClass () INVARIANT EVEN IN The Presence of Inheritance. This classic program is especially suitable for inheritance system. Place, because the super.clone () string chain will eventually cause the native java.lang.Object.clone () method. It is necessary to say that there is two reasons for this. First, the native method (Native Method) has a magical ability, which is always capable of creating a category entity at the end of the inheritance system for the current object.

That is to say, the execution of Super.clone () in TestBaseClass is a TestClass entity because TestBaseClass.clone () is one of the series of string of string from TestClass.clone (). In this way, even if it is also easy to implement in the inheritance system (). 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. Second, if you When you look at the JVM source code, you will see the core part of java.lang.object.clone () is a C function Memcpy, which is implemented with very efficient assembly code on the target platform; therefore, you can expect this java.lang. The implementation of the Object.clone () method is the shallow clone (Shallow Clone) that is quickly "Bit-blasting", and can quickly copy all shallow fields. In this case, in many cases, the only code that needs to be manually prepared is only responsible for "the reference to" unshareable mutable objects "is depressed. 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: the OBJECT_CLONE variable is set to true, in a Sun Microsystems JDK 1.4.1 installation of Windows 550-MHz above the machine running the test program produces the following results: 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. This is not bad for the category with multiple basic types and object reference fields. However, in order to better examine, I have to compare this result with other solutions.

Despite its advantages, this approach is plagued with problems due to poor java.lang.Object.clone () design. It can not 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 Although the program has its own advantage, the Java.LANG.Object.clone () method is designed to be designed to give it torture. Unless the final field can be copied by shallow layers, the program cannot be used to clone the Final field. Since Cloneable is just a tag interface, the java.lang.object.clone () method is not public, so it is more complex to create a CONTAINER CLASSES (container category) that is clever and deeply cloning capabilities. . Finally, since the outer references also incurrable problems, the program cannot be applied to the case of the INNER CLASSES. For the earliest discussions on this topic, see the article in Mark Davis and Steve Ball in the reference resource. Approach 2: Cloning Via Copy Construction Scheme 2: Clone This Approach Complements Approach 1. IT Involves Thase Stens Approach 1. IT INVOLVES THESE Steps: This is an enhancement scheme for the solution 1, which is implemented containing the following steps:

For EVERY CLASS X, Provide A Copy Constructor with Signature X (x). For each class x, a Copy Constructor is provided in a markup x (x). 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. Copying the Copy Construction Contemcuctor of the Base Class Changes to all categories of Category Class Systems, the first one of the top of the class system Type. You can bring its string to the Clone () method of these classes, or direct string to the copy constructor of their base class (COPY Constructor). The former practice is more polymorphic, and the copy constructor is a copy constructor for the private; the latter approach is sometimes possible to avoid the return value transformation of the clone () method (CAST) Timed performance consumption from a particular type of doing. 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. The following up the input to the call chaining copy parameters to all the category field ( Fields). Then you can decide whether to depth clone of each object reference field.

Setting COPY_CONSTRUCTOR to true and rerunning the test produces: the COPY_CONSTRUCTOR set to true, then re-run the test program results in the following: 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. this time Results Means 2 wins the program 1. Perhaps this is not surprising, because the call to the original method is added, and the consumption of the new object is reduced by the increase in JDK version. If you re-run the same test under Sun's JDK 1.2.2, the solution 1 will win. Of course, performance depends on the mixing method of Shallow Fields and Deep Fields in the Category Class System. The category of many basic types of fields will benefit from the program 1. For categories that only have a few fields and mostly non-variable fields, the program 2 operates very efficient, and the advantage of speed is at least twice as much as possible.

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 do't Ned to Implement Cloneable or Even Provide aclone () Override if you do not intend to clone at the base class level. Scenario 2 is easier to errors than scheme 1, Because it is easy to forget the override clone () method, and thus accidentally inherited the Clone () version of the Superclass, it returns an error-free object. But if you commit the same mistake in Scheme 1, the consequences will not be so heavy. In addition, when the field of the category is added or deleted, the implementation code of the scheme 2 is more difficult to maintain (to compare the object_clone branch in TestBaseClass.clone () with the corresponding code in the copy constructor). Moreover, the program 1 is less demand for the category in some cases: For the base class with only shallow fields, you don't need to implement a Cloneable method; if you don't intend to perform clone action on the level of the base class You don't even need to provide a Clone () method overridden version. 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. However, by Copy Construction Action Crane (Mode 2) has an undeniable advantage, which is that the scheme can process the Final field or handle inner classes. In view of the hazards that the program is involved in the inheritance, I recommend conservative adoption, and it is best to declare the relevant category when using the program.

Approach 3: Cloning via Java serialization Scheme 3: Cloning Java serialization is convenient by Java serialization (sequential write) 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 Extreme Generic Method: Java Serialization is convenient to use. Many categories can have Serializable nature as long as it is a simple statement as "Java.io.Serializable". Thus, if the entire class system is derived from base class Serializable, then all categories of the class system can have clONEABLE nature, and if they want to ask the base class Serializable to achieve a simple, and most common clone () method: 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 ruventimeException ("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 () a Nd testclass.clone () to the serialization branch i get: This implementation is so common, after you write the base class for a long time, others should use the newly written category to your application, you can also use this method to clone Those newly written categories. However, this convenience has a price.

Switching TestBaseClass.clone () and TestClass.clone () to the SERIALIZATION branch, I get the following results: Clone Implementation: Serialization: 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 proct. This is about 100 times slower than Solution 1 and Scheme 2. If you are a defensive clone that is the parameter for this very fast intra-JVM, you probably want to adopt this solution. Although this method can be applied to universal Containers with depth clones, it is like this to clone hundreds of objects, you will get time consumption within 1 second - its application prospects. 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. This program is so slowly for several reasons. First, the Serialization mechanism relies on the image-type detection of the Metadata (Metadata), which is known to be much slower than the ordinary function. More even, because SERIALIZATION uses a temporary input / output (I / 0) stream (stream), the entire process involves UTF8 encoding action (UTF8-Encoding) , Universal transformation format and except for each character (such as TestBaseClass.m_string) that is written to an existing object component.

In contrast (then TestBaseClass.m_String as an example), scheme 1 and the scheme 2 only need to copy String reference, and each copy has the same fixed time consumption. 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 With The Same ClassLoader Namespace. Worse, ObjectOutputStream and ObjectInputStream have made many unnecessary work. For example, write the classification data (Metadata, which includes category name, field name, metadata checksum, etc.), only to match different versions of the receiving end of the write operation, and this for you In the case of a ClassLoader Namespace (Namespace), it is purely an additional load. 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 (Sometting That Can't Be Done in Pure Java) From a good side, the second reading and writing is quite small for the specific needs of the constructor (the first non-serializable base class must have an accessible No parameter constructor) and correctly handle the Final field and the inherent situation. This is because native code can construct a field that does not use the constructor and the Populates object (which is unable to rely on Java).

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). Scenario 3 There is also an advantage: it can keep the root based reading "Object Graph" structure of the source object. To observe the Dummy TestBaseClass constructor. This constructor is fill the entire m_strings array in the same m_string reference. In our code, you can keep m_strings [0] == m_string unchanged (Invariant) within the object of clone without any special action. In order to achieve the same effect in Scheme 1 and Scheme 2, it is either purely counted (for example, the non-variable object is shared by reference), or additional encoding is required (such as m_Object1 and m_object2 in TestClass). It is often difficult to do the latter case, especially in the case where the object's identity is established (as in TestClass).

Approach 4: Cloning via Java reflection Scheme 4: Cloning Approach 4 draws inspiration from Approach 3. Anything that uses reflection can work on a variety of classes in a generic way by the Java reflection (image) 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 does not 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: Scenario 4 draws some essentials from program 3. For a variety of categories, any moving image can be processed in a universal manner. If I want the category in your hand, I can have a parameter constructor (not required for public), I can use the image to create a blank entity simply. Using an image (Reflection) is particularly efficient in the case where the parameter constructor does not do anything. Thus, we can directly cross the inheritance chain, all the way until Object.class, and set all the declared entity fields (not only containing a field containing not only in each of the base classes in the inheritance chain. I check for each of these fields to see if it is: the basic type, or the reference to the non-variable object, or an object reference to be recursively cloned. The whole idea is straightforward, but wants to make it properly, we need to handle several details. The full sample of my writes is implemented in the ReflectiveClone category, and is used as a separate download for you.

The full realization of the false code is as follows, in order to simply see some details and all error handles: public abstract class reflectiveclone {/ ** * Makes a reflection-based deep clone of 'obj'. This method is mutual- 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 Reflective, So Need To Do C opy 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

NULL) {Final Object Slotclone = Clone (slot); Array.Set (Result, I, Slotclone);}}}}}}}}}}} else}}) {Return Obj;} // Fall THROUGH to reflectively populating an instance created // via a noarg constructor: // clone = objClass.newInstance () can not 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, Resu LT, 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

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 valuePe = value.getClass (); if (! valuePE .isprimitive () &&! final_immutable_classes.contains (valueType)) {// value is an Object Reference, And It Could Be e ITHER 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. Please note that the java.lang.reflect.AccessibleObject.setAccessible () to obtain nonpublic Visit of fields. Of course, this also needs to have sufficient level of permissions to be. Since the introduction of JDK 1.3, final fields via setting reflection is no longer possible (see Note 1 in Resources); so, this approach resembles Approach 1 because it can not handle final fields Note also that inner classes can not have no-arg. Constructors by Definition (See Note 2 in Resources), So this Approach Will Not Work for the approach. Since JDK 1.3, the Final field is no longer allowed by the image (REFLECTION). (See Note 1 in Reference Resources 1); Therefore, this scheme is similar in scheme 1. It cannot process the final field. Also note that inner classes (Inner Classes) cannot contain non-parameter constructs in its definition (see Note 2 in Reference Resources), the present scheme cannot handle the 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: This scheme is restricted by the "needless to constructor" and the category that can be handled. But you might be surprised to what you can do. A complete implementation adds several useful features. During traversing the root based object graph (Object graph) based on the cloned source object, the implementation retains an internal OBJMAP parameter to correspond to the value of the cloned source object to its graphics. go with. This will reply to the "maintenance object map" in scheme 3. In addition, MetadAmap parameters are used to cache all category metadata encountered during the cloning process, so as to avoid slow images (REFLECTION) to improve performance. The living space of the relevant data structure is defined in a separate clone () call, and its overall idea is very similar to "Patching Java Serialization) in order to make it specific to the object.

The situation here is equipped with a general method for the base class: the entire mutual matching class class system has cloneable properties: public object clone () {return reflectiveclone.clone (} what is this How is the performance of this method? In REFLECTION branch run the test program again produces the following results: 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 works and Other Types That USUALLY DO NOT HAVE FINAL FIELDS. This is 5 times faster than straight-cutting-type secondary reading program - as a general solution Not too bad. From its performance and processing capabilities, the program represents a trade-off against the other three solutions. The scheme is very good for the category of javabean and other types that usually have no Final field. Resource considerations consideration of resources 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. Metric memory load than measuring performance more It is difficult. In the memory load, the first two options should have a significant advantage, as in which data is used only to the populate cloning field will be reduced (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, you may have not yet been paying attention to it, and there is another shortcoming through the second reading and writing (SERIALIZATION). Although it is possible to keep the object based object grafting based on the object, the non-variable value is replicated during a single call of the clone () method.

As an example, you can verify: 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. As a result, only the use of the program 3 The time is printed. In this regard, it has a tendency to clone through the secondary read and write, which is easy to generate redundant, such as a non-variable object such as Strings, thereby polluting the heap space. This problem is completely absent from the programs 1 and the program 2, and the plan 3 is almost no such problem.

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: There is a foot-cheaper way to confirm the above Discover, as long as the main.main () function body is changed, keep the cloned body in memory, and increase the number of homes to a certain size to track the object count: int count = 0; list = new linkedList (); Try {while (true) {list.add (ipj.clone ()); count;}} catch (throwable t) {system.out.println ("count =" count);} Run this in a jvm WITH A -XMX8M STTING AND You WILL SEE Sometying Similar To this: If you run the above code in the JVM, you will see the following results:> java-xmx8m mainclone importation: 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 In The Number Of Immutable Fields IN A Class. REMOVING THIS OVERHEADS IS NONTRIVIAL. Solution 3 increases with the increase in the number of illegal fields in the category. Eliminating this load requires some heart. The recap summary list 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 The following table compiled from all aspects of cloning program in this article, these. Aspects include: speed; resource utilization; constraints on categories design; object map controls.

Object.clone () SpeedHighResource utilizationLowClass design constraintsDoes not work with deep final fields; does not work with inner classes; must implement Cloneable; medium amount of manual class maintenanceObject graphsDoes not handle object graphs transparently Copy constructionSpeedHighResource utilizationLowClass design constraintsSuperclasses and subclasses must cooperate; copy constructor required; a lot of manual class maintenanceObject graphsDoes not handle object graphs transparently SerializationSpeedLowResource utilizationHigh; creates redundant immutable fieldsClass design constraintsMust implement Serializable; first non-Serializable class needs an accessible no-arg constuctorObject graphsHandles object graphs ReflectionSpeedMediumResource utilizationMediumClass design constraintsDoes not work with final Fields; Does Not Work with Inner Classes; Each Class Must Provide No-Arg ConstructorObject Graphshandles Object Graphs

Object.clone () Constraints in low-class font utilization low category cannot be applied to deep Final fields; inner classes (INNER CLASS); must implement cloneable interface; custom work required The amount is medium. The object map cannot be transparently controlled. Copy constructive speed high resource utilization constraints and subclasses must cooperate with each other; need to copy constructor; the amount of work required for category maintenance. The object map cannot be transparently controlled. The SERIALIZATION speed is high; the constraint on the IMMutable field category design must implement the Serializable interface; the first non-serializable category requires an accessible parameter constructor. Object map controls object map. Reflection Speed ​​Empatomid Resource Utilization Constraints Constraints Constraints cannot be applied to the Final field; the case where the inner class is not applied; the category must provide a parameter constructor. Object map controls object map. 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 This article discusses the implementation of Object.clone () this separate method. Amazing is that a method can have so many implementation programs and so many subtle detail points. I hope this article brings you some considerable material and provides useful guidance for your application. About the author About the author 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 has more than 12 years of Multi-language programming experience, the language masters includes Java, starting from 1995. At present, he serves the development of enterprise software as senior developers in Austin, Texas. Resources

related resources

Download The Complete Source Code That That Acompanies this article: http://www.javaworld.com/javaworld/javaqa/2003-01/clone/02-qa-0124-clone.zip here Download this article Matching Source Code: 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 The high-resolution library used to measure time in this paper is developed in another Java question and answer. http://www.javaworld.com/javaworld/javaqaqa/2003-01/01-qa-0110-timing.html for more oncloning 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 Deprondable Clone Methods," Steve Ball (Java Report, April 2000) PP. 68-82 want to learn more For the content of cloning technology, 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-82Note 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 Note 1: In Sun JDK 1.2, as long as you use setAccessible (), you can even use the REFLECTION mechanism to set Final Fields, but in subsequent Sun JDK versions, this details have changed.

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. Note 2: From the perspective of syntax, the Inner Class (Inner Class) can have a parameter-free constructor. However, in the final Bytecode, each constructor of the intrinsic category has at least one parameter, which is a reference to the outer object. Be careful, INNER CLASSES I talk about here means a non-static NESTED CLASSES. Static inline category is not described above.

Java 101's "Object-Oriented Language Basics, Part 5" by Jeff Friesen (Javaworld, August 2001) Contains a section About Clang: 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) There is a section on cloning technology: 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 I still want to see more? Please browse the Java question and answer index page to view the complete question and answer set Category: 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 wants to learn more than 100 Java expert skills, please visit JavaWorld's Java Tips index page: 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 browse to the JavaWorld's Topical Index of Core Java section: http: // www. JavaWorld.com/channel_content/jw-core-index.shtml get more of Your Questions answers: http://forums.devworld.com/webx?50@@.ee6b804 Please go to our Java Beginner Forum to get More answers to yourself: http://forums.devworld.com/webx?devworld.com/webx?50@@.ee6b804 Sign Up for JavaWorld's Free Weekly Email Newsletters: http://www.javaworld.com/subscribe wants to register Subscribe to JavaWorld Free News Mail Week to: http://www.javaworld.com/subscribe You'll Find A Wealth Of It-Related Articles from Our Sister Publications At IDG.NET You can be in our brother Find rich IT related articles in idg.net.

转载请注明原文地址:https://www.9cbs.com/read-429.html

New Post(0)