美文网首页
Java的浅克隆和深克隆

Java的浅克隆和深克隆

作者: zivxia | 来源:发表于2018-03-10 22:12 被阅读16次

克隆就是对象复制的过程,而生成的副本与被复制的对象只是值相等而不是真正意义上的同一个对象。而我们平时通过更改对象的引用来复制对象,这种方法的两个对象引用其实都指向堆中的同一块内存,是真正意义上的同一个对象,举个例子来说,

        Person person1 = new Person();
        person1.setName("小明");
        person1.setAge(18);

        Person person2 = person1;
        person2.setName("小丽");

        System.out.println("person1 的名字为: "+person1.getName());
        System.out.println("person2 的名字为: "+person2.getName());

打印结果如下:


image.png

改变person2的name,竟然连person1的name一起改了,实际上person1和person2在内存中表现形式如下:


image.png
person1和person2是指向同一对象的,所有更改了任何一个对象的属性,则都会同步到另外一个对象。
如果只是想获取一个对象的副本,就可以通过克隆的方式
  /*
Creates and returns a copy of this object. The precise meaning of "copy" may depend on the class of the object.
The general intent is that, for any object x, the expression:
1) x.clone() != x will be true
2) x.clone().getClass() == x.getClass() will be true, but these are not absolute requirements.
3) x.clone().equals(x) will be true, this is not an absolute requirement.
*/
protected native Object clone() throws CloneNotSupportedException;

查看源码,大致意思如下

  • 原始和克隆两者不是同一个对象
  • 原始和克隆的对象应该具有相同的类类型,但它不是强制性的
  • 原始和克隆的对象应该是平等的equals()方法使用,但它不是强制性的

浅克隆

因为每个类都是直接或者间接的继承Object类,如果想要实现对象的clone,则必须实现Cloneable接口。在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制(严格来说是对象地址的复制)。

public class Person implements Cloneable {

    private String name;
    private int 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
    public Object clone() {
        Person person = null;
        try {
            person = (Person) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        return person;
    }

}

打印结果如下:


image.png

当原始对象含引用类型时,

public class Person implements Cloneable {

    private String name;
    private int age;
    private Student student;

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

    public void setStudent(Student student) {
        this.student = student;
    }

    public Student getStudent() {
        return student;
    }

    @Override
    public Object clone() {
        Person person = null;
        try {
            person = (Person) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        return person;
    }
    public static void main(String[] args) {

        Person person1 = new Person();
        Student student = new Student();
        student.setHeight(180);
        person1.setStudent(student);
        person1.setName("小明");
        person1.setAge(18);

        Person person2 = (Person) person1.clone();
        person2.getStudent().setHeight(200);

        System.out.println(person1.getStudent() == person2.getStudent());
        System.out.println("person1 高度为: " + person1.getStudent().getHeight());
        System.out.println("person2 高度为: " + person2.getStudent().getHeight());

    }

打印结果如下:


image.png

这里还是发生了开头的那个问题。对象的属性被篡改了,并且student对象是同一个对象,这是因为我们并未对student对象进行同步克隆的原因。

深克隆

在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。
下面对student进行克隆修改如下:

public class Student implements Cloneable {

    private int height;

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    @Override
    public Object clone() {
        Student student = null;
        try {
            student = (Student) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        return student;
    }
}
public class Person implements Cloneable {

    private String name;
    private int age;
    private Student student;

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

    public void setStudent(Student student) {
        this.student = student;
    }

    public Student getStudent() {
        return student;
    }

    @Override
    public Object clone() {
        Person person = null;
        try {
            person = (Person) super.clone();
            person.setStudent((Student)student.clone());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        return person;
    }

这时候再去运行上面的测试例子,打印结果如下:


jiu'de'dao'wo'men

就得到了我们想要的结果了。
还要考虑一种情况,当成员变量为引用类型时,并且引用类型的成员变量也为引用类型时,这时候就需要每个引用类型都要实现克隆接口,折个工作量就很大了,有没有一种很简单的方式呢?答案肯定是有的,我们可以使用对象序列化的方式来实现。

public class Person implements Serializable {

    private static final long serialVersionUID = -71293312083100779L;

    private String name;
    private int age;
    private Student student;

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

    public void setStudent(Student student) {
        this.student = student;
    }

    public Student getStudent() {
        return student;
    }

    public Person myClone() {
        Person person = null;
        try {
            // 将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝   
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(this);

            // 将流序列化成对象
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            person = (Person) ois.readObject();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return person;
    }
public class Student implements Serializable {

    private static final long serialVersionUID = 503951312982338998L;

    private int height;

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }
}

在测试代码中调用myClone方法创建person2,打印结果如下:


image.png

这样也能使两个对象在内存空间内完全独立存在,互不影响对方的值。

综合上述对象实现克隆有两种方法:

    1. 实现Cloneable接口。
    1. 实现序列化。

相关文章

  • java克隆

    java克隆 java克隆分为浅克隆和深克隆,概念如下: 浅拷贝(浅克隆)克隆出来的对象的所有变量都含有与原来的对...

  • 使用对象序列化和反序列化实现深度克隆

    java对象进行克隆,分为浅克隆和深克隆 浅克隆需要实现java.lang.Cloneable接口,并重写java...

  • Java深复制浅复制解析.md

    Java 克隆概念 Java克隆分为深克隆和浅克隆两种类型。 浅复制(浅克隆)被复制对象的所有变量都含有与原来的对...

  • 震惊!没看这篇文章之前,我以为真的懂深克隆和浅克隆。

    面试题:深克隆和浅克隆的实现方式 面试官考察点 考察目的: 深克隆和浅克隆,考察的是Java基础知识的理解。 考察...

  • Java基础 - 深拷贝和浅拷贝

    Java 的深拷贝和浅拷贝 什么是深拷贝、浅拷贝 (深克隆、浅克隆)? 在 Java 中,数据类型分为 基本数据类...

  • 深克隆--何时是尽头?

    As we all know Java有两种拷贝,浅拷贝和深拷贝,高大上的叫法也叫浅克隆和深克隆。 深克隆有时会碰...

  • java深克隆和浅克隆

    浅 深 深克隆的另外一种方式

  • java 浅克隆和深克隆

    在使用克隆时,我们需要知道使用的目的:就是为了快速构造一个和已有对象相同的副本。一、浅克隆:要实现java.lan...

  • Java的浅克隆和深克隆

    克隆就是对象复制的过程,而生成的副本与被复制的对象只是值相等而不是真正意义上的同一个对象。而我们平时通过更改对象的...

  • Java 浅克隆、深克隆

    浅克隆 只复制了基本数据类型和String数据类型以及对应的数组类型,其他引用数据类型只是复制了引用地址; 使用方...

网友评论

      本文标题:Java的浅克隆和深克隆

      本文链接:https://www.haomeiwen.com/subject/dcbbfftx.html