深拷贝与浅拷贝的问题,也是面试中的常客。虽然大家都知道两者表现形式不同点在哪里,但是很少去深究其底层原理,也不知道怎么才能优雅的实现一个深拷贝。其实工作中也常常需要实现深拷贝,今天一灯就带大家一块深入剖析一下深拷贝与浅拷贝的实现原理,并手把手教你怎么优雅的实现深拷贝。
1. 什么是深拷贝与浅拷贝浅拷贝: 只拷贝栈内存中的数据,不拷贝堆内存中数据。
深拷贝: 既拷贝栈内存中的数据,又拷贝堆内存中的数据。
2. 浅拷贝的实现原理由于浅拷贝只拷贝了栈内存中数据,栈内存中存储的都是基本数据类型,堆内存中存储了数组、引用数据类型等。
使用代码验证一下:
想要实现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. 深拷贝的实现原理深拷贝是既拷贝栈内存中的数据,又拷贝堆内存中的数据。
实现深拷贝有很多种方法,下面就详细讲解一下,看使用哪种方式更方便快捷。
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测试数据/s/SofWhy86i8UFwgstQNAGqg
查看更多关于死磕Java面试:深拷贝与浅拷贝的实现原理的详细内容...