深刻理解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对象在浅克隆中也表现得与基本属性同样。

相关文章
相关标签/搜索