1. Overiew
在实际编程中,我们经常会遇到这样一个情景:有一个对象A,存在属性方法,现在需要一个和A完全相同的新对象B,并且B的任何改动都不会影响到A中的值。那么,最常用的办法就是对A进行克隆。
2. How to Clone
在java.lang.Object中有一个clone方法,该方法的签名如下:
protected native Object clone() throws CloneNotSupportedException;
该方法返回一个Object实例的拷贝,该实例拷贝具有:
1)拷贝对象是一个新对象而不是一个原对象引用。
2)拷贝的对象中包含的是原有对象信息,而不是对象初始化的信息(也就是说,拷贝过程没有调用构造函数)。
从第1条我们可以看出,拷贝的对象与原对象存在这样一种关系:
1. x.clone() != x will be true
2. x.clone().getClass() == x.getClass() will be true, but these are not absolute requirements
3. x.clone().equals(x) will be true, but these are not absolute requirements
要实现克隆,必须满足以下三点:
1)类必须实现java.lang.Cloneable接口
2)类必须重载Object类中的clone()方法
3)重载的clone()方法中,必须显示调用super.clone()。
我们看一下Cloneable接口的定义:
public interface Cloneable { }
Cloneable接口是不包含任何方法的,仅仅表示一个标志(类似Serializable接口),而且这个标志也是针对Object类中clone()方法的,如果clone类没有实现Cloneable接口,并调用了Object的clone()方法(也就是调用了super.Clone()方法),那么Object的clone()方法就会抛出CloneNotSupportedException异常。
按照要求,我们写一个克隆类的小例子:
public class DummyClone implements Cloneable{
public Object clone(){
DummyClone dc = null;
try {
dc = (DummyClone)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return dc;
}
}
3. Shadow Clone
首先,我们来看一个例子:ClassA没有实现Cloneable接口。ClassB存在ClassA的实例,实现了Cloneable接口,并重载了clone方法。ClassC实例化一个ClassB对象,然后克隆一个对象。
class ClassA {
public int a;
public void doubleA(){
a = a * 2;
}
public ClassA(int aa){
this.a = aa;
}
public String toString(){
return Integer.toString(a);
}
}
class ClassB implements Cloneable{
public int b;
public ClassA ca = new ClassA(11);
public Object clone(){
ClassB cb = null;
try {
cb = (ClassB)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return cb;
}
}
public class ClassC {
public static void main(String[] args) {
ClassB cb = new ClassB();
cb.b = 222;
System.out.println("before clone : cb.b = " + cb.b);
System.out.println("before clone : cb.ca = " + cb.ca);
//
ClassB cb2 = (ClassB)cb.clone();
cb2.b = 3333;
cb2.ca.doubleA();
System.out.println("========================");
System.out.println("after clone : cb.b = " + cb.b);
System.out.println("after clone : cb.ca = " + cb.ca);
System.out.println("========================");
System.out.println("after clone : cb2.b = " + cb2.b);
System.out.println("after clone : cb2.ca = " + cb2.ca);
}
}
输出结果如下:
before clone : cb.b = 222
before clone : cb.ca = 11
========================
after clone : cb.b = 222
after clone : cb.ca = 22
========================
after clone : cb2.b = 3333
after clone : cb2.ca = 22
从结果我们看到,int类型的b被完全的克隆了,而ClassA类型的ca却没有被克隆。因为克隆后cb2对b的赋值不会影响原有cb中b的值,而调用cb2.ca.doubleA()方法后,对cb2.ca的改变同时改变了cb.ca,表明cb2.ca与cb.ca仅仅指向同一个对象的不同引用。从中可以看出,调用Object类中clone()方法产生的效果是:先在内存中开辟一块和原始对象一样的空间,然后原样拷贝原始对象中的内容。对基本数据类型,这样的操作是没有问题的,但对非基本类型变量clone后的变量和原始对象中相应的变量指向的是同一个对象。
这就是影子克隆。影子克隆,并没有完整的完成克隆,有时候这并不是我们想要的结果。我们有时需要,就像列子中调用cb2.ca.doubleA()方法时,不会对原对象产生改变,这时候我们就需要deep clone;
4. Deep Clone
要实现深度克隆,在上面的例子基础上,我们只需要这样修改即可:
class ClassA implements Cloneable{
public int a;
public void doubleA(){
a = a * 2;
}
public ClassA(int aa){
this.a = aa;
}
public String toString(){
return Integer.toString(a);
}
public Object clone(){
ClassA ca = null;
try {
ca = (ClassA)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return ca;
}
}
class ClassB implements Cloneable{
public int b;
public ClassA ca = new ClassA(11);
public Object clone(){
ClassB cb = null;
try {
cb = (ClassB)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
cb.ca = (ClassA)ca.clone();
return cb;
}
}
public class ClassC {
public static void main(String[] args) {
ClassB cb = new ClassB();
cb.b = 222;
System.out.println("before clone : cb.b = " + cb.b);
System.out.println("before clone : cb.ca = " + cb.ca);
//
ClassB cb2 = (ClassB)cb.clone();
cb2.b = 3333;
cb2.ca.doubleA();
System.out.println("========================");
System.out.println("after clone : cb.b = " + cb.b);
System.out.println("after clone : cb.ca = " + cb.ca);
System.out.println("========================");
System.out.println("after clone : cb2.b = " + cb2.b);
System.out.println("after clone : cb2.ca = " + cb2.ca);
}
}
输出结果:
before clone : cb.b = 222
before clone : cb.ca = 11
========================
after clone : cb.b = 222
after clone : cb.ca = 11
========================
after clone : cb2.b = 3333
after clone : cb2.ca = 22
5. BeanUtils
在Apache的common项目下,BeanUtils子项目中,org.apache.commons.beanutils.BeanUtils中的cloneBean(Object bean)也实现了对象的克隆。其方法签名如下:
public static Object cloneBean(Object bean)
throws IllegalAccessException,
InstantiationException,
InvocationTargetException,
NoSuchMethodException
它对于bean的要求是,只要内部包含属性的get和set方法即可(不需要实现Cloneable接口)。它的内部实现是基于java.bean.PropertyDescriptor。首先通过get方法获取原始bean属性的value,然后对应set到克隆后bean的属性中。
public class Person {
private int age = 3;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class TestBeanUtils {
public static void main(String[] args) {
try {
Person original = new Person();
Person copy = (Person) BeanUtils.cloneBean(original);
System.out.println("original.age = " + original.getAge());
copy.setAge(2);
System.out.println("copy.age = " + copy.getAge());
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
6. Cloning Immutable Objects
基本的数据类型可以自动的实现深度的克隆,然而并不是所有的类都可以进行深度的克隆,就比如String类,其类定义为final,且没有重载clone方法,我们就无法完成对String类的深度克隆。关于不可变对象的克隆,我们通常没有必要去克隆。
分享到:
相关推荐
标签:robust、cloning、中文文档、jar包、java; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译,文档中的代码和结构保持不变,注释和说明精准翻译,请放心使用...
Object Cloning 249 Interfaces and Callbacks 255 Inner Classes 258 Proxies 275 Chapter 7: Graphics Programming 281 Introducing Swing 282 Creating a Frame 285 Positioning a Frame 288 ...
标签:robust、cloning、中英对照文档、jar包、java; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译,文档中的代码和结构保持不变,注释和说明精准翻译,请放心...
Real-Time-Voice-Cloning-master项目包。
Paper - Automatic Multispeaker Voice Cloning
CorentinJ/Real-Time-Voice-Cloning项目训练模型,包含encoder、synthesizer、vocoder三部分。
Input/Output, Serialization and Cloning Chapter 10. Generics, java.util and other API Chapter 11. Network Programming Chapter 12. Applets Chapter 13. Event Handling in Java Chapter 14. Abstract ...
Object Cloning 249 Interfaces and Callbacks 255 Inner Classes 258 Proxies 275 Chapter 7: Graphics Programming 281 Introducing Swing 282 Creating a Frame 285 Positioning a Frame 288 Displaying...
Packt.Cloning.Internet.Applications.with.Ruby
用于驾驶员行为建模的Python源代码,供各位参考。
Structural Cloning by powerpoint by AMA
Chapter 1: Plasmids and Their Usefulness in Molecular Cloning Chapter 2: Bacteriophage and Its Vectors Chapter 3: Working with Bacteriophage M13 Vectors Chapter 4: Working with High-Capacity Vectors ...
Molecular cloning and functional characterization of apple MdPIF1,Li Yuanyuan,Qiao Yu,In the angiosperm seed, the decision to germinate is dependent on the interaction of various environmental ...
erage of cloning and equivalence testing in the context of data structures. • A new chapter, dedicated to the topic of recursion, provides comprehensive coverage of material that was previously ...
选修6 Module 5 Cloning语篇提能练习题及答案解析6份12精选.doc
行为克隆是模仿学习经典算法之一。本文主要介绍行为克隆的框架。
Real-Time-Voice-Cloning-master2.zip源代码
第1章 Java概述、安装及简易教学 14 1-1 Java概述 14 1-2 Java安装 16 1-3 Eclipse安装 18 1-4 GUI设计工具WindowBuilder 18 1-5 在Eclipse开发第一个Java程式 23 1-6 在Eclipse开发第一个Java视窗程式-显示影像 26 ...
Cloning and Cryptography with Quantum Continuous Variables
EBS12-Advanced Cloning Options