服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - 死磕Java面试:深拷贝与浅拷贝的实现原理

死磕Java面试:深拷贝与浅拷贝的实现原理

2022-11-08 20:33一灯架构 Java教程

深拷贝与浅拷贝的问题,也是面试中的常客。虽然大家都知道两者表现形式不同点在哪里,但是很少去深究其底层原理,也不知道怎么才能优雅的实现一个深拷贝。其实工作中也常常需要实现深拷贝,今天一灯就带大家一块深入剖

深拷贝与浅拷贝的问题,也是面试中的常客。虽然大家都知道两者表现形式不同点在哪里,但是很少去深究其底层原理,也不知道怎么才能优雅的实现一个深拷贝。其实工作中也常常需要实现深拷贝,今天一灯就带大家一块深入剖析一下深拷贝与浅拷贝的实现原理,并手把手教你怎么优雅的实现深拷贝。

1. 什么是深拷贝与浅拷贝

浅拷贝: 只拷贝栈内存中的数据,不拷贝堆内存中数据。

深拷贝: 既拷贝栈内存中的数据,又拷贝堆内存中的数据。

2. 浅拷贝的实现原理

由于浅拷贝只拷贝了栈内存中数据,栈内存中存储的都是基本数据类型,堆内存中存储了数组、引用数据类型等。

死磕Java面试:深拷贝与浅拷贝的实现原理

使用代码验证一下:

想要实现clone功能,需要实现 Cloneable 接口,并重写 clone 方法。

  • 先创建一个用户类
// 用户的实体类,用作验证
public class User implements Cloneable { private String name; // 每个用户都有一个工作
    private Job job; public String getName(){ return name; } public void setName(String name){ this.name = name; } public Job getJob(){ return job; } public void setJob(Job job){ this.job = job; } @Override
    public User clone() throws CloneNotSupportedException { User user = (User) super.clone(); return user; } }
  • 再创建一个工作类
// 工作的实体类,并没有实现Cloneable接口
public class Job { private String content; public String getContent(){ return content; } public void setContent(String content){ this.content = content; } }
  • 测试浅拷贝
/**  * @author 一灯架构  * @apiNote Java浅拷贝示例  **/ public class Demo { public static void main(String[] args) throws CloneNotSupportedException { // 1. 创建用户对象,{"name":"一灯架构","job":{"content":"开发"}} User user1 = new User(); user1.setName("一灯架构"); Job job1 = new Job(); job1.setContent("开发"); user1.setJob(job1); // 2. 拷贝用户对象,name修改为"张三",工作内容修改"测试" User user2 = user1.clone(); user2.setName("张三"); Job job2 = user2.getJob(); job2.setContent("测试"); // 3. 输出结果
        System.out.println("user原对象= " + user1); System.out.println("user拷贝对象= " + user2); } }

输出结果:

user原对象= {"name":"一灯架构","job":{"content":"测试"}} user拷贝对象= {"name":"张三","job":{"content":"测试"}}

从结果中可以看出,对象拷贝把name修改为”张三“,原对象并没有变,name是String类型,是基本数据类型,存储在栈内存中。对象拷贝了一份新的栈内存数据,修改并不会影响原对象。

然后对象拷贝把Job中content修改为”测试“,原对象也跟着变了,原因是Job是引用类型,存储在堆内存中。对象拷贝和原对象指向的同一个堆内存的地址,所以修改会影响到原对象。

3. 深拷贝的实现原理

深拷贝是既拷贝栈内存中的数据,又拷贝堆内存中的数据。

死磕Java面试:深拷贝与浅拷贝的实现原理

实现深拷贝有很多种方法,下面就详细讲解一下,看使用哪种方式更方便快捷。

3.1 实现Cloneable接口

通过实现Cloneable接口来实现深拷贝是最常见的。

想要实现clone功能,需要实现**Cloneable接口,并重写clone**方法。

  • 先创建一个用户类
// 用户的实体类,用作验证
public class User implements Cloneable { private String name; // 每个用户都有一个工作
    private Job job; public String getName(){ return name; } public void setName(String name){ this.name = name; } public Job getJob(){ return job; } public void setJob(Job job){ this.job = job; } @Override
    public User clone() throws CloneNotSupportedException { User user = (User) super.clone(); // User对象中所有引用类型属性都要执行clone方法
        user.setJob(user.getJob().clone()); return user; } }
  • 再创建一个工作类
// 工作的实体类,需要实现Cloneable接口
public class Job implements Cloneable { private String content; public String getContent(){ return content; } public void setContent(String content){ this.content = content; } @Override
    protected Job clone() throws CloneNotSupportedException { return (Job) super.clone(); } }
  • 测试浅拷贝
/**  * @author 一灯架构  * @apiNote Java深拷贝示例  **/ public class Demo { public static void main(String[] args) throws CloneNotSupportedException { // 1. 创建用户对象,{"name":"一灯架构","job":{"content":"开发"}} User user1 = new User(); user1.setName("一灯架构"); Job job1 = new Job(); job1.setContent("开发"); user1.setJob(job1); // 2. 拷贝用户对象,name修改为"张三",工作内容修改"测试" User user2 = user1.clone(); user2.setName("张三"); Job job2 = user2.getJob(); job2.setContent("测试"); // 3. 输出结果
        System.out.println("user原对象= " + user1); System.out.println("user拷贝对象= " + user2); } }

输出结果:

user原对象= {"name":"一灯架构","job":{"content":"开发"}} user拷贝对象= {"name":"张三","job":{"content":"测试"}}

从结果中可以看出,user拷贝对象修改了name属性和Job对象中内容,都没有影响到原对象,实现了深拷贝。

通过实现Cloneable接口的方式来实现深拷贝,是Java中最常见的实现方式。

缺点是: 比较麻烦,需要所有实体类都实现Cloneable接口,并重写clone方法。如果实体类中新增了一个引用对象类型的属性,还需要添加到clone方法中。如果继任者忘了修改clone方法,相当于挖了一个坑。

3.2 使用JSON字符串转换

实现方式就是:

  • 先把user对象转换成json字符串
  • 再把json字符串转换成user对象

这是个偏方,但是偏方治大病,使用起来非常方便,一行代码即可实现。

下面使用fastjson实现,使用Gson、Jackson也是一样的:

import com.alibaba.fastjson.JSON; /**  * @author 一灯架构  * @apiNote Java深拷贝示例  **/ public class Demo { public static void main(String[] args) throws CloneNotSupportedException { // 1. 创建用户对象,{"name":"一灯架构","job":{"content":"开发"}} User user1 = new User(); user1.setName("一灯架构"); Job job1 = new Job(); job1.setContent("开发"); user1.setJob(job1); //// 2. 拷贝用户对象,name修改为"张三",工作内容修改"测试" User user2 = JSON.parseObject(JSON.toJSONString(user1), User.class); user2.setName("张三"); Job job2 = user2.getJob(); job2.setContent("测试"); // 3. 输出结果
        System.out.println("user原对象= " + JSON.toJSONString(user1)); System.out.println("user拷贝对象= " + JSON.toJSONString(user2)); } }

输出结果:

user原对象= {"name":"一灯架构","job":{"content":"开发"}} user拷贝对象= {"name":"张三","job":{"content":"测试"}}

从结果中可以看出,user拷贝对象修改了name属性和Job对象中内容,并没有影响到原对象,实现了深拷贝。

3.3 集合实现深拷贝

再说一下Java集合怎么实现深拷贝?

其实非常简单,只需要初始化新对象的时候,把原对象传入到新对象的构造方法中即可。

以最常用的ArrayList为例:

/**  * @author 一灯架构  * @apiNote Java深拷贝示例  **/ public class Demo { public static void main(String[] args) throws CloneNotSupportedException { // 1. 创建原对象
        List<User> userList = new ArrayList<>(); // 2. 创建深拷贝对象
        List<User> userCopyList = new ArrayList<>(userList); } }

原文地址:https://mp.weixin.qq.com/s/SofWhy86i8UFwgstQNAGqg

延伸 · 阅读

精彩推荐
  • Java教程java面向国际化项目开发需遵循的命名规范

    java面向国际化项目开发需遵循的命名规范

    这篇文章主要为大家介绍了在参与开发国际化项目时需遵循的java命名规范,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步...

    字母哥哥3742022-09-25
  • Java教程java 虚拟机深入了解

    java 虚拟机深入了解

    这篇文章主要介绍了java 虚拟机深入了解的相关资料,java虚拟机有自己完善的硬体架构,如处理器、堆栈、寄存器等,还具有相应的指令系统,需要的朋友可...

    Java之家2872020-08-24
  • Java教程Java抽象定义以及举例代码

    Java抽象定义以及举例代码

    这篇文章主要给大家介绍了关于Java抽象定义以及举例的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需...

    热度三分3732021-09-03
  • Java教程线程池ThreadPoolExecutor使用简介与方法实例

    线程池ThreadPoolExecutor使用简介与方法实例

    今天小编就为大家分享一篇关于线程池ThreadPoolExecutor使用简介与方法实例,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一...

    小飞侠-24522021-07-21
  • Java教程scala 读取txt文件的方法示例

    scala 读取txt文件的方法示例

    这篇文章主要介绍了scala 读取txt文件的方法示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面...

    张乐19938692019-06-29
  • Java教程Springmvc的运行流程图文详解

    Springmvc的运行流程图文详解

    今天小编就为大家分享一篇关于Springmvc的运行流程图文详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来...

    431937973862021-07-01
  • Java教程Servlet实现简单文件上传功能

    Servlet实现简单文件上传功能

    这篇文章主要为大家详细介绍了Servlet实现简单文件上传功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    黄盖苦肉六张闪5442021-01-27
  • Java教程redis 使用lettuce 启动内存泄漏错误的解决方案

    redis 使用lettuce 启动内存泄漏错误的解决方案

    这篇文章主要介绍了redis 使用lettuce 启动内存泄漏错误的解决方案,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    AugustWhite9892021-09-02