深刻理解Java的淺克隆與深克隆

前言ide

克隆,即複製一個對象,該對象的屬性與被複制的對象一致,若是不使用Object類中的clone方法實現克隆,能夠本身new出一個對象,並對相應的屬性進行數據,這樣也能實現克隆的目的。測試

但當對象屬性較多時,這樣的克隆方式會比較麻煩,因此Object類中實現了clone方法,用於克隆對象。this

Java中的克隆分爲淺克隆與深克隆spa

1、實現克隆的方式code

1.對象的類須要實現Cloneable接口對象

2.重寫Object類中的clone()方法blog

3.根據重寫的clone()方法獲得想要的克隆結果,例如淺克隆與深克隆。接口

2、淺克隆與深克隆的區別內存

淺克隆:複製對象時僅僅複製對象自己,包括基本屬性,但該對象的屬性引用其餘對象時,該引用對象不會被複制,即拷貝出來的對象與被拷貝出來的對象中的屬性引用的對象是同一個。get

深克隆:複製對象自己的同時,也複製對象包含的引用指向的對象,即修改被克隆對象的任何屬性都不會影響到克隆出來的對象。

 

例子以下:

class Person implements Cloneable{

    private int age;
    private String name;

    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

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

    @Override
    protected Person clone() throws CloneNotSupportedException {  
        return (Person)super.clone();   //調用父類的clone方法
    }
}

測試代碼:

public class CloneTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person = new Person(22,"LiLei");
        Person newPerson = person.clone();
        person.setAge(21);
        person.setName("HanMeimei");
        System.out.println(person.toString());
        System.out.println(newPerson.toString());
    }
}

測試結果:

Person{age=21, name='HanMeimei'}
Person{age=22, name='LiLei'}

即在克隆出新的對象後,修改被克隆對象的基本屬性,並不會影響克隆出來的對象。但當被克隆的對象的屬性引用其餘對象時,此時會有不一樣的結果。

例子以下

/**
 * 學生類
 */
class Student implements Cloneable{
    private String name;
    private Achievement achievement; //成績

    public Student(String name, Achievement achievement) {
        this.name = name;
        this.achievement = achievement;
    }

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

    public void setAchievement(Achievement achievement) {
        this.achievement = achievement;
    }

    public Achievement getAchievement() {
        return achievement;
    }

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

    @Override
    protected Student clone() throws CloneNotSupportedException {
       return (Student) super.clone(); 
    }
}
    
/**
 * 成績類
 */
class Achievement implements Cloneable{
    private float Chinese;
    private float math;
    private float English;

    public Achievement(float chinese, float math, float english) {
        Chinese = chinese;
        this.math = math;
        English = english;
    }

    public void setChinese(float chinese) {
        Chinese = chinese;
    }

    public void setMath(float math) {
        this.math = math;
    }

    public void setEnglish(float english) {
        English = english;
    }

    @Override
    public String toString() {
        return "Achievement{" +
                "Chinese=" + Chinese +
                ", math=" + math +
                ", English=" + English +
                '}';
    }

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

測試代碼:

public class CloneTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Achievement achievement = new Achievement(100,100,100);
        Student student = new Student("LiLei",achievement);
        // 克隆出一個對象
        Student newStudent = student.clone();

        // 修改原有對象的屬性
        student.setName("HanMeimei");
        student.getAchievement().setChinese(90);
        student.getAchievement().setEnglish(90);
        student.getAchievement().setMath(90);

        System.out.println(newStudent);
        System.out.println(student);

    }
}

測試結果:

Student{name='LiLei', achievement=Achievement{Chinese=90.0, math=90.0, English=90.0}}
Student{name='HanMeimei', achievement=Achievement{Chinese=90.0, math=90.0, English=90.0}}

以上現象代表,上述克隆方式爲淺克隆,並不會克隆對象的屬性引用的對象,當修改被克隆對象的成績時,克隆出來的對象也會跟着改變,即兩個對象的屬性引用指向的是同一個對象。

但只要修改一下Student類中重寫的clone()方法,便可實現深克隆。

修改代碼以下:

@Override
    protected Student clone() throws CloneNotSupportedException {
        Student student =  (Student) super.clone();
        Achievement achievement = student.getAchievement().clone();
        student.setAchievement(achievement);
        return student;
    }

 測試結果:

Student{name='LiLei', achievement=Achievement{Chinese=100.0, math=100.0, English=100.0}}
Student{name='HanMeimei', achievement=Achievement{Chinese=90.0, math=90.0, English=90.0}}

 即在Student類中的clone()方法中再克隆一次Achievement對象,並賦值給Student對象。

值得一提的是,上文所說的淺拷貝只會克隆基本數據屬性,而不會克隆引用其餘對象的屬性,但String對象又不屬於基本屬性,這又是爲何呢?

這是由於String對象是不可修改的對象,每次修改其實都是新建一個新的對象,而不是在原有的對象上修改,因此當修改String屬性時實際上是新開闢一個空間存儲String對象,並把引用指向該內存,而克隆出來的對象的String屬性仍是指向原有的內存地址,因此String對象在淺克隆中也表現得與基本屬性同樣。

相關文章
相關標籤/搜索