Java has a deep understanding of shallow copy and deep copy

brief introduction

Copying objects is a common problem in Java. There are two types in Java, base type and reference type.

Java values are passed. For the basic type, the specific content will be copied, but for the reference object, the stored value only points to the address of the actual object, and the copy will only copy the reference address.

Because of the existence of the reference object, it is often different from what is expected.

This article will explore the shallow copy and deep copy in the copy object.

Copy interface

All objects in Java inherit from Java lang.Object。 Object object provides a clone method for us to copy Java objects.

    protected native Object clone() throws CloneNotSupportedException;

This clone method is native, so we don't need to implement it. However, note that the clone method is still protected, which means that the clone method can only be implemented in Java Lang package or its subclasses are visible.

If we want to call the clone method of an object in a program, it is not possible. Because the clone method is defined in the object, the object has no externally visible clone method.

The JDK suggests that we implement the interface clonable, which means that the object can call the clone method of the object.

Note that even if you implement the Cloneable interface, you can't invoke the clone method of the object in an external program:

public interface Cloneable {
}

Because clonable is empty, you are not forced to implement the clone method.

This is a problem in the design of JDK, which makes the clone method not as easy to use as expected.

First, clone is just a copy of the object. It simply copies the object without executing the constructor of the object.

Second, clone can cause shallow copy problems.

Shallow copy caused by clone

Let's take a shallow copy generated by clone as an example. We define an object in an object and try to copy it:

@Data
public class Address implements Cloneable{
    private String name;

    //不是好的方式
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();

    }
}
@Data
public class CustUser implements Cloneable{
    private String firstName;
    private String lastName;
    private Address address;
    private String[] cars;

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

In the above example, we defined custuser and address.

 public void testShallowcopy() throws CloneNotSupportedException {
        Address address= new Address();
        address.setName("北京天安门");
        CustUser custUser = new CustUser();
        custUser.setAddress(address);
        custUser.setLastName("李");
        custUser.setFirstName("雷");
        String[] cars = new String[]{"别克","路虎"};
        custUser.setCars(cars);

        CustUser custUserCopy=(CustUser) custUser.clone();
        custUserCopy.setFirstName("梅梅");
        custUserCopy.setLastName("韩");
        custUserCopy.getAddress().setName("北京颐和园");
        custUserCopy.getCars()[0]="奥迪";

        log.info("{}",custUser);
        log.info("{}",custUserCopy);
    }

For shallow copy, we only called the clone method of custuser. Look at the output:

CustUser(firstName=雷,lastName=李,address=Address(name=北京颐和园),cars=[奥迪,路虎])

CustUser(firstName=梅梅,lastName=韩,路虎])

We can see that the address change after copying will affect the copied object.

In the above example, we should also pay attention to two points: first, string is immutable. String is immutable whether it is copied or assigned.

Second, in the above example, we defined an array. You can see that if you just call clone, the array is also a shallow copy.

Deep copy using clone

To use deep copy, you only need to modify the constructor of custuser:

//不是很好的使用方式
    @Override
    public Object clone() throws CloneNotSupportedException{
        CustUserDeep custUserDeep=(CustUserDeep)super.clone();
        custUserDeep.address=(Address)address.clone();
        custUserDeep.cars=cars.clone();
            return custUserDeep;
    }

In the rewritten clone method, we call the clone methods of custuser, address and array to copy.

Run the above test code again:

CustUserDeep(firstName=雷,address=Address(name=北京天安门),cars=[别克,路虎])

CustUserDeep(firstName=梅梅,路虎])

You can see that address and cars are different, which means that our deep copy is successful.

Don't override clone

The above example is implemented through the clone method of overridden object.

But the best practice is not to override clone. So what do we do?

Use the constructor to build a new object:

    //好的方式
    Address(Address address){
        this.name=address.name;
    }
//很好的方式
    CustUserDeep(CustUserDeep custUserDeep){
    this.firstName=custUserDeep.firstName;
    this.lastName=custUserDeep.lastName;
    this.cars=custUserDeep.getCars().clone();
    this.address=new Address(custUserDeep.getAddress());
    }

It is said that copying arrays directly with clone will be faster. You can also copy arrays in the following way:

this.cars= Arrays.copyOf(custUserDeep.getCars(),custUserDeep.getCars().length);
The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>