编程过程中常常遇到如下情况: 假设有一个对象obj1,在某处需要和obj1一样的实例obj2,强调obj1和obj2是两个独立的实例,只是在开始的时候,它们具有一样的属性。这种情况下,一般的一种解决方法是:重新new一个对象obj2,然后将obj1的属性字段值依次赋予obj2。该种方法可行,但是也比较土。java提供了clone方法,使用clone方法,我们可以高效地解决上述的问题。
在理解clone方法前,有必要先了解下浅拷贝(shallow copy)和深拷贝(deep copy)。先看一个与浅拷贝相关的代码段:
- public class FamilyInfo {
- public String address;
- public int memberNum;
- public FamilyInfo(String address, int memberNum) {
- super();
- this.address = address;
- this.memberNum = memberNum;
- }
- }
public class FamilyInfo {
public String address;
public int memberNum;
public FamilyInfo(String address, int memberNum) {
super();
this.address = address;
this.memberNum = memberNum;
}
}
- public class Employee {
- public String name;
- public FamilyInfo familyInfo;
- public Employee(String name, FamilyInfo familyInfo) {
- super();
- this.name = name;
- this.familyInfo = familyInfo;
- }
- }
public class Employee {
public String name;
public FamilyInfo familyInfo;
public Employee(String name, FamilyInfo familyInfo) {
super();
this.name = name;
this.familyInfo = familyInfo;
}
}
- public class CloneTest {
- /**
- * @param args
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- FamilyInfo familyInfoA=new FamilyInfo("No.1100 Lianhang Rd.",2);
- Employee employeeA=new Employee("Lily",familyInfoA);
- System.out.println("employeeA's address "+employeeA.familyInfo.address);
- Employee employeeB=employeeA;
- System.out.println("employeeB's address "+employeeB.familyInfo.address);
- System.out.println("---------------------");
- employeeA.familyInfo.address="No.1588 Pulian Rd.";
- System.out.println("employeeA's address "+employeeA.familyInfo.address);
- System.out.println("employeeB's address "+employeeB.familyInfo.address);
- }
- }
public class CloneTest {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
FamilyInfo familyInfoA=new FamilyInfo("No.1100 Lianhang Rd.",2);
Employee employeeA=new Employee("Lily",familyInfoA);
System.out.println("employeeA's address "+employeeA.familyInfo.address);
Employee employeeB=employeeA;
System.out.println("employeeB's address "+employeeB.familyInfo.address);
System.out.println("---------------------");
employeeA.familyInfo.address="No.1588 Pulian Rd.";
System.out.println("employeeA's address "+employeeA.familyInfo.address);
System.out.println("employeeB's address "+employeeB.familyInfo.address);
}
}
我们可以看到,随着employeeA对象 familyInfo值的改变,employeeB对象的值也受到影响。产生这种现象的原因是java中
Employee employeeB=employeeA;这条语句实际上是直接将对象employeeA的引用赋予employeeB,这样两个引用指向的是同一个对象。因此,当emploA对象改变的时候,employeeB的值也改变了。这就是浅拷贝。浅拷贝只拷贝对象引用本身,而不去拷贝引用的成员属性。从这个层次上来说,深clone并不是特别困难,简单地说,就是创建好对象,再设置一些成员属性。
接下来看看java的clone技术是怎么实现深浅拷贝的。
java中跟克隆有关的两个类分别是Cloneable接口和Object类中的clone方法,通过两者的协作来实现克隆。首先看一下java api doc中关于Cloneable接口和Object类中的clone方法的描述:
java.lang.Cloneable 接口,
此类实现了 Cloneable 接口,以指示 Object.clone() 方法可以合法地对该类实例进行按字段复制。如果在没有实现 Cloneable 接口的实例上调用 Object 的 clone 方法,则会导致抛出 CloneNotSupportedException异常。 按照惯例,实现此接口的类应该使用公共方法重写 Object.clone(它是受保护的)。请参阅 Object.clone(),以获得有关重写此方法的详细信息。
Cloneable接口没有任何方法,仅是个标志接口(tagging interface),若要具有克隆能力,实现Cloneable接口的类必须重写从Object继承来的clone方法,并调用Object的clone方法,重写后的方法应为public 的。注意的是:java默认的clone()方法是浅拷贝,若要拷贝基本类型外加String类型以外的类型,即聚合或组合类间关系的时候,就需要进行深拷贝了。此时,常用的解决方法是纵深clone,即克隆到基本类型,外加String类型以外,不能再克隆为止。如下述例子:
- public class FamilyInfo implements Cloneable{
- public String address;
- public int memberNum;
- public FamilyInfo(String address, int memberNum) {
- super();
- this.address = address;
- this.memberNum = memberNum;
- }
- public FamilyInfo clone(){
- FamilyInfo familyInfo=null;
- try {
- familyInfo=(FamilyInfo) super.clone();
- } catch (CloneNotSupportedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return familyInfo;
- }
- }
public class FamilyInfo implements Cloneable{
public String address;
public int memberNum;
public FamilyInfo(String address, int memberNum) {
super();
this.address = address;
this.memberNum = memberNum;
}
public FamilyInfo clone(){
FamilyInfo familyInfo=null;
try {
familyInfo=(FamilyInfo) super.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return familyInfo;
}
}
- public class Employee implements Cloneable{
- public String name;
- public FamilyInfo familyInfo;
- public Employee(String name, FamilyInfo familyInfo) {
- super();
- this.name = name;
- this.familyInfo = familyInfo;
- }
- public Employee clone(){
- Employee employee=null;
- try {
- employee=(Employee) super.clone();
- if(this.familyInfo!=null){
- employee.familyInfo=this.familyInfo.clone();
- }
- } catch (CloneNotSupportedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return employee;
- }
- }
public class Employee implements Cloneable{
public String name;
public FamilyInfo familyInfo;
public Employee(String name, FamilyInfo familyInfo) {
super();
this.name = name;
this.familyInfo = familyInfo;
}
public Employee clone(){
Employee employee=null;
try {
employee=(Employee) super.clone();
if(this.familyInfo!=null){
employee.familyInfo=this.familyInfo.clone();
}
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return employee;
}
}
- public class CloneTest {
- /**
- * @param args
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- FamilyInfo familyInfoA=new FamilyInfo("No.1100 Lianhang Rd.",2);
- Employee employeeA=new Employee("Lily",familyInfoA);
- System.out.println("employeeA's address "+employeeA.familyInfo.address);
- Employee employeeB=employeeA.clone();
- System.out.println("employeeB's address "+employeeB.familyInfo.address);
- System.out.println("---------------------");
- employeeA.familyInfo.address="No.1588 Pulian Rd.";
- System.out.println("employeeA's address "+employeeA.familyInfo.address);
- System.out.println("employeeB's address "+employeeB.familyInfo.address);
- }
- }
public class CloneTest {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
FamilyInfo familyInfoA=new FamilyInfo("No.1100 Lianhang Rd.",2);
Employee employeeA=new Employee("Lily",familyInfoA);
System.out.println("employeeA's address "+employeeA.familyInfo.address);
Employee employeeB=employeeA.clone();
System.out.println("employeeB's address "+employeeB.familyInfo.address);
System.out.println("---------------------");
employeeA.familyInfo.address="No.1588 Pulian Rd.";
System.out.println("employeeA's address "+employeeA.familyInfo.address);
System.out.println("employeeB's address "+employeeB.familyInfo.address);
}
}
输出结果为:
可以看到,深拷贝后,两个对象彼此独立,不受影响。