Java 对象克隆

93 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 3 天,点击查看活动详情

在日常开发中,Java的引用类型在传递的时候是属于引用传递,那么我们想将引用对象完整的复制一份出来,并不能使用简单的=赋值来实现,那么我们就需要对象克隆。

在Java中,想要复制一个基本类型很简单,使用=进行赋值就行了。如果我们要对对象进行克隆,采用简单的赋值,只是传递了地址,其实两者还是指向同一个对象,修改任何一个变量的值都会导致该对象的改变。

// 基本类型直接赋值,因为是值传递
int age = 22;
double altitude = 300;
int num = age

// 对象直接赋值,copyUserInfo与userInfo指向的是同一个对象,因为是引用传递
UserInfo userInfo = new UserInfo();
UserInfo copyUserInfo = userInfo;

克隆对象有两种实现方法

1. clone()

  1. 首先在类中实现Cloneable接口
    1. 被复制类中必须实现Cloneable接口,该接口为标记接口,不含任何方法,就只是表示这个类可以被克隆
    2. 重写equal()是为了后续更清晰的查看结果
public class User implements Cloneable {
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

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

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

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

    @Override
    public boolean equals(Object obj) {
        User user = (User) obj;
        if (name.equals(user.name) && age == user.age) return true;
        return false;
    }


}
  1. 使用clone()来克隆对象

我们可以看到输出的结果中,两个变量所指的对象值是完全一样的,但是两个变量所指的对象地址完全不同,为两个对象,那么对象克隆成功。

private static void cloneTest() {
    User user = new User("aHua", 22);
    try {
        User userClone = (User) user.clone();
        System.out.println("equal: " + (userClone.equals(user)));
        System.out.println("==: " + (userClone == user));
    } catch (CloneNotSupportedException e) {
        throw new RuntimeException(e);
    }
}

但是该方法还不能实现完全的克隆,只能够克隆对象中的基本类型,不能克隆对象中引用类型,那么这种克隆被称为浅克隆,并不是完完全全的克隆。

深克隆和浅克隆的区别就在于支不支持对象中引用类型变量的克隆

2. Serializable

除了使用clone()可以克隆对象之外,还可以使用Serializable克隆对象,该方法克隆的对象则是深克隆,不论对象内存储的是什么类型,所有内容都将完全克隆,变成一个新的对象。

  1. 类中需要实现Serializable接口
    1. 类支持序列化必须实现Serializable接口,标记接口,标记该类可被序列化
public class User implements Cloneable, Serializable {
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

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

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

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

    @Override
    public boolean equals(Object obj) {
        User user = (User) obj;
        if (name.equals(user.name) && age == user.age) return true;
        return false;
    }


}
  1. 使用流克隆对象
private static void serializableTest() {
    System.out.println("------- serializableTest ------");
    User user = new User("aHua", 22);
    try {
        User copyUser = deepCopyObject(user);
        System.out.println("user name: " + user.getName() + ", age: " + user.getAge());
        System.out.println("copyUser name: " + copyUser.getName() + ", age: " + copyUser.getAge());
        System.out.println("equal: " + (user.equals(copyUser)));
        System.out.println("==: " + (user == copyUser));
    } catch (IOException e) {
        System.out.println("IOException: " + e);
        throw new RuntimeException(e);
    } catch (ClassNotFoundException e) {
        throw new RuntimeException(e);
    }
}

private static <E> E deepCopyObject(E src) throws IOException, ClassNotFoundException {
    if (src == null) return null;
    // 将对象序列化成流
    ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
    ObjectOutputStream out = new ObjectOutputStream(byteOut);
    out.writeObject(src);
    // 重新将流序列化成对象
    ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
    ObjectInputStream in = new ObjectInputStream(byteIn);
    return (E) in.readObject();
}

根据输出结果看出Serializable也可以克隆对象,而且使用该方法克隆对象是深克隆,因为将原对象序列化成流,流和原对象就失去了关联,再将流重新反序列化成新对象,那么就新对象就是原对象的完全克隆。

使用Serializable克隆对象效率很低,但它是完全克隆的最好的办法


如有错误,欢迎在评论区指出!