说明书排版印刷:解析深clone与浅clone

来源:百度文库 编辑:九乡新闻网 时间:2024/04/29 16:16:11
  

★ Java对象赋值

Java代码
  1. Employee e1=new Employee("李"); //Employee是一个自定义类       
  2. Employee e2=e1; //赋值对象       
  3. e2.setName("王");//改变对象e2的名字       
  4. System.out.println(e1.getName()); //打印e1的结果: 王      

      这就是Java的对象赋值,改变e2的内容竟然会影响e1的内容。原因很简单,就是e1和e2这两个对象引用都指向了堆中同一个Employee类对象的内容。也就是说: Java的对象赋值的是引用(相当于C的指针)。如何让e1,e2成为内容相同的两个完全不同的对象呢,这就需要用到Java的对象克隆机制(将e2复制成e1的一个独立副本)。

 

Java对clone的支持

 

(1) 继承Object的clone方法的疑问?

     有一点我们很清楚,Java的万类之祖Object中有一个clone()方法:  

               protected native Object clone() throws CloneNotSupportedException

     既然这个方法是protected的,显然是为了让子类能够使用。看看下面的代码:

Java代码
  1. //Employee类中没有clone方法,也没有实现Cloneable接口      
  2. Employee original=new Employee("John Public");      
  3. Employee copy=original.clone(); //wrong     

     有人会提出这样的疑问:不是所有的类都是Object的子类吗?不是所有的子类都可以访问受保护的方法吗 ? 毫无疑问,这两句提问没有任何错误。但是有一点必须搞清楚:你是否正真理解protected的作用范围呢?《【Java语言】你是否真正理解了protected的作用范围呢?》 。

(2) Java支持克隆

      既然如此,难道我们就没有办法在某一个类的作用域外部来克隆这个类了吗?

      答案是否定的! 我们可以在任何一个类中重写clone方法,并升级它的访问作用域。事实上,使用的时候也就是这样做的!

      首先我们必须在需要克隆的类上实现一个重要的接口——Cloneable接口。这种接口我们叫作标记接口(tagging interface) 。这种标记接口没有任何方法,唯一的作用就是作为一个标志,用来告诉JVM一个类是否具有某个特定的功能。

       如此一来,我们只要定义一个具有 Clone 功能的类就可以了:

       1. 在类的声明中加入“ implements Cloneable ”,标志该类有克隆功能;
       2. 重载类 Object 的 clone() 方法,在该方法中调用 super.clone() :

Java代码
  1. class Employee implements Cloneable{       
  2.             public Object clone() throws CloneNotSupportedException{//重载clone()方法       
  3.               Employee cloned=(Employee)super.clone();       
  4.                      return cloned;       
  5.             }       
  6. }     

深Clone和浅Clone


       拷贝副本的问题并没有完全解决。clone技术并不是那么简单的。Object中的clone()方法是对具体类对象的各个域进行对应的赋值。如果具体类对象中还有子对象,这个问题就复杂了。

Java代码
  1. // 具备浅克隆的Employee类      
  2. class Employee implements Cloneable{      
  3.        public String name="";        
  4.        public Date hireDay=null;      
  5.      
  6.        public Object clone(){      
  7.             Employee cloned=(Employee)super.clone();      
  8.             return cloned;      
  9.       }      
  10. }      
  11.      
  12. Employee orignal=new Employee();      
  13. Employee copy=orignal.copy();    
copy中的hireDay指向的是同样的存储位置。也就是说当我们调用copy.hireDay.setTime()方法后,orignal中的hireDay也会发生改变。但String类(由于常量池的存储方式)和基本数据类型变量时不会改变的。这种对子对象克隆无效的方式我们叫做浅克隆

       很多情况下,我们需要将对象中的所有域(包括子对象)都进行真正的克隆。要做到这种深克隆,我们必须在重载clone()方法时克隆子对象:

Java代码
  1. //具备深度克隆功能的Employee类      
  2. class Employee implement Cloneable{      
  3.          public String name="";      
  4.          private Date hireDay=null;      
  5.      
  6.         public Object clone(){      
  7.                 Employee cloned=(Employee)super.clone();  //浅克隆      
  8.            cloned.hireDay=(Date)hireDay.clone(); //克隆子对象      
  9.            return cloned;      
  10.         }      
  11. }