好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

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

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

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面试:深拷贝与浅拷贝的实现原理的详细内容...

  阅读:10次