在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语言回收机制 #### 一、垃圾回收(Garbage Collection)概述 在编程领域,特别是对于初学者来说,了解并掌握Java的垃圾回收机制是非常重要的。与C++等需要手动管理内存的语言不同,...
Java核心技术是Java编程领域的一本经典著作,涵盖了Java语言的基础以及高级特性,是广大Java开发者必备的参考书籍。本PDF版本为《Java核心技术 卷1》,主要关注Java的基础知识,适合初学者和有一定经验的开发者深入...
#### 四、Java语言基础 1. **Overload和Override的区别。Overloaded的方法是否可以改变返回值的类型?** - **Overload**(重载)指的是在同一类中定义多个同名方法,但参数列表不同。 - **Override**(重写)是指...
在这个项目中,我们使用Java语言来开发这个游戏,这是一个非常实用的语言,尤其适合开发跨平台的应用程序。下面,我们将详细讨论这个Java拼图游戏程序及其背后的编程知识点。 首先,我们需要理解Java的基础知识。...
1. **Java语言特性** - 讲解Java的面向对象特性:封装、继承、多态。 - 掌握Java异常处理机制,了解try-catch-finally语句块和throws关键字的使用。 - 了解Java内存管理,包括垃圾回收机制和内存泄漏的概念。 2....
内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的...
Java语言的特点** - **面向对象**:Java是一种完全面向对象的语言,支持封装、继承、多态。 - **平台无关性**:通过JVM实现了跨平台的能力,编写一次,到处运行(Write Once, Run Anywhere)。 - **健壮性**:异常...
内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的...
这份笔记涵盖了Java的核心概念,通过18天的学习计划,帮助读者逐步理解并熟练运用Java语言。 第1天:Java概述 - Java的历史和发展:了解Java由Sun Microsystems开发,以及其后被Oracle收购的过程。 - Java的特点:...
45、JAVA语言如何进行异常处理,关键字:throws,throw,try,catch,finally分别代表什么意义?在try块中可以抛出异常吗? 29 46、java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法...
45、JAVA语言如何进行异常处理,关键字:throws,throw,try,catch,finally分别代表什么意义?在try块中可以抛出异常吗? 29 46、java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法...
84.8. 将一个键盘输入的数字转化成中文输出(例如:输入1234567,输出:一百二拾三万四千五百六拾七),请用java语言编一段程序实现! 114 84.9. 题目1:用1、2、2、3、4、5这六个数字,用java写一个main函数,打印出所有...
Java语言具有以下特点: - **面向对象**:Java是一种完全面向对象的语言。 - **跨平台性**:通过JVM实现了一次编写,到处运行的能力。 - **安全性**:通过字节码校验、安全异常处理等机制提高系统的安全性。 - ...
### Java面试2024-7.5基础必备知识点详解...以上是Java面试中可能涉及的基础知识点,这些知识点涵盖了Java语言的核心概念和常用技术。准备面试时,除了理解这些基本概念外,还需要掌握相关的实践经验和解决问题的能力。
Java语言中没有传统的`goto`语句,但在某些场景下,通过使用标签化的`break`或`continue`语句,可以达到类似的效果,这通常用于复杂的循环结构中提前退出或跳转。 #### 3. 逻辑运算符&和&&的区别 `&`和`&&`都是逻辑...
根据提供的文件信息,我们可以整理出一系列关于Java编程的重要知识点,主要涵盖了面试常见问题及解答、基础知识、面向对象特性、垃圾回收机制、序列化概念、集合框架等关键领域。下面将详细解析这些知识点: ### 1....
45、JAVA语言如何进行异常处理,关键字:throws,throw,try,catch,finally分别代表什么意义?在try块中可以抛出异常吗? 29 46、java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法...
45、JAVA语言如何进行异常处理,关键字:throws,throw,try,catch,finally分别代表什么意义?在try块中可以抛出异常吗? 32 46、java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法...