【Java】Clonable接口(超强细节技术性,坐稳发车)

【Java】Clonable接口(超强细节技术性,坐稳发车)_第1张图片

引言

Java 中内置了一些很有用的接口, Clonable 就是其中之一。
本文会对Clonable接口进行讨论,这个接口指示一个类提供了一个安全的clone方法,调用这个方法可以创建一个对象的 “拷贝”。

一、 浅拷贝

clone方法Object的一个protected方法,这说明你的代码不能直接调用clone。只有…类可以克隆…对象。必须重新定义clone为public才能允许所有方法克隆对象

( •̀ ω •́ )✧这样子的限制是有原因的:
想想看Object如何实现clone。他对你要克隆的对象一无所知,所以只能逐个域的进行拷贝(让人家干活还不告诉人家怎么干?)。如果该对象中的所有数据域都是数值或其他基本类型,拷贝这些域没有任何问题。但如果对象包含子对象的引用,拷贝后就会得到相同子对象的另一个引用,这样一来原对象和克隆的对象仍然会共享信息,此时发生的就是浅拷贝而默认的克隆操作就是浅拷贝

但是即便clone的默认(浅拷贝)实现能够满足要求,还是需要实现Cloneable接口,将clone重新定义成public,再调用super.clone().

Eg:

class Employee implements Cloneable{
    public Employee clone() throws CloneNotSupportedException
    {
        return (Employee) super.clone();
    }
    ..........
}

与Object.clone提供的浅拷贝相比,上面的clone方法没有为他增加任何功能,只是让这个方法变成公开的。想要建立深拷贝还要做更多工作,克隆对象中的可变实例域。

注意:使用clone方法必须实现Cloneable接口,这个接口只是作为标记(叫做标记接口),具体来说,他没有指定clone方法,这个方法是从Object类继承的。但是如果一个对象请求克隆但是没有实现Cloneable接口,就会生成一个异常)

二、深拷贝

下面将对深拷贝的实现给出代码举例:

//main
package clone;

public class CloneTest {
    public static void main(String[] args) {
        try {
            Employee original = new Employee("Ethan",50000);
            original.setHireDay(2000,1,1);
            Employee copy=original.clone();
            copy.raiseSalary(10);
            copy.setHireDay(2022,6,3);
            System.out.println(original);
            System.out.println(copy);
        }
        catch (CloneNotSupportedException e){
            e.printStackTrace();
        }
    }
}
//Employee类
package clone;

import java.util.Date;
import java.util.GregorianCalendar;

public class Employee implements Cloneable{
    private String name;
    private double salary;
    private Date hireDay;

    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    @Override
    public Employee clone() throws CloneNotSupportedException {
        Employee cloned=(Employee) super.clone();
        cloned.hireDay=(Date) hireDay;
        return cloned;
    }

    public void setHireDay(int year,int month,int day){
        Date newHireDay = new GregorianCalendar(year,month-1,day).getTime();
        hireDay.setTime(newHireDay.getTime());
    }

    public void raiseSalary(double byPercent){
        double raise = salary * byPercent / 100;
        salary += raise;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", salary=" + salary +
                ", hireDay=" + hireDay +
                '}';
    }
}

上述程序克隆了Employee类的一个实例,然后调用两个更改器方法。raiseSalary方法会改变salary域的值,而setHireDay方法改变hireDay域的状态。这两个更改其方法都不会影响原来的对象,因为clone定义为建立一个深拷贝

下面我们将对程序的关键步骤运行时内存发生的事情进行图解,提便于更好的理解clone:

Employee copy=original.clone();

【Java】Clonable接口(超强细节技术性,坐稳发车)_第2张图片

copy.raiseSalary(10);

【Java】Clonable接口(超强细节技术性,坐稳发车)_第3张图片

copy.setHireDay(2022,6,3);

【Java】Clonable接口(超强细节技术性,坐稳发车)_第4张图片

你可能感兴趣的:(Java,java,开发语言,jvm)