前言

常见的创建对象有两种方式: new 和 clone

当一个对象创建过程复杂,我们是否可以根据已有的对象直接来克隆一份,而不必关系创建的细节呢(原型模式)。

1、实现 Cloneable 接口,重写 clone 方法

Object 默认的 clone 方法实际是对域的简单拷贝,对于简单数据类型,是值的拷贝;

对于复杂类型的字段,则是指针地址的拷贝,clone 后的对象和原对象指向的还是一个地址空间。

所以说默认的 clone 方法是浅克隆。我们用下面例子验证一下:

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());
    }
}

执行结果:

这种克隆方式显然表示原始对象和克隆对象的 Car 是同一个 引用。也就是说,Car 对象没有被克隆。如果修改了 Car 对象的值,原始对象和克隆对象都将会发生变化。这并不是我们希望看到的。

所以,我们需要连对象里面的对象也要是一个新的对象。每一个属性都被完全拷贝,这才是深克隆。
为了实现深度克隆,我们需要对 Person 中的 clone 方法进行改造一下,getCar() 测试代码不变。

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

再次进行测试:

这么做就要在 super.clone 的基础上 继续对非基本类型的对象递归地再次 clone.

显然这么方式是繁琐的且不可靠的。

2、实现序列化接口

2.1 使用 java 自身的序列化转为二进制数 ,再反序列化为对象

ObjectStream 序列化的工具类

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;
    }
}

测试类:

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());
    }
}

运行结果:

其他方式还可以是用序列化工具如 fastjson 进行序列化和反序列化进行对象 clone。

2.2 fastjson 序列化

Maven 依赖

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

Car 类:

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 类

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());
    }
}

运行结果:

总结:

实现对象克隆主要有两种方式:
1、实现 Cloneable 接口并重写其中的 clone() 方法完成对象的浅拷贝

  • Object 默认的 clone 方法实际是对域的简单拷贝,对于简单数据类型,是值的拷贝;
  • 对于复杂类型的字段,则是指针地址的拷贝,clone 后的对象和原对象指向的还是一个地址空间。
  • 所以说默认的 clone 方法是浅克隆。
  • 想要实现深克隆需要复杂类实现中为每个类都实现 Cloneable 接口并重写 clone 方法(复杂类中的对象也要是新的对象)这么做就要在 super.clone 的基础上 继续对非基本类型的对象递归的再次 clone.
  • 显然这么方式是繁琐的且不可靠的。

2、实现序列化接口 Serializable,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。

  • 基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛型限定,可以检查出要克隆的对象是否支持序列化,这项检查是通过编译器完成的,
  • 不是在运行时抛出异常,这汇总方案明显优于使用 Object 类的 clone 方法克隆对象。让问题在编译的时候暴露出来中是好过把问题留到运行时。