This is the sixth day of my participation in Gwen Challenge

preface

Before we talk about deep and shallow copies, let’s look at what primitive and reference types are.

Base and reference types

The basic types, also known as value types, are char, Boolean, and byte, short, int, long, float, and double.

Reference types include classes, interfaces, arrays, enumerations, and so on.

Java divides memory space into stacks and heaps. Primitive types store values directly on the stack, whereas reference types put references on the stack. The actual stored values are stored in the heap, and references in the stack point to data stored in the heap.

Both a and B are primitive types whose values are stored directly on the stack. C and D are declared by String, which is a reference type, and the reference address is stored on the stack and then refers to the memory space of the heap.

D = c; This statement assigns a reference to C to D, so c and D refer to the same heap memory space.

Shallow copy

A shallow copy is a bitwise copy of an object that creates a new object with an exact copy of the original object’s property values. If the property is of a primitive type, the value of the primitive type is copied. If the property is a memory address (reference type), the memory address is copied, so if one object changes the address, the other object will be affected. That is, the default copy constructor only copies objects shallowly (member by member), that is, only the object space is copied, not the resource.

Classes that copy objects need to implement the Cloneable interface and override the Clone () method.

public class Address {

    private String province;
    private String city;

    public void setAddress(String province, String city) {
        this.province = province;
        this.city = city;
    }

    @Override
    public String toString(a) {
        return "Address [province=" + province + ", city=" + city + "]"; }}Copy the code
public class Student implements Cloneable {

    private String name;
    private int age;
    private Address address;

    public Student(a) {}public Student(String name, int age) {
        this.name = name;
        this.age = age;
        this.address = new Address();
    }

    public String getName(a) {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge(a) {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setAddress(String province, String city) {
        address.setAddress(province, city);
    }

    public void display(String name) {
        System.out.println(name + ":" + "name=" + name + ", age=" + age + "," + address);
    }

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

This is the original class Student that we’re going to assign to. Next we generate a Student object and call its Clone method to copy a new object.

Note: To call the clone method of an object, you must have the class implement the Cloneable interface and override the Clone method.

Testing:

    public static void main(String[] args) throws CloneNotSupportedException {
        Student s1 = new Student("Xiao Ming".20);
        s1.setAddress("Anhui province".Hefei "");
        Student s2 = (Student) s1.clone();
        System.out.println("S1:"+s1);
        System.out.println("s1.getName:"+s1.getName().hashCode());
        System.out.println("S2:"+s2);
        System.out.println("s2.getName:"+s2.getName().hashCode());

        s1.display("s1");
        s2.display("s2");
        s2.setAddress("Anhui province"."Anqing");
        s1.display("s1");
        s2.display("s2");
    }
Copy the code

Output result:

S1:org.example.jvm.Student@2a84aee7
s1.getName:756703
S2:org.example.jvm.Student@a09ee92
s2.getName:756703
s1:name=s1, age=20,Address [province] s2:name=s2, age= s220,Address [province] s1:name=s1, age= s120,Address [province] s2:name=s2, age= s220,Address [province],Address [province]Copy the code

Create s1 (name = xiaoming,age = 20, Address = Anhui, Address = Hefei); create s1 (name = xiaoming,age = 20); Next, we call the clone() method to copy another object, S2, and then print the contents of both objects.

Analysis results:

  • From the print on line 1 and line 3, these are two different objects.

  • From the object contents printed in lines 5 and 6, the original object S1 and the cloned object S2 have exactly the same contents.

  • We changed the attribute Address of the cloned object S2 to Anqing, Anhui (the original object S1 was Hefei, Anhui), but from the print results in line 7 and line 8, the Address attribute of the original object S1 and the cloned object S2 have been modified.

  • The Address attribute of Student is cloned, and the reference to the Address attribute of Student is copied. When the Address attribute of Student is cloned, the reference to the Address attribute of Student is copied, and the reference to the Address attribute of Student is copied.

Shallow copy: Create a new object and copy the non-static fields of the current object into the new object, or if the fields are of value type. If the field is a reference type, the reference is copied but not the referenced object. Therefore, the original object and its copy refer to the same object.

Deep copy

Deep copy: when copying a reference type member variable, it creates an independent memory space for the reference type data member, realizing the true content copy.

For Student’s reference type member variable Address, you need to implement Cloneable and override the Clone () method.

public class Address implements Cloneable{

    private String province;
    private String city;

    public void setAddress(String province, String city) {
        this.province = province;
        this.city = city;
    }

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

    @Override
    public String toString(a) {
        return "Address [province=" + province + ", city=" + city + "]"; }}Copy the code

In Student’s clone() method, we need to get the new object generated after copying ourselves, and then call the copy operation on the reference type of the new object to achieve the deep copy of the reference type member variables.

public class Student implements Cloneable {

    private String name;
    private int age;
    private Address address;

    public Student(a) {}public Student(String name, int age) {
        this.name = name;
        this.age = age;
        this.address = new Address();
    }

    public String getName(a) {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge(a) {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setAddress(String province, String city) {
        address.setAddress(province, city);
    }

    public void display(String name) {
        System.out.println(name + ":" + "name=" + name + ", age=" + age + "," + address);
    }

    @Override
    protected Object clone(a) throws CloneNotSupportedException {
        Student s = (Student) super.clone();
        s.address = (Address) address.clone();
        returns; }}Copy the code

Testing:

    public static void main(String[] args) throws CloneNotSupportedException {
        Student s1 = new Student("Xiao Ming".20);
        s1.setAddress("Anhui province".Hefei "");
        Student s2 = (Student) s1.clone();
        System.out.println("S1:"+s1);
        System.out.println("s1.getName:"+s1.getName().hashCode());
        System.out.println("S2:"+s2);
        System.out.println("s2.getName:"+s2.getName().hashCode());

        s1.display("s1");
        s2.display("s2");
        s2.setAddress("Anhui province"."Anqing");
        s1.display("s1");
        s2.display("s2");
    }
Copy the code

Output result:

S1:org.example.jvm.Student@2a84aee7
s1.getName:756703
S2:org.example.jvm.Student@a09ee92
s2.getName:756703
s1:name=s1, age=20,Address [province] s2:name=s2, age= s220,Address [province] s1:name=s1, age= s120,Address [province] s2:name=s2, age= s220,Address [province],Address [province]Copy the code

From the output, it can be seen that after deep copy, changes in the values of member variables of both the underlying data type and reference type do not affect each other.

Note:

The Student class has only one Address reference type, and the Address class does not, so we can override the Clone method of the Address class. However, if the Address class also has a reference type, We would have to rewrite the Clone method as many times as there are reference types. If there are many reference types, the code would obviously be a lot of code, so this method would not be appropriate.

There is another way to achieve deep copy: with serialization

Serialization is writing an object to a stream for easy transfer, while deserialization is reading an object from the stream.

Here the object written to the stream is a copy of the original object, which still exists in the JVM, so we can use the serialization of the object to produce a clone object, which can then be retrieved by deserialization.

Note that each class that needs to be serialized implements the Serializable interface. If a property does not need to be serialized, it can be declared transient, excluding it from the clone property.

public class Address implements Serializable {

    private String province;
    private String city;

    public void setAddress(String province, String city) {
        this.province = province;
        this.city = city;
    }

    @Override
    public String toString(a) {
        return "Address [province=" + province + ", city=" + city + "]"; }}Copy the code
public class Student implements Serializable {

    private String name;
    private int age;
    private Address address;

    public Student(a) {}public Student(String name, int age) {
        this.name = name;
        this.age = age;
        this.address = new Address();
    }

    public String getName(a) {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge(a) {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setAddress(String province, String city) {
        address.setAddress(province, city);
    }

    public void display(String name) {
        System.out.println(name + ":" + "name=" + name + ", age=" + age + "," + address);
    }

    // Deep copy
    public Object deepClone(a) throws Exception{
        / / the serialization
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);
        // deserialize
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        returnois.readObject(); }}Copy the code

Testing:

    public static void main(String[] args) throws Exception {
        Student s1 = new Student("Xiao Ming".20);
        s1.setAddress("Anhui province".Hefei "");
        Student s2 = (Student) s1.deepClone();
        System.out.println("S1:"+s1);
        System.out.println("s1.getName:"+s1.getName().hashCode());
        System.out.println("S2:"+s2);
        System.out.println("s2.getName:"+s2.getName().hashCode());

        s1.display("s1");
        s2.display("s2");
        s2.setAddress("Anhui province"."Anqing");
        s1.display("s1");
        s2.display("s2");
    }
Copy the code

Output result:

S1:org.example.jvm.Student@3f99bd52
s1.getName:756703
S2:org.example.jvm.Student@1f17ae12
s2.getName:756703
s1:name=s1, age=20,Address [province] s2:name=s2, age= s220,Address [province] s1:name=s1, age= s120,Address [province] s2:name=s2, age= s220,Address [province],Address [province]Copy the code

Because serialization produces two completely independent objects, serialization can achieve deep copy no matter how many reference types are nested.

conclusion

At this point, believe what is deep copy, what is shallow copy, I believe you must understand.