java中浅克隆与深克隆解析

x33g5p2x  于2021-12-11 转载在 Java  
字(5.5k)|赞(0)|评价(0)|浏览(242)

简介

所谓克隆,就是指依照已存在的数据,复制出一份一样的数据。

java中的克隆有浅克隆和深克隆之分,造成这种区别是因为java中对于基本数据类型和引用类型类型的存储是不一样的,基本数据类型存储在栈中,而引用数据类型存储在堆中,因此造成了在克隆时的不同操作。

实现克隆的方法可以按照三步进行:

  1. 实现Clonable接口;
  2. 重写clone()方法。
  3. 调用clone()方法克隆对象。

浅克隆

package test;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
class Person implements Cloneable{
    private String name;
    private Integer age;
    private String home;
    private Friend friend;

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

@Data
@NoArgsConstructor
@AllArgsConstructor
class Friend{
    private String name;
    private Integer age;
    private String home;
}

public class CloneTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person_old = new Person();
        person_old.setName("张三");
        person_old.setAge(18);
        person_old.setHome("chengdu");
        person_old.setFriend(new Friend("李四",19,"chongqin"));
        System.out.println("初始化下的person:" + person_old);

        Person person_clone = (Person) person_old.clone();

        person_clone.setName("张武");
        person_clone.setAge(19);
        person_clone.getFriend().setAge(20);

        System.out.println("克隆出来的person:" + person_clone);
        System.out.println("修改之后的person:" + person_clone);
        System.out.println("修改后的原person:" + person_old);
    }
}

从控制台打印的输出可以看到,修改克隆出来的对象person_clone的age属性(基本数据类型)时,最先创建对象person_old 的age属性值没有收到影响;但是当修改person_clone的Friend属性(引用数据类型)时,person_old 的Friend属性值也被修改了。

这是因为通过浅克隆复制对象时仅仅复制对象本身,包括基本属性,但该对象的属性引用其他对象时,该引用对象不会被复制,即拷贝出来的对象与被拷贝出来的对象中的属性引用的对象是同一个。

所以,浅克隆没有达到完全复制、相互之间完全没有影响的目的。
而这通过深克隆就可以做到。

深克隆

深克隆有两种实现方式:嵌套使用clone()方法、序列化方式。

嵌套使用clone()方法

我们可以对为引用数据类型属性也实现Cloneable接口,并重写clone()方法。

针对上面的例子只需要在Friend也实现Cloneable接口,并重写clone()方法,然后更改Person中的clone()方法。

  1. Friend实现Cloneable接口,并重写clone()方法。
@Data
@NoArgsConstructor
@AllArgsConstructor
class Friend implements Cloneable{
    private String name;
    private Integer age;
    private String home;

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
  1. 更改Person中的clone()方法
@Data
@NoArgsConstructor
@AllArgsConstructor
class Person implements Cloneable{
    private String name;
    private Integer age;
    private String home;
    private Friend friend;

    @Override
    public Object clone() throws CloneNotSupportedException {
        Person person = (Person) super.clone();
        person.friend = (Friend) friend.clone();
        return person;
    }
}

完整示例代码:

package test;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
class Person implements Cloneable{
    private String name;
    private Integer age;
    private String home;
    private Friend friend;

    @Override
    public Object clone() throws CloneNotSupportedException {
        Person person = (Person) super.clone();
        person.friend = (Friend) friend.clone();
        return person;
    }
}

@Data
@NoArgsConstructor
@AllArgsConstructor
class Friend implements Cloneable{
    private String name;
    private Integer age;
    private String home;

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

public class CloneTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person_old = new Person();
        person_old.setName("张三");
        person_old.setAge(18);
        person_old.setHome("chengdu");
        person_old.setFriend(new Friend("李四",19,"chongqin"));
        System.out.println("初始化下的person:" + person_old);

        Person person_clone = (Person) person_old.clone();

        person_clone.setName("张武");
        person_clone.setAge(19);
        person_clone.getFriend().setAge(20);

        System.out.println("克隆出来的person:" + person_clone);
        System.out.println("修改之后的person:" + person_clone);
        System.out.println("修改后的原person:" + person_old);
    }
}

打印结果


可以看到,在使用深克隆的时候,不管修改person_clone(克隆出来的对象)的基本数据类型属性还是引用数据类型属性时,最先创建的对象person_old都没有受到影响。

这就实现了完全复制、相互之间完全没有影响。

序列化方式

如果当类中的属性存在数组(数组不能实现Cloneable接口)或者属性之间的关系比较复杂时,上面的方法都不能很好的实现深克隆了。

序列化的方式是让每个类都实现Serializable接口,然后通过序列化和反序列化操作达到深克隆的目的。

package test;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.*;

@Data
@NoArgsConstructor
@AllArgsConstructor
class Person implements Serializable{
    private String name;
    private Integer age;
    private String home;
    private Friend friend;

}

@Data
@NoArgsConstructor
@AllArgsConstructor
class Friend implements Serializable {
    private String name;
    private Integer age;
    private String home;

}

public class CloneTest {
    public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
        Person person_old = new Person();
        person_old.setName("张三");
        person_old.setAge(18);
        person_old.setHome("chengdu");
        person_old.setFriend(new Friend("李四",19,"chongqin"));
        System.out.println("初始化下的person:" + person_old);

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(person_old);     // 序列化

        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        Person person_clone = (Person) objectInputStream.readObject();	//反序列化

        person_clone.setName("张武");
        person_clone.setAge(19);
        person_clone.getFriend().setAge(20);

        System.out.println("克隆出来的person:" + person_clone);
        System.out.println("修改之后的person:" + person_clone);
        System.out.println("修改后的原person:" + person_old);
    }
}


同样实现了完全复制、相互之间完全没有影响。

使用序列化实现深克隆时:对象序列化后写入流中,再反序列化读取流,生成新的对象,新对象和原对象之间自然完全互不影响的。

浅克隆和深克隆的比较

浅克隆深克隆
浅克隆复制对象时仅仅复制对象本身,包括基本属性,但该对象的属性引用其他对象时,该引用对象不会被复制,即拷贝出来的对象与被拷贝出来的对象中的属性引用的对象是同一个。复制后的对象与原对象之间完全互不影响
实现Cloneable接口,并重写clone()方法嵌套使用clone()方法;实现Serializable接口,通过序列化和反序列化操作

相关文章

微信公众号

最新文章

更多