Java中深浅克隆的区别

目录

深克隆与浅克隆区别

浅克隆demo

深克隆demo

文末思考


克隆与浅克隆区别

深浅克隆的区别在于,能否支持引用类型(包括类、接口、数组等)的成员变量的复制。
浅克隆:对象只复制了它本身和其中包含的值类型的成员变量,引用类型的成员对象并没有复制。
深克隆:对象本身以及包含的所有成员变量都会被复制。

浅克隆demo

关键点:Student类,实现Cloneable接口。

Student中数值类型属性 name sex,引用类型teacher

public class Student implements Cloneable {
    String name;
    String sex;
    Teacher teacher;

    public Student(String name, String sex, Teacher teacher) {
        this.name = name;
        this.sex = sex;
        this.teacher = teacher;
    }

    public String getName() {
        return name;
    }

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

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", teacher=" + teacher +
                '}';
    }
}
/**
 * 教师类
 */
public class Teacher {
    String name;
    String sex;

    public String getName() {
        return name;
    }

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


    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

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

    public Teacher(String name, String sex) {
        this.name = name;
        this.sex = sex;
    }

}

测试类:

public class LightCloneTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Teacher teacher = new Teacher("laoshi", "男");
        Student a = new Student("zhangsan", "男", teacher);
        Student b = (Student) a.clone();
        System.out.println(a);
        System.out.println(b);
        System.out.println(a.equals(b));
        System.out.println("b为a克隆的一个对象,两者引用不相等");

        System.out.println("对克隆的的值属性修改,a的性别属性修改为女");
        b.setSex("女");
        System.out.println(a);
        System.out.println(b);
        System.out.println("对克隆的的值属性修改,被克隆的对象中对应值属性无变化了");

        // 对a的引用类型成员变量teacher进行修改,将teacher的name属性由laoshi改为laosi
        System.out.println("对a的引用类型成员变量teacher进行修改,将teacher的name属性由laoshi改为laosi");
        a.getTeacher().setName("laosi");
        System.out.println(a);
        System.out.println(b);
        System.out.println("对被克隆的的对象进行属性修改,克隆的对象中对应属性也变化了");

        // 对b的引用类型成员变量teacher进行修改,将teacher的name属性由laoshi改为laosi
        System.out.println("对克隆的的对象进行属性修改,将teacher的name属性由改为laowu\"");
        b.getTeacher().setName("laowu");
        System.out.println(a);
        System.out.println(b);
        System.out.println("对克隆的的对象进行属性修改,被克隆的对象中对应属性也变化了");

    }
}

运行结果:

Student{name='zhangsan', sex='男', teacher=Teacher{name='laoshi', sex='男'}}
Student{name='zhangsan', sex='男', teacher=Teacher{name='laoshi', sex='男'}}
false
b为a克隆的一个对象,两者引用不相等
对克隆的的值属性修改,a的性别属性修改为女
Student{name='zhangsan', sex='男', teacher=Teacher{name='laoshi', sex='男'}}
Student{name='zhangsan', sex='女', teacher=Teacher{name='laoshi', sex='男'}}
对克隆的的值属性修改,被克隆的对象中对应值属性无变化了
对a的引用类型成员变量teacher进行修改,将teacher的name属性由laoshi改为laosi
Student{name='zhangsan', sex='男', teacher=Teacher{name='laosi', sex='男'}}
Student{name='zhangsan', sex='女', teacher=Teacher{name='laosi', sex='男'}}
对被克隆的的对象进行属性修改,克隆的对象中对应属性也变化了
对克隆的的对象进行属性修改,将teacher的name属性由改为laowu"
Student{name='zhangsan', sex='男', teacher=Teacher{name='laowu', sex='男'}}
Student{name='zhangsan', sex='女', teacher=Teacher{name='laowu', sex='男'}}
对克隆的的对象进行属性修改,被克隆的对象中对应属性也变化了

可以看出:对象只复制了它本身和其中包含的值类型的成员变量,修改克隆对象的值属性,不会影响被克隆对象属性的值,但修改引用会影响被克隆的引用属性。

扫描二维码关注公众号,回复: 10847007 查看本文章

深克隆demo

关键点:Student类,实现Cloneable接口 重写clone方法,引用属性teacher类也需要实现Cloneable接口。

如:Student的重写clone方法:

    @Override
    public Object clone() throws CloneNotSupportedException {
        Student student = (Student)super.clone();
        student.setTeacher((Teacher)student.getTeacher().clone());
        return student;
    }

引用属性teacher类需要实现Cloneable接口。

测试类:

public class DeepCloneTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Teacher teacher = new Teacher("laoshi", "男");
        Student a = new Student("zhangsan", "男", teacher);
        Student b = (Student) a.clone();
        System.out.println(a);
        System.out.println(b);
        System.out.println(a.equals(b));
        System.out.println("b为a克隆的一个对象,两者引用不相等");

        System.out.println("对克隆的的值属性修改,a的性别属性修改为女");
        b.setSex("女");
        System.out.println(a);
        System.out.println(b);
        System.out.println("对克隆的的值属性修改,被克隆的对象中对应值属性未变化");

        // 对a的引用类型成员变量teacher进行修改,将teacher的name属性由laoshi改为laosi
        System.out.println("对a的引用类型成员变量teacher进行修改,将teacher的name属性由laoshi改为laosi");
        a.getTeacher().setName("laosi");
        System.out.println(a);
        System.out.println(b);
        System.out.println("对被克隆的的对象进行属性修改,克隆的对象中对应属性未变化");

        // 对b的引用类型成员变量teacher进行修改,将teacher的name属性由laoshi改为laosi
        System.out.println("对克隆的的对象进行属性修改,将teacher的name属性由改为laowu\"");
        b.getTeacher().setName("laowu");
        System.out.println(a);
        System.out.println(b);
        System.out.println("对克隆的的对象进行属性修改,被克隆的对象中对应属性未变化");

    }
}

运行结果:

Student{name='zhangsan', sex='男', teacher=Teacher{name='laoshi', sex='男'}}
Student{name='zhangsan', sex='男', teacher=Teacher{name='laoshi', sex='男'}}
false
b为a克隆的一个对象,两者引用不相等
对克隆的的值属性修改,a的性别属性修改为女
Student{name='zhangsan', sex='男', teacher=Teacher{name='laoshi', sex='男'}}
Student{name='zhangsan', sex='女', teacher=Teacher{name='laoshi', sex='男'}}
对克隆的的值属性修改,被克隆的对象中对应值属性未变化
对a的引用类型成员变量teacher进行修改,将teacher的name属性由laoshi改为laosi
Student{name='zhangsan', sex='男', teacher=Teacher{name='laosi', sex='男'}}
Student{name='zhangsan', sex='女', teacher=Teacher{name='laoshi', sex='男'}}
对被克隆的的对象进行属性修改,克隆的对象中对应属性未变化
对克隆的的对象进行属性修改,将teacher的name属性由改为laowu"
Student{name='zhangsan', sex='男', teacher=Teacher{name='laosi', sex='男'}}
Student{name='zhangsan', sex='女', teacher=Teacher{name='laowu', sex='男'}}
对克隆的的对象进行属性修改,被克隆的对象中对应属性未变化

可以看到手动实现clone方法对引用对象进行克隆,如果引用对象还有引用类型属性,那么只能继续低引用对象进行clone实现。

那么有没有更好的方法实现深度克隆呢?

当然有,使用序列化,之需要将Student和Teacher类都实现Serializable接口,Student重写clone方法。

如下:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class Student implements Serializable {
    String name;
    String sex;
    Teacher teacher;

    public Student(String name, String sex, Teacher teacher) {
        this.name = name;
        this.sex = sex;
        this.teacher = teacher;
    }

    public String getName() {
        return name;
    }

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

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        Student student = null;
        try{
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(this);
            // 将流序列化成对象
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            student = (Student) ois.readObject();
        }catch (Exception e){
            System.out.println(e.toString());
        }
        return student;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", teacher=" + teacher +
                '}';
    }
}


import java.io.Serializable;

/**
 * 教师类
 */
public class Teacher implements Serializable {
    String name;
    String sex;

    public String getName() {
        return name;
    }

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


    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

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

    public Teacher(String name, String sex) {
        this.name = name;
        this.sex = sex;
    }

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

使用Serializable接口序列化,引用对象也必须实现Serializable接口。

文末思考

除了使用Serializable接口序列化实现深克隆,还有其他方法吗?

提示:使用使用Json。

微信扫码关注更精彩
发布了142 篇原创文章 · 获赞 258 · 访问量 16万+

猜你喜欢

转载自blog.csdn.net/conconbenben/article/details/103133982