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方法更方便吧。

标签:Java,深拷贝,浅拷贝
0
投稿

猜你喜欢

  • 使用Feign动态设置header和原理分析

    2021-07-11 10:30:46
  • 一篇文章弄懂Spring MVC的参数绑定

    2023-09-17 01:01:21
  • java最新版本连接mysql失败的解决过程

    2022-05-21 17:29:58
  • Android EditText长按菜单中分享功能的隐藏方法

    2021-08-27 19:30:45
  • Android实现文字垂直滚动、纵向走马灯效果的实现方式汇总

    2023-03-02 22:24:54
  • java发送get请求和post请求示例

    2022-01-30 10:45:52
  • C# 遍历文件夹子目录下所有图片及遍历文件夹下的文件

    2022-03-12 02:55:47
  • C# 调用命令行执行Cmd命令的操作

    2022-08-03 10:43:26
  • Java中重定向输出流实现用文件记录程序日志

    2021-12-28 20:57:13
  • 基于idea Maven中的redis配置使用详解

    2023-11-29 11:57:28
  • Android基于AlarmManager实现用户在线心跳功能示例

    2021-12-02 20:26:46
  • c#生成随机数示例分享

    2023-10-04 06:54:50
  • Android 静默安装实现方法

    2021-08-04 05:11:25
  • Flutter应用集成极光推送的实现示例

    2023-06-24 03:51:04
  • java时区转换的理解及示例详解

    2022-01-19 08:35:20
  • Java详细讲解堆排序与时间复杂度的概念

    2023-10-20 02:00:11
  • Jmeter如何基于命令行运行jmx脚本

    2023-11-01 14:16:24
  • Android轻松实现多语言的方法示例

    2023-02-17 19:33:38
  • Java Resource路径整理总结

    2021-11-24 06:52:31
  • C++类与对象深入之构造函数与析构函数详解

    2021-06-29 13:44:44
  • asp之家 软件编程 m.aspxhome.com