简介:
Java克隆(Clone)是Java语言的特性之一,但在实际中应用比较少见。但有时候用克隆会更方便更有效率。
对于克隆(Clone),Java有一些限制:
1、被克隆的类必须自己实现Cloneable 接口,以指示 Object.clone() 方法可以合法地对该类实例进行按字段复制。Cloneable 接口实际上是个标识接口,没有任何接口方法。
2、实现Cloneable接口的类应该使用公共方法重写 Object.clone(它是受保护的)。某个对象实现了此接口就克隆它是不可能的。即使 clone 方法是反射性调用的,也无法保证它将获得成功。
3、在Java.lang.Object类中克隆方法是这么定义的:
protected Object clone()
throws CloneNotSupportedException
创建并返回此对象的一个副本。表明是一个受保护的方法,同一个包中可见。
按照惯例,返回的对象应该通过调用 super.clone 获得。
引题:
举个例子说吧,现在有一个对象比如叫foo,你需要在创建当前对象的一个副本作为存根你能怎么做?
假如你不用Clone,那么你可以先new一个对象foo1:Foo foo1=new Foo(),然后用foo给foo1对象set值,这样就得到foo的副本foo1;除此之外,别无选择。
这样说,也许有人会觉得说的过于绝对了,不过事实如此啊。
要产生一个副本,那副本要不要内存?----当然要了,那就对了!既然需要内存,(不克隆的情况下)你不new还有什么办法呢?请大家时刻铭记对象是Java运行时产生的,驻留在计算机内存中。
常见错误:
下面我澄清几个初学者容易犯迷糊的错误,同样的问题,产生foo对象的副本:
1、Foo foo1=new Foo();
foo1=foo;
然后就想当然的认为副本foo1生成了!
错误原因:foo1没错是申请了内存,但是执行foo1=foo后,foo1就不在指向刚申请的内存区域了,转而指向foo对象的内存区域,这时候,foo1、foo指向了同一内存区域。刚才new的操作制造一堆垃圾等着JVM回收。
2、Foo foo1=foo;
错误原因:还是两个变量都指向了同一块内存。
3、有些老鸟更厉害一些:在Foo中定义一个返回自身的方法:
public Foo getInstance(){
return this;
}
然后,Foo foo1=foo.getInstance();
错误原因:同上,主要还是没有重新开辟内存,this在对象里是什么?----就是对象自己的引用!那么getInstance()自然返回的就是对象自己,反正又是两个对象穿了一条裤子----***,哈哈。错得心服口服吧。为了节省篇幅,我在最后写个例子,留给那些对此有异议的人看。
引入克隆
看了这么多方法都不行,还很麻烦!干脆用克隆吧,简单明了。
废话不说了,看例子:
定义两个类CloneFooA、CloneFooB,然后写个测试类CloneDemo分别克隆这两个类的对象,然后打印测试结果到控制台。
/**
* Created by IntelliJ IDEA.
* User: leizhimin
* Date: 2007-9-20
* Time: 19:40:44
* 简单类克隆实现
* 要实现克隆,必须实现Cloneable接口,这是一个标识接口,没有接口方法
* 实现了 Cloneable 接口,以指示 Object.clone() 方法可以合法地对该类实例进行按字段复制。
* 按照惯例,实现此接口的类应该使用公共方法重写 Object.clone(它是受保护的)。
*/
public class CloneFooA implements Cloneable {
private String strA;
private int intA;
public CloneFooA(String strA, int intA) {
this.strA = strA;
this.intA = intA;
}
public String getStrA() {
return strA;
}
public void setStrA(String strA) {
this.strA = strA;
}
public int getIntA() {
return intA;
}
public void setIntA(int intA) {
this.intA = intA;
}
/**
* @return 创建并返回此对象的一个副本。
* @throws CloneNotSupportedException
*/
public Object clone() throws CloneNotSupportedException {
//直接调用父类的clone()方法,返回克隆副本
return super.clone();
}
}
/**
* Created by IntelliJ IDEA.
* User: leizhimin
* Date: 2007-9-20
* Time: 19:59:55
* 深度克隆对象,当类存在聚合关系的时候,克隆就必须考虑聚合对象的克隆
*/
public class CloneFooB implements Cloneable {
private CloneFooA fooA;
private Double douB;
public CloneFooB(Double douB) {
this.douB = douB;
}
public CloneFooB(CloneFooA fooA, Double douB) {
this.fooA = fooA;
this.douB = douB;
}
public CloneFooA getFooA() {
return fooA;
}
public void setFooA(CloneFooA fooA) {
this.fooA = fooA;
}
public Double getDouB() {
return douB;
}
public void setDouB(Double douB) {
this.douB = douB;
}
/**
* 克隆操作
*
* @return 自身对象的一个副本
* @throws CloneNotSupportedException
*/
public Object clone() throws CloneNotSupportedException {
//先调用父类的克隆方法进行克隆操作
CloneFooB cloneFooB = (CloneFooB) super.clone();
//对于克隆后出的对象cloneFooB,如果其成员fooA为null,则不能调用clone(),否则出空指针异常
if (this.fooA != null)
cloneFooB.fooA = (CloneFooA) this.fooA.clone();
return cloneFooB;
}
}
/**
* Created by IntelliJ IDEA.
* User: leizhimin
* Date: 2007-9-20
* Time: 19:52:01
* 测试类:分别克隆CloneFooA和CloneFooB类,并打印克隆前后的结果.
*/
public class CloneDemo {
public static void main(String args[]) throws CloneNotSupportedException {
//CloneFooA克隆前
CloneFooA fooA1 = new CloneFooA("FooA", 11);
System.out.println("CloneFooA的对象克隆前对象fooA1值为: " + fooA1.getStrA() + "," + fooA1.getIntA());
//CloneFooA克隆后
CloneFooA fooA2 = (CloneFooA) fooA1.clone();
System.out.println("CloneFooA的对象克隆后对象fooA2值为: " + fooA2.getStrA() + "," + fooA2.getIntA());
//比较fooA1和fooA2内存地址
if (fooA1 == fooA2) System.out.println("比较fooA1和fooA2内存地址:相等!");
else System.out.println("比较fooA1和fooA2内存地址:不相等!");
System.out.println("-------------------------");
//CloneFooB克隆前
CloneFooB fooB1 = new CloneFooB(fooA1, new Double("33"));
System.out.println("CloneFooB的对象克隆前对象fooB1值为: " + fooB1.getFooA().getStrA() + "," + fooB1.getFooA().getIntA() + " | " + fooB1.getDouB());
//CloneFooB克隆后
CloneFooB fooB2 = (CloneFooB) fooB1.clone();
System.out.println("CloneFooB的对象克隆前对象fooB2值为: " + fooB2.getFooA().getStrA() + "," + fooB2.getFooA().getIntA() + " | " + fooB2.getDouB());
if (fooA1 == fooA2) System.out.println("比较fooB1和fooB2内存地址:相等!");
else System.out.println("比较fooB1和fooB2内存地址:不相等!");
}
}
运行结果:
CloneFooA的对象克隆前对象fooA1值为: FooA,11
CloneFooA的对象克隆后对象fooA2值为: FooA,11
比较fooA1和fooA2内存地址:不相等!
-------------------------
CloneFooB的对象克隆前对象fooB1值为: FooA,11 | 33.0
CloneFooB的对象克隆前对象fooB2值为: FooA,11 | 33.0
比较fooB1和fooB2内存地址:不相等!
Process finished with exit code 0
反面教材:
最后,我给出我上面提出到最后要给出的反面例子。
随便写一个,在CloneFooA 的基础上做了少许改动,内容如下:
public class CloneFooA implements Cloneable {
private String strA;
private int intA;
public CloneFooA(String strA, int intA) {
this.strA = strA;
this.intA = intA;
}
public String getStrA() {
return strA;
}
public void setStrA(String strA) {
this.strA = strA;
}
public int getIntA() {
return intA;
}
public void setIntA(int intA) {
this.intA = intA;
}
/**
* @return 创建并返回此对象的一个副本。
* @throws CloneNotSupportedException
*/
public Object clone() throws CloneNotSupportedException {
//直接调用父类的clone()方法,返回克隆副本
return super.clone();
}
/**
* @return 返回运行时的对象
*/
public CloneFooA getInstance(){
return this;
}
public static void main(String args[]){
CloneFooA fooA=new CloneFooA("aa",11);
System.out.println(fooA.getStrA()+" "+fooA.getIntA());
CloneFooA fooA1=fooA.getInstance();
System.out.println(fooA1.getStrA()+" "+fooA1.getIntA());
if(fooA==fooA1) System.out.println("fooA和fooA1内存地址相等!");
System.out.println("-------------------------");
//改变后fooA或者fooA1中任何一个,看看另外一个是否会改变
fooA1.setStrA("bb");
System.out.println(fooA.getStrA()+" "+fooA.getIntA());
System.out.println(fooA1.getStrA()+" "+fooA1.getIntA());
if(fooA==fooA1) System.out.println("fooA和fooA1内存地址相等,改变fooA1后,fooA的值也跟着变化了");
}
}
运行结果:
aa 11
aa 11
fooA和fooA1内存地址相等!
-------------------------
bb 11
bb 11
fooA和fooA1内存地址相等,改变fooA1后,fooA的值也跟着变化了
Process finished with exit code 0
分享到:
相关推荐
在Java编程语言中,`clone()`方法是一个非常重要的概念,特别是在对象复制和克隆方面。这个小例子将帮助初学者理解如何在Java中使用`clone()`来创建对象的副本。让我们深入探讨`clone()`方法以及它在实际编程中的...
首先,Java提供了两种基本的克隆方式:浅克隆(shallow clone)和深克隆。浅克隆仅仅复制对象本身,而不复制对象所引用的对象。而深克隆则会递归地复制对象及所有引用的对象,确保新创建的对象和原始对象及其引用的...
Java代码克隆,通常指的是在编程过程中,两个或多个代码段具有相似或完全相同的功能,这种现象被称为代码重复或代码克隆。在大型项目中,代码克隆可能会导致维护困难、增加bug出现的可能性以及降低代码的可读性和可...
实现深克隆通常需要自定义实现,因为Java的`clone()`方法不支持自动深度克隆。一种常见的实现方法是序列化和反序列化对象。首先,将对象序列化为字节数组,然后从字节数组反序列化为新的对象。这种方法可以保证所有...
本资料"Java中clone方法共6页.pdf.zip"可能包含了关于如何理解和使用`clone()`方法的详细解释,以及它在实际开发中的应用示例。 `clone()`方法的主要用途是创建一个现有对象的副本,这个副本与原始对象具有相同的...
在Java中,实现深度克隆通常有两种方式:一是通过实现Cloneable接口并重写Object类的clone()方法;二是使用序列化和反序列化技术。前者需要特别注意的是,只有实现了Cloneable接口的类才能调用默认的clone()方法,...
在Java编程语言中,克隆和序列化是两个...总结来说,Java中的克隆和序列化是两个强大的工具,它们在数据持久化、对象复制以及网络通信等方面有着广泛的应用。理解和熟练掌握这两个概念,对于Java开发者来说至关重要。
Java中的对象复制与克隆是程序开发中常见的需求,主要用于创建一个对象的副本,而不会影响原始对象的状态。这里主要讨论两种类型的对象拷贝:浅拷贝和深拷贝。 浅拷贝,也称为表面拷贝,是创建一个新的对象,然后将...
本文将深入探讨Java中的引用以及对象的克隆机制,包括浅拷贝和深拷贝的区别及其应用场景。 #### 二、Java中的引用 在Java中,当使用`new`关键字创建一个对象时,实际上创建的是一个指向该对象的引用。例如,以下...
本文将详细介绍Java中的深度克隆技术,包括其实现原理及应用场景。 #### 二、深度克隆的重要性 在实际开发中,深度克隆具有重要意义: 1. **确保数据独立性**:深度克隆可以创建出与原对象完全独立的新对象,即使...
Java提供了两种主要的克隆方式:浅克隆(Shallow Clone)和深克隆(Deep Clone)。下面我们将深入探讨这两种克隆方法。 ### 浅克隆 浅克隆是指创建一个新的对象,其成员变量的值与原对象相同,但引用类型成员变量...
在Java中,克隆主要涉及到`Object`类中的`clone()`方法,以及两种不同的克隆类型:浅克隆和深克隆。 一、克隆的原理与应用 `clone()`方法的工作原理是在堆上创建一个新的对象,这个新对象的内存分配与源对象相同,...
在Java中,克隆分为两种类型:浅克隆(Shallow Clone)和深克隆(Deep Clone)。浅克隆创建的新对象只复制了原对象的引用,这意味着如果原对象中包含可变的引用类型,如数组或复杂的数据结构,那么新对象和原对象会...
下面我们将详细介绍Java中的clone方法,并讨论它的实现机制和应用场景。 什么是clone方法 clone方法顾名思义,就是复制,在Java语言中,clone方法被对象调用,所以会复制对象。所谓的复制对象,首先要分配一个和源...
在Java编程语言中,`clone()`方法是一个非常重要的概念,特别是在处理对象复制和数据持久化时。本示例将深入探讨`clone()`方法的工作原理、使用场景以及如何在实际编程中应用。首先,我们来理解一下`clone()`方法的...
1. `clone`方法抛出`CloneNotSupportedException`,这意味着如果你尝试克隆一个没有实现`Cloneable`接口的对象,将会抛出异常。 2. 默认的`clone`方法创建的对象具有与原对象相同的类,且所有的字段值都相同(原始...
Java 提供了一个特殊的 clone() 方法,为所有的引用类型提供了一套标准的克隆机制。 Java 中的对象克隆可以分为两种类型:浅克隆(Shallow Cloning)和深克隆(Deep Cloning)。 浅克隆是指创建一个新的对象,并将...