首先,区分一下拷贝和克隆:
拷贝:当拷贝一个变量时,原始变量与拷贝变量引用的是同一个对象。当改变一个变量所引用的对象,则会对另一个变量造成影响。
克隆:当克隆一个对象时,是重新的创建了和该对象内容相同的对象。
clone方法是Object类受保护(preteced)方法,用户编写的代码不能直接调用。只有相同的类才能克隆其本身。
问题:
如果待克隆的对象中的所有数据域都是数值或基本类型,这样的克隆没有问题。
但是,如果在对象中包含了子对象的引用,拷贝的结果就会使得两个域引用同一个子对象,此时,原始对象和克隆对象将共享这一部分信息。
这样,当克隆对象改变这部分时,就会造成原始对象中数据的改变。
默认的克隆操作都是浅拷贝,它并没有克隆包含在对象中的内部对象。
进行浅拷贝会发生什么?
如果原始对象与浅克隆对象共享的子对象是不可变的,这样就不会产生问题。
但是,更多的情况是子对象是可变的。这样,就必须重写clone方法,以实现克隆子对象的深度拷贝。
对于待克隆的对象,需要作出一些判断:
(1)默认的克隆方法是否能够满足要求;
(2)默认的克隆方法是能够通过调用可变子对象的clone方法进行修补;
(3)是否不应该使用clone。
如果选1或2,类必须:
实现Cloneable接口,并使用public访问修饰符重新定义clone方法。
下面举例说明克隆机制:
import java.util.Date; import java.util.GregorianCalendar; public class TestClone { public static void main(String[] args) { try { /**原始对象**/ Employee origin = new Employee("Tom", 10000); origin.setHireDay(2014, 10, 10); /**克隆Employee对象**/ Employee copy = origin.clone(); copy.raiseSalary(10); copy.setHireDay(2015, 11, 11); System.err.println(origin); /**Tom,10000.0,Mon Nov 10 00:00:00 CST 2014**/ System.err.println(copy); /**Tom,11000.0,Fri Dec 11 00:00:00 CST 2015**/ /** * 可以看出数据域属于数值或基本类型时,克隆不会对其值造成影响, * 而类似Date这样的对象在进行克隆时就需要特别注意,必须进行深度克隆,将子对象也进行克隆 */ } catch (CloneNotSupportedException e) { e.printStackTrace(); } } } class Employee implements Cloneable { private String name; private double salary; private Date hireDay; public Employee(String n, double s) { this.name = n; this.salary = s; this.hireDay = new Date(); } public Employee clone() throws CloneNotSupportedException { /** 调用Object的clone方法 **/ Employee cloned = (Employee) super.clone(); /** 克隆可变的域对象 对可变的子对象进行克隆 **/ cloned.hireDay = (Date) hireDay.clone(); /**如果注释掉上面hireDay的克隆,最终将会得到如下结果,原始对象和克隆对象都指向同一个对象**/ /**Tom,10000.0,Fri Dec 11 00:00:00 CST 2015**/ /**Tom,11000.0,Fri Dec 11 00:00:00 CST 2015**/ return cloned; } public void setHireDay(int year, int month, int day) { Date newHireday = new GregorianCalendar(year, month, day).getTime(); hireDay.setTime(newHireday.getTime()); } public void raiseSalary(double byPrecent){ double raise = salary * byPrecent / 100; salary += raise; } /**重写Object类的toString方法**/ public String toString(){ return new StringBuffer().append(name).append(",").append(salary).append(",") .append(hireDay).toString(); } }