`
qqdwll
  • 浏览: 131610 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

简述Java语言的对象克隆特性

阅读更多
在Java中传值及引伸深度克隆的思考中,我们讲过引申到克隆技术Java中的所有对象都是Object类的子类。我们知道,Java是纯面向对象的程序设计语言。Java里,所有的类的顶级父类都是java.lang.Object类,也就是说,如果一个类没有显示 申明继承关系,它的父类默认就是java.lang.Object。

有一个很简单的方法可以证明这一点,我们写一个Test类,如下:

   1. public class Test {  
   2.     public void someMethod() {  
   3.         super.clone();  
   4.     }  
   5. }  
   6. 

里面调用了super.clone(),编译时并不报错。其实clone()方法为java.lang.Object类提供的一个 protected型方法。
对象克隆

本文通过介绍java.lang.Object#clone()方法来说明Java语言的对象克隆特性。

java.lang.Object#clone()方法由java.lang.Object加以实现,主要对对象本身加以克隆。

首先我们看看下面的例子:

   1. public class TestClone {  
   2.     public static void main(String[] args) {  
   3.         MyClone myClone1 = new MyClone("clone1");  
   4.          
   5.         MyClone myClone2 = (MyClone)myClone1.clone();  
   6.          
   7.         if (myClone2 != null) {  
   8.             System.out.println(myClone2.getName());  
   9.             System.out.println("myClone2 equals myClone1: " + myClone2.equals(myClone1));  
  10.         } else {  
  11.             System.out.println("Clone Not Supported");  
  12.         }  
  13.     }  
  14. }  
  15. class MyClone {  
  16.     private String name;  
  17.     public MyClone(String name) {  
  18.         this.name = name;  
  19.     }  
  20.      
  21.     public String getName() {  
  22.         return name;  
  23.     }  
  24.     public void setName(String name) {  
  25.         this.name = name;  
  26.     }  
  27.      
  28.     public Object clone() {  
  29.         try {  
  30.             return super.clone();  
  31.         } catch (CloneNotSupportedException e) {  
  32.             return null;  
  33.         }  
  34.     }  
  35. 

编译执行TestClone,打印出:

   1. C:\clone>javac *.java  
   2. C:\clone>java TestClone  
   3. Clone Not Supported  
   4. C:\clone>  
   5. 

说明MyClone#clone()方法调用super.clone()时抛出了CloneNotSupportedException异常,不支持克隆。

为什么父类java.lang.Object里提供了clone()方法,却不能调用呢?

原来,Java语言虽然提供了这个方法,但考虑到安全问题, 一方面将clone()访问级别设置为protected型,以限制外部类访问;

另一方面,强制需要提供clone功能的子类实现java.lang.Cloneable接口,在运行期,JVM会检查调用clone()方法的 类,如果该类未实现java.lang.Cloneable接口,则抛出CloneNotSupportedException异常。

java.lang.Cloneable接口是一个空的接口,没有申明任何属性与方法。该接口只是告诉JVM,该接口的实现类需要开放“克隆”功能。

我们再将MyClone类稍作改变,让其实现Cloneable接口:

   1. class MyClone implements Cloneable {  
   2.     ...//其余不做改变  
   3. }  
   4. 
   5. 编译执行TestClone,打印出:  
   6. 
   7. C:\clone>javac *.java  
   8. C:\clone>java TestClone  
   9. clone1  
  10. myClone2 equals myClone1: false  
  11. C:\clone>  
  12. 

根据结果,我们可以发现:

1,myClone1.clone()克隆了跟myClone1具有相同属性值的对象

2,但克隆出的对象myClone2跟myClone1不是同一个对象(具有不同的内存空间)
小结

如果要让一个类A提供克隆功能,该类必须实现java.lang.Cloneable接口,并重载 java.lang.Object#clone()方法。

   1. public class A extends Cloneable {  
   2.     public Object clone() {  
   3.         try {  
   4.             return super.clone();  
   5.         } catch (CloneNotSupportedException e) {  
   6.             //throw (new InternalError(e.getMessage()));  
   7.             return null;  
   8.         }  
   9.     }  
  10. }  
  11. 

对象的深层次克隆

上例说明了怎么样克隆一个具有简单属性(String,int,boolean等)的对象。

但如果一个对象的属性类型是List,Map,或者用户自定义的其他类时,克隆行为是通过怎样的方式进行的?

很多时候,我们希望即使修改了克隆后的对象的属性值,也不会影响到原对象,这种克隆我们称之为对象的深层次克隆。怎么样实现对象的深层次克隆呢?
验证对象的克隆方式

为了验证对象的克隆方式,我们对上面的例子加以改进,如下(为了节省篇幅,我们省略了setter与getter方法):

   1. public class TestClone {  
   2.     public static void main(String[] args) {  
   3.         //为克隆对象设置值  
   4.          MyClone myClone1 = new MyClone("clone1");  
   5.          myClone1.setBoolValue(true);  
   6.          myClone1.setIntValue(100);  
   7.          
   8.         //设置List值  
   9.          List <Element>listValue = new ArrayList<Element>();  
  10.          listValue.add(new Element("ListElement1"));  
  11.          listValue.add(new Element("ListElement2"));  
  12.          listValue.add(new Element("ListElement3"));  
  13.          myClone1.setListValue(listValue);  
  14.          
  15.         //设置Element值  
  16.          Element element1 = new Element("element1");  
  17.          myClone1.setElement(element1);  
  18.          
  19.          
  20.         //克隆  
  21.          MyClone myClone2 = (MyClone)myClone1.clone();  
  22.          
  23.         if (myClone2 != null) {  
  24.              
  25.             //简单属性  
  26.              System.out.println("myClone2.name=" + myClone2.getName()  
  27.                      + " myClone2.boolValue=" + myClone2.isBoolValue()  
  28.                      + " myClone2.intValue=" + myClone2.getIntValue() );  
  29.              
  30.             //复合属性(List<Element>与Element)  
  31.              List clonedList = myClone2.getListValue();  
  32.              Element element2 = myClone2.getElement();  
  33.              
  34.              System.out.println("myClone2.listValue.size():" + clonedList.size());  
  35.              System.out.println("myClone2.element.equals(myClone1.element):" + element2.equals(element1));  
  36.              System.out.println("myClone2.element.name:" + element2.getName());  
  37.              
  38.             //下面我们测试一下myClone2.element是否等于myClone1.element  
  39.             //以及myClone2.listValue是否等于myClone1.listValue  
  40.             //为此,我们修改myClone2.element与myClone2.listValue,如果myClone1的相应值也跟着被修改了,则它们引用 的是同一个内存空间的变量,我们认为它们相等  
  41.              
  42.              clonedList.add("ListElement4");  
  43.              
  44.              System.out.println("myClone1.listValue.size():" + listValue.size());  
  45.              
  46.              element2.setName("Element2");  
  47.              System.out.println("myClone1.element.name:" + element1.getName());  
  48.              
  49.          } else {  
  50.              System.out.println("Clone Not Supported");  
  51.          }         
  52.          
  53.      }  
  54. 
  55. }  
  56. 
  57. 
  58. class MyClone implements Cloneable {  
  59.     private int intValue;  
  60.     private boolean boolValue;  
  61.     private String name;  
  62.     private List <Element>listValue;  
  63.     private Element element;  
  64. 
  65.     public MyClone(String name) {  
  66.          this.name = name;  
  67.      }  
  68. 
  69.      ...//setter与getter方法(略)  
  70. }  
  71. 
  72. class Element implements Cloneable   {  
  73.     private String name;  
  74.      
  75.     public Element (String name) {  
  76.          this.name = name;  
  77.      }  
  78. 
  79.      ...//setter与getter方法(略)  
  80. }  
  81. 

编译执行TestClone,打印出:

   1. C:\clone>javac *.java  
   2. C:\clone>java TestClone  
   3. myClone2.name=clone1 myClone2.boolValue=true myClone2.intValue=100  
   4. myClone2.listValue.size():3  
   5. myClone2.element.equals(myClone1.element):true  
   6. myClone2.element.name:element1  
   7. myClone1.listValue.size():4  
   8. myClone1.element.name:Element2  
   9. myClone2 equals myClone1: false  
  10. C:\clone>  
  11. 

我们发现,对于对象里的List,Element等复合属性,super.clone()只是简单地赋值,没有采取克隆手段。也就是说,修改被克 隆后的对象值,会影响到原对象。

怎么进行深层次的克隆呢?

答案是,我们只能手动在重载的clone()方法里,对属性也分别采用克隆操作。当然条件是,属性类也得支持克隆操作

   1. class MyClone implements Cloneable {  
   2.      ...  
   3.     public Object clone() {  
   4.         try {  
   5.              MyClone myClone = (MyClone)super.clone();  
   6.             //分别对属性加以克隆操作  
   7.              myClone.element = this.element.clone();  
   8.              
   9.              myClone.listValue = new ArrayList();  
  10.             for (Element ele:this.listValue) {  
  11.                  myClone.listValue.add(ele.clone());  
  12.              }  
  13.                          
  14.             return myClone;  
  15.          } catch (CloneNotSupportedException e) {  
  16.             return null;  
  17.          }  
  18.      }  
  19.      ...  
  20. }  
  21. 
  22. //让Element类也支持克隆操作  
  23. class Element implements Cloneable   {  
  24.      ...  
  25.     public Element clone() {  
  26.         try {  
  27.             return (Element)super.clone();  
  28.          } catch (CloneNotSupportedException e) {  
  29.             return null;  
  30.          }  
  31.      }  
  32. }  
  33. 

深层次的克隆操作往往存在效率问题,尤其是需要让List,Map等集合类也支持深层次的克隆操作时。
总结

本文结合范例,比较深入地介绍了Java语言的克隆属性,以及克隆的实现方法等。同时分析了深层次克隆的概念,实现,以及存在的问题等。 但是有没有更好的方法呢?当然,是有的,串行化来实现。
分享到:
评论

相关推荐

    java 面试题 总结

    内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的...

    超级有影响力霸气的Java面试题大全文档

    内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的...

    Java面试宝典-经典

    45、JAVA语言如何进行异常处理,关键字:throws,throw,try,catch,finally分别代表什么意义?在try块中可以抛出异常吗? 29 46、java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法...

    java面试题大全(2012版)

    45、JAVA语言如何进行异常处理,关键字:throws,throw,try,catch,finally分别代表什么意义?在try块中可以抛出异常吗? 29 46、java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法...

    java面试题

    84.8. 将一个键盘输入的数字转化成中文输出(例如:输入1234567,输出:一百二拾三万四千五百六拾七),请用java语言编一段程序实现! 114 84.9. 题目1:用1、2、2、3、4、5这六个数字,用java写一个main函数,打印出所有...

    最新Java面试宝典pdf版

    45、JAVA语言如何进行异常处理,关键字:throws,throw,try,catch,finally分别代表什么意义?在try块中可以抛出异常吗? 29 46、java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法...

    Java面试笔试资料大全

    45、JAVA语言如何进行异常处理,关键字:throws,throw,try,catch,finally分别代表什么意义?在try块中可以抛出异常吗? 29 46、java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法...

    java面试宝典2012

    45、JAVA语言如何进行异常处理,关键字:throws,throw,try,catch,finally分别代表什么意义?在try块中可以抛出异常吗? 32 46、java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法...

    JAVA面试宝典2010

    45、JAVA语言如何进行异常处理,关键字:throws,throw,try,catch,finally分别代表什么意义?在try块中可以抛出异常吗? 29 46、java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法...

    Java面试宝典2012新版

    45、JAVA语言如何进行异常处理,关键字:throws,throw,try,catch,finally分别代表什么意义?在try块中可以抛出异常吗? 29 二. Jav线程部分 7 46、java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? ...

    Java面试宝典2012版

    45、JAVA语言如何进行异常处理,关键字:throws,throw,try,catch,finally分别代表什么意义?在try块中可以抛出异常吗? 29 二. Jav线程部分 7 46、java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? ...

    Java 面试宝典

    Java 基础部分..................................................................................................................... 7 1、一个".java"源文件中是否可以包括多个类(不是内部类)?有什么...

    RED HAT LINUX 6大全

    5.7 GNU Window Maker窗口管理器特性 82 5.7.1 重要的文件 82 5.7.2 配置Window Maker 82 5.8 fvwm2窗口管理器 83 5.9 fvwm窗口管理器 83 5.10 twm窗口管理器 84 5.11 公共桌面环境特性 86 5.12 使用RPM安装Red Hat ...

    asp.net知识库

    与DotNet数据对象结合的自定义数据对象设计 (一) 数据对象与DataRow ASP.NET中大结果集的分页[翻译] .net 2.0 访问Oracle --与Sql Server的差异,注意事项,常见异常 Ado.net 与NHibernate的关系? 动态创建数据库...

    JavaScript高级教程

    3.1.1 面向对象语言的要求..............................................58 3.1.2 对象的构成..............................................59 3.2 对象应用..............................................59 ...

Global site tag (gtag.js) - Google Analytics