Java 深拷贝与浅拷贝的分析
作者:lqh 发布时间:2023-07-30 14:13:13
在正式的进入主题之前,我们先来了解下深拷贝和前拷贝的概念:
浅拷贝:
会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝,如果属性是基本类型,拷贝的是基本类型的值;如果属性是内存地址,拷贝的就是内存地址,因此如果一个对象改变了这个地址就会影响到另一个对象;
深拷贝:
不仅要复制对象的所有非引用成员变量值,还要为引用类型的成员变量创建新的实例,并且初始化为形式参数实例值;
了解完概念之后,我们来测试下普通的对象赋值操作属于深拷贝还是浅拷贝:
测试代码:
public class DepthCopy {
public static void main(String[] args) {
Copy first = new Copy("hzw", 24);
Copy second = first;
second.name = "shanxi";
System.out.println(first.name);//输出shanxi
}
}
class Copy
{
public String name;
public int age;
public Copy(String name,int age) {
this.name = name;
this.age = age;
}
}
可以发现,在second将name属性值修改为shanxi之后,first的name属性值也变成了shanxi,这点就可以看出普通的对象赋值属于浅拷贝;
明白了对象之间赋值是浅拷贝之后,接下来我们来看看克隆到底是深拷贝还是浅拷贝,测试代码是让上面的Copy对象实现Cloneable接口里面的clone方法:
public class DepthCopy {
public static void main(String[] args) {
Copy first = new Copy("hzw", 24);
Copy second = null;
try {
second = (Copy) first.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
second.name = "shanxi";
System.out.println(first.name);//输出: hzw
System.out.println(first);//输出: com.hzw.day33.Copy@7f39ebdb
System.out.println(second);//输出: com.hzw.day33.Copy@33abb81e
}
}
class Copy implements Cloneable
{
public String name;
public int age;
public Copy(String name,int age) {
this.name = name;
this.age = age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
可以看出原先创建出的对象first和克隆创建出的对象second是两个实例,因此对于second中name属性的修改并不会影响first中的name属性;但是,我们并不能单纯的认为克隆就是深拷贝的,比如下面这个例子:
public class DepthCopy {
public static void main(String[] args) {
Student student = new Student(95);
Copy first = new Copy("hzw", 24,student);
Copy second = null;
try {
second = (Copy) first.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
second.name = "shanxi";
second.student.score = 60;
System.out.println(first == second);//false
System.out.println(first.student == second.student);//true
System.out.println(first.student.score);//60
}
}
class Copy implements Cloneable
{
public String name;
public int age;
public Student student;
public Copy(String name,int age,Student student) {
this.name = name;
this.age = age;
this.student = student;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Student
{
public int score;
public Student(int score) {
this.score = score;
}
}
看到没有呢?我们通过克隆的方式创建了second,很明显发现first和second是两个实例,因为first == second输出为false,但是first和second里面的student对象却是一样的,通过second修改了student的score值之后,first里面student的score也发生了改变,这也就是说first和second里面的student是相同的,这也就说明了克隆是浅拷贝的,我们要想实现克隆的深拷贝,必须让Copy对象里面的Student对象也要实现Cloneable接口里面的clone方法,并且在Copy里面的克隆方法返回Student的一个克隆即可,这样就可以保证Student的唯一啦,修改之后的代码如下:
public class DepthCopy {
public static void main(String[] args) {
Student student = new Student(95);
Copy first = new Copy("hzw", 24,student);
Copy second = null;
try {
second = (Copy) first.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
second.name = "shanxi";
second.student.score = 60;
System.out.println(first == second);//false
System.out.println(first.student == second.student);//false
System.out.println(first.student.score);//95
System.out.println(second.student.score);//60
}
}
class Copy implements Cloneable
{
public String name;
public int age;
public Student student;
public Copy(String name,int age,Student student) {
this.name = name;
this.age = age;
this.student = student;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Copy copy = (Copy)super.clone();
copy.student = (Student) student.clone();
return copy;
}
}
class Student implements Cloneable
{
public int score;
public Student(int score) {
this.score = score;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
可以看到此时first和second和first.student和second.student都不是相同的,因此我们修改second的student的score之后并没有影响到first里的student的score值,达到了深拷贝的目的;
但是,仔细一想问题就出来了,假如我们上面例子的Student类中也存在引用类型的属性,比如College类,那么我们必须让College类实现Cloneable接口,然后在Student类里面的clone方法里面调用College类的clone方法,在Copy类的clone方法中调用Student类的clone方法,发现没有了,这个过程好复杂,必须让类中的有关引用类型全部实现Cloneable接口,感觉好麻烦是不是,好的,接下来就该牛人登场了;
解决深拷贝问题最好的方式就是采用序列化方式,这样各种类均不用实现Cloneable接口的,直接序列化反序列化就可以啦,我们来见识下吧。
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class DepthCopy {
public static void main(String[] args) {
College school = new College("nongda");
Student student = new Student(95, school);
Copy copy = new Copy("hzw",23, student);
Copy another = null;//表示反序列化出来的类实例
//进行序列化操作
try {
FileOutputStream fos = new FileOutputStream(new File("d:/copy.txt"));
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(copy);
} catch (Exception e) {
e.printStackTrace();
}
//进行反序列化操作
FileInputStream fis;
try {
fis = new FileInputStream(new File("d:/copy.txt"));
ObjectInputStream ois = new ObjectInputStream(fis);
another = (Copy) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(copy == another);//false
System.out.println(copy.student == another.student);//false
System.out.println(copy.student.school == another.student.school);//false
another.student.school.schoolName = "wuda";
System.out.println(copy.student.school.schoolName);//nongda
}
}
class Copy implements Serializable
{
public String name;
public int age;
public Student student;
public Copy(String name,int age,Student student) {
this.name = name;
this.age = age;
this.student = student;
}
}
class Student implements Serializable
{
public int score;
public College school;
public Student(int score,College school) {
this.score = score;
this.school = school;
}
}
class College implements Serializable
{
public String schoolName;
public College(String schoolName) {
this.schoolName = schoolName;
}
}
从输出就可以看出来,反序列化之后生成的对象完全就是对原对象的一份拷贝,除了属性值相同之外并不和原对象有任何关系,因此当我们修改反序列化生成对象的schoolName为"wuda"的时候并没有修改原来实例的schoolName值,还是输出"nongda",因此达到了真正的深拷贝效果,但是要想实现序列化,所有的有关类都必须实现Serializable接口,这总也比既实现Cloneable接口又实现clone方法更方便吧。


猜你喜欢
- 源代码:http://github.com/lovewenyo/HttpDemo1. HttpURLConnection使用JDK原生提供的
- 项目需求需要生成一个PDF文档,使用的是VS2010,ASP.NET。网络上多次搜索没有自己想要的,于是硬着头皮到itextpdf官网看英文
- intellij idea是一款非常优秀的软件开发工具,它拥有这强大的插件体系,可以帮助开发者完成很多重量级的功能。今天,我们来学习一下如何
- 概述现实生活中,我们常会看到这样的一种集合:IP地址与主机名,身份证号与个人,系统用户名与系统用户对象等,这种一一对应的关系,就叫做映射。J
- 学习Java 本身是一个挺枯燥的过程,说白了就是每天敲代码而已。但如果换一种思路,可以编写各种各样的程序,不仅加深对代码的理解,同时提高兴趣
- 本文实例为大家分享了使用aop实现全局异常处理的具体代码,供大家参考,具体内容如下日常业务中存在的问题使用大量的try/catch来捕获异常
- 具体代码如下所示:import java.util.ArrayList;import java.util.List;import java.
- 1. Java中程序的逻辑控制语句1.1顺序结构顺序结构比较简单,按照代码书写的顺序一行一行执行1.2分支结构1.2.1 switch语句这
- 前言Json反序列化有两种方式【本人】,一种是生成实体的,方便处理大量数据,复杂度稍高,一种是用匿名类写,方便读取数据,较为简单。使用了Ne
- 本文将向大家展示如何拍照截图。先看看效果图:拍照截图有点儿特殊,要知道,现在的Android智能手机的摄像头都是几百万的像素,拍出来的图片都
- 问题一次面试遇到的一个问题,其实也是实际开发中很容易遇到的问题,特此记录一下。当请求某个接口的时候,我们会在请求的header中携带toke
- 最近看spring的JDBCTemplete的模板方式调用时,对模板和回调产生了浓厚兴趣,查询了一些资料,做一些总结。回调函数:所谓回调,就
- 本文实例为大家分享了java实现webservice方式的具体代码,供大家参考,具体内容如下经过测试 jdk1.6.10以下会出现bug 建
- 本文实例讲述了Android编程实现添加低电流提醒功能的方法。分享给大家供大家参考,具体如下:特殊需求,检测电流是否正常。监听如下广播:In
- 定义弱引用是使用WeakReference创建的引用,弱引用也是用来描述非必需对象的,它是比软引用更弱的引用类型。在发生GC时,只要发现弱引
- class ColorComboBox : ComboBox {&
- 一,FileWritter写入文件FileWritter, 字符流写入字符到文件。默认情况下,它会使用新的内容取代所有现有的内容,然而,当指
- Gson是一个Java库,用来实现Json和Java对象之间的相互转换。Gson是一个托管在https://github.com/googl
- 使用idea进行JavaWeb开发时,在前端与后台交互常常出现乱码问题,包括日志/控制台输出乱码,参数乱码等问题,归根结底是编码格式不对,解
- 激活码:9MWZD5CC4E-eyJsaWNlbnNlSWQiOiI5TVdaRDVDQzRFIiwibGljZW5zZWVOY