[Java] Three ways to implement object cloning (Cloneable interface, Java serialization, FastJson serialization)

Preface

There are two common ways to create objects: new and clone

When the process of creating an object is complicated, can we clone an existing object directly without having to deal with the details of creation (prototype mode).

1. Implement the Cloneable interface and rewrite the clone method

The default clone method of Object is actually a simple copy of the field, and for simple data types, it is a copy of the value;

For complex types of fields, it is a copy of the pointer address, and the cloned object and the original object point to an address space.

So the default clone method is shallow clone . Let's verify it with the following example:

package com.dl.JavaBase;

class Car implements Cloneable{
    
    
    private String brand;//品牌
    private int maxSpeed;//最高时速

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

    @Override
    public String toString() {
    
    
        return "Car{" +
                "brand='" + brand + '\'' +
                ", maxSpeed=" + maxSpeed +
                '}';
    }

    public String getBrand() {
    
    
        return brand;
    }

    public void setBrand(String brand) {
    
    
        this.brand = brand;
    }

    public int getMaxSpeed() {
    
    
        return maxSpeed;
    }

    public void setMaxSpeed(int maxSpeed) {
    
    
        this.maxSpeed = maxSpeed;
    }

    public Car(String brand, int maxSpeed) {
    
    
        this.brand = brand;
        this.maxSpeed = maxSpeed;
    }
}
public class Person implements Cloneable {
    
    
    private String name;
    private Car car;

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

    @Override
    public String toString() {
    
    
        return "Person{" +
                "name='" + name + '\'' +
                ", car=" + car +
                '}';
    }

    public String getName() {
    
    
        return name;
    }

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

    public Car getCar() {
    
    
        return car;
    }

    public void setCar(Car car) {
    
    
        this.car = car;
    }

    public Person(String name, Car car) {
    
    
        this.name = name;
        this.car = car;
    }

    public static void main(String[] args) throws CloneNotSupportedException {
    
    
        Car car = new Car("audi", 150);
        Person person=new Person("ding",car);
        Person person1= (Person) person.clone();
        System.out.println("修改car之前:");
        System.out.println(person);
        System.out.println(person1);
        System.out.println("修改car之后:");
        car.setBrand("benchi");
        car.setMaxSpeed(200);
        System.out.println(person);
        System.out.println(person1);
        System.out.print("使用Object默认的clone方法:");
        System.out.println(person.getCar()==person1.getCar());
    }
}

Execution result:
Insert picture description here
This cloning method obviously means that the Car of the original object and the cloned object are the same reference. In other words, the Car object is not cloned. If the value of the Car object is modified, both the original object and the cloned object will change. This is not what we want to see.

Therefore, we need even the object in the object to be a new object. Every attribute is completely copied, which is a deep clone.
In order to achieve deep cloning , we need to modify the clone method in Person, and the getCar() test code remains unchanged.

    @Override
    protected Object clone() throws CloneNotSupportedException {
    
    
        Person person= (Person) super.clone();
        person.setCar((Car) person.getCar().clone());
        return person;
    }

Test again: To
Insert picture description here
do so is to continue to recursively clone non-basic types of objects on the basis of super.clone.

Obviously this method is cumbersome and unreliable.

2. Implement serialization interface

2.1 Use java's own serialization to convert to binary numbers, and then deserialize to objects

ObjectStream serialization tool class

package com.dl.JavaBase;

import java.io.*;

public class SerialiazableUtil {
    
    

    public SerialiazableUtil() {
    
    
        throw new AssertionError();
    }

    @SuppressWarnings("unchecked")
    public static <T extends Serializable> T deepCloneObject(Object object) throws IOException {
    
    
        T deepClone = null;
        ObjectInputStream ois = null;
        try(ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
        )
        {
    
    
            oos.writeObject(object);
            ByteArrayInputStream bais = new ByteArrayInputStream(baos
                    .toByteArray());
            ois = new ObjectInputStream(bais);
            deepClone = (T)ois.readObject();
        } catch (IOException | ClassNotFoundException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            if(ois != null){
    
    
                ois.close();
            }
        }
        return deepClone;
    }
}

Test category:

package com.dl.JavaBase;

import java.io.IOException;
import java.io.Serializable;

class Car implements Serializable {
    
    
    private static final long serialVersionUID = 4982206063131788088L;
    private String brand;//品牌
    private int maxSpeed;//最高时速

    @Override
    public String toString() {
    
    
        return "Car{" +
                "brand='" + brand + '\'' +
                ", maxSpeed=" + maxSpeed +
                '}';
    }

    public String getBrand() {
    
    
        return brand;
    }

    public void setBrand(String brand) {
    
    
        this.brand = brand;
    }

    public int getMaxSpeed() {
    
    
        return maxSpeed;
    }

    public void setMaxSpeed(int maxSpeed) {
    
    
        this.maxSpeed = maxSpeed;
    }

    public Car(String brand, int maxSpeed) {
    
    
        this.brand = brand;
        this.maxSpeed = maxSpeed;
    }
}
public class Person implements Serializable {
    
    

    private static final long serialVersionUID = 6957528274628957691L;
    private String name;
    private Car car;

    @Override
    public String toString() {
    
    
        return "Person{" +
                "name='" + name + '\'' +
                ", car=" + car +
                '}';
    }

    public String getName() {
    
    
        return name;
    }

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

    public Car getCar() {
    
    
        return car;
    }

    public void setCar(Car car) {
    
    
        this.car = car;
    }

    public Person(String name, Car car) {
    
    
        this.name = name;
        this.car = car;
    }

    public static void main(String[] args) throws CloneNotSupportedException, IOException {
    
    
        Person person=new Person("ding",new Car("audi",150));
        Person person1= SerialiazableUtil.deepCloneObject(person);
        System.out.print("Java默认序列化方式:");
        System.out.println(person.getCar()==person1.getCar());
    }
}

operation result:

Insert picture description here
Another way is to use serialization tools such as fastjson to serialize and deserialize objects to clone.

2.2 fastjson serialization

Maven dependency

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.62</version>
</dependency>

Car class:

package com.dl.JavaBase;

import java.io.Serializable;

public class Car implements Serializable {
    
    
    private static final long serialVersionUID = 4982206063131788088L;
    private String brand;//品牌
    private int maxSpeed;//最高时速

    @Override
    public String toString() {
    
    
        return "Car{" +
                "brand='" + brand + '\'' +
                ", maxSpeed=" + maxSpeed +
                '}';
    }

    public String getBrand() {
    
    
        return brand;
    }

    public void setBrand(String brand) {
    
    
        this.brand = brand;
    }

    public int getMaxSpeed() {
    
    
        return maxSpeed;
    }

    public void setMaxSpeed(int maxSpeed) {
    
    
        this.maxSpeed = maxSpeed;
    }

    public Car(String brand, int maxSpeed) {
    
    
        this.brand = brand;
        this.maxSpeed = maxSpeed;
    }
}

Person class

package com.dl.JavaBase;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import java.io.IOException;
import java.io.Serializable;
import java.util.List;
public class Person implements Serializable {
    
    

    private static final long serialVersionUID = 6957528274628957691L;
    private String name;
    private Car car;

    @Override
    public String toString() {
    
    
        return "Person{" +
                "name='" + name + '\'' +
                ", car=" + car +
                '}';
    }

    public String getName() {
    
    
        return name;
    }

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

    public Car getCar() {
    
    
        return car;
    }

    public void setCar(Car car) {
    
    
        this.car = car;
    }

    public Person(String name, Car car) {
    
    
        this.name = name;
        this.car = car;
    }

    public static void main(String[] args) throws CloneNotSupportedException, IOException {
    
    
        Person person=new Person("ding",new Car("audi",150));
        //Person person1 = JSONObject.parseObject(JSONObject.toJSONString(person), Person.class);
        Person person1 = JSONObject.parseObject(JSONObject.toJSONBytes(person), Person.class);
        System.out.println("fastjson方式:");
        System.out.println(person.getCar()==person1.getCar());
    }
}

operation result:
Insert picture description here

to sum up:

There are two main ways to implement object cloning:
1. Implement the Cloneable interface and rewrite the clone() method to complete the shallow copy of the object

  • The default clone method of Object is actually a simple copy of the field, and for simple data types, it is a copy of the value;
  • For complex types of fields, it is a copy of the pointer address, and the cloned object and the original object point to an address space.
  • So the default clone method is shallow clone.
  • To achieve deep cloning, you need to implement the Cloneable interface and rewrite the clone method for each class in the complex class implementation (the objects in the complex class should also be new objects). To do so, you need to continue to treat non-basic ones on the basis of super.clone Type objects are cloned again recursively.
  • Obviously this method is cumbersome and unreliable.

2. Realize the serialization interface Serializable, and realize the cloning through the serialization and deserialization of the object, which can realize the real deep cloning.

  • Cloning based on serialization and deserialization is not only deep cloning, but more importantly, through generic restriction, you can check whether the object to be cloned supports serialization. This check is done by the compiler.
  • Instead of throwing an exception at runtime, this summary solution is significantly better than using the clone method of the Object class to clone an object. It is better to let the problem be exposed at compile time than to leave the problem at runtime.

Guess you like

Origin blog.csdn.net/dl962454/article/details/114780240