Java copy

There are two types of copy in Java, shallow copy and deep copy. Why there are two copy methods instead of one involves two types of data in Java. Java's deep and shallow copies are all for reference types. There is no difference between deep and shallow copies of basic types. Similar to the C++ language, the references in the shallow copy are a bit like pointers in the C++ language.

1. Shallow copy

Shallow copy only copies the reference of the reference type, but does not copy the entire contents of the entire reference object. That is to say, during the copy process, only the reference of the reference object on the stack is copied, and the content pointed to by it is not copied. Therefore, when the content of the new shallow copy object is modified, the content of the original object will also be modified. Modify at the same time.

Shallow copy in Java is implemented by default through the clone method of the Object class:

import java.util.ArrayList;

class ShallowCopyExample implements Cloneable { // 自定义类必须实现 Cloneable 接口
    public int num = 0; // 基本类型
    public String string = "shallow copy"; // 引用类型
    public ArrayList<Integer> arrayList = new ArrayList<>(); // 引用类型

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone(); // 直接用父类的,不做任何修改,为浅拷贝
    }
}

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        ShallowCopyExample shallowCopyExample = new ShallowCopyExample();
        ShallowCopyExample copiedObject = (ShallowCopyExample) shallowCopyExample.clone(); // 浅拷贝
        copiedObject.num = 1;
        copiedObject.string = "copied"; // String 不可修改,指向一个新的 String 对象
        copiedObject.arrayList.add(1); // 修改 arrayList 本身
        System.out.println(shallowCopyExample.num); // Output: 0(未修改)
        System.out.println(shallowCopyExample.string); // Output: shollow copy(未修改)
        System.out.println(shallowCopyExample.arrayList); // Output: [1](已修改)
    }
}

As can be seen from the above example, shallow copy will modify the reference type of the object synchronously, because what is changed is the content of the referenced object, and the reference itself is not changed. But the String above is also a reference object, so why hasn't it been changed? In fact, the String type field of copiedObject above directly modifies its reference, not the referenced content, because the String type cannot be changed. It seems to be the content of the referenced object, but in fact it is the reference itself.

To summarize:Shallow copy will copy all the contents of the object, but for the contents of the referenced objects, only their references will be copied, not their content.

This is equivalent to a pointer in C language. The pointer is copied, but the memory space pointed by the pointer is not copied.

Taking field copy as an example, here is an understanding diagram:

Shallow copy process understanding diagram

Java's shallow copy is actually very similar to Python's copy, as is the case with Python's built-in class list. A shallow copy is equivalent to a new reference, or alias.

2. Deep copy

Shallow copy is inherent in Java, but sometimes we don’t want the effect of shallow copy. We want a complete copy, that is, while copying the reference of the reference object, also copy its content. This will completely remove the impact of the copied object on the original object. We can use the understanding diagram similar to the shallow copy above to show the deep copy by taking the copy of the field as an example:

Deep copy process understanding diagram

 

To achieve this effect, you must implement deep copy, that is, a complete copy. Unfortunately, Java does not have a tool as convenient as the clone method of the Object object to implement deep copy. If you want to implement deep copy, you can use the method have:

  • Override the clone method to implement deep copy;
  • Use serialization and deserialization to achieve an effect similar to deep copy;

Simply put:Deep copy is a complete copy, whether it is a value type or a reference type.

2.1 Override clone method

Because deep copy must completely copy the reference objects at each layer, each reference type involved in the copy process must override their clone method (rather than simply inheriting the clone method of the parent class) to implement deep copy. Here is a rewritten example:

import java.util.ArrayList;

class ShallowCopyExample implements Cloneable {
    public int num = 0; // 基本类型
    public String string = "shallow copy"; // 引用类型
    public ArrayList<Integer> arrayList = new ArrayList<>(); // 引用类型

    @Override
    protected Object clone() throws CloneNotSupportedException {
        ShallowCopyExample shallowCopyExample = (ShallowCopyExample) super.clone();
        shallowCopyExample.arrayList = new ArrayList<>(arrayList); // 手动将 arrayList 复制一份
        return shallowCopyExample;
    }
}

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        ShallowCopyExample shallowCopyExample = new ShallowCopyExample();
        ShallowCopyExample copiedObject = (ShallowCopyExample) shallowCopyExample.clone();
        copiedObject.num = 1;
        copiedObject.string = "copied"; // String 不可修改,指向一个新的 String 对象
        copiedObject.arrayList.add(1); // 修改 arrayList 本身
        System.out.println(shallowCopyExample.num); // Output: 0(未修改)
        System.out.println(shallowCopyExample.string); // Output: shollow copy(未修改)
        System.out.println(shallowCopyExample.arrayList); // Output: [](未修改)
    }
}

From the above code, we can see that the implementation of deep copy is not difficult, but when a class contains many different types of reference types, it will be very troublesome and cumbersome to rewrite each one. Things, at this time, we have to adopt the second method, which is serialization.

2.2 Serialization and Deserialization

Java serialization is the process of converting an object into a stream of bytes so that the object can be saved to disk, transmitted over the network, or stored in memory and later deserialized to convert the bytes The stream is converted back into an object. Note that every class that needs to be serialized must implement the Serializable interface.

The following is an example of serialization and deserialization:

import java.io.*;
import java.util.ArrayList;

class ShallowCopyExample implements Serializable { // 序列化对象必须实现 Serializable 接口
    public int num = 0; // 基本类型
    public String string = "deep copy"; // 引用类型
    public ArrayList<Integer> arrayList = new ArrayList<>(); // 引用类型

    public ShallowCopyExample deepClone() {
        // 序列化
        try (
                FileOutputStream fileOutputStream = new FileOutputStream("object.ser"); // 对象文件为 object.ser
                ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
        ) {
            objectOutputStream.writeObject(this);
        } catch(IOException ioException) {
            ioException.printStackTrace();
        }

        // 反序列化
        try (
                FileInputStream fileInputStream = new FileInputStream("object.ser");
                ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
        ) {
          return (ShallowCopyExample) objectInputStream.readObject();
        } catch (IOException ioException) {
            ioException.printStackTrace();
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }

        // 失败返回 null
        return null;
    }
}

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        ShallowCopyExample shallowCopyExample = new ShallowCopyExample();
        ShallowCopyExample copiedObject = shallowCopyExample.deepClone(); // 改用自己定义的,用序列化实现的深拷贝方法
        copiedObject.num = 1;
        copiedObject.string = "copied"; // String 不可修改,指向一个新的 String 对象
        copiedObject.arrayList.add(1); // 修改 arrayList 本身
        System.out.println(shallowCopyExample.num); // Output: 0(未修改)
        System.out.println(shallowCopyExample.string); // Output: deep copy(未修改)
        System.out.println(shallowCopyExample.arrayList); // Output: [](未修改)
    }
}

Because serialization produces two completely independent objects, serialization can achieve deep copying no matter how many reference types are nested. This process can be clearly understood with the following picture:

Serialization and deserialization

Serialization is not only available in Java, but also in other programming languages. In fact, the main role of serialization is not deep copying, but to persist objects for network transmission, etc. The built-in module pickle in Python is a module related to serialization. However, deep copy in Python does not need to be implemented using the pickle module, because its built-in module copy has a deepcopy deep copy function, which can easily obtain the deep copy of most objects. Copy, I hope that one day Java will also have such convenient built-in tools, and there will no longer be the need to manually rewrite the clone method.​ 

Guess you like

Origin blog.csdn.net/weixin_62651706/article/details/133808076