先来看一下为一个包含对象引用的变量建立副本时会发生什么。原变量和副本都是同一个对象的引用,如图,这说明,任何一个变量改变都会影响另一个变量。
Employee original = new Employee("John Public", 50000)
Employee copy = original;
copy.raiseSalary(10); // oops--also changed original
如果希望copy是一个新对象,它的初始状态与original相同,但是之后它们各自会有自己不同的状态,这种情况下就可以使用clone方法。
Employee copy = origin.clone();
copy.raiseSalary(10); // ok -- original unchanged
实例:
以下程序克隆了Employee类的一个实例,然后调用两个更改器方法。raiseSalary方法会改变salary域的值,而setHireDay方法改变hireDay域的状态。这两个更改器方法都不会影响原来的对象,因为clone是一个深拷贝。
Employee:
import java.util.Date;
import java.util.GregorianCalendar;
public class Employee07 implements Cloneable{
// cloneable : 标记接口
private String name;
private double salary;
private Date hireDay;
public Employee07(String name, double salary) {
this.name = name;
this.salary = salary;
hireDay = new Date();
}
public Employee07 clone() throws CloneNotSupportedException {
// call object.clone()
Employee07 cloned = (Employee07) super.clone();
// clone mutable fields
cloned.hireDay = (Date) hireDay.clone();
return cloned;
}
/**
* set the hire day to a given date
* @param year the year of the hire day
* @param month the month of the hire day
* @param day the day of the hire day
*/
public void setHireDay(int year, int month, int day) {
Date newHireDay = new GregorianCalendar(year, month-1,day).getTime();
//Example of 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 + "]";
}
}
CloneTest:
public class CloneTest {
public static void main(String[] args) {
try {
Employee07 original = new Employee07("John. public",50000);
original.setHireDay(2000,1,1);
Employee07 copy = original.clone();
copy.raiseSalary(10);
copy.setHireDay(2020,6,1);
System.out.println("original="+original);
System.out.println("copy="+copy);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}