Java object copy is a method of assigning a value to an object. In simple terms, you create an object that is identical to the original object. The newly created object is a copy of the original object. Because it involves references to objects, it involves whether Java passes values or references, which is usually the focus of the interview. So before we talk about deep and shallow copies, let’s talk about referential relationships.

About the reference

In Java, in addition to the basic data types (four types and eight data types), there are reference data types. When you assign a value to a primitive data type, you actually copy the value, but when you assign to an object, you only assign a reference to that object, passing a reference to the original object, but they’re still referring to the same object. The following code is shown

public class Food{

    String name;
    int num;
    String taste;

    constructor()
		get and set(a)
    toString(a)
}
Copy the code

The test class:

public static void main(String[] args) {

  int i1 = 10;
  int i2 = i1; // Copy the basic data type, copy the value
  System.out.println("i2 = " + i2);

  Food milk = new Food("milk".1."fragrance");
  Food food = milk; 
  System.out.printf("food = " + food);
  System.out.println("milk = " + milk); // Milk and food both point to the same heap object
}
Copy the code

On a graph, it would look something like this:

Instead of getting into the nonsense of pass-by-value or pass-by-reference debates in Java, remember that for basic data types, you pass the value of the data type, and for reference types, you pass a reference to the object, which is the address of the object.

About shallow and deep copies

A shallow copy and a deep copy are distinguished on the basis of references. If only the basic data types are copied and the reference data types are passed, without creating a new object, this copy mode is considered shallow copy. Conversely, when a reference data type is copied, a new object is created and its member variables are copied. This copy is called a deep copy.

Shallow copy

So how do you implement Shallow copies? Simply implement the Cloneable interface and override its Clone () method on the class you want to copy.

Let’s change the Food class by having it implement the Cloneable interface and override the Clone () method.

public class Food implements Cloneable{...@Override
  protected Object clone(a) throws CloneNotSupportedException {
    return super.clone(); }... }Copy the code

Then the code in the test class is as follows

Food milk = new Food("milk".1."fragrance");
Food food = (Food)milk.clone();
System.out.println("milk = " + milk);
System.out.println("food = " + food);
Copy the code

As you can see, now the food object is copied from the milk object. Is the food object the same as the milk object? We can see the native Hashcode for both objects by printing.

milk = com.cxuan.objectclone.Food@3cd1a2f1
food = com.cxuan.objectclone.Food@4d7e1886
Copy the code

We can find that food and milk are not the same object, so there are three attribute values in milk. Are these three attribute values the same in food? To test this conjecture, we override the toString method.

@Override
public String toString(a) {
  return "Food{" +
    "name='" + name + '\' ' +
    ", num=" + num +
    ", taste='" + taste + '\' ' +
    '} ';
}
Copy the code

Then print food and milk again, and you can see the following results

milk = Food{name='milk', num=1, taste='fragrance'}
food = Food{name='milk', num=1, taste='fragrance'}
Copy the code

Ahem, although it seems that “Cxuan” and “Cuan” are two completely different terms! But they have one thing in common: writing!

Let’s use the diagram to illustrate:

Do you see the way in this picture? Two Food objects appear in the heap, indicating that the Clone method recreates an object and allocates a memory area for it. Although there are two objects, the property values in both objects are the same. This is the same thing. The soup and the medicine are different things (objects), but they are both soluble in water (property values).

Deep copy

While shallow copy is a term that has changed over time, there is a saying in the Java world that… What is it again?

I’m running out of words…

A Deep copy is a copy of an object’s member variables. A Deep copy of an object’s member variables is a copy of an object’s member variables.

Ha ha ha ha, this pretended to be a deep copy of the original in the shallow copy on the basis of a copy of its property values ah, I thought it is what profound things! The code!

Let’s add a Drink class first.

public class Drink implements Cloneable {

    String name;

    get and set(a)

    @Override
    protected Object clone(a) throws CloneNotSupportedException {
        return super.clone();
    }

    toString()
}
Copy the code

Then change the Food class, because Drink is also a Food, so we add a reference to Drink in the Food class, and then change the get set, toString, Clone, and constructor methods. The modified Food class code looks like this

public class Food implements Cloneable{

    String name;
    int num;
    String taste;
    Drink drink;

    public Food(String name, int num, String taste,Drink drink) {
        this.name = name;
        this.num = num;
        this.taste = taste;
        this.drink = drink;
    }

    get and set...

    @Override
    protected Object clone(a) throws CloneNotSupportedException {
        Food food = (Food)super.clone();
        food.drink = (Drink) drink.clone();
        return super.clone();
    }

    @Override
    public String toString(a) {
        return "Food{" +
                "name='" + name + '\' ' +
                ", num=" + num +
                ", taste='" + taste + '\' ' +
                ", drink=" + drink +
                '} '; }}Copy the code

The biggest change you can see is in the Clone method, where we implement a copy of the Food object, and we also implement a copy of the Drink object, which is what we said above about copying the object and copying the object’s member variables.

Then let’s test Deep Copy:

public static void main(String[] args) throws CloneNotSupportedException {

  Drink drink = new Drink("milk");
  Food food = new Food("humberge".1."fragrance",drink);
  Food foodClone = (Food)food.clone();
  Drink tea = new Drink("tea");
  food.setDrink(tea);
  System.out.println("food = " + food);
  System.out.println("foodClone = " + foodClone.getDrink());

}
Copy the code

After running, the output is as follows:

food = Food{name='humberge', num=1, taste='fragrance', drink=Drink{name='tea'}}
foodClone = Drink{name='milk'}
Copy the code

As you can see, after we copied foodClone, we modified the drink variable in food without changing foodClone, indicating that foodClone has successfully implemented a deep copy.

Graphically, it would look something like this:

Here is the memory allocation map after the deep copy, and now you can see that food and foodClone are completely different objects, with no ties between them.

The main way we discussed above is that the object implements the Cloneable interface and calls the overwritten Clone method. In Java, another way to implement object copy is to use serialization.

serialization

The serialization approach is mainly to use the Serializable interface. This approach also solves the problem of multi-level copy, where reference types are nested within reference types. The key code for using Serializable is as follows

public Person clone(a) {
  Person person = null;
  try {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(baos);
    oos.writeObject(this);
    // Serialize the stream into objects
    ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
    ObjectInputStream ois = new ObjectInputStream(bais);
    person = (Person) ois.readObject();
  } catch (IOException e) {
    e.printStackTrace();
  } catch (ClassNotFoundException e) {
    e.printStackTrace();
  }
  return person;
}
Copy the code

Using serialization, you can achieve deep copy, which is to write the contents of the binary stream to a text or byte array, and then read the data from the text or byte array. The original object is written to the text or byte array and then copied to the Clone object. Changes to the original object do not affect the Clone object. Because the Clone object is read from a text or byte array.

How do I select a copy mode

So far we have covered both shallow and deep copies, so how to choose between shallow and deep copies? Here are a few notes ⚠️

  • A shallow copy can be used if the properties of an object are of basic data types.

  • If the object has a reference type, the choice of shallow or deep copy depends on the specific requirements.

  • If there are many nested layers, you are advised to use the Serializable interface to implement deep copy.

  • If the object reference does not change at any time, then there is no need to use a deep copy, only a shallow copy. If object references change frequently, then use a deep copy. There are no set rules; it all depends on specific needs.

Other copying methods

In addition to object copying, Java provides other copying methods

For example, you can use Arrays.copyof, or the default clone, but both are shallow copies.

public void test(a) {
    int[] lNumbers1 = new int[5];
    int[] rNumbers1 = Arrays.copyOf(lNumbers1, lNumbers1.length);

    int[] lNumbers2 = new int[5];
    int[] rNumbers2 = lNumbers2.clone();
}
Copy the code

In addition to copying the basic array data types, there are also copies of objects, but the use is basically the same.

Collections can also be copied because the underlying use of collections is an array, so the use is the same.

Some instructions

There are three instructions for using the Cloneable interface

  • If the class implements the Cloneable interface, a call to the Clone () method on Object can legally copy the class instance by field.

  • If does not implement the Cloneable interface call on the instance of the Object’s clone () method, which leads to throw CloneNotSupporteddException.

  • Classes that implement this interface should override the clone() method of Object with a public method, because the Clone () method of Object is a protected method.