天天看点

java:深拷贝与浅拷贝

拷贝的实现:

只有子类实现了Cloneable接口后才可以使用Object类提供的clone方法。

protected native Object clone() throws CloneNotSupportedException;

Cloneable也是一个标识接口:

public interface Cloneable { }

要想让对象具有拷贝的功能,必须实现Cloneable接口,并且在类中自定义clone() 调用Object类提供的继承权限clone方法。

浅拷贝:浅拷贝就是对对象的值(对于基本数据类型而言,值就是内容,对于引用类型,值就是它所指向的地址)进行拷贝。

浅拷贝存在的的问题:牵一发而动全身

只要任何一个拷贝对象(或原对象)中的引用发生了变化,所有的对象都会受到影响

直接来看个例子:

class Teacher{
    private String name;
    private String job;
    public Teacher(String name, String job) {
        this.name = name;
        this.job = job;
    }
}
// 1,实现Cloneable接口
class Student implements Cloneable{
    private String name;
    private int age;
    private Teacher teacher;
    public Student(String name, int age, Teacher teacher) {
        this.name = name;
        this.age = age;
        this.teacher = teacher;
    }
    // 2, 自己定义clone()
    public Student clone(){
        Student student = null;
        try {
            // 实现拷贝处理
            // 产生一个新的student对象,并且拷贝原有所有属性值
            student = (Student) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return student;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Teacher getTeacher() {
        return teacher;
    }
    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }
}
    public static void main(String[] args) {
        Teacher teacher = new Teacher("张三百","Teacher");
        Student student = new Student("王五",10,teacher);
        Student studentClone = student.clone();
        System.out.println(student);
        System.out.println(studentClone); // 两个地址不同,有拷贝了新的对象
        System.out.println("-------------------------------------");
        System.out.println(studentClone.getAge());
        System.out.println(student.getAge());
        System.out.println(studentClone.getName());
        System.out.println(student.getName()); // 属性都相同,拷贝到了结果
        System.out.println("---------------------------------------");
        // 结果为true,拷贝对象和原对象共同引用teacher
        System.out.println(teacher == studentClone.getTeacher());
    }
}
           
java:深拷贝与浅拷贝

从运行结果我们可以看到我们确实拷贝到了student对象的克隆对象studentClone,而且两者的所有属性都相同,并且也指向了同一个对象teacher。如下图。

java:深拷贝与浅拷贝

深拷贝:理解了浅拷贝就不难理解深拷贝了,深拷贝拷贝出来的对象产生了所有引用的新对象。

特点:任何对象的改变不会对其他对象产生影响

深拷贝的实现有两种方式:

  1. 递归实现深拷贝:如果要拷贝的对象中包含对其他对象的引用,则让那个被引用的对象所在的类也实现Cloneable接口,和clone方法。
  2. 序列化实现深拷贝:使用序列化实现深拷贝时,无需再实现Clonable接口,只需要实现Serializable接口即可,但是clone()还是要写的。

    对序列化有疑问的,可以看java对象的序列化与反序列化

    看两个例子:

    递归实现深拷贝:

// Student引用了Teacher,Teacher也需要序列化
class Teacher implements Cloneable{
    private String name;
    private String job;
    public Teacher clone(){
        Teacher teacher = null;
        try {
            teacher = (Teacher) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return teacher;
    }
    public Teacher(String name, String job) {
        this.name = name;
        this.job = job;
    }
}
class Student implements Cloneable{
    private String name;
    private int age;
    private Teacher teacher;
    public Student(String name, int age, Teacher teacher) {
        this.name = name;
        this.age = age;
        this.teacher = teacher;
    }
    public Student clone(){
        Student student = null;
        try {
            student = (Student) super.clone();
            // 产生新的引用
            student.teacher = this.teacher.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return student;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Teacher getTeacher() {
        return teacher;
    }
    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }
}
public class Test {
    public static void main(String[] args) {
        Teacher teacher = new Teacher("张三百","Teacher");
        Student student = new Student("王五",10,teacher);
        Student studentClone = student.clone();
        System.out.println(student);
        System.out.println(studentClone);
        System.out.println("-------------------------------------");
        System.out.println(studentClone.getAge());
        System.out.println(student.getAge());
        System.out.println(studentClone.getName());
        System.out.println(student.getName());
        System.out.println("---------------------------------------");
        System.out.println(teacher == studentClone.getTeacher());
    }
}
           
java:深拷贝与浅拷贝

从结果可以看到,拷贝对象与原对象确实对teacher的引用不同。

java:深拷贝与浅拷贝

序列化实现深拷贝:

import java.io.*;
// 序列化实现深拷贝
class Teacher implements Serializable{
    private String name;
    private String job;
    public Teacher clone(){
        Teacher teacher = null;
        try {
            teacher = (Teacher) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return teacher;
    }
    public Teacher(String name, String job) {
        this.name = name;
        this.job = job;
    }
}
class Student implements Serializable{
    private String name;
    private int age;
    private Teacher teacher;
    public Student(String name, int age, Teacher teacher) {
        this.name = name;
        this.age = age;
        this.teacher = teacher;
    }
    public Student cloneObject() throws Exception{
        Student student = null;
        ByteOutputStream bos = new ByteOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);
        //参数是从内存中把数据读进来
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.getBytes());
        ObjectInputStream ois = new ObjectInputStream(bis);
        // 从内存中反序列化出来
        return (Student) ois.readObject();
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Teacher getTeacher() {
        return teacher;
    }
    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }
}
public class Test {
    public static void main(String[] args) throws Exception{
        Teacher teacher = new Teacher("张三百","Teacher");
        Student student = new Student("王五",10,teacher);
        Student studentClone = student.cloneObject();
        System.out.println(student);
        System.out.println(studentClone);
        System.out.println("-------------------------------------");
        System.out.println(studentClone.getAge());
        System.out.println(student.getAge());
        System.out.println(studentClone.getName());
        System.out.println(student.getName());
        System.out.println("---------------------------------------");
        System.out.println(teacher == studentClone.getTeacher());
    }
}
           

其实除了深浅拷贝,还有延迟拷贝,我们可以理解为延迟拷贝就是深拷贝+浅拷贝

演出拷贝在对象只需要读取时采用浅拷贝,在需要对值做修改时采用深拷贝。

继续阅读