Java中Object的clone()及浅拷贝、深拷贝分析
首先,看一下源码:
public class Object {
protected native Object clone() throws CloneNotSupportedException;
}
由源代码我们会发现:
第一:Object类的clone()方法是一个native方法,native方法的效率一般来说都是远高于Java中的非native方法。这也解释了为什么要用Object中clone()方法而不是先new一个类,然后把原始对象中的信息复制到新对象中,虽然这也实现了clone功能。(JNI是Java Native Interface的 缩写。从Java 1.1开始,Java Native Interface (JNI)标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的,比如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI标准至少保证本地代码能工作在任何Java 虚拟机实现下。)
第二:Object类中的 clone()方法被protected修饰符修饰。这也意味着如果要应用 clone()方 法,必须继承Object类,在 Java中所有的类是缺省继承 Object类的,也就不用关心这点了。然后重载 clone()方法。还有一点要考虑的是为了让其它类能调用这个 clone类的 clone()方法,重载之后要把 clone()方法的属性设置为 public。
第三:Object.clone()方法返回一个Object对象。我们必须进行强制类型转换才能得到我们需要的类型。
浅层复制与深层复制概念:
浅层复制: 被复制的对象的所有成员属性都有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅层复制仅仅复制所考虑的对象,而不复制它所引用的对象。(概念不好理解,请结合下文的示例去理解)
深层复制:被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不是原有的那些被引用的对象。换言之,深层复制要复制的对象引用的对象都复制一遍。
一、浅层复制
package other;
public class test3 {
//浅层复制
public static void main(String[] args) throws CloneNotSupportedException {
Dog dog = new Dog(10, "刘泽城");
Animal a = new Animal(11, "严海斌", dog);
System.out.println(a.getAge() + "," + a.getName() + "," + a.getDog().getName() + "," + a.getDog().getAge());
Animal a2 = (Animal) a.clone();
a2.setAge(13);
a2.setName("许春杰");
a2.getDog().setAge(14);
a2.getDog().setName("李旺红");
System.out.println(a.getAge() + "," + a.getName() + "," + a.getDog().getName() + "," + a.getDog().getAge());
System.out.println(a2.getAge() + "," + a2.getName() + "," + a2.getDog().getName() + "," + a2.getDog().getAge());
}
}
class Animal implements Cloneable{
private int age;
private String name;
private Dog dog;
public Animal(){}
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 Animal(int age, String name, Dog dog) {
super();
this.age = age;
this.name = name;
this.dog = dog;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
public Object clone() throws CloneNotSupportedException{
return super.clone();
}
}
class Dog implements Cloneable{
private int age;
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 Dog(int age, String name) {
super();
this.age = age;
this.name = name;
}
}
输出:
11,严海斌,刘泽城,10
11,严海斌,李旺红,14
13,许春杰,李旺红,14
二、深层复制
package other;
//深层复制
public class test4 {
public static void main(String[] args) {
B b = new B(10, "许春杰");
A a = new A(11, "李旺红", b);
System.out.println(a.getAge()+","+a.getName()+","+a.getB().getName()+","+a.getB().getAge());
A a1 = (A)a.clone();
a1.setAge(12);
a1.setName("许必宵");
a1.getB().setAge(13);
a1.getB().setName("严海斌");
System.out.println(a.getAge()+","+a.getName()+","+a.getB().getName()+","+a.getB().getAge());
System.out.println(a1.getAge()+","+a1.getName()+","+a1.getB().getName()+","+a1.getB().getAge());
}
}
class A implements Cloneable{
private int age;
private String name;
private B b;
public A(int age, String name, B b) {
super();
this.age = age;
this.name = name;
this.b = b;
}
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 B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
public Object clone(){
A o = null;
try{
o = (A)super.clone();
}catch(Exception e){
e.printStackTrace();
}
o.b = (B)b.clone();
return o;
}
}
class B implements Cloneable{
private int age;
private String name;
public B(int age, String name) {
super();
this.age = age;
this.name = 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 Object clone(){
Object o = null;
try{
o = super.clone();
}catch(Exception e){
e.printStackTrace();
}
return o;
}
}
输出:
11,李旺红,许春杰,10
11,李旺红,许春杰,10
12,许必宵,严海斌,13
三、利用串行化深层复制
把对象写到流中的过程是串行化(Serilization)过程,而把对象从流中读出来是并行化(Deserialization)过程。应当指出的是,写在流中的是对象的一个拷贝,而原来对象仍然存在JVM里面。
在Java语言里深层复制一个对象,常常可以先使对象实现Serializable接口,然后把对象(实际上只是对象的一个拷贝)写到一个流中,再从流中读出来,便可以重建对象。
这样做的前提是对象以及对象内部所有引用到的对象都是可串行化的,否则,就需要仔细考察那些不可串行化的对象是否设成transient,从而将之排除在复制过程之外。代码改进如下:
package other;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
//利用串行化实现对象深层复制,这样做的前提是对象以及对象内部所有引用到的对象都是可串行化的
public class test5 {
public static void main(String[] args) throws Exception {
D d = new D(10, "刘泽城");
C c = new C(11, "许春杰", d);
System.out.println(c.getAge()+","+c.getName()+","+c.getD().getAge()+","+c.getD().getName());
C c1 = (C)c.deepClone();
c1.setAge(12);
c1.setName("严海斌");
c1.getD().setAge(13);
c1.getD().setName("李旺红");
System.out.println(c.getAge()+","+c.getName()+","+c.getD().getAge()+","+c.getD().getName());
System.out.println(c1.getAge()+","+c1.getName()+","+c1.getD().getAge()+","+c1.getD().getName());
}
}
class C implements Serializable{
private int age;
private String name;
private D d;
public C(){}
public C(int age, String name, D d) {
super();
this.age = age;
this.name = name;
this.d = d;
}
public D getD() {
return d;
}
public void setD(D d) {
this.d = d;
}
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 Object deepClone() throws Exception{
//将对象写到流中
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(bo);
oo.writeObject(this);
//从流中读出来
ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
ObjectInputStream oi = new ObjectInputStream(bi);
return oi.readObject();
}
}
class D implements Serializable{
private int age;
private String name;
public D(){}
public D(int age, String name) {
super();
this.age = age;
this.name = 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;
}
}
输出:
11,许春杰,10,刘泽城
11,许春杰,10,刘泽城
12,严海斌,13,李旺红