天天看點

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

其實除了深淺拷貝,還有延遲拷貝,我們可以了解為延遲拷貝就是深拷貝+淺拷貝

演出拷貝在對象隻需要讀取時采用淺拷貝,在需要對值做修改時采用深拷貝。

繼續閱讀