Understand deep copy and shallow copy _java in Java

Source: Internet
Author: User
Tags shallow copy stringbuffer

One of the advantages of the Java language is that it removes the notion of pointers, but it also causes many programmers to ignore the distinction between objects and references in programming, and this article tries to clarify this 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.

Package com.zoer.src; 
 
public class ObjRef { 
  obj aobj = new OBJ (); 
  int aint = one; 
 
  public void Changeobj (OBJ inobj) { 
    inobj.str = ' changed value '; 
  } 
 
  public void Changepri (int inint) { 
    inint =; 
  } 
 
  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 called 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 called Changepri () method:" + Oref.aint); 
 
  } 
 

Package com.zoer.src; 
 
public class OBJ { 
 
  String str = "init value"; 
 
  Public String toString () {return 
    str; 
  } 
} 

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 "=". is similar to giving a variable an alias. All two names point to the same object in memory.
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:

public 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. 
  } 
} 

There are three notable places, one is the Cloneclass class that wants to implement 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?

Package com.zoer.src; 
 
  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 ObjRef {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); 
 } 
}

Output results:

Before clone,b1.aint =
before Clone,b1.unca = ================================= after
clone, B1.aint = after the
Clone,b1.unca = 222
================================= after
clone,b2.aint =
After Clone,b2.unca = 222

       output shows that the aint and Unclonea instance objects of type int are inconsistent with the clone results of UNCA, the int type is truly clone, Because the aint variable in B2 is changed, it has no effect on the aint of B1, that is, B2.aint and B1.aint have occupied different memory space, B2.aint is a real 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 known as "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?
       Changing the above example to deep clone is simple, Two changes are required: one is to allow the Unclonea class to implement the same clone function as the Cloneb class (Implementing the Cloneable interface, overloading the Clone () method). The second is to add a sentence O.unca = (unclonea) unca.clone () in the Cloneb Clone () method;

Package com.zoer.src; 
 
  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); 
 } 
}

Output results:

Before clone,b1.aint =
before Clone,b1.unca = ================================= after
clone, B1.aint = after the
Clone,b1.unca =
================================= after
clone,b2.aint =
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
It should be explained 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 com.zoer.src; 
  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); 
 } 
}

Execution results:

<span style= "font-family: ' Microsoft Yahei ';" ><span style= "FONT-SIZE:16PX;" >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 
</span></span> 

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, here 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.

The above is the entire content of this article, I hope that you understand the Java deep copy and shallow copy is helpful.

Contact Us

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.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.