简单介绍Java克隆对象的特性

开发 后端
本文主要介绍的是java的对象克隆特性,结合实例,分析了克隆的概念和实现等问题。希望对大家有帮助,一起来看。

java面向对象的编程当中,要复制引用类型的对象,就必须克隆对象。通过调用对所有引用类型和对象都是可用的clone方法,来实现克隆。

在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. }   

 

里面调用了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. MyClone myClone2 = (MyClone)myClone1.clone();   
  5. if (myClone2 != null) {   
  6. System.out.println(myClone2.getName());  
  7. System.out.println("myClone2 equals myClone1: " + myClone2.equals(myClone1));   
  8. else {   
  9. System.out.println("Clone Not Supported");   
  10. }   
  11. } }   
  12. class MyClone {   
  13. private String name;   
  14. public MyClone(String name) {   
  15. this.name = name;   
  16. }  
  17. public String getName() {   
  18. return name;   
  19. }   
  20. public void setName(String name) {   
  21. this.name = name; }  
  22. public Object clone() {   
  23. try {  
  24. return super.clone();   
  25. catch (CloneNotSupportedException e) {   
  26. return null;   
  27. }} 

 

编译执行TestClone,打印出:

 

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

 

说明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. 编译执行TestClone,打印出:   
  5. C:\clone>javac *.java   
  6. C:\clone>java TestClone   
  7. clone1   
  8. myClone2 equals myClone1: false   
  9. C:\clone>  

 

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

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. }   

 

对象的深层次克隆

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

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

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

#p#

验证对象的克隆方式

为了验证对象的克隆方式,我们对上面的例子加以改进,如下(为了节省篇幅,我们省略了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. //设置List值   
  8. List <Element>listValue = new ArrayList<Element>();   
  9. listValue.add(new Element("ListElement1"));   
  10. listValue.add(new Element("ListElement2"));   
  11. listValue.add(new Element("ListElement3"));   
  12. myClone1.setListValue(listValue);   
  13. //设置Element值   
  14. Element element1 = new Element("element1");   
  15. myClone1.setElement(element1);   
  16. //克隆   
  17. MyClone myClone2 = (MyClone)myClone1.clone();   
  18. if (myClone2 != null) {   
  19. //简单属性   
  20. System.out.println("myClone2.name=" + myClone2.getName()   
  21. " myClone2.boolValue=" + myClone2.isBoolValue()   
  22. " myClone2.intValue=" + myClone2.getIntValue() );   
  23. //复合属性(List<Element>与Element)   
  24. List clonedList = myClone2.getListValue();   
  25. Element element2 = myClone2.getElement();   
  26. System.out.println("myClone2.listValue.size():" + clonedList.size());   
  27. System.out.println("myClone2.element.equals(myClone1.element):" + element2.equals(element1));   
  28. System.out.println("myClone2.element.name:" + element2.getName());  
  29. //下面我们测试一下myClone2.element是否等于myClone1.element   
  30. //以及myClone2.listValue是否等于myClone1.listValue   
  31. //为此,我们修改myClone2.element与myClone2.listValue,如果myClone1的相应值也跟着被修改了,
  32. 则它们引用 的是同一个内存空间的变量,我们认为它们相等   
  33. clonedList.add("ListElement4");   
  34. System.out.println("myClone1.listValue.size():" + listValue.size());   
  35. element2.setName("Element2");   
  36. System.out.println("myClone1.element.name:" + element1.getName());   
  37. else {   
  38. System.out.println("Clone Not Supported");   
  39. }   
  40. }   
  41. }   
  42. class MyClone implements Cloneable {   
  43. private int intValue;   
  44. private boolean boolValue;   
  45. private String name;   
  46. private List <Element>listValue;   
  47. private Element element;   
  48. public MyClone(String name) {   
  49. this.name = name;   
  50. }  
  51. ...//setter与getter方法(略)   
  52. }   
  53. class Element implements Cloneable {   
  54. private String name;   
  55. public Element (String name) {   
  56. this.name = name;   
  57. }   
  58. ...//setter与getter方法(略)   
  59. }  

 

编译执行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 09.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. myClone.listValue = new ArrayList();   
  9. for (Element ele:this.listValue) {   
  10. myClone.listValue.add(ele.clone());   
  11. }   
  12. return myClone;   
  13. catch (CloneNotSupportedException e) {   
  14. return null;   
  15. }   
  16. }   
  17. ...  
  18. }   
  19. //让Element类也支持克隆操作   
  20. class Element implements Cloneable {   
  21. ...   
  22. public Element clone() {   
  23. try {   
  24. return (Element)super.clone();   
  25. catch (CloneNotSupportedException e) {   
  26. return null;   
  27. }   
  28. }   
  29. }  

 

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

总结

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

【编辑推荐】

  1. 详细介绍Java EE开发四大常用框架
  2. Java 多线程同步问题的探究(一)
  3. Java 多线程同步问题的探究(二)
  4. 谈java web server与io形式
  5. JAVA开发 分析JNI标准原理
责任编辑:于铁 来源: 博客园
相关推荐

2010-10-28 13:06:45

Java克隆特性

2009-09-07 17:46:18

LINQ高级特性

2009-09-09 09:36:25

Linq对象引用

2018-04-03 13:10:27

Java对象克隆

2011-05-10 10:00:41

克隆

2011-07-04 17:27:42

JSP

2012-03-13 16:39:52

Java

2009-08-31 14:54:35

C#对象浏览器

2009-12-30 14:59:42

ADO.NET数据集

2021-04-30 19:53:41

Java表达式代码

2011-07-11 14:12:19

Java串口通信

2011-07-11 14:19:54

JAVA串口通信

2011-06-27 13:17:07

Java EE

2011-07-04 10:04:52

java网络程序

2021-06-03 10:00:47

JavaScript 前端数克隆对象

2010-03-18 18:20:34

Java Socket

2010-01-15 16:59:52

VB.NET对象克隆

2013-12-12 16:37:12

Lua脚本语言

2009-08-24 18:01:45

C#3.0新特性

2011-03-11 09:35:01

LAMP介绍
点赞
收藏

51CTO技术栈公众号