Java 对象克隆

摘要:Java最大的简洁性在于去除了指针,以引用来代替,在函数传参过程中,一般分为值传递引用传递,值传递是直接拷贝一份值单元传递过去,原有值不会随被传递值改变而改变;引用传递则传递的是该引用数据类型变量的内存地址,因此原有对象属性值会随着传递引用变量对其的改变而达到间接改变的目的。Java中对所有复杂数据类型包括数组的传递都是引用传递方式,即传其地址,那么如何使Java具有像C和C++那样传递复杂数据类型,而不改变原有值的属性,这就需要利用到Java对象克隆知识了。

String 传递

首先声明一点,虽然String也属于引用数据类型,但是它属于Java语言当中设计的特殊类型,具有值不可更改的特点,因此在进行参数传递时,不具备引用传递的特点,而是当成值传递方式来代替。

浅克隆

所有的对象都具有拷贝(克隆)的功能,因为Object类中就定义了clone方法。一个类需要被克隆,则该类必须明确继承Cloneable接口,该接口只是其标识作用,另外还必须在该类中重写Object类提供的clone方法,使之支持对象克隆。

Java对象一般性的克隆,属于浅克隆。浅克隆是指只克隆当前对象的副本,而不管该对象内部成员对象是否需要拷贝副本,因此成员对象还是使用原来的引用指向,所以一旦改变克隆对象的成员对象的值,原有值的成员对象属性变量的值也会发生变化。

深克隆

如果需要彻底解决这种弊端,则需要一次性克隆到底,使用深克隆的方式。不但对该对象拷贝副本,对该对象成员对象也进行拷贝,甚至逐级拷贝下去,需要注意的是,这种方式下,成员对象所在类必须支持克隆属性,重写clone方法。

示例程序

public class TestObjClone{
  	public static void main(String[] args) {
  		Person per = new Person("zhangsan", 20);
  		Animal a = new Animal("xiaohei", 8, per);
  		Animal b = null;

  		System.out.println("before clone: ");
  		System.out.println("\tAnimal b is null and Animal a is: \n" + a);

  		b = (Animal)a.clone();
  		b.getMaster().setName("wangwu");
  		b.getMaster().setAge(50);

  		System.out.println("after clone: ");
  		System.out.println("\tAnimal b master has been changed and Animal a is: \n" + a);
 	}
}

class Animal implements Cloneable{
 	private String name;
 	private int age;
 	private Person master;

 	public Animal(){
 	}

 	public Animal(String name, int age, Person master){
  		this.name = name;
  		this.age = age;
  		this.master = master;
 	}

 	public String getName() {
     	return this.name;
	}

 	public int getAge() {
     	return this.age;
 	}

 	public Person getMaster(){
  		return this.master;
 	}

 	public void setName(String name) {
     	this.name = name;
 	}

 	public void setAge(int age) {
     	this.age = age;
 	}

 	public void setMaster(Person master){
  		this.master = master;
 	}

 	public String toString(){
  		return "Animal(name = " + this.getName() + ", age = " + this.getAge() 
  		+ ", master = \n"+ this.getMaster() +")";
 	}

 	public Object clone(){
  		Animal obj = null;
  		try{
  	 		obj = (Animal)super.clone();
   			obj.master = (Person)master.clone();
  		}catch(CloneNotSupportedException e){
  		}
  		return obj;
 	}
}

class Person implements Cloneable{
 	private String name;
 	private int age;

 	public Person(){
 	}

 	public Person(String name, int age){
  		this.name = name;
  		this.age = age;
 	}

 	public String getName() {
     	return this.name;
 	}

 	public int getAge() {
     	return this.age;
 	}

 	public void setName(String name) {
     	this.name = name;
 	}

 	public void setAge(int age) {
     	this.age = age;
 	}

 	public String toString(){
  		return "Per(name = " + this.getName() + ", age = " + this.getAge() + ")";
 	}

 	public Object clone(){
  		Person obj = null;
  		try{
   			obj = (Person)super.clone();
  		}catch(CloneNotSupportedException e){
  	}
 	return obj;
 	}
}

程序输出如下内容:

before clone: Animal b is null and Animal a is: Animal(name = xiaohei, age = 8, master = Per(name = zhangsan, age = 20)) after clone: Animal b master has been changed and Animal a is: Animal(name = xiaohei, age = 8, master = Per(name = zhangsan, age = 20))

以上输出说明进行深克隆后,原有对象中成员对象属性值不会再发生改变。要想测试浅克隆只需将Person类的克隆属性去掉,即取消Cloneable接口和clone方法,另外还需注释掉Animal类clone方法里的master成员对象的克隆,即为浅克隆方式,此种方式下,原有对象的成员对象属性值也会发生改变。