您的位置:首页 > 编程语言 > Java开发

Java的深克隆与浅克隆

2016-05-06 10:42 387 查看
1.Java的基本数据类型和引用数据类型

基本数据类型包括byte、int、char、long、float、double、boolean和short八种基本数据类型 创建的对象在Java虚拟机栈中,

引用数据类型创建对象的时候会创建两个对象,一个在栈中,一般称作 '‘引用“,另一个存放在Java堆中。

2.浅克隆与深克隆

浅克隆的外在表现是:对象的基础类型对象相同,引用对象相同,包括存放在堆里的对象。也就是说所有的对其他对象的引用仍然指向原来的对象。

深克隆的外在表现是:复制前后的对象是相等的,也就是说引用的对象是相等的,而不是同一块Java堆中的内存。

对于对象来说相同和相等是充分不必要条件,相同一定相等,相等确往往可能不相同。

浅克隆是发生在栈中的拷贝,深克隆包括堆中对象的引用对象的拷贝。

3.Java对象的clone研究

<span style="font-family:Courier New;">package com.dusk.bean;

public class Student implements Cloneable{
private String id;
private Address address;
public Student(){
}
public Student(String id){
this.id=id;
}
public String getId() {
return id;
}

public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public String toString() {
return "Student [id=" + id + "]";
}

public void sayHello(){
System.out.println("hello,everyone!");
}
@Override
public Student clone() throws CloneNotSupportedException {
return (Student) super.clone();
}

}</span>
Address.java

<span style="font-family:Courier New;">package com.dusk.bean;

public class Address {
<span style="white-space:pre">	</span>private String address;
<span style="white-space:pre">	</span>public String getAddress() {
<span style="white-space:pre">		</span>return address;
<span style="white-space:pre">	</span>}

<span style="white-space:pre">	</span>public void setAddress(String address) {
<span style="white-space:pre">		</span>this.address = address;
<span style="white-space:pre">	</span>}</span>
}


Client.java

<span style="font-family:Courier New;">package com.dusk;

import org.junit.Test;

import com.dusk.bean.Student;

public class Client {
@Test
public void test() throws Exception {
Student stu1=new Student("1");
Student stu2=stu1.clone();
System.out.println(stu1==stu2);
System.out.println(stu1.getAddress()==stu2.getAddress());
}
}</span>
运行结果:

<span style="font-family:Courier New;">false
true
</span>


结论: Object提供的clone方法默认是浅克隆操作,对Student对象中的address没有进行clone操作。

4.同理对System.arraycopy进行验证,上代码:

<span style="font-family:Courier New;">package com.dusk;

import org.junit.Test;

import com.dusk.bean.Student;

public class Test5 {
@Test
public void test() {
Student[] arr1=new Student[]{new Student("1"),new Student("2"),new Student("3"),new Student("4")};
Student[] arr2=new Student[4];
System.arraycopy(arr1, 0, arr2, 0, 4);
for(int i=0;i<arr2.length;i++){
System.out.println(arr1[i]==arr2[i]);
}
}

}

</span>
运行结果:

<span style="font-family:Courier New;">true
true
true
true</span>


发现,System.arraycopy提供的也是浅克隆。

如果实现深克隆怎么办? 答案就是自己重写clone方法。

例如:

<span style="font-family:Courier New;"><span style="white-space:pre">	</span>@Override
public Student clone() throws CloneNotSupportedException {
Student stu=new Student();
stu.setId(this.id);
stu.setAddress(new Address());
return stu;
}</span>
结果肯定是:

<span style="font-family:Courier New;">false
false</span>
是不是到此为止了?怎么可能,

如果我们把Address也实现了Cloneable怎么样?

Address.java变身为

<span style="font-family:Courier New;">package com.dusk.bean;

public class Address implements Cloneable{

@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return super.clone();
}

}</span>
继续调用3中的场景, 结果是:

<span style="font-family:Courier New;">false
true
</span>


尽然不是我预想的递归调用,看来如果自己的对象引用比较深的情况下做深克隆,自能自己退下自己的苦果了。

只能这样了吗



看我必杀技,ObjectOutputStream和ObjectInputStream,祭法宝:

<span style="font-family:Courier New;">package com.dusk;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

import org.junit.Test;

import com.dusk.bean.Address;
import com.dusk.bean.Student;

public class Client{
@Test
public void test() throws CloneNotSupportedException {
Student stu1=new Student("1");
Address add = new Address();
stu1.setAddress(add);
Student stu2=(Student) deepCopy(stu1);
System.out.println(stu1==stu2);
System.out.println(stu1.getAddress()==stu2.getAddress());
}

private Object deepCopy(Object obj){
//将对象写到流里
try {
ByteArrayOutputStream bo=new ByteArrayOutputStream();
ObjectOutputStream oo;
oo = new ObjectOutputStream(bo);
oo.writeObject(obj);
//从流里读出来
ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray());
ObjectInputStream oi=new ObjectInputStream(bi);
return(oi.readObject());
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
}</span>


结果:

<span style="font-family:Courier New;">false
false</span>


完美的解决了对象多次套用的深克隆问题。

只要思想不滑坡,办法总比困难多!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: