0.1) 本文描述+源代码均 转自 core java volume 1, 旨在理解 对象拷贝 的概念 , 特别是对 深拷贝和浅拷贝 的理解;
0.2) 最后,我们还要看一个 clone 的荔枝;
1.1)出现的问题: 当copy一个变量时, 原始变量和 copy 变量引用同一个对象, 也就是说, 改变一个变量所引用的对象将会 对另一个变量产生影响;
Employee original = new Employee("tang", 50000);
Employee copy = original;
copy.raiseSalary(10);//oops -- also changed original
1.2)解决方法:
Employee copy = original.clone();
copy.raiseSalary(10); // OK--original unchanged
2.1)深拷贝:如果原始对象 与 浅拷贝对象共享的子对象是不可变的, 将不会产生任何问题;然而,更常见的 是 子对象是可变的, 因此必须重新定义 clone 方法, 以便实现 克隆子对象的 深拷贝;
2.2)在列举的实例中, hireDay域 属于 Date类, 这就是一个可变的子对象;
2.3)对于每一个类, 都需要做如下判断:
2.4)实际上, 选项3是默认的, 如果要选择1或2, 类必须:
Attention)
2.5)即使clone 的默认实现(浅拷贝)能够满足需求, 也应该实现 Cloneable接口, 将 clone 重定义为 public, 并调用super.clone();
class Employee implements Cloneable
{
public Employee() throws CloneNotSupportedException
{
return (Employee)super.clone();
}
}
2.6)为了实现深拷贝, 必须克隆所有可变的实例域(Key):
class Employee implements Cloneable
{
public Employee() throws CloneNotSupportedException
{
Employee cloned = (Employee)super.clone();
cloned.hireDay = (Date)hireDay.clone();
}
}
2.7)只要在 clone 中含有没有实现 Cloneable接口的对象,Object类的 clone方法就会抛出一个CLoneNotSupportedException异常。所以,我们需要声明异常:
public Employee clone() throws CloneNotSupportedException
public Employee()
{
try
{
return (Employee)super.clone();
}
catch(CloneNotSupportedException e) {return null;}
}
Attention)以上写法比较适用于 final类,否则最好还是保留 throws的形式;
2.8)必须谨慎地实现子类的克隆
2.9)在自定义的类中应该实现 clone方法吗?
如果客户需要深拷贝,那就应该实现它。而且, 克隆的应用并不像人们想象的那样普遍, 在标准类库中, 只有不到 5%的类实现了 clone;
Annotation)所有的数组类型均包含clone方法, 这个方法被设为 public, 而不是 protected。 可以利用 这个 方法创建一个包含所有数据元素拷贝的一个新数组,如:
int[] luckyNumbers = {2, 3, 5, 7, 11, 13];
int[] cloned = luckyNumbers.clone();
cloned[5] = 12; // doesn't change luckyNumbers[5]
package com.corejava.chapter6_2;
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 n, double s)
{
name = n;
salary = s;
hireDay = new Date();
}
public Employee clone() throws CloneNotSupportedException
{
// call Object.clone()
Employee cloned = (Employee) 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 + "]";
}
}
package com.corejava.chapter6_2;
/** * This program demonstrates cloning. * @version 1.10 2002-07-01 * @author Cay Horstmann */
public class CloneTest {
public static void main(String[] args)
{
try
{
Employee original = new Employee("John Q. Public", 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();
}
}
}