工学1号馆

home

java核心系列12-对象克隆

By Wu Yudong on June 13, 2015

原创文章,转载请注明: 转载自工学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

No comments yet.
To verify that you are human, please fill in "七"(required)