📜  通过示例了解Java中的对象克隆

📅  最后修改于: 2022-05-13 01:54:28.959000             🧑  作者: Mango

通过示例了解Java中的对象克隆

在这篇文章中,我们讨论了Cloneable 接口,它表明一个类已经提供了一个安全的clone() 方法。要了解克隆的含义,请回想一下当您复制包含对象引用的变量时会发生什么。原件和副本是对同一对象的引用。这意味着原件和副本是相互依赖的,即一个的变化也会导致另一个的变化。
如果我们希望一个副本成为一个新对象,它的生命开始时与原始对象相同,但其状态会随着时间而改变,我们必须使用clone() 方法
clone() 方法Object 类中声明为protected ,因此我们的代码不能简单地调用obj.clone() 。现在我们可能会问,但不是任何子类都可以访问受保护的方法,而且不是每个类都是Object的子类。幸运的是,受保护访问的规则要微妙得多。子类可以调用受保护的clone() 方法来克隆自己的对象。我们必须将克隆重新定义为公开的,以供任何方法访问。
即使clone的默认实现已经足够,你仍然需要实现Cloneable接口,将clone() 方法重新定义为public ,并调用public
例子:

Java
class Student implements Cloneable {
 
    // raise visibility level to public
    // and change the return type
    public Student clone()
        throws CloneNotSupportedException
    {
        return (Student)super.clone();
    }
    .
        .
        .
}


Java
class Student implements Cloneable {
 
    // other components
 
    public Student clone()
        throws CloneNotSupportedException
    {
 
        // call Object.clone()
        Student obj = (Student)super.clone();
 
        // clone mutable fields
        obj.birthDay = (Date)birthDay.clone();
    }
}


Java
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;
        hireDay = new Date();
    }
 
    public Employee clone()
        throws CloneNotSupportedException
    {
 
        // call Object.clone()
        Employee obj = (Employee)super.clone();
 
        // clone mutable fields
        obj.hireDay = (Date)hireDay.clone();
 
        return obj;
    }
 
    // Set the hireDay to a given date
    public void setHireDay(int year, int month, int day)
    {
 
        Date newHireDay
            = new GregorianCalendar(year,
                                    month - 1,
                                    day)
                  .getTime();
 
        // instance field mutation
        hireDay.setTime(newHireDay.getTime());
    }
 
    public void raiseSalary(double byPercent)
    {
 
        double raise = salary * byPercent / 100;
        salary += raise;
    }
 
    public String toString()
    {
        return ("Employee[name=" + name
                + ", salary=" + salary
                + ", hireDay=" + hireDay
                + "]");
    }
 
    // main
    public static void main(String[] args)
    {
 
        try {
 
            Employee original
                = new Employee("ABC X. YZ", 50000);
 
            original.setHireDay(2000, 1, 1);
            Employee copy = original.clone();
 
            copy.raiseSalary(10);
            copy.setHireDay(2002, 12, 31);
 
            System.out.println("original= " + original);
            System.out.println("copy= " + copy);
        }
 
        catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}


您刚刚看到的clone() 方法没有向Object.clone提供的副本添加任何功能。它只是将方法公开并更改其返回类型。要进行更深的复制,我们必须克隆可变实例字段。
这是修改后的版本

Java

class Student implements Cloneable {
 
    // other components
 
    public Student clone()
        throws CloneNotSupportedException
    {
 
        // call Object.clone()
        Student obj = (Student)super.clone();
 
        // clone mutable fields
        obj.birthDay = (Date)birthDay.clone();
    }
}

每当在未实现Cloneable接口的类上调用clone时, Objectclone() 方法将尝试抛出ClassNotSupportedException
例子:

Java

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;
        hireDay = new Date();
    }
 
    public Employee clone()
        throws CloneNotSupportedException
    {
 
        // call Object.clone()
        Employee obj = (Employee)super.clone();
 
        // clone mutable fields
        obj.hireDay = (Date)hireDay.clone();
 
        return obj;
    }
 
    // Set the hireDay to a given date
    public void setHireDay(int year, int month, int day)
    {
 
        Date newHireDay
            = new GregorianCalendar(year,
                                    month - 1,
                                    day)
                  .getTime();
 
        // instance field mutation
        hireDay.setTime(newHireDay.getTime());
    }
 
    public void raiseSalary(double byPercent)
    {
 
        double raise = salary * byPercent / 100;
        salary += raise;
    }
 
    public String toString()
    {
        return ("Employee[name=" + name
                + ", salary=" + salary
                + ", hireDay=" + hireDay
                + "]");
    }
 
    // main
    public static void main(String[] args)
    {
 
        try {
 
            Employee original
                = new Employee("ABC X. YZ", 50000);
 
            original.setHireDay(2000, 1, 1);
            Employee copy = original.clone();
 
            copy.raiseSalary(10);
            copy.setHireDay(2002, 12, 31);
 
            System.out.println("original= " + original);
            System.out.println("copy= " + copy);
        }
 
        catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}
输出:
原始=员工[姓名=ABC X. YZ,薪水=50000.0,hireDay=2000 年 1 月 1 日星期六 00:00:00 UTC]
复制=员工[姓名=ABC X. YZ,薪水=55000.0,hireDay=2002 年 12 月 31 日星期二 00:00:00 UTC]

克隆的优点:

  • 在Java中,'='(赋值)运算符不能用于克隆,因为它只是创建引用变量的副本。为了克服这种差异,可以在赋值运算符上使用Object 类clone() 方法
  • clone() 方法Object类的受保护方法,这意味着只有 Employee 类可以克隆 Employee 对象。这意味着除了 Employee 之外的任何类都不能克隆 Employee 对象,因为它不知道 Employee 类的属性。

克隆在Java中的应用

  • 它允许逐字段复制对象,这在处理具有相似特征的对象时非常方便。
  • 可以通过在可变子对象上调用clone来修补默认的clone() 方法