结合实例详解clone()函数,Cloneable接口以及深拷贝与浅拷贝的问题

[java]  view plain  copy
  1. package job;  
  2. /** 
  3.  * ps:java中的clone()方法的功用类似于C++中的拷贝构造函数 
  4.  * 拷贝对象还可以用对象流中转一下获得,需要实现标记型接口Serializable 
  5.  * Serializable接口中的方法对程序是不可见的,因此实现了该接口的类不需要实现 
  6.  * 额外的方法,当把一个序列化的对象写入ObjuctOutputStream时,JVM就会实现 
  7.  * Serializable接口中的方法,将一定格式的文本-对象的序列化信息,写入OBjuceOutputStream 
  8.  * 输出流的目的地 
  9.  * 使用对象流把一个对象写入文件时不仅需要保证该对象是序列化的,而且该对象的 
  10.  * 成员对象也必须是序列化的 
  11. */  
  12. import java.io.Serializable;  
  13. import java.util.Date;  
  14.   
  15. /** 
  16.  * Cloneable是标记型接口(另一个常见的就是Serializable),声明了该接口表明可用clone()方法 
  17.  * 如果不声明Cloneable接口使用clone()方法就会报CloneNotSupportException 
  18.  * 按惯例要重写protected clone()方法为public的,但是clone()是Object的protected方法 
  19.  * 已有一个field to field的浅拷贝实现,因此不像其它接口的抽象方法那样必须重写 
  20.  * 但是因为Object中的clone()方法的访问权限是protected的,这就意味着,如果一个对象想使用 
  21.  * 该方法得到自己的一个复制品,就必须保证自己的类与Object类在同一个包中,这显然是不可能的, 
  22.  * 因为Java不允许用户编写的类拥有java.lang这样的包名(尽管可以编译拥有java.lang包名的类, 
  23.  * 但运行时JVM拒绝加载这样的类),为了能让一个对象使用clone()方法,创建该对象的类需要重写(覆盖) 
  24.  * clone()方法,并且讲访问权限提升为public权限,为了能使用被覆盖的clone()方法,只需在重写的clone() 
  25.  * 方法中使用关键字super调用Object类的clone()方法即可。 
  26.  * 再者对象中有非基本类型的域且其内容有可能会发生改变时,必须重写clone()方法为深拷贝 
  27.  * @author shijin 
  28.  * 
  29.  */  
  30. public class TestClone implements Cloneable{  
  31.   
  32.     private Date date;  
  33.     private DeepCopy d;  
  34.       
  35.     public Date getT() {  
  36.         return date;  
  37.     }  
  38.       
  39.     public DeepCopy getD() {  
  40.         return d;  
  41.     }  
  42.   
  43.     public void setT(Date date) {     
  44.         this.date = date;  
  45.     }  
  46.       
  47.     public void setD(DeepCopy d) {  
  48.         this.d = d;  
  49.     }  
  50.   
  51.     @Override  
  52.     public boolean equals(Object obj) {  
  53.         if(obj == null)  
  54.             return false;  
  55.         else {  
  56.             if(obj instanceof TestClone) {  
  57.                 TestClone t = (TestClone)obj;  
  58.                 if(this.date.equals(t.date) && this.d.equals(t.d))  
  59.                     return true;  
  60.             }  
  61.             return false;  
  62.         }  
  63.           
  64.     }  
  65.   
  66.     /** 
  67.      * 其实clone()的默认实现类似于函数函数中对date域的处理:field to field,浅拷贝,shallow copy 
  68.      * 对于域中存在的非初始类型且其内容有可能发生变化的情况 
  69.      * 就要改写clone()方法实现深拷贝deep copy,如函数中对d域的处理 
  70.      * 不然拷贝对象与原对象不独立,两个对象的非初始类型域会共享内存空间 
  71.      * 一个对象内存内容的改变会造成两个对象同步改变 
  72.      *  
  73.      * 这里改变对象的内容与改变对象的引用不是一个概念 
  74.      * field to field类型的复制只会造成两个相等的对象 
  75.      * 对两个相等的对象,即为两个对象引用指向同一块内存空间 
  76.      * 一个对象引用指向的改变不会影响另一个对象 
  77.      * 只不过改变指向的对象指向了另一块内存,原指向断开被新的指向替代 
  78.      * 未改变的对象依然指向原内存,两个对象不再相等 
  79.      * 但是对象内容的改变会影响另一个对象,因为修改的是同一块内存 
  80.      * 对此处的理解可以参考String变量的不可变性 
  81.      * String类型的变量作为变量可以相继指向不同字符串常量 
  82.      * 但对于一个固定的指向,其内存空间里的内容是不能改变的 
  83.      * String s = new String("a");s ="a" + "b";两句话的内存分析 
  84.      * s变量在栈内存 
  85.      * new出来的String对象在堆内存 
  86.      * "a"、"b"、"ab"三个字符串常量在数据段(常量池) 
  87.      * s定义时指向堆内存中的String对象"a" 
  88.      * 第二句执行后指向数据段中的字符串常量"ab" 
  89.      * 内存中的内容不变,只是字符串变量的指向变了 
  90.      *           
  91.     */  
  92.     @Override  
  93.     public Object clone() throws CloneNotSupportedException {  
  94. //      TestClone t = (TestClone)super.clone();  
  95. //      t.setT(this.getT());  
  96. //      t.setD(this.getD());//浅拷贝  
  97. //      t.setD(new DeepCopy(this.d.getX()));//深拷贝  
  98. //      或者如下,如果DeepCopy实现了Cloneable接口的话,这里就有点递归的意思了  
  99. //      t.setD((DeepCopy)this.d.clone());  
  100. //      return t;  
  101.           
  102.         return super.clone();   
  103.   
  104. //      return new TestClone();  
  105.     }  
  106.   
  107.     /** 
  108.      * @param args 
  109.      */  
  110.     public static void main(String[] args) {  
  111.         TestClone t = new TestClone();  
  112.         t.setT(new Date());  
  113.         t.setD(new DeepCopy(1));  
  114.         TestClone tClone = null;  
  115.         try {  
  116.             tClone = (TestClone)t.clone();  
  117.         } catch (CloneNotSupportedException e1) {  
  118.             e1.printStackTrace();  
  119.         }  
  120.           
  121. //      这个!=是必须的,定义要求拷贝对象与原对象独立,不然就失去拷贝的意义了  
  122.         System.out.println("t " + ((t != tClone)?"!=":"==") + " t.clone()");  
  123. //      clone()中对d进行深拷贝时t.d != t.clone().d  
  124.         System.out.println("t.d " + ((t.d != tClone.d)?"!=":"==") + " t.clone().d");  
  125. //      clone()中对t进行浅拷贝t.date == t.clone().date  
  126.         System.out.println("t.date " + ((t.date != tClone.date)?"!=":"==") + " t.clone().date");  
  127.           
  128. //      这个==虽然不是必须的,但是只要类及其父类clone()方法中的拷贝对象都是通过super.clone()获得  
  129. //      t.getClass() == tClone.getClass()就成立  
  130.         System.out.println("t.getClass() " + ((t.getClass() == tClone.getClass())?"==":"!=") + " tClone.getClass()");  
  131.         System.out.println("\tt = " + t);  
  132.         System.out.println("\ttClone = " + tClone);  
  133. //      即使clone()采用默认实现,仍然返回true,自动识别类型  
  134. //      Object中的clone执行的时候使用了RTTI(run-time type identification)的机制,即多态的实现机制  
  135. //      动态得找到目前正在调用clone方法的那个reference,根据它的大小申请内存空间  
  136. //      然后进行bitwise的复制,将该对象的内存空间完全复制到新的空间中去,从而达到shallow copy的目的。   
  137. //      所以调用super.clone() 得到的是当前调用类的副本,而不是父类的副本。  
  138.         System.out.println("\ttClone 是不是TestClone的实例:" + (tClone instanceof TestClone));  
  139.         System.out.println("\tt.getClass() = " + t.getClass());  
  140.         System.out.println("\ttClone.getClass() = " + tClone.getClass());  
  141.           
  142. //      这个==也不是必须的,比如在clone()和equals()都改写时  
  143. //      若对象中有一个域为unique,则该对象的克隆就可能与该对象不相等  
  144.         System.out.println("t " + ((t.equals(tClone))?"equales":"doesn't eaual") + " t.clone");  
  145.                   
  146. //      本段代码验证浅拷贝与深拷贝的区别          
  147.         System.out.println("t.d.x改动前:" + t.d);  
  148.         System.out.println("tClone.d.x改动前:" + tClone.d);  
  149.           
  150.         t.d.setX(2);  
  151.           
  152.         System.out.println("t.d.x改动后:" + t.d);  
  153.         System.out.println("tClone.d.x改动前:" + tClone.d);  
  154.   
  155. //      本段代码参考上面验证改变引用与改变内容的区别  
  156. //      对date的实验中直接更换date域的引用,而不是改变date的内容,所以体现不出浅拷贝的弊端  
  157. //      后期date域指向外部引用后两个对象也独立了  
  158.         System.out.println(t.date);  
  159.         System.out.println(tClone.date);  
  160.           
  161.         try {  
  162.             Thread.sleep(1000);  
  163.         } catch (InterruptedException e) {  
  164.             e.printStackTrace();  
  165.         }  
  166.           
  167.         tClone.setT(new Date());  
  168.           
  169.         System.out.println(t.date);  
  170.         System.out.println(tClone.date);      
  171.     }  
  172. }  
  173. /** 
  174.  * 验证深拷贝的辅助类 
  175.  * @author shijin 
  176.  * 
  177.  */  
  178. class DeepCopy implements Cloneable{  
  179.     int x;  
  180.       
  181.     public DeepCopy(int x) {  
  182.         this.x = x;  
  183.     }  
  184.   
  185.     public int getX() {  
  186.         return x;  
  187.     }  
  188.   
  189.     public void setX(int x) {  
  190.         this.x = x;  
  191.     }  
  192.   
  193.     @Override  
  194.     public String toString() {  
  195.         return x+"";  
  196.     }  
  197.       
  198.     @Override  
  199.     protected Object clone() throws CloneNotSupportedException {  
  200.         //成员为基本类型,不用改写方法  
  201.         return super.clone();  
  202.     }  
  203.   
  204.     @Override  
  205.     public boolean equals(Object obj) {  
  206.         if(obj != null && obj instanceof DeepCopy) {  
  207.             DeepCopy d = (DeepCopy)obj;  
  208.             if(d.x == this.x)  
  209.                 return true;  
  210.         }  
  211.         return false;  
  212.     }  
  213. }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值