Java的深克隆与浅克隆

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>

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

只要思想不滑坡,办法总比困难多!

时间: 2024-10-09 01:43:13

Java的深克隆与浅克隆的相关文章

Java克隆--深克隆与浅克隆的区别

克隆,就是复制一个对象的副本,而克隆又分浅克隆和深克隆.浅克隆是指克隆得到的对象基本类型的值改变了,而源对象的值不会变.但如果被克隆对象引用类型的值改变了,那么源对象的值同样会改变,因为引用类型在栈内存中存放的是一个引用地址,被克隆对象后也和源对象的引用地址一样,都是指向同样的内存空间的值.所以在克隆时,任何一个对象的值的改变都会令另外的值改变,所以这种情况下要用深克隆. 要注意的是要克隆的对象的泪必须继承cloneable接口.浅克隆的特点是只克隆该对象本体,它的优缺点就是一改皆改:深克隆的特

Java中的深克隆和浅克隆——Cloneable接口

一.没有使用克隆带来的问题 public class CloneTest { static Student s = new Student("aaa", 20); // 直接赋值带来的问题 public static void noclone() { // 传的是引用的副本,改变了noCloneStudent也改变了s Student noCloneStudent = new Student(); noCloneStudent = s; noCloneStudent.setName(&

深克隆和浅克隆

深克隆和浅克隆 标签(空格分隔): 常用函数 浅克隆 浅克隆: 被复制对象的所有基本变量都含有和原来变量完全相同的值, 而其他所有的引用对象任然指向原来的对象. 换言之, 浅克隆仅仅复制锁考虑的对象, 而不复制它所引用的对象. 克隆的深度仅仅到栈内存中. 深克隆 深克隆: 和浅克隆不一样的点在于其重写了clone函数. 在克隆本身对象的时候, 也对其中的引用类型的属性进行克隆. 看下面的代码, 为什么String就这么吊, 好像直接就是深克隆呢? 不同点在于, 在浅克隆的时候 gg属性是 栈内存

【java开发系列】—— 深克隆和浅克隆

Java支持我们对一个对象进行克隆,通常用在装饰模式和原型模式中.那么什么是深克隆,什么是浅克隆呢. [浅克隆],通常只是对克隆的实例进行复制,但里面的其他子对象,都是共用的. [深克隆],克隆的时候会复制它的子对象的引用,里面所有的变量和子对象都是又额外拷贝了一份. 下面的两个例子可以很好的说明他们的区别: 首先看一下类图 Husband类有一个对wife的引用,当进行浅克隆的时,wife变量都会指向同一个Wife:而进行深克隆时,会指向不同的Wife.下面进行一下验证: [浅克隆] 1 pu

JAVA深克隆与浅克隆1

复制就是得到一个副本 克隆就是复制一个对象的复本.但一个对象中可能有基本数据类型,如:int,long,float    等,也同时含有非基本数据类型如(数组,集合等)被克隆得到的对象基本类型的值修改了,原对象的值不会改变.这种适合shadow clone(浅克隆). 但如果你要改变一个非基本类型的值时,原对象的值却改变了.比如一个数组,内存中只copy他的地址,而这个地址指向的值并没有copy,当clone时,两个地址指向了一个值,这样一旦这个值改变了,原来的值当然也变了,因为他们共用一个值.

Java深拷贝和浅拷贝(深克隆和浅克隆)

Java中创建对象有两种方式: 通过new操作符创建一个对象 通过clone方法来复制一个对象 使用反序列化来创建一个对象 通过使用Class类的newInstance方法来创建一个对象 使用Constructor类的newInstance方法来创建一个对象 第一种方法,通过new操作符来创建一个对象,分配内存,调用构造函数来填充各个域,这是我们最熟悉的:第二种clone也是分配内存,分配的内存和被clone对象相同,然后再使用原对象中对应的各个域,填充新对象的域, 填充完成之后,clone方法

Java中的深克隆和浅克隆

为什么要克隆 首先思考一个问题, 为什么需要克隆对象? 直接new一个对象不行吗? 克隆的对象可能包含一些已经修改过的属性, 而new出来的对象的属性都还是初始化时候的值, 所以当需要一个新的对象来保存当前对象的"状态"时就要靠克隆了. 当然, 把对象的属性一个一个的赋值给新new的对象也是可以的, 但是这样一来麻烦不说, 二来, 我们通过源码查看 Object的clone方法是一个native方法(native方法是非Java语言实现的代码, 供Java程序调用, 要想访问比较底层的

Java基础--深克隆补充

深克隆文章很多,这里推荐Java提高篇--对象克隆(复制). 以上文章条理清晰,一目了然,但最近在项目中碰到的实际问题是,所要克隆的对象是加上子类父类共计207个,无论用上述两种方式的哪一种,都要一一修改类文件,比较繁琐. 因此又尝试了Spring的BeanUtils.copyProperties,证明copyProperties方法并不是用来实现深克隆的.最终抱着尝试的心态使用了Jackson,什么接口都不用实现,竟然成功了. code maven <dependency> <grou

原型模式的深克隆和浅克隆

深克隆核心代码: public Object deepclone () throws IOException, ClassNotFoundException{ //将对象写入流中 ByteArrayOutputStream bao=new ByteArrayOutputStream(); ObjectOutputStream oos=new ObjectOutputStream(bao); oos.writeObject(this); //将对象从流中取出 ByteArrayInputStrea