Object | One advantage of the Java language is that it cancels the concept of pointers, but it also causes many programmers to ignore the differences between objects and references in programming, and this article tries to clarify the concept. And since Java cannot solve the problem of object duplication through simple assignment, in the development process, the Clone () method is often used to replicate objects. This article will let you know what is Shadow Clone and deep clone, understand their differences, advantages and disadvantages. See this title, is not a bit confusing: the Java language clearly illustrates the cancellation of pointers, because pointers are often convenient and cause the source of code insecurity, but also make the program becomes very complex and difficult to understand, the misuse of pointers written code is no less than the use of the already notorious "GOTO" statement. The notion of Java abandoning pointers is absolutely sensible. But there is no explicit pointer definition in the Java language, and virtually every new statement returns a reference to a pointer, but most of the time Java does not care about how to manipulate the pointer, much less the fear of manipulating C + + pointers. The only thing to be more concerned about is passing objects to a function. The following routine:
Package reference;
Class OBJ
{
String str = "init value";
Public String toString ()
{
return str;
}
}
public class ObjRef
{
obj aobj = new obj ();
int aint = 11;
public void Changeobj (OBJ inobj)
{
INOBJ.STR = "changed value";
}
public void Changepri (int inint)
{
Inint = 22;
}
public static void
Main (string[] args)
{
ObjRef oref = new ObjRef (); System.out.println ("Before call
Changeobj () Method:
"+ oref.aobj);
Oref.changeobj (Oref.aobj);
System.out.println
("After call Changeobj ()
Method: "+ oref.aobj); System.out.println
("==================print primtive=================");
System.out.println ("Before call
Changepri () Method: "+ Oref.aint";
Oref.changepri (Oref.aint);
System.out.println ("After call
Changepri () Method: "+ Oref.aint);}
}/* RUN result
Before call Changeobj () method:
Init value
After call Changeobj () method:
Changed value
==================print primtive=================
Before call Changepri () method:11
After call CHANGEPRI () method:11 *
*/
The main part of this code calls two very similar methods, Changeobj () and Changepri (). The only difference is that they take the object as an input parameter, and the other takes the basic type int in Java as an input parameter. And the input parameters are changed inside both function bodies. Seemingly the same way, the results of the program output are not the same. The Changeobj () method actually changes the input parameters, while the Changepri () method does not change any of the input parameters. From this example, we know that Java is different from the processing of objects and basic data types. As with C, when the basic data type of Java (such as int,char,double, etc.) is passed to the function body as the entry parameter, the incoming parameter becomes a local variable inside the function body, and the local variable is a copy of the input parameter. All functions within the body of the operation is for this copy of the operation, after the completion of the function, the local variable also completes its mission, it does not affect the input parameters as a variable. Parameter passing in this way is referred to as "value delivery." In Java, the passing of an object as an entry parameter defaults to "Reference pass", which means that only a "reference" to the object is passed, the concept of which is the same as a pointer reference in the C language. When an input variable is changed within the function body, it is essentially a direct manipulation of the object. In addition to "reference passing" when a function passes a value, it is "reference pass" whenever a value is assigned to an object variable with "=". Such as:
Package reference;
Class Passobj
{
String str = "init value";
}
public class Objpassvalue
{public static void
Main (string[] args)
{
Passobj obja = new Passobj ();
Passobj OBJB = Obja; OBJA.STR = "changed in Obja";
System.out.println
("Print objb.str value:
"+ objb.str);
}
}
/* RUN Result
Print Objb.str Value:
Changed in Obja
*/
The first sentence is to generate a new Passobj object in memory, and then assign the Passobj reference to the variable Obja, and the second sentence is to assign the reference of Passobj object to the variable OBJB. At this point Obja and OBJB are two identical variables, and any subsequent change to Obja is tantamount to a change in OBJB. Even if you understand the concept of "pointer" in the Java language, you may inadvertently make the following mistake. Can hashtable really store objects? Take a look at the simple code below, first declaring a Hashtable and StringBuffer object, and then putting the Striingbuffer object into the Hashtable table four times. The StringBuffer object append () a few new strings before each placement:
Package reference;
Import java.util.*;
public class hashtableadd{
public static void
Main (string[] args) {
Hashtable ht = new Hashtable ();
StringBuffer SB =
New StringBuffer ();
Sb.append ("ABC,");
Ht.put ("1", SB);
Sb.append ("Def,");
Ht.put ("2", SB);
Sb.append ("MnO,");
Ht.put ("3", SB);
Sb.append ("xyz.");
Ht.put ("4", SB); int numobj=0;
Enumeration it = Ht.elements ();
while (It.hasmoreelements ())
{
System.out.print ("Get Stringbufffer
"+ (++numobj) +" from Hashtable: ");
System.out.println (It.nextelement ());
}
}
}
If you think the result of the output is:
Get Stringbufffer 1
From HASHTABLE:ABC,
Get Stringbufffer 2
From Hashtable:abc,def,
Get Stringbufffer 3
From Hashtable:abc,def,mno,
Get Stringbufffer 4
From HASHTABLE:ABC,DEF,MNO,XYZ.
Then you have to go back and look closely at a problem, the object as an entry parameter to the function, essentially passed the object reference, to Hashtable pass StringBuffer object is also only passed the StringBuffer object reference. Every time you put a stringbuffer in the Hashtable table, you don't generate a new StringBuffer object, just a reference to the same StringBuffer object in the Hashtable table. Changes to any StringBuffer object (or, more specifically, an object reference) stored in the Hashtable table are actually changes to the same "StringBuffer". So Hashtable does not really store the energy objects, but only the references to the objects. It should also be known that this principle is similar to the vector, List, Map, set, and so on Hashtable. The result of the actual output of the above routines is:
/* RUN Result
Get Stringbufffer 1
From HASHTABLE:ABC,DEF,MNO,XYZ.
Get Stringbufffer 2
From HASHTABLE:ABC,DEF,MNO,XYZ.
Get Stringbufffer 3
From HASHTABLE:ABC,DEF,MNO,XYZ.
Get Stringbufffer 4
From HASHTABLE:ABC,DEF,MNO,XYZ.
*/
Class, objects and references Java the most basic concept is the class, the class includes functions and variables. If you want to apply a class, you generate the object, which is called "instantiation of the class." There are several ways to instantiate a class as an object, most commonly using the "new" operator. When a class is instantiated as an object, it means that it occupies an instance of space in memory. To apply this space operation to the object's reference. A reference in the Java language is a variable, and the type of the variable is the object of that reference. Although it is syntactically possible to call a function or variable of an object directly after it is generated, such as:
New String ("Hello NDP")). Substring (0,3)
Return Result:hel
However, because there is no corresponding reference, the use of this object can only be limited by this statement. Generation: References always occur automatically in the process of "passing" objects as parameters, without the need for artificial generation, or the generation of human control references. This pass includes the case of the object as the entry parameter of the function, and also the time when the object is assigned with "=". Scope: Only local references, no local objects. References in the Java language are variables, and variables are scoped in the Java language, can be local, or global. Lifetime: A program can only control the life cycle of a reference. The lifetime of an object is controlled by Java. To generate a new object with the "new object ()" statement is to declare a zone storage object in the computer's memory, and only the Java garbage collector can decide to reclaim the memory occupied by the object at the appropriate time. There is no way to prevent changes to the reference. What is "clone"? In the actual programming process, we often encounter this situation: there is an object A, in a moment a has already included some valid values, you may need one and a exactly the same new object B, and any subsequent changes to B will not affect the value of a, that is, A and B are two separate objects, But the initial value of B is determined by the A object. In the Java language, this requirement is not met with a simple assignment statement. While there are many ways to meet this requirement, implementing the Clone () method is one of the simplest and most efficient means. All classes in Java inherit the Java.lang.Object class by default, and there is a Method clone () in the Java.lang.Object class. A description document for the JDK API explains that this method returns a copy of the object. There are two points to note: one is that a copy object returns a new object, not a reference. The second is the difference between the Copy object and the new object returned with the operator, that the copy already contains some information about the original object, not the initial information of the object. How do I apply the Clone () method? A typical call to the clone () code is as follows:
Class Cloneclass
Implements Cloneable
{
public int aint;
Public Object Clone ()
{
Cloneclass o = null;
try{
o = (cloneclass) super.clone ();
}catch (clonenotsupportedexception e)
{
E.printstacktrace ();
}
return o;
}
}
has three notable places, one is the Cloneclass class that wants to realize the clone function implements the Cloneable interface, This interface belongs to the Java.lang package, and the Java.lang package is already in the default import class, so there is no need to write java.lang.Cloneable. Another noteworthy note is the overloaded clone () method. Finally, Super.clone () is invoked in the Clone () method, which means that regardless of the inheritance structure of the Clone class, Super.clone () invokes the Clone () method of the Java.lang.Object class directly or indirectly. Let's explain these points in more detail below. It should be said that the 3rd is the most important, carefully observe the object class clone () A native method, the efficiency of the native method is generally much higher than the native method in Java. This also explains why the clone () method in object is used instead of the new one, and then the information in the original object is assigned to the object, although this also implements the Clone function. For the 2nd, it is also a way to observe whether the clone () in the object class is a protected property. This also means that if you want to apply the Clone () method, you must inherit the object class, where all the classes in Java inherit the object class by default, and you don't have to worry about that. Then overload the Clone () method. Another thing to consider is that in order for other classes to invoke the Clone () method of this clone class, the attribute of the Clone () method is set to public after the overload. So why should the clone class implement the Cloneable interface? With a little attention, the Cloneable interface does not contain any methods! In fact, this interface is only a flag, and this flag is only for the object class in the Clone () method, if the clone class does not implement the Cloneable interface, and called the object's Clone () method (that is, called super. Clone () method), the Clone () method of object throws a Clonenotsupportedexception exception. The above is the most basic step of cloning, to complete a successful clone, but also to understand what is "shadow Clone" and "Deep clone." What is Shadow clone? The following example contains three class Unclonea,cloneb,clonemain. The Cloneb class contains a Unclonea instance and an int type variable, and overloads the Clone () method. ClonemainClass Initializes an instance of the Unclonea class B1, and then calls the Clone () method to generate a B1 copy b2. Finally, examine the output of B1 and B2:
Package clone;
Class Unclonea {
private int i;
Public Unclonea (int ii) {i = II}
public void Doublevalue () {i *= 2;}
Public String toString () {
return integer.tostring (i);
}
}
Class Cloneb implements cloneable{
public int aint;
Public Unclonea Unca = new Unclonea (111);
Public Object Clone () {
Cloneb o = null;
try{
o = (cloneb) super.clone ();
}catch (Clonenotsupportedexception e) {
E.printstacktrace ();
}
return o;
}
}
public class Clonemain {
public static void Main (string[] a) {
Cloneb B1 = new Cloneb ();
B1.aint = 11;
System.out.println ("Before
Clone,b1.aint = "+ B1.aint);
System.out.println ("Before
Clone,b1.unca = "+ B1.unca); Cloneb b2 = (cloneb) b1.clone ();
B2.aint = 22;
B2.unCA.doublevalue ();
System.out.println ("=================================");
System.out.println ("After clone,b1.aint =" + B1.aint);
System.out.println ("After Clone,b1.unca =" + B1.unca);
System.out.println ("=================================");
System.out.println ("After clone,b2.aint =" + B2.aint);
System.out.println ("After Clone,b2.unca =" + B2.unca);
}
}
/** RUN Result:
Before clone,b1.aint = 11
Before Clone,b1.unca = 111
=================================
After Clone,b1.aint = 11
After Clone,b1.unca = 222
=================================
After Clone,b2.aint = 22
After Clone,b2.unca = 222
*/
The results of the output show that the variant aint of type int and Unclonea instance object Unca have inconsistent clone results, the int type is truly clone, because changing the aint variable in B2 has no effect on B1 aint, that is to say, B2.aint and B1.aint already occupy a different memory space, B2.aint is a true copy of B1.aint. Conversely, changes to B2.unca change the B1.unca, and obviously B2.unca and B1.unca are just different references to the same object! As you can see, the result of invoking the Clone () method in the object class is to first create a space in memory that is the same as the original object, and then copy the contents of the original object as it is. For basic data types, such operations are not problematic, but for non-basic type variables, we know that they hold only references to objects, which also cause the primitive type variables after the clone to point to the same object as the corresponding variable in the original object. Most of the time, the result of this clone is often not what we want, and this clone is also called "Shadow Clone". To make B2.unca point to objects different from B2.unca, and B2.unca to include the information in B1.unca as an initial message, you need to implement a deep clone. How do I do a deep clone? To change the above example to deep clone is simple and requires two changes: first, let the Unclonea class also implement the same clone function as the Cloneb class (Implementing the Cloneable interface, overloading the Clone () method). The second is to add a sentence in the Clone () method of Cloneb: O.unca = (unclonea) unca.clone ();
The procedure is as follows:
Package clone.ext;
Class Unclonea implements cloneable{
private int i;
Public Unclonea (int ii) {i = II}
public void Doublevalue () {i *= 2;}
Public String toString () {
return integer.tostring (i);
}
Public Object Clone () {
Unclonea o = null;
try{
o = (unclonea) super.clone ();
}catch (Clonenotsupportedexception e) {
E.printstacktrace ();
}
return o;
}
}
Class Cloneb implements cloneable{
public int aint;
Public Unclonea Unca = new Unclonea (111);
Public Object Clone () {
Cloneb o = null;
try{
o = (cloneb) super.clone ();
}catch (Clonenotsupportedexception e) {
E.printstacktrace ();
}
O.unca = (unclonea) unca.clone ();
return o;
}
}
public class Clonemain {
public static void Main (string[] a) {
Cloneb B1 = new Cloneb ();
B1.aint = 11;
System.out.println ("Before
Clone,b1.aint = "+ B1.aint);
System.out.println ("Before
Clone,b1.unca = "+ B1.unca); Cloneb b2 = (cloneb) b1.clone ();
B2.aint = 22;
B2.unCA.doublevalue ();
System.out.println ("=================================");
System.out.println ("After clone,b1.aint =" + B1.aint);
System.out.println ("After Clone,b1.unca =" + B1.unca);
System.out.println ("=================================");
System.out.println ("After clone,b2.aint =" + B2.aint);
System.out.println ("After Clone,b2.unca =" + B2.unca);
}
/** RUN Result:
Before clone,b1.aint = 11
Before Clone,b1.unca = 111
=================================
After Clone,b1.aint = 11
After Clone,b1.unca = 111
=================================
After Clone,b2.aint = 22
After Clone,b2.unca = 222
*/
It can be seen that the change in B2.unca now has no effect on B1.unca. At this point B1.unca and B2.unca pointed to two different unclonea instances, and at the Cloneb b2 = (cloneb) b1.clone (), the moment of invocation B1 and B2 had the same value, where b1.i = b2.i = 11. Be aware that not all classes can achieve deep clone. For example, if you change the Unclonea type variable in the Cloneb class above to a stringbuffer type, look at the description of the StringBuffer in the JDK API, StringBuffer does not overload the Clone () method. More serious is stringbuffer or a final class, which is also said that we can not use the inheritance method to realize the StringBuffer clone indirectly. If a class contains objects with StringBuffer type objects or stringbuffer similar classes, we have two choices: either only shadow clones are implemented, or one sentence is added to the class's Clone () method (assuming the Sringbuffer object, And the variable name is still Unca):
O.unca = new StringBuffer
(Unca.tostring ());
The original is:
O.unca = (unclonea) unca.clone ();
Also know that in addition to the basic data type can automatically achieve depth clone, the string object is an exception, it appears after the clone is also the implementation of the deep clone, although this is an illusion, but greatly facilitate our programming. The difference between string and StringBuffer in clones should indicate that the difference between string and StringBuffer is not highlighted here, but it is also possible to see some of the different parts of the string class in this example. The following example includes two classes, the Clonec class contains a string type variable and a stringbuffer type variable, and the Clone () method is implemented. The CLONEC type variable C1 is declared in the Strclone class, and then the C1 clone () method is invoked to generate a copy c1 of C2, which prints the results after the string and C2 type variables in the StringBuffer are modified with the appropriate method:
Package clone;
Class Clonec implements cloneable{
Public String str;
Public StringBuffer Strbuff;
Public Object Clone () {
Clonec o = null;
try{
o = (Clonec) super.clone ();
}catch (Clonenotsupportedexception e) {
E.printstacktrace ();
}
return o;
} }
public class Strclone {
public static void Main (string[] a)
{
Clonec C1 = new Clonec ();
C1.str = new String ("Initializestr");
C1.strbuff = new StringBuffer
("Initializestrbuff");
System.out.println ("Before
Clone,c1.str = "+ c1.str);
System.out.println ("Before
Clone,c1.strbuff = "+ C1.strbuff); Clonec C2 = (Clonec) c1.clone ();
C2.str = c2.str.substring (0,5);
C2.strbuff = C2.strBuff.append
("Change Strbuff clone");
System.out.println ("=================================");
System.out.println ("After
Clone,c1.str = "+ c1.str);
System.out.println
("After Clone,c1.strbuff =" + C1.strbuff);
System.out.println ("=================================");
System.out.println ("After
Clone,c2.str = "+ c2.str);
System.out.println
("After Clone,c2.strbuff =" + C2.strbuff);
}
}
/* RUN Result
Before clone,c1.str = Initializestr
Before clone,c1.strbuff = Initializestrbuff
=================================
After clone,c1.str = Initializestr
After Clone,c1.strbuff =
Initializestrbuff Change Strbuff Clone
=================================
After clone,c2.str = Initi
After Clone,c2.strbuff =
Initializestrbuff Change Strbuff Clone
*
*/
As you can see from the printed results, the string variable seems to have implemented a deep clone because the C2.STR changes did not affect the c1.str! Does Java think of sring classes as basic data types? In fact, there is a small trick, the secret lies in C2.str = C2.str.substring (0,5) this statement. Essentially, C1.STR and c2.str are still references at clone, and all point to the same string object. However, when executing c2.str = c2.str.substring (0,5), it acts as a new string type and then assigns it back to C2.STR. This is because string was written by Sun's engineers as an immutable class (immutable Class), and functions in all string classes cannot change their own values. Here's a very simple example:
Package clone; public class Strtest
{
public static void Main (string[] args)
{
String str1 = "This is a test for immutable";
String str2 = str1.substring (0,8);
System.out.println ("Print str1:" + str1);
System.out.println ("Print str2:" + str2);
}
}
/* RUN result print str1:
This is a test for immutable print str2:
This is * *
In the example, although str1 called the substring () method, the STR1 value did not change. Similarly, this is true of other methods in the String class. Of course, if we put these two statements in the top case
C2.str = c2.str.substring (0,5);
C2.strbuff = C2.strBuff.append
("Change Strbuff clone");
Change it to the following:
C2.str.substring (0,5);
C2.strBuff.append
("Change Strbuff clone");
Remove the value of the process, C2.STR also can not be changed, our trick is revealed. But in the programming process only call
C2.str.substring (0,5);
Statement does not make any sense. What you should know is that all of the basic data types in Java have a corresponding class, like the int type for the integer class, the double type, and so on, and these classes are the same as the string class, and they are immutable classes. In other words, all of the methods in these classes cannot change their own values. It also gives us a lot more choice when it comes to cloning a clone class. We can also weave our own classes into immutable classes.
The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion;
products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the
content of the page makes you feel confusing, please write us an email, we will handle the problem
within 5 days after receiving your email.
If you find any instances of plagiarism from the community, please send an email to:
info-contact@alibabacloud.com
and provide relevant evidence. A staff member will contact you within 5 working days.