如何对一个对象进行深拷贝?

介绍

在Java语言里,当我们需要拷贝一个对象时,有两种类型的拷贝:浅拷贝与深拷贝。浅拷贝只是拷贝了源对象的地址,所以源对象的值发生变化时,拷贝对象的值也会发生变化。而深拷贝则是拷贝了源对象的所有值,所以即使源对象的值发生变化时,拷贝对象的值也不会改变。

了解了浅拷贝和深拷贝的区别之后,本篇博客将教大家几种深拷贝的方法。

拷贝对象

首先,我们定义一下需要拷贝的简单对象。

/**  * 用户  */  
public class User {  private String name;  private Address address;  // constructors, getters and setters  }  /**  * 地址  */  
public class Address {  private String city;  private String country;  // constructors, getters and setters  }  

如上述代码,我们定义了一个User用户类,包含name姓名,和address地址,其中address并不是字符串,而是另一个Address类,包含country国家和city城市。构造方法和成员变量的get()、set()方法此处我们省略不写。接下来我们将详细描述如何深拷贝User对象。

方法一 构造函数

我们可以通过在调用构造函数进行深拷贝,形参如果是基本类型和字符串则直接赋值,如果是对象则重新new一个。

测试用例

@Test  
public void constructorCopy() {  Address address = new Address("杭州", "中国");  User user = new User("大山", address);  // 调用构造函数时进行深拷贝  User copyUser = new User(user.getName(), new Address(address.getCity(), address.getCountry()));  // 修改源对象的值  user.getAddress().setCity("深圳");  // 检查两个对象的值不同  assertNotSame(user.getAddress().getCity(), copyUser.getAddress().getCity());  }  

方法二 重载clone()方法

Object父类有个clone()的拷贝方法,不过它是protected类型的,我们需要重写它并修改为public类型。除此之外,子类还需要实现Cloneable接口来告诉JVM这个类是可以拷贝的。

重写代码

让我们修改一下User类,Address类,实现Cloneable接口,使其支持深拷贝。

/**  * 地址  */  
public class Address implements Cloneable {  private String city;  private String country;  // constructors, getters and setters  @Override  public Address clone() throws CloneNotSupportedException {  return (Address) super.clone();  }  }  
/**  * 用户  */  
public class User implements Cloneable {  private String name;  private Address address;  // constructors, getters and setters  @Override  public User clone() throws CloneNotSupportedException {  User user = (User) super.clone();  user.setAddress(this.address.clone());  return user;  }  }  

需要注意的是,super.clone()其实是浅拷贝,所以在重写User类的clone()方法时,address对象需要调用address.clone()重新赋值。

测试用例

@Test  
public void cloneCopy() throws CloneNotSupportedException {  Address address = new Address("杭州", "中国");  User user = new User("大山", address);  // 调用clone()方法进行深拷贝  User copyUser = user.clone();  // 修改源对象的值  user.getAddress().setCity("深圳");  // 检查两个对象的值不同  assertNotSame(user.getAddress().getCity(), copyUser.getAddress().getCity());  }  

方法三 Apache Commons Lang序列化

Java提供了序列化的能力,我们可以先将源对象进行序列化,再反序列化生成拷贝对象。但是,使用序列化的前提是拷贝的类(包括其成员变量)需要实现Serializable接口。Apache Commons Lang包对Java序列化进行了封装,我们可以直接使用它。

重写代码

让我们修改一下User类,Address类,实现Serializable接口,使其支持序列化。

/**  * 地址  */  
public class Address implements Serializable {  private String city;  private String country;  // constructors, getters and setters  }  
/**  * 用户  */  
public class User implements Serializable {  private String name;  private Address address;  // constructors, getters and setters  } 

测试用例

@Test  
public void serializableCopy() {  Address address = new Address("杭州", "中国");  User user = new User("大山", address);  // 使用Apache Commons Lang序列化进行深拷贝  User copyUser = (User) SerializationUtils.clone(user);  // 修改源对象的值  user.getAddress().setCity("深圳");  // 检查两个对象的值不同  assertNotSame(user.getAddress().getCity(), copyUser.getAddress().getCity());  } 

方法四 Gson序列化

Gson可以将对象序列化成JSON,也可以将JSON反序列化成对象,所以我们可以用它进行深拷贝。

测试用例

@Test  
public void gsonCopy() {  Address address = new Address("杭州", "中国");  User user = new User("大山", address);  // 使用Gson序列化进行深拷贝  Gson gson = new Gson();  User copyUser = gson.fromJson(gson.toJson(user), User.class);  // 修改源对象的值  user.getAddress().setCity("深圳");  // 检查两个对象的值不同  assertNotSame(user.getAddress().getCity(), copyUser.getAddress().getCity());  }  

方法五 Jackson序列化

Jackson与Gson相似,可以将对象序列化成JSON,明显不同的地方是拷贝的类(包括其成员变量)需要有默认的无参构造函数。

重写代码

让我们修改一下User类,Address类,实现默认的无参构造函数,使其支持Jackson。

/**  * 用户  */  
public class User {  private String name;  private Address address;  // constructors, getters and setters  public User() {  }  }  
/**  * 地址  */  
public class Address {  private String city;  private String country;  // constructors, getters and setters  public Address() {  }  }  

测试用例

@Test  
public void jacksonCopy() throws IOException {  Address address = new Address("杭州", "中国");  User user = new User("大山", address);  // 使用Jackson序列化进行深拷贝  ObjectMapper objectMapper = new ObjectMapper();  User copyUser = objectMapper.readValue(objectMapper.writeValueAsString(user), User.class);  // 修改源对象的值  user.getAddress().setCity("深圳");  // 检查两个对象的值不同  assertNotSame(user.getAddress().getCity(), copyUser.getAddress().getCity());  }  

总结

说了这么多深拷贝的实现方法,哪一种方法才是最好的呢?最简单的判断就是根据拷贝的类(包括其成员变量)是否提供了深拷贝的构造函数、是否实现了Cloneable接口、是否实现了Serializable接口、是否实现了默认的无参构造函数来进行选择。如果需要详细的考虑,则可以参考下面的表格:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/555406.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

电子科大16秋计算机应用基础在线作业一,电子科大16秋《计算机应用基础(本科)》在线作业3...

电子科技大学电子科大16秋《计算机应用基础(本科)》在线作业3一、单选题(共35 道试题,共70 分。)1. 在Windows,打开一个菜单后,其中某菜单项会出现下属级联菜单的标识是()。A. 菜单右侧有一组英文提示B. 菜单右侧有一个黑色三角形C. 菜单左侧有一个黑色圆点D. 菜单左…

wince手机投屏代码_除了 iOS,这些手机系统你肯定没用过

今年是2020年,智能手机市场经过大浪淘沙,留存下来的品牌可以说都是精品,国外的像苹果、三星、索尼、LG,国内有华为、小米、OV、魅族....都是有特色有竞争力的。华为在海外的巨幅广告牌但是大家有没有发现,如此多的手机…

SpringBoot读取Resource下文件的几种方式

最近在项目中涉及到Excle的导入功能,通常是我们定义完模板供用户下载,用户按照模板填写完后上传;这里待下载模板位置为resource/excleTemplate/test.xlsx,尝试了四种读取方式,并且测试了四种读取方式分别的windows开发…

电子游戏跟计算机有什么关联,电脑和电子游戏对小学生的影响

小学生个人电脑的普及率在飞速上升。随着年龄的增长,孩子使用电脑、网络的机会和时间不断增加。而与电脑相关的电子游戏也已成为孩子的主要活动内容之一,对他们的生活与发展产生了诸多影响。很多家长都在担忧电脑和电子游戏会对孩子产生负面影响。规律一…

kido机器人用流量吗_国脉电信200元天网卡,交1700流量无线用,拉人加入还会有不错的收益,你信吗?反正我是不信!...

最近又很多人咨询小编国脉天电信网卡是真的吗,是不是骗人的。关于国脉天网骗局,反传销联合会曾在网站发表了一篇关于国脉天网骗局的文章。今天来说说200元的国脉天网和1700的流量包。首先来说说200元购买的国脉天网卡,这个也就是国脉电信推广…

孩子学计算机最佳年龄,孩子学编程最佳年龄是多少

随着IT技术几十年的发展,编程也正变得越来越简单、易用,而不再是那么复杂、繁琐。那么孩子学编程最佳年龄是多少呢?孩子学编程的最佳年龄3-5岁的儿童可以先进行少儿编程语言启蒙,正式学习编程建议在上小学后。国外脑科学实证研究表…

Java:File.separator作用相当于 ‘ \ ‘

其实 File.separator 的作用相当于 ’ \ ’ 在 windows 中 文件文件分隔符用 ’ \ ’ 或者 ’ / ’ 都可以 但是在 Linux 中,是不识别 ’ \ ’ 的,而 File.separator 是系统默认的文件分隔符号,在 UNIX 系统上,此字段的值为 ’ /…

messageformat.format() 自定义参数名_DedeCMS的Java版mcms 第四季之一: 自定义插件

自定义mcms插件包含自定义字典、自定义搜索、自定义模型、自定义表单、自定义页面等功能依赖: Apache Maven<dependency><groupId>net.mingsoft</groupId><artifactId>ms-mdiy</artifactId><version>当前版本</version> </de…

linux给文件加可执行权限

1、加最高权限 chmod 775 文件名 2、加可执行权限 chmod x 文件名

jenkins重启 linux_在Linux中,Jenkins无法启动

问题描述升级后去运行jenkins&#xff0c;并获得以下信息&#xff1a;start jenkinsstart: Job failed to start就是这样…詹金的日志中什么也没有显示…所以很难说很难调试。 (它尚未运行&#xff0c;或类似的东西)。我应该在其他地方查看另一个日志会有所帮助吗&#xff1f; …

现在学html4,HTML学习心得(4)

HTML学习心得(4)CSS的简单框架结构和元素渲染什么是CSS​ CSS&#xff0c;中文名&#xff1a;层叠样式表。是一种用来表现HTML等文件样式的计算机语言。CSS不仅可以静态地修饰网页&#xff0c;还可以配合各种脚本语言动态地对网页各元素进行格式化。​ CSS 能够对网页中元素位置…

JAVA中的内部类(一)静态内部类

Java中的静态内部类&#xff1a;在定义的内部类前加static修饰符&#xff0c;此时的内部类就是静态内部类。通过一个例子来了解静态内部类都有哪些特点。 public class Outer {//定义一个实例变量和一个静态变量private int a;private static int b;//定义一个静态方法和一个非…

hadoop 单机单间_Hadoop单机模式配置

Required Software1. 安装Java环境推荐的版本在链接中有介绍HadoopJavaVersions.2. 安装ssh以使用hadoop脚本管理远程Hadoop daemons.Download HadoopInstalling Software安装JDK&#xff0c;网上的教程比较多&#xff0c;不做详述安装ssh&#xff0c;在Ubuntu Linux系统上通过…

JAVA中的内部类(二)成员内部类

Java中的成员内部类&#xff08;实例内部类&#xff09;&#xff1a;相当于类中的一个成员变量&#xff0c;下面通过一个例子来观察成员内部类的特点 public class Outer {//定义一个实例变量和一个静态变量private int a;private static int b;//定义一个静态方法和一个非静态…

计算机里的网络是什么意思啊,计算机网络中本地站点是什么意思

Dreamweaver 站点提供一种组织所有与 Web 站点关联的文档的方法。通过在站点中组织文件&#xff0c;可以利用 Dreamweaver 将站点上传到 Web 服务器、自动跟踪和维护链接、管理文件以及共享文件。若要充分利用 Dreamweaver 的功能&#xff0c;需要定义一个站点。Dreamweaver 站…

angularjs input标签用一个日期插件后数据不能双向绑定了_微信如何定时发朋友圈?(最方便最好用的办法!)...

微信怎么发朋友圈&#xff08;微信如何定时发朋友圈&#xff09;作为一个运营新媒体的小编&#xff0c;很多情况下&#xff0c;我都会遇到定时发文的情况&#xff0c;对于我来说&#xff0c;定时发文很简单。只要将文案编辑好&#xff0c;使用平台的定时发文功能就可以&#xf…

分类学计算机面试什么,史上最全的机器学习面试题-机器学习爱好者必看

1.什么是机器学习机器学习是为了应对系统程序设计&#xff0c;属于计算机科学类的学科&#xff0c;它能根据经验进行自动学习和提高。例如&#xff1a;一个由程序操纵的机器人&#xff0c;它能根据从传感器搜集到的数据&#xff0c;完成一系列的任务和工作。它能根据数据自动地…

POI的getLastRowNum() getPhysicalNumberOfRows()区别

// 获得总记录数&#xff08;行数&#xff09;int lastRowNum sheet.getLastRowNum();int rowNumsheet.getPhysicalNumberOfRows();同样的都是获取Excel工作sheet行数的两个方法 getLastRowNum()getPhysicalNumberOfRows()他们的主要区别是&#xff1a; getPhysicalNumberOfR…

java获取当月有几天_腾讯程序员裸辞3个月,转行去送外卖,曝出当月收入网友:又骗我去送外卖...

最近在职业论坛看到这样一个热门的帖子&#xff0c;“腾讯员工裸辞3个月&#xff0c;转行去送外卖&#xff0c;曝出当月收入网友&#xff1a;又骗我去送外卖“到底怎么回事&#xff1f;请往下看。原来一位腾讯员工分享了自己从程序员到外卖员的经历&#xff0c;据男子说在腾讯&…

锦州哪家计算机学校好,锦州十大排名中专

一、招生专业序号专业代码专业名称学制学习年限科类学费(元/年/生)1600108铁道交通运营管理3年3-5年文史50002600106铁道信号自动控制3年3-5年理工50003600107铁道通信与信息化技术3年3-5年理工50004600101铁道机车3年3-5年理工50005600102铁道车辆...技校中专招生大学招生信息…