原创文章,转载请注明: 转载自工学1号馆
在这篇文章将系统的剖析java中的深拷贝与浅拷贝相关的原理和技巧
当拷贝一个变量的时候,原始变量和拷贝变量引用同一个对象,即改变一个变量所引用的对象将会对另一个变量产生影响。例如:
Employee original = new Employee("wuyudong", 50000); Employee copy = original; copy.raiseSalary(1000); //original对象也发生了改变
要实现不改变原始对象的目的,需要使用clone方法
Employee copy = original.clone(); copy.raiseSalary(50000); //OK
但是问题并没有如此的简单,首先来看一下Employee类的代码:
class Employee { private String name; private double salary; private Date hireDay; public Employee(String n, double s, int year, int month, int day) { name = n; salary = s; GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day); hireDay = calendar.getTime(); } //... }
Employee类初始化对象后,对象中既包含salary这样的int基础类型,还包含像Date、String这样的内部对象。默认的克隆操作是使用率Object类的clone方法克隆Employee对象,称为浅拷贝。
对于原始对象和克隆对象而言,像String这样不可变的内部类而言,就不存在问题了,然而更多新情况是像Date这样的类型,就必须重新定义clone方法,以便实现克隆对象的深拷贝。
接下来介绍一种名为Cloneable的接口,其实它跟通常所说的接口没有太大的关系,这里只是起到一个标记的作用,表明类的设计者知道要进行克隆处理,如果一个对象需要克隆,但是没有实现Cloneable接口,将会产生CloneNotSupportedException异常。
即使使用clone实行浅拷贝能够满足要求也要实现Cloneable接口,将clone重定义为public,并调用super.clone()
class Employee implements Cloneable{ public Employee clone() throws CloneNotSupportedException { return (Employee)super.clone(); } //.......... }
上面的代码只是从形式上阐明了代码的大体框架,但是本质功能还是浅拷贝,为了实现深拷贝,必须克隆所有可变的实例域。看下面的代码:
class Employee implements Cloneable{ public Employee clone() throws CloneNotSupportedException { Employee cloned = (Employee)super.clone(); //调用Object.clone() cloned.hireDay = (Date)hireDay.clone(); //克隆可变域 return cloned; } //......... }
自定义类中如果需要实现深拷贝,就必须实现clone方法:
import java.util.Date; import java.util.GregorianCalendar; public class test { public static void main(String[] args) { try { Employee original = new Employee("wuyudong", 10000); original.setHireDay(1000, 1, 1); Employee copy = original.clone(); copy.raiseSalary(10); //深拷贝可变对象 copy.setHireDay(2015, 12, 31); //深拷贝可变对象 System.out.println("original=" + original); System.out.println("copy=" + copy); } catch(CloneNotSupportedException e) { e.printStackTrace(); } } } 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 { Employee cloned = (Employee)super.clone(); //调用Object.clone() cloned.hireDay = (Date)hireDay.clone(); //克隆可变域 return cloned; } public void setHireDay(int year, int month, int day) { Date newHireDay = new GregorianCalendar(year, month - 1, day).getTime(); 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 + "]"; } }
运行结果如下:
original=Employee[name=wuyudong,salary=10000.0,hireDay=Mon Jan 01 00:00:00 GMT+08:00 1000]
copy=Employee[name=wuyudong,salary=11000.0,hireDay=Thu Dec 31 00:00:00 GMT+08:00 2015]
Comments