`
diaoaa
  • 浏览: 18630 次
  • 性别: Icon_minigender_1
  • 来自: 广州
文章分类
社区版块
存档分类
最新评论

JavaSE学习笔记——深克隆与浅克隆

 
阅读更多

浅复制(浅克隆shallow clone:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象

深复制(深克隆Deep clone:被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍

Java的clone方法是定义在JDK的Object类中,JDK对它的使用说明如下:

 
clone方法将对象复制了一份并返回给调用者。一般而言,clone()方法满足:
 

①对任何的对象x,都有x.clone() !=x

•克隆对象与原对象不是同一个对象

 

②对任何的对象x,都有x.clone().getClass()= =x.getClass()

•克隆对象与原对象的类型一样

 

③如果对象x的equals()方法定义恰当,那么x.clone().equals(x)应该成立。

 

一些说明:
–①为什么我们在派生类中覆盖Object的clone()方法时,一定要调用super.clone()呢?
•在运行时刻,Object中的clone()识别出你要复制的是哪一个对象,然后为此对象分配空间,并进行对象的复制,将原始对象的内容一一复制到新对象的存储空间中。


–②继承自java.lang.Object类的clone()方法是浅复制

2.实验代码:(浅克隆)

1.测试clone对象引用与原对象引用不是指向同一对象
public class CloneTest {

	public static void main(String[] args) throws CloneNotSupportedException {
		//创建一个对象s1
		Student s1 = new Student(20,"villa");
		//Clone s1 给s2
		Student s2 = (Student)s1.clone();
		//检验s1与s2是不是指向同一个对象
		System.out.println("s2.name= "+s2.getSname());
		System.out.println("s2.age= "+s2.getSage());
		System.out.println("---------------------");
		//将s1的name变为"tony"
		s1.setSname("tony");
		System.out.println("s1.name= "+s1.getSname());
		System.out.println("s2.name= "+s2.getSname());
		
		System.out.println("---------------------");
		System.out.println(s1==s2);
	}
}


class Student implements Cloneable{
	
	private int sage;
	private String sname;

	public Student() {}
	
	public Student(int age,String name) {
		this.sage = age;
		this.sname = name;
	}
	
	public int getSage() {
		return sage;
	}

	public void setSage(int sage) {
		this.sage = sage;
	}

	public String getSname() {
		return sname;
	}

	public void setSname(String sname) {
		this.sname = sname;
	}
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
		// TODO Auto-generated method stub
		return super.clone();
	}
	
}
结果输出:
s2.name= villa
s2.age= 20
---------------------
s1.name= tony
s2.name= villa
---------------------
false
2.测试含有引用对象的克隆
①浅克隆不能复制引用对象,复制后的对象与原对象使用同一个引用对象
说明:加入一个Teacher类,并在student类中加入teacher的引用
public class DeepCloneTest {

	public static void main(String[] args) throws CloneNotSupportedException {
		//创建一个Teacher对象
		Teacher teacher = new Teacher(50,"david");
		//创建一个Student2对象,并将teacher赋给它
		Student2 ss1 = new Student2(20,"rooney",teacher);
		//克隆ss1
		Student2 ss2 = (Student2)ss1.clone();
		//把ss1的name改为"linda",其teacher的name改为"robert"
		ss1.setSname("linda");
		ss1.getTeacher().setTname("robert");
		//测试
		System.out.println("ss2.name= "+ss2.getSname());
		System.out.println(ss1==ss2);
		System.out.println("-------------------------");
		System.out.println("ss1.teacher.name= "+ss1.getTeacher().getTname());
		System.out.println("ss2.teacher.name= "+ss2.getTeacher().getTname());
		System.out.println(ss1.getTeacher()==ss2.getTeacher());
	}
}


class Student2 implements Cloneable{
	
	private int sage;
	private String sname;
	private Teacher teacher;

	public Student2() {}
	
	public Student2(int age,String name,Teacher teacher) {
		this.sage = age;
		this.sname = name;
		this.teacher = teacher;
	}
	
	public int getSage() {
		return sage;
	}

	public void setSage(int sage) {
		this.sage = sage;
	}

	public String getSname() {
		return sname;
	}

	public void setSname(String sname) {
		this.sname = sname;
	}
	
	public Teacher getTeacher() {
		return teacher;
	}

	public void setTeacher(Teacher teacher) {
		this.teacher = teacher;
	}

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

class Teacher{
	private int tage;
	private String tname;

	public Teacher() {
		
	}
	
	public Teacher(int age,String name) {
		this.tage = age;
		this.tname = name;
	}
	
	public int getTage() {
		return tage;
	}

	public void setTage(int tage) {
		this.tage = tage;
	}

	public String getTname() {
		return tname;
	}

	public void setTname(String tname) {
		this.tname = tname;
	}
	
}
结果输出:
ss2.name= rooney
false
-------------------------
ss1.teacher.name= robert
ss2.teacher.name= robert
true
结果说明:ss1和ss2不是同一个对象,但ss1的teacher对象和ss2的teacher对象是同一个对象,clone的时候没有复制teacher对象,以上就是所谓的浅克隆
那么假如想复制的时候也同时复制引用对象teacher,可以使用一下的方法:
 

②深克隆

代码说明:①让Teacher类实现Cloneable接口,重写clone方法②在student的clone()方法中,也同时复制teacher对象
public class DeepCloneTest {

	public static void main(String[] args) throws CloneNotSupportedException {
		//创建一个Teacher对象
		Teacher teacher = new Teacher(50,"david");
		//创建一个Student2对象,并将teacher赋给它
		Student2 ss1 = new Student2(20,"rooney",teacher);
		//克隆ss1
		Student2 ss2 = (Student2)ss1.clone();
		//把ss1的name改为"linda",其teacher的name改为"robert"
		ss1.setSname("linda");
		ss1.getTeacher().setTname("robert");
		//测试
		System.out.println("ss2.name= "+ss2.getSname());
		System.out.println(ss1==ss2);
		System.out.println("-------------------------");
		System.out.println("ss1.teacher.name= "+ss1.getTeacher().getTname());
		System.out.println("ss2.teacher.name= "+ss2.getTeacher().getTname());
		System.out.println(ss1.getTeacher()==ss2.getTeacher());
	}
}


class Student2 implements Cloneable{
	
	private int sage;
	private String sname;
	private Teacher teacher;

	public Student2() {}
	
	public Student2(int age,String name,Teacher teacher) {
		this.sage = age;
		this.sname = name;
		this.teacher = teacher;
	}
	
	public int getSage() {
		return sage;
	}

	public void setSage(int sage) {
		this.sage = sage;
	}

	public String getSname() {
		return sname;
	}

	public void setSname(String sname) {
		this.sname = sname;
	}
	
	public Teacher getTeacher() {
		return teacher;
	}

	public void setTeacher(Teacher teacher) {
		this.teacher = teacher;
	}

	@Override
	protected Object clone() throws CloneNotSupportedException {
		Student2 ss = (Student2)super.clone();
		ss.setTeacher((Teacher)ss.getTeacher().clone());
		return ss;
	}
	
}

class Teacher implements Cloneable{
	private int tage;
	private String tname;

	public Teacher() {
		
	}
	
	public Teacher(int age,String name) {
		this.tage = age;
		this.tname = name;
	}
	
	public int getTage() {
		return tage;
	}

	public void setTage(int tage) {
		this.tage = tage;
	}

	public String getTname() {
		return tname;
	}

	public void setTname(String tname) {
		this.tname = tname;
	}
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
}
结果输出:
ss2.name= rooney
false
-------------------------
ss1.teacher.name= robert
ss2.teacher.name= david
false
 
 
上述就可以实现对象的深克隆,但是这样做有什么缺点或者不方便的地方呢?我们使用JDK提供给我的clone方法,当一个对象含有多个对象的引用的时候,重写clone方法就显得非常的麻烦。
 

3.利用序列化来做深克隆

问题的关键出在对象的引用上,很自然就会想到java的序列化机制跟这个很相似,事实上我们可以利用序列化来做深克隆。
概念说明:
①把对象写到流里的过程是序列化(Serilization)过程,而把对象从流中读出来的过程则叫做反序列化(Deserialization)过程。应当指出的是,写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。
②在Java语言里深复制一个对象,常常可以先使对象实现Serializable接口,然后把对象(实际上只是对象的一个拷贝)写到一个流里,再从流里读出来,便可以重建对象。
③这样做的前提是对象以及对象内部所有引用到的对象都是可串行化的,否则,就需要仔细考察那些不可串行化的对象可否设成transient,从而将之排除在复制过程之外。
注意:Cloneable与Serializable都是marker Interface,也就是说他们只是一个标识接口,没有定义任何方法。
 

实验代码:

1.代码说明:不再实现cloneable接口,不再重写clone方法,现在我们实现Serializable接口,利用序列化机制,先将对象写入文件,然后再反序列化读出,由于序列化时会一并序列化里面的引用所以利用这一点进行深克隆。

package com.dr.test.clone;

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

public class SerializableClone {

	public static void main(String[] args) throws Exception {
		//创建一个Teacher对象
		Teacher3 teacher = new Teacher3(50,"david");
		//创建一个Student2对象,并将teacher赋给它
		Student3 ss = new Student3(20,"rooney",teacher);
		//克隆ss1
		Student3 ss2 = (Student3)ss.deepClone();
		//把ss1的name改为"linda",其teacher的name改为"robert"
		ss.setSname("linda");
		ss.getTeacher().setTname("robert");
		//测试
		System.out.println("ss2.name= "+ss2.getSname());
		System.out.println(ss==ss2);
		System.out.println("-------------------------");
		System.out.println("ss.teacher.name= "+ss.getTeacher().getTname());
		System.out.println("ss2.teacher.name= "+ss2.getTeacher().getTname());
		System.out.println(ss.getTeacher()==ss2.getTeacher());
	}
}


class Student3 implements Serializable{
	
	private static final long serialVersionUID = 1L;
	
	private int sage;
	private String sname;
	private Teacher3 teacher;

	public Student3() {}
	
	public Student3(int age,String name,Teacher3 teacher) {
		this.sage = age;
		this.sname = name;
		this.teacher = teacher;
	}
	
	public int getSage() {
		return sage;
	}

	public void setSage(int sage) {
		this.sage = sage;
	}

	public String getSname() {
		return sname;
	}

	public void setSname(String sname) {
		this.sname = sname;
	}
	
	public Teacher3 getTeacher() {
		return teacher;
	}

	public void setTeacher(Teacher3 teacher) {
		this.teacher = teacher;
	}
	
	public Object deepClone() throws Exception {
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		ObjectOutputStream oos = new ObjectOutputStream(bos);
		oos.writeObject(this);
		ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
		ObjectInputStream ois = new ObjectInputStream(bis);
		return ois.readObject();
	}
	
}

class Teacher3 implements Serializable{
	private int tage;
	private String tname;

	public Teacher3() {}
	
	public Teacher3(int age,String name) {
		this.tage = age;
		this.tname = name;
	}
	
	public int getTage() {
		return tage;
	}

	public void setTage(int tage) {
		this.tage = tage;
	}

	public String getTname() {
		return tname;
	}

	public void setTname(String tname) {
		this.tname = tname;
	}
}

结果输出:

ss2.name= rooney
false
-------------------------
ss.teacher.name= robert
ss2.teacher.name= david
false

 

还可以去进一步验证,当Student3含有多个对象引用时,也能完成深克隆。
分享到:
评论

相关推荐

    非常详细javaSE学习笔记.rar

    这份“非常详细JavaSE学习笔记.rar”压缩包显然是一份全面的Java SE学习资源,包含了从基础知识到高级特性的全方位讲解。下面,我们将详细探讨这份笔记可能涵盖的关键知识点。 1. **Java起源与环境搭建**:笔记可能...

    javase学习笔记(全)

    这份"javase学习笔记(全)"涵盖了刘意版传智播客课程的主要内容,是学习Java编程语言的重要参考资料。以下将对Java SE的一些关键知识点进行详细解释: 1. **Java基础**:Java的基础语法包括数据类型(如整型、浮点型...

    JavaSE学习笔记(个人)

    JavaSE学习笔记(个人)

    JavaSE学习笔记(知识点总结)

    JavaSE知识点总结,适合初学者,放在手机里,当做电子书来看。

    JavaSE学习笔记(1).nyf

    JavaSE学习笔记(1).nyf

    尚硅谷JAVASE基础笔记

    尚硅谷JAVASE基础笔记

    javase和javaee学习笔记

    javase和javaee学习笔记, 下载绝对没错, 这是自己在学习的时候写的笔记包括javase和javaee

    java李兴华学习笔记之JAVASE基础部分.pdf

    java李兴华学习笔记之JAVASE基础部分.pdf

    黑马程序员Javase笔记

    "黑马程序员Javase笔记"是一个自学者在学习黑马程序员提供的Java全套课程过程中整理的笔记,主要涵盖了Java Standard Edition (Javase) 的核心内容。下面将详细讨论其中的关键知识点。 首先,DOS命令是操作系统中的...

    圣思园张龙JavaSE课堂笔记

    以上只是张龙老师JavaSE课堂笔记的部分内容概述,实际笔记中还会涉及更多的细节和实例,旨在帮助学习者全面掌握JavaSE的核心知识,为后续的JavaEE和Android开发打下坚实基础。通过系统学习和实践,读者能够熟练运用...

    javase基础学习笔记

    javase基础学习笔记

    JavaSE学习笔记.rar

    这个学习笔记包含了Java开发的基础阶段知识,是初学者掌握Java编程语言的关键。以下将详细解析JavaSE中的主要知识点: 1. **Java基础语法**:Java是一种强类型、面向对象的编程语言,它的基础语法包括变量声明、...

    JavaSE之学习笔记

    JavaSE之学习笔记

    javaSE基础笔记整合

    JavaSE基础笔记整合 本文档总结了JavaSE的基础知识点,涵盖了Java语言的发展史、特点、环境搭建、编写Java应用程序、ClassPath、Path环境变量的配置等。 一、Java语言发展史 Java语言由SUN公司在1991年研发,后被...

    javaSE笔记

    JavaSE学习笔记,多线程,I/O流,集合,反射,常用设计模式

    JAVASE学习笔记

    8. **Spring与Hibernate**(马士兵spring.doc、马士兵hibernate学习笔记(文字整理版).doc):虽然Spring和Hibernate属于JavaEE领域,但它们也常在JavaSE项目中使用。Spring是一个全面的框架,涵盖依赖注入、AOP、MVC...

    javase-学习笔记.pdf

    javase-学习笔记.pdf

    JavaSE基础学习笔记2020.pdf

    JavaSE基础学习笔记2020涉及到Java技术的基础部分、Java语言的基础语法、面向对象编程以及JavaSE的核心内容。接下来我将针对各个章节的知识点进行详细说明。 一、Java技术基础 1. 编程语言:程序设计语言经历了从...

Global site tag (gtag.js) - Google Analytics