当前位置: 首页 > 科技观察

致命Java面试系列:深拷贝和浅拷贝的实现原理

时间:2023-03-18 15:50:33 科技观察

深拷贝和浅拷贝的问题也是面试中的常客。虽然大家都知道这两种表现形式的区别,但很少去深究底层原理,也不知道如何优雅地实现深拷贝。其实在工作中经常需要实现深拷贝。今天一灯带大家深入剖析深拷贝和浅拷贝的实现原理,教大家如何优雅的实现深拷贝。1、什么是深拷贝和浅拷贝浅拷贝:只拷贝栈内存中的数据,不拷贝堆内存中的数据。深拷贝:既拷贝栈内存中的数据,也拷贝堆内存中的数据。2、浅拷贝的实现原理由于浅拷贝只拷贝栈内存中的数据,所有的基本数据类型都存放在栈内存中,数组和引用数据类型存放在堆内存中。使用代码验证:要实现clone功能,需要实现Cloneable接口,重写clone方法。首先创建一个用户类//用于验证的用户实体类publicclassUserimplementsCloneable{privateStringname;//每个用户都有一个jobprivateJobjob;publicStringgetName(){返回名称;}publicvoidsetName(Stringname){this.name=name;}publicJobgetJob(){返回工作;}publicvoidsetJob(Jobjob){this.job=job;}@OverridepublicUserclone()throwsCloneNotSupportedException{Useruser=(User)super.clone();返回用户;}}创建另一个作业类//作业的实体类没有实现Cloneable接口publicclassJob{privateStringcontent;publicStringgetContent(){返回内容;}publicvoidsetContent(Stringcontent){this.content=content;}}测试浅拷贝/***@authorYidengarchitecture*@apiNoteJava浅拷贝示例**/publicclassDemo{publicstaticvoidmain(String[]args)throwsCloneNotSupportedException{//1.创建用户对象,{"name":"一灯架构","job":{"content":"开发"}}Useruser1=newUser();用户1.setName("一登建筑");工作job1=newJob();job1.setContent("开发");user1.setJob(job1);//2.复制用户对象,改名为“张三”,修改作业内容“测试”Useruser2=user1.clone();user2.setName("张三");作业job2=user2.getJob();job2.setContent("测试");//3.输出结果System.out.println("useroriginalobject="+user1);System.out.println("用户复制对象="+user2);}}输出结果:user原对象={"name":"onelamparchitecture","job":{"content":"test"}}usercopyobject={"name":"张三","job"":{"content":"test"}}从结果可以看出,对象copy把名字改成了“张三”,原来的对象没有变,名字是String类型,是一个基础数据类型,并且存储在栈内存中的对象复制一份新的栈内存数据,修改不会影响原对象。然后对象副本把Job中的内容改成了“test”,原来的对象也跟着变了。原因是Job是引用类型,存放在堆内存中。对象副本和原对象指向同一堆内存地址,所以修改会影响原对象。3、深拷贝的实现原理深拷贝就是把栈内存中的数据和堆内存中的数据都拷贝过来。有很多方法可以实现深拷贝。下面就来详细解释一下,看看哪种方式更方便快捷。3.1实现Cloneable接口实现Cloneable接口实现深拷贝是最常见的。实现clone功能需要实现**Cloneable接口,重写clone**方法。首先创建一个用户类//用于验证的用户实体类publicclassUserimplementsCloneable{privateStringname;//每个用户都有一个jobprivateJobjob;publicStringgetName(){返回名称;}publicvoidsetName(Stringname){this.name=name;}publicJobgetJob(){返回工作;}publicvoidsetJob(Jobjob){this.job=job;}@OverridepublicUserclone()throwsCloneNotSupportedException{Useruser=(User)super.clone();//User对象中的所有引用类型属性都要执行clone方法user.setJob(user.getJob().clone());返回用户;}}创建一个作业类//作业的实体类需要实现Cloneable接口publicclassJobimplementsCloneable{privateStringcontent;publicStringgetContent(){返回内容;}publicvoidsetContent(Stringcontent){this.content=content;}@OverrideprotectedJobclone()throwsCloneNotSupportedException{return(Job)super.clone();}}测试浅拷贝/***@authorYidengArchitecture*@apiNoteJava深拷贝示例**/publicclassDemo{publicstaticvoidmain(String[]args)throwsCloneNotSupportedException{//1.创建用户对象,{"name":"YidengArchitecture","job":{"content":"开发"}}用户user1=newUser();user1.setName("OneLightArchitecture");工作job1=newJob();job1.setContent("开发");user1.setJob(job1);//2.复制用户对象,改名字为“张三”,修改作业内容为“测试”Useruser2=user1.clone();user2.setName("张三");作业job2=user2.getJob();job2.setContent("测试");//3.输出结果System.out.println("useroriginalobject="+user1);System.out.println("用户复制对象="+user2);}}输出结果:user原对象={"name":"一登建筑","job":{"content":"development"}}user复制对象={"name":"张三","job":{"content":"test"}}从结果可以看出,用户复制对象修改了job对象的name属性和内容,对原对象没有影响。深拷贝是通过实现Cloneable接口来实现的。,是Java中最常见的实现。缺点是:比较麻烦,所有实体类都需要实现Cloneable接口,重写clone方法。如果将引用对象类型的属性添加到实体类中,则还需要将其添加到克隆方法中。如果后继者忘记修改clone方法,就相当于挖了一个坑。3.2使用JSON字符串转换的实现方法是:先将user对象转为json字符串,再将json字符串转为user对象。下面是使用fastjson实现的,Gson和Jackson也是如此:importcom.alibaba.fastjson.JSON;/***@authorOneLightArchitecture*@apiNoteJava深拷贝示例**/publicclassDemo{publicstaticvoidmain(String[]args)throwsCloneNotSupportedException{//1.创建一个用户对象,{"name":"OneLightArchitecture","job":{"content":"Development"}}Useruser1=new用户();user1.setName("一灯建筑");工作job1=newJob();job1.setContent("开发");user1.setJob(job1);////2.复制用户对象,改名为“张三”,作业内容修改“test”Useruser2=JSON.parseObject(JSON.toJSONString(user1),User.class);user2.setName("张三");作业job2=user2.getJob();job2.setContent("测试");//3.输出结果System.out.println("useroriginalobject="+JSON.toJSONString(user1));System.out.println("用户复制对象="+JSON.toJSONString(user2));}}输出结果:user原对象={"name":"一登建筑","job":{"content":"development"}}user复制对象={"name":"张三","job":{"content":"test"}}从结果可以看出,用户复制对象修改了name属性和Job对象中的内容,不影响原对象,实现了深拷贝。3.3集合实现深拷贝下面说说Java集合是如何实现深拷贝的?其实很简单。只需要在初始化新对象的时候将原对象传入新对象的构造方法即可。以最常用的ArrayList为例:/***@authorYidengArchitecture*@apiNoteJava深拷贝示例**/publicclassDemo{publicstaticvoidmain(String[]args)throwsCloneNotSupportedException{//1.创建原始对象列表userList=newArrayList<>();//2.创建深拷贝对象ListuserCopyList=newArrayList<>(userList);}}