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


前言

克隆,即復制一個對象,該對象的屬性與被復制的對象一致,如果不使用Object類中的clone方法實現克隆,可以自己new出一個對象,並對相應的屬性進行數據,這樣也能實現克隆的目的。

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

Java中的克隆分為淺克隆與深克隆

一、實現克隆的方式

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

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

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

二、淺克隆與深克隆的區別

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

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

 

例子如下:

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對象在淺克隆中也表現得與基本屬性一樣。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM