Java中怎么拷贝一个对象呢?可以通过调用这个对象类型的构造器构造一个新对象,然后将要拷贝对象的属性设置到新对象里面。Java中也有另一种不通过构造器来拷贝对象的方式,这种方式称为
克隆。
Java提供了java.lang.Cloneable和java.lang.Object中的clone()方法来支持克隆。Cloneable接口表示一种可以克隆的行为或特性,但这个接口中却没有提供clone方法(Cloneable接口中没有任何方法,看起来更像是一个标记接口)。
package java.lang;
/**
* A class implements the <code>Cloneable</code> interface to
* indicate to the {@link java.lang.Object#clone()} method that it
* is legal for that method to make a
* field-for-field copy of instances of that class.
* <p>
* Invoking Object's clone method on an instance that does not implement the
* <code>Cloneable</code> interface results in the exception
* <code>CloneNotSupportedException</code> being thrown.
* <p>
* By convention, classes that implement this interface should override
* <tt>Object.clone</tt> (which is protected) with a public method.
* See {@link java.lang.Object#clone()} for details on overriding this
* method.
* <p>
* Note that this interface does <i>not</i> contain the <tt>clone</tt> method.
* Therefore, it is not possible to clone an object merely by virtue of the
* fact that it implements this interface. Even if the clone method is invoked
* reflectively, there is no guarantee that it will succeed.
*
* @author unascribed
* @version %I%, %G%
* @see java.lang.CloneNotSupportedException
* @see java.lang.Object#clone()
* @since JDK1.0
*/
public interface Cloneable {
}
相反在Object中却存在一个clone方法,这一点很奇怪。
/**
* Creates and returns a copy of this object. The precise meaning
* of "copy" may depend on the class of the object. The general
* intent is that, for any object <tt>x</tt>, the expression:
* <blockquote>
* <pre>
* x.clone() != x</pre></blockquote>
* will be true, and that the expression:
* <blockquote>
* <pre>
* x.clone().getClass() == x.getClass()</pre></blockquote>
* will be <tt>true</tt>, but these are not absolute requirements.
* While it is typically the case that:
* <blockquote>
* <pre>
* x.clone().equals(x)</pre></blockquote>
* will be <tt>true</tt>, this is not an absolute requirement.
* <p>
* By convention, the returned object should be obtained by calling
* <tt>super.clone</tt>. If a class and all of its superclasses (except
* <tt>Object</tt>) obey this convention, it will be the case that
* <tt>x.clone().getClass() == x.getClass()</tt>.
* <p>
* By convention, the object returned by this method should be independent
* of this object (which is being cloned). To achieve this independence,
* it may be necessary to modify one or more fields of the object returned
* by <tt>super.clone</tt> before returning it. Typically, this means
* copying any mutable objects that comprise the internal "deep structure"
* of the object being cloned and replacing the references to these
* objects with references to the copies. If a class contains only
* primitive fields or references to immutable objects, then it is usually
* the case that no fields in the object returned by <tt>super.clone</tt>
* need to be modified.
* <p>
* The method <tt>clone</tt> for class <tt>Object</tt> performs a
* specific cloning operation. First, if the class of this object does
* not implement the interface <tt>Cloneable</tt>, then a
* <tt>CloneNotSupportedException</tt> is thrown. Note that all arrays
* are considered to implement the interface <tt>Cloneable</tt>.
* Otherwise, this method creates a new instance of the class of this
* object and initializes all its fields with exactly the contents of
* the corresponding fields of this object, as if by assignment; the
* contents of the fields are not themselves cloned. Thus, this method
* performs a "shallow copy" of this object, not a "deep copy" operation.
* <p>
* The class <tt>Object</tt> does not itself implement the interface
* <tt>Cloneable</tt>, so calling the <tt>clone</tt> method on an object
* whose class is <tt>Object</tt> will result in throwing an
* exception at run time.
*
* @return a clone of this instance.
* @exception CloneNotSupportedException if the object's class does not
* support the <code>Cloneable</code> interface. Subclasses
* that override the <code>clone</code> method can also
* throw this exception to indicate that an instance cannot
* be cloned.
* @see java.lang.Cloneable
*/
protected native Object clone() throws CloneNotSupportedException;
Object中的clone方法为受保护方法。既然所有的对象都存在一个clone方法,那是不是都可以调用(可通过反射)这个方法来得到克隆对象呢?clone方法有如下规定:
如果一个对象没有实现Cloneable接口,调用它的clone方法会抛出CloneNotSupportedException异常。但数组例外,可以认为数组实现了Cloneable,所以可以直接调用其clone方法获取克隆对象。
我们开发中应该怎样使用克隆呢?一般认为,如果希望一个类拥有克隆的行为,要做一下几步:
①该类实现java.lang.Cloneable接口。
②覆盖Object的clone方法,将访问修饰符改为public,修改返回类型。
③clone方法内部调用父类的clone方法。
示例如下:
public class CloneableObj implements Cloneable{
//...
@Override
public CloneableObj clone(){
try {
return (CloneableObj) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
按照Object克隆方法描述中的规范,克隆得到对象应该独立于被克隆对象,从根本上说,它们的内存地址(不严格的说)应该不同。但是如果有这种情况,一个可克隆类中包含其他的引用类型属性,那么克隆后会是什么情况呢?举个例子说明一下:
public class MyObj implements Cloneable{
private MyPro myPro;
public MyPro getMyPro() {
return myPro;
}
public void setMyPro(MyPro myPro) {
this.myPro = myPro;
}
@Override
public MyObj clone(){
try {
return (MyObj) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
public class MyPro {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Test {
public static void main(String[] args) {
MyObj myObj = new MyObj();
MyPro myPro = new MyPro();
myPro.setName("aa");
myObj.setMyPro(myPro);
MyObj clone = myObj.clone();
//myObj和clone是否指向相同的内存地址
System.out.println(clone == myObj);
//myObj的myPro和clone的myPro是否指向相同的内存地址
System.out.println(clone.getMyPro() == myObj.getMyPro());
}
}
运行结果:
false
true
可见,虽然克隆对象与被克隆对象内存地址不同,但他们的引用类型属性却指向相同的内存地址。这便涉及到了
浅克隆和
深克隆的问题。
上面的情况就是浅克隆,可见clone方法默认的行为就是浅克隆。那么也很容易想到,如果克隆一个对象,并递归的克隆其所有的引用类型属性,这样的方式就是深克隆了。
对于一个复杂的类,如果需要提供克隆方法的话,一般会先调用父类的clone方法,然后将相应的域(属性)设置为相应的值(有些域需要保存状态,有些域需要清空。)或者将域清空然后调用内部方法来初始化状态,并且要在方法注释上说明该方法的行为是深克隆还是浅克隆。如HashMap的克隆方法:
/**
* Returns a shallow copy of this <tt>HashMap</tt> instance: the keys and
* values themselves are not cloned.
*
* @return a shallow copy of this map
*/
public Object clone() {
HashMap<K,V> result = null;
try {
result = (HashMap<K,V>)super.clone();
} catch (CloneNotSupportedException e) {
// assert false;
}
result.table = new Entry[table.length];
result.entrySet = null;
result.modCount = 0;
result.size = 0;
result.init();
result.putAllForCreate(this);
return result;
}
分享到:
相关推荐
Java 基础知识点总结 Java 基础学习难点是一系列重要的知识点,掌握这些知识点对于 Java 开发者来说非常重要。下面是对 Java 基础学习难点的总结: 1. 接口和继承: * 接口不能被继承,但是可以被实现多次 * 类...
1. **Java编程基础**: Java是一种广泛使用的面向对象的编程语言,具有跨平台、性能高效和安全稳定等特点。在实习网站项目中,Java可能被用来处理后端逻辑,包括用户认证、数据处理、业务逻辑等。 2. **JSP(Java...
- 使用Git将fabric-java-sdk仓库克隆到本地: ```bash git clone https://github.com/hyperledger/fabric-sdk-java.git cd fabric-sdk-java git checkout v1.0.1 ``` 2. **编译fabric-java-sdk** - 在fabric...
Java编程语言中的java.lang包是一个核心包,它提供了Java语言运行时的基础类库,包含了Java程序运行的基本数据类型、异常处理类以及一些用于系统级别的工具类和接口。java.lang包中的类和接口不需要程序员显示地导入...
GRPC-Java 源码环境构建是一个相对复杂的过程,需要具备一定的技术基础和经验。本文将详细介绍 GRPC-Java 源码环境构建的步骤和过程,旨在帮助读者快速搭建 GRPC-Java 源码环境。 一、Clone GRPC-Java 源码 GRPC-...
【压缩包子文件的文件名称列表】:"java-swing-addressbook-master"可能是一个GitHub仓库的克隆或下载,其中“master”分支代表项目的主分支,通常包含项目的主要和稳定版本。这个压缩包很可能包含了项目的所有...
5. **Java编程**:无论是使用AWS RDS SDK还是理解Java 8解析器,都需要扎实的Java编程基础,包括类、对象、异常处理、多线程、集合框架等。 通过学习和使用这个压缩包中的内容,开发者可以深入了解如何利用Java与...
Java基础知识包括Java语言的基本构成、编程规范、面向对象的概念等,而面向对象是Java语言的核心特性之一,本篇文章将从Java基础知识和面向对象两大块内容出发,重点梳理Java中的编程习惯、代码结构、关键字使用以及...
【Java-Tetris-Game: Java Tetris游戏克隆源代码】 这个项目是一个基于Java语言实现的俄罗斯方块游戏,其源代码可供学习者研究和理解。使用Java 12作为开发环境,该项目遵循了良好的编程实践,为初学者提供了一个...
本书主要针对那些已经具备一定Java编程基础的开发者。对于希望深入了解设计模式并在实际项目中有效运用它们的开发者来说,《Design Patterns Java Workbook》是一个非常有价值的资源。通过学习本书,开发者能够提升...
首先,确保你的Linux系统上安装了必要的基础工具,如GCC编译器、make、cmake等。这些是构建C++项目的基本需求。你可能还需要安装一些开发库,例如libcurl-dev、libjsoncpp-dev、libgeos-dev等,因为GDAL依赖于这些库...
总之,Git 是 Java 开发者不可或缺的工具,它简化了代码版本控制,提高了团队协作效率,并确保了代码的安全性。通过熟练掌握 Git 的使用,开发者可以更好地管理项目,追踪代码变更,以及高效地协同工作。
克隆 git 仓库: git clone https://github.com/2nis6mon/dojo-java8.git 在 IDE 上打开项目 运行项目中的所有测试。 如果它们是绿色的,您就可以开始了。 练习 1:Lambda 基础知识 结帐« java8-1 »分支 git ...
java-tronJava实现 •••••什么是TRON? TRON是一个致力于为真正的去中心化Internet构建基础设施的项目。 Tron协议是世界上最大的基于区块链的操作系统之...部署请在克隆项目后再java-tron Run java-tron部署方式向
在Java编程语言中,`java.lang`包是所有Java程序的基础,因为它包含了许多核心类和接口,这些类和接口是编写任何Java应用程序所必需的。由于这个包是自动导入的,因此程序员无需在代码中显式地导入`java.lang`。 1....
在开始编译之前,确保你的Linux系统安装了以下基础工具和库: - GCC编译器 - CMake构建系统 - Java开发工具包(JDK) - OpenSSL库(对于某些GDAL驱动可能需要) - 安装必要的依赖库,如proj,geos,zlib,...
流浪者-javadev-box 使用通过 puppet 安装的 Jdk、... 克隆这个 repo git clone https://github.com/rob-murray/vagrant-javadev-box.git 初始化子模块git submodule update --init 全部做完; vagrant up和 box 将
### Java基础知识点汇总 ...以上内容总结了Java基础知识点汇总中的一些核心概念,涵盖了变量的存储位置、泛型的基础、静态变量的应用场景、克隆机制以及数组的基本特性。这些知识点对于理解和使用Java语言至关重要。
1. **Java基础** - **语法**:Java的语法与C++相似,但更加简化,比如自动内存管理(垃圾回收)。 - **类与对象**:Java是面向对象的语言,程序由类组成,类是对象的模板。 - **封装、继承和多态**:面向对象的三...