`

Java克隆对象的特性

    博客分类:
  • Java
阅读更多

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

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

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

 

public class Test {   
public void someMethod() {   
super.clone();   
}   
} 

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

 

 

对象克隆

 

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

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

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

 

public class TestClone {   
public static void main(String[] args) {   
MyClone myClone1 = new MyClone("clone1");   
MyClone myClone2 = (MyClone)myClone1.clone();   
if (myClone2 != null) {   
System.out.println(myClone2.getName());  
System.out.println("myClone2 equals myClone1: " + myClone2.equals(myClone1));   
} else {   
System.out.println("Clone Not Supported");   
}   
} }   
class MyClone {   
private String name;   
public MyClone(String name) {   
this.name = name;   
}  
public String getName() {   
return name;   
}   
public void setName(String name) {   
this.name = name; }  
public Object clone() {   
try {  
return super.clone();   
} catch (CloneNotSupportedException e) {   
return null;   
}} 

 编译执行TestClone,打印出:

 

 

C:\clone>javac *.java   
C:\clone>java TestClone   
Clone Not Supported   
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接口:

 

 

class MyClone implements Cloneable {   
...//其余不做改变   
}   
编译执行TestClone,打印出:   
C:\clone>javac *.java   
C:\clone>java TestClone   
clone1   
myClone2 equals myClone1: false   
C:\clone>  

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

 

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

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

 

小结

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

 

public class A extends Cloneable {   
public Object clone() {   
try {   
return super.clone();   
} catch (CloneNotSupportedException e) {   
//throw (new InternalError(e.getMessage()));   
return null;   
}   
}   
} 

 对象的深层次克隆

 

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

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

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

 

验证对象的克隆方式

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

 

public class TestClone {   
public static void main(String[] args) {   
//为克隆对象设置值   
MyClone myClone1 = new MyClone("clone1");   
myClone1.setBoolValue(true);   
myClone1.setIntValue(100);   
//设置List值   
List <Element>listValue = new ArrayList<Element>();   
listValue.add(new Element("ListElement1"));   
listValue.add(new Element("ListElement2"));   
listValue.add(new Element("ListElement3"));   
myClone1.setListValue(listValue);   
//设置Element值   
Element element1 = new Element("element1");   
myClone1.setElement(element1);   
//克隆   
MyClone myClone2 = (MyClone)myClone1.clone();   
if (myClone2 != null) {   
//简单属性   
System.out.println("myClone2.name=" + myClone2.getName()   
+ " myClone2.boolValue=" + myClone2.isBoolValue()   
+ " myClone2.intValue=" + myClone2.getIntValue() );   
//复合属性(List<Element>与Element)   
List clonedList = myClone2.getListValue();   
Element element2 = myClone2.getElement();   
System.out.println("myClone2.listValue.size():" + clonedList.size());   
System.out.println("myClone2.element.equals(myClone1.element):" + element2.equals(element1));   
System.out.println("myClone2.element.name:" + element2.getName());  
//下面我们测试一下myClone2.element是否等于myClone1.element   
//以及myClone2.listValue是否等于myClone1.listValue   
//为此,我们修改myClone2.element与myClone2.listValue,如果myClone1的相应值也跟着被修改了,
则它们引用 的是同一个内存空间的变量,我们认为它们相等   
clonedList.add("ListElement4");   
System.out.println("myClone1.listValue.size():" + listValue.size());   
element2.setName("Element2");   
System.out.println("myClone1.element.name:" + element1.getName());   
} else {   
System.out.println("Clone Not Supported");   
}   
}   
}   
class MyClone implements Cloneable {   
private int intValue;   
private boolean boolValue;   
private String name;   
private List <Element>listValue;   
private Element element;   
public MyClone(String name) {   
this.name = name;   
}  
...//setter与getter方法(略)   
}   
class Element implements Cloneable {   
private String name;   
public Element (String name) {   
this.name = name;   
}   
...//setter与getter方法(略)   
}  

 编译执行TestClone,打印出:

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

 

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

 

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

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

class MyClone implements Cloneable {   
...   
public Object clone() {   
try {   
MyClone myClone = (MyClone)super.clone();   
//分别对属性加以克隆操作   
myClone.element = this.element.clone();   
myClone.listValue = new ArrayList();   
for (Element ele:this.listValue) {   
myClone.listValue.add(ele.clone());   
}   
return myClone;   
} catch (CloneNotSupportedException e) {   
return null;   
}   
}   
...  
}   
//让Element类也支持克隆操作   
class Element implements Cloneable {   
...   
public Element clone() {   
try {   
return (Element)super.clone();   
} catch (CloneNotSupportedException e) {   
return null;   
}   
}   
}  

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

 

总结

 

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

 

 

转自:http://developer.51cto.com/art/201106/271883.htm

分享到:
评论

相关推荐

    MongoDB、Java与对象关系映射

    MongoDB不仅提供了高性能的数据访问,而且还支持高级功能,如索引、分片(sharding)、复制(replication)和MapReduce等,这些特性使其成为处理大规模数据的理想选择。此外,MongoDB拥有一个庞大且活跃的社区,这...

    Java面对对象实验(项目文件)2013521

    Java面向对象编程是Java语言的核心特性,也是软件开发中的重要概念。这个实验项目"Java面对对象实验(项目文件)2013521"旨在深入理解和掌握面向对象编程的基本概念,为后续学习JavaEE、JavaME等高级技术打下坚实的...

    java类复制一个持久化对象

    在Java编程语言中,对象复制是一项常见的操作,尤其是在处理持久化对象时,为了保持数据的一致性和完整性,正确地复制对象变得尤为重要。本文将深入探讨如何利用反射机制在Java中复制一个持久化对象,理解其背后的...

    java 面向对象程序设计全部课件各部分重点内容

    本课程全面覆盖了Java编程的关键知识点,包括基础语法、面向对象特性、Applet应用、图形用户界面(GUI)设计、异常处理、线程管理、集合框架、文件输入输出以及网络编程。 1. **Java语法基础**:Java的基础语法是...

    浅谈java面向对象和引用

    - **封装**:封装是面向对象编程的重要特性之一,它隐藏了对象的内部实现细节,只对外提供公共接口。通过封装,可以保护数据的安全性,同时也便于修改和维护代码。 - **多态**:多态性是指程序中定义的引用变量所...

    基于java8新特性+反射机制实现list不同实体类互转.zip

    这个压缩包文件“基于java8新特性+反射机制实现list不同实体类互转.zip”提供了一种解决方案,它利用了Java 8的新特性和反射机制来实现这种转换,并将这个功能封装为一个工具类。 首先,Java 8引入了许多新特性,...

    Java-Io流,练习

    在Java中,IO流被设计为处理任何类型的数据,包括字符、字节甚至对象。本练习旨在帮助初学者理解和掌握Java IO流的基础知识。 一、IO流的概念 IO流在Java中分为两大类:输入流(Input Stream)和输出流(Output ...

    jvm特性与java特性

    1. 面向对象:Java是一种纯面向对象的编程语言,它支持封装、继承和多态这三大面向对象的基本特性,为开发者构建复杂和可维护的软件系统提供了强大的支持。 2. 垃圾收集:Java提供了内置的垃圾收集机制,通过GC自动...

    关于 Java 对象序列化您不知道的 5 件事

    Java对象序列化是Java平台的一项重要特性,它允许将对象的状态转换为字节流,以便存储、传输或恢复。在本文中,我们将深入探讨关于Java对象序列化你可能不知道的五件事情,这些知识点对于理解和优化你的Java应用程序...

    java语言与面向对象程序设计作业题与参考答案汇编.doc

    【Java语言基础】 Java是一种广泛使用的面向对象编程语言,它具有严格的类型检查和垃圾...综上所述,这些题目涵盖了Java语言的基础知识,包括语法、面向对象特性和集合框架的使用,这些都是学习Java编程所必备的基础。

    Java-Java面向对象中引用传递教程

    在Java编程语言中,面向对象特性是其核心概念之一,其中引用来传递对象是一个非常重要的知识点。本教程将深入探讨Java中的引用传递机制,并通过视频教程的形式帮助学习者更好地理解和应用这一概念。 首先,理解...

    对象数组 java

    这种特性体现了Java的多态性。例如: ```java Animal[] animalArray = new Animal[3]; animalArray[0] = new Cat(); // Cat是Animal的子类 animalArray[1] = new Dog(); ``` - 这样,尽管数组声明为`Animal`...

    Java面向对象的基础(笔记)

    3. **继承**:继承是另一个重要的面向对象特性,允许一个类(子类)继承另一个类(父类)的属性和方法。这样可以提高代码的复用性,减少重复编写代码。Java中的单一继承机制使得类间的继承关系更为明确。 4. **多态...

    java深度克隆

    在Java中,深度克隆是一种用于创建对象副本的方法,它可以确保复制出的新对象与原对象之间没有任何引用关系,也就是说,新对象中的所有成员变量(包括引用类型)都是原对象对应成员变量的副本。这种特性使得深度克隆...

    java中对象的序列化

    Java对象序列化的一个关键特性是它能够处理对象引用。当一个对象被序列化时,所有被它引用的对象也会被递归地序列化。这使得整个对象层次结构可以被保存和恢复。不过,需要注意的是,并非所有类都可以被序列化,比如...

    孙卫琴java面向对象编程源码

    这个压缩包包含的源代码旨在帮助读者深入理解Java语言的核心面向对象特性,通过实际示例来阐述理论知识。下面将对其中涉及的主要知识点进行详细阐述: 1. **面向对象编程基础**:Java是一种典型的面向对象编程(OOP...

    java面向对象习题

    Java面向对象编程是Java的核心特性之一,它基于对象的概念,将数据和操作数据的方法封装在一起,使得代码更加模块化、易于维护。面向对象编程包括类、对象、继承、封装、多态等核心概念。 1. **类与对象**:类是...

    java面向对象和多线程实验

    文件操作是文档管理系统的基础,Java的`java.io`和`java.nio`包提供了丰富的文件I/O功能,包括读写文件、创建目录、复制移动文件等。在本实验中,可能涉及到文件的存储、检索和更新,需要合理运用这些API。 最后,...

    基于Java的对象模型消息机制实现

    本文介绍了一种基于Java的面向对象消息机制的实现方法,该方法主要利用了Java中的接口、继承和多态等特性来构建。通过这种方式,可以创建一种类似于微软基础类库(Microsoft Foundation Class, MFC)中消息机制的...

    《剑指offer》Java创建对象的五种方式.pdf

    在Java编程中,创建对象是程序执行的基本操作。根据标题和描述,我们将详细探讨Java创建对象的五种主要方式。 1. **使用`new`关键字创建对象** 这是最常见的创建对象的方式,通过调用类的构造器来实例化对象。例如...

Global site tag (gtag.js) - Google Analytics