Java中的深拷贝(深复制)和浅拷贝(浅复制)

摘要:
深度拷贝(深度拷贝)和浅拷贝(浅拷贝)是两个常见的概念。特别是在C++语言中,如果你不理解它们,你会遇到删除问题。幸运的是,我们在这里使用Java。虽然Java自动管理对象回收,但我们应该对深度复制(深度复制)和浅层复制(浅层复制)给予足够的关注,因为有时这两个概念经常会给我们带来很多困惑。深度复制不仅复制对象本身,还复制对象中包含的引用指向的所有对象。

深拷贝(深复制)和浅拷贝(浅复制)是两个比较通用的概念,尤其在C++语言中,若不弄懂,则会在delete的时候出问题,但是我们在这幸好用的是Java。虽然java自动管理对象的回收,但对于深拷贝(深复制)和浅拷贝(浅复制),我们还是要给予足够的重视,因为有时这两个概念往往会给我们带来不小的困惑。

浅拷贝是指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象。深拷贝不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象。举例来说更加清楚:对象A1中包含对B1的引用,B1中包含对C1的引用。浅拷贝A1得到A2,A2 中依然包含对B1的引用,B1中依然包含对C1的引用。深拷贝则是对浅拷贝的递归,深拷贝A1得到A2,A2中包含对B2(B1的copy)的引用,B2 中包含对C2(C1的copy)的引用。

若不对clone()方法进行改写,则调用此方法得到的对象即为浅拷贝,下面我们着重谈一下深拷贝。

运行下面的程序,看一看浅拷贝:

class Professor0 implements Cloneable {
	String name;
	int age;

	Professor0(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
}

class Student0 implements Cloneable {
	String name;// 常量对象。
	int age;
	Professor0 p;// 学生1和学生2的引用值都是一样的。

	Student0(String name, int age, Professor0 p) {
		this.name = name;
		this.age = age;
		this.p = p;
	}

	public Object clone() {
		Student0 o = null;
		try {
			o = (Student0) super.clone();
		} catch (CloneNotSupportedException e) {
			System.out.println(e.toString());
		}

		return o;
	}
}

public class ShallowCopy {
	public static void main(String[] args) {
		Professor0 p = new Professor0("wangwu", 50);
		Student0 s1 = new Student0("zhangsan", 18, p);
		Student0 s2 = (Student0) s1.clone();
		s2.p.name = "lisi";
		s2.p.age = 30;
		s2.name = "z";
		s2.age = 45;
		System.out.println("学生s1的姓名:" + s1.name + "\n学生s1教授的姓名:" + s1.p.name + "," + "\n学生s1教授的年纪" + s1.p.age);// 学生1的教授
	}
}

s2变了,但s1也变了,证明s1的p和s2的p指向的是同一个对象。这在我们有的实际需求中,却不是这样,因而我们需要深拷贝:

class Professor implements Cloneable {
	String name;
	int age;

	Professor(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public Object clone() {
		Object o = null;
		try {
			o = super.clone();
		} catch (CloneNotSupportedException e) {
			System.out.println(e.toString());
		}
		return o;
	}
}

class Student implements Cloneable {
	String name;
	int age;
	Professor p;

	Student(String name, int age, Professor p) {
		this.name = name;
		this.age = age;
		this.p = p;
	}

	public Object clone() {
		Student o = null;
		try {
			o = (Student) super.clone();
		} catch (CloneNotSupportedException e) {
			System.out.println(e.toString());
		}
		o.p = (Professor) p.clone();
		return o;
	}
}

public class DeepCopy {
	public static void main(String args[]) {
		long t1 = System.currentTimeMillis();
		Professor p = new Professor("wangwu", 50);
		Student s1 = new Student("zhangsan", 18, p);
		Student s2 = (Student) s1.clone();
		s2.p.name = "lisi";
		s2.p.age = 30;
		System.out.println("name=" + s1.p.name + "," + "age=" + s1.p.age);// 学生1的教授不改变。
		long t2 = System.currentTimeMillis();
		System.out.println(t2-t1);
	}
}

当然我们还有一种深拷贝方法,就是将对象串行化:

import java.io.*;
//Serialization is time-consuming
class Professor2 implements Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	String name;
	int age;

	Professor2(String name, int age) {
		this.name = name;
		this.age = age;
	}
}

class Student2 implements Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	String name;// 常量对象。
	int age;
	Professor2 p;// 学生1和学生2的引用值都是一样的。

	Student2(String name, int age, Professor2 p) {
		this.name = name;
		this.age = age;
		this.p = p;
	}

	public Object deepClone() throws IOException, OptionalDataException,
			ClassNotFoundException {
		// 将对象写到流里
		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());
	}

}

public class DeepCopy2 {

	/**
	 * @param args
	 */
	public static void main(String[] args) throws OptionalDataException,
			IOException, ClassNotFoundException {
		long t1 = System.currentTimeMillis();
		Professor2 p = new Professor2("wangwu", 50);
		Student2 s1 = new Student2("zhangsan", 18, p);
		Student2 s2 = (Student2) s1.deepClone();
		s2.p.name = "lisi";
		s2.p.age = 30;
		System.out.println("name=" + s1.p.name + "," + "age=" + s1.p.age); // 学生1的教授不改变。
		long t2 = System.currentTimeMillis();
		System.out.println(t2-t1);
	}

}

但是串行化却很耗时,在一些框架中,我们便可以感受到,它们往往将对象进行串行化后进行传递,耗时较多。

免责声明:文章转载自《Java中的深拷贝(深复制)和浅拷贝(浅复制)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇使用docker搭建gitlab服务器Math.radom()函数获取随机数组下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Linux 和Windows之间命令行实现目录或文件互传

Linux 和Windows之间命令行实现目录或文件互传 在linux中,我们常用scp命令传输文件: 如以下实例,我们想把当前服务器文件abc.sql传输到192.168.1.1服务器上 我们可以执行以下命令: scp /home/person/hww/abc.sql root@192.168.1.1:/home/person/hww  回车后输入...

【VS开发】【C/C++开发】memcpy和memmove的区别

memcpy和memmove()都是C语言中的库函数,在头文件string.h中,作用是拷贝一定长度的内存的内容,原型分别如下: void *memcpy(void *dst, const void *src, size_t count); void *memmove(void *dst, const void *src, size_t count);...

如何提升大容量文件上传性能

背景618压测过程中,涉及大规格的参数化文件上传平台,由于文件过大超过2G,在平台上传过程中经常失败,超时,重试也要等老半天,这就会造成人力资源等待影响工作效率。那么应该怎么做才能快速上传,如何提高文件上传性能以及做到就算失败了再次重试也能从上次中断的地方继续上传提升系统的容错能力呢 ?我学习整理了一些优化思路,在此分享给大家,请君看下文分解~~~  ...

大数据迁移

由于服务器调整,需要把服务器A上面约2T的数据库表空间文件迁移到同网段的另一台服务器B。 具体来说,就是N个32G的表空间文件(注意,是单个文件32G) 局域网传输文件的工具很多,但是没想到的是,一旦处理起这么大的文件,基本全部歇菜。。 先试了FeiQ,传输速度是很快,1000M网卡下速度可以达到90M/s,但是,文件传输到5280M的时刻就出错了,试了N...

84、智能指针的原理、常用的智能指针及实现

原理 智能指针是一个类,用来存储指向动态分配对象的指针,负责自动释放动态分配的对象,防止堆内存泄漏。动态分配的资源,交给一个类对象去管理,当类对象声明周期结束时,自动调用析构函数释放资源 常用的智能指针 (1) shared_ptr 实现原理:采用引用计数器的方法,允许多个智能指针指向同一个对象,每当多一个指针指向该对象时,指向该对象的所有智能指针内部的引...

ADB常用命令(一)

转自:https://blog.csdn.net/qq_26552691/article/details/81348222 一.操作前请确认电脑上已配置好ADB环境。可在CMD命令行输入adb,如果出现如下提示,说明已配置好。 二.下面,我们可以进行一些ADB常用命令的操作。 1.在命令行输入:adb start-server 或者adb shell,来启...