通过示例了解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时, Object的clone() 方法将尝试抛出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() 方法。