Java对象的复制三种方式

概述

在实际编程过程中,我们常常要遇到这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能 会需要一个和A完全相同新对象B,并且此后对B任何改动都不会影响到A中的值,也就是说,A与B是两个独立的对象,但B的初始值是由A对象确定的。例如下面程序展示的情况:

结果:

为什么改变学生2的学号,学生1的学号也发生了变化呢?

原因出在(stu2 = stu1) 这一句。该语句的作用是将stu1的引用赋值给stu2,

这样,stu1和stu2指向内存堆中同一个对象。如图:

那么,怎么能干干净净清清楚楚地复制一个对象呢。在 Java语言中,用简单的赋值语句是不能满足这种需求的。要满足这种需求有很多途径,

(1)将A对象的值分别通过set方法加入B对象中;

(2)通过重写java.lang.Object类中的方法clone();

(3)通过org.apache.commons中的工具类BeanUtils和PropertyUtils进行对象复制;

(4)通过序列化实现对象的复制。

2.将A对象的值分别通过set方法加入B对象中

对属性逐个赋值,本实例为了演示简单就设置了一个属性:

我们发现,属性少对属性逐个赋值还挺方便,但是属性多时,就需要一直get、set了,非常麻烦。

3.重写java.lang.Object类中的方法clone()

先介绍一下两种不同的克隆方法,浅克隆(ShallowClone)和深克隆(DeepClone)。

在Java语言中,数据类型分为值类型(基本数据类型)和引用类型,值类型包括int、double、byte、boolean、char等简单数据类型,引用类型包括类、接口、数组等复杂类型。浅克隆和深克隆的主要区别在于是否支持引用类型的成员变量的复制,下面将对两者进行详细介绍。

3.1浅克隆

一般步骤:

1.被复制的类需要实现Clonenable接口(不实现的话在调用clone方法会抛出CloneNotSupportedException异常), 该接口为标记接口(不含任何方法)

2.覆盖clone()方法,访问修饰符设为public。方法中调用super.clone()方法得到需要的复制对象。(native为本地方法)

在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。

简单来说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。

在Java语言中,通过覆盖Object类的clone()方法可以实现浅克隆

3.2深克隆

怎么两个学生的地址都改变了?

原因是浅复制只是复制了addr变量的引用,并没有真正的开辟另一块空间,将值复制后再将引用返回给新对象。

为了达到真正的复制对象,而不是纯粹引用复制。我们需要将Address类可复制化,并且修改clone方法,完整代码如下:

在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。

简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制。

在Java语言中,如果需要实现深克隆,可以通过覆盖Object类的clone()方法实现,也可以通过序列化(Serialization)等方式来实现。

(如果引用类型里面还包含很多引用类型,或者内层引用类型的类里面又包含引用类型,使用clone方法就会很麻烦。这时我们可以用序列化的方式来实现对象的深克隆。)

4.工具类BeanUtils和PropertyUtils进行对象复制

这种写法无论多少种属性都只需要一行代码搞定,很方便吧!除BeanUtils外还有一个名为PropertyUtils的工具类,它也提供copyProperties()方法,作用与BeanUtils的同名方法十分相似,主要的区别在于BeanUtils提供类型转换功能,即发现两个JavaBean的同名属性为不同类型时,在支持的数据类型范围内进行转换,而PropertyUtils不支持这个功能,但是速度会更快一些。在实际开发中,BeanUtils使用更普遍一点,犯错的风险更低一点。

5.通过序列化实现对象的复制

序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中。通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制其引用的成员对象,因此通过序列化将对象写到一个流中,再从流里将其读出来,可以实现深克隆。需要注意的是能够实现序列化的对象其类必须实现Serializable接口,否则无法实现序列化操作。

调用ByteArrayOutputStream或ByteArrayInputStream对象的close方法没有任何意义。这两个基于内存的流只要垃圾收集器清理对象就能够释放资源,这一点不同于对外部资源(如文件流)的释放。

修改克隆的Person对象person1关联的汽车对象的品牌属性,原来的Person对象person关联的汽车不会受到任何影响,因为在克隆Person对象时其关联的汽车对象也被克隆了。

基于序列化和反序列化实现的克隆不仅仅时深度克隆,更重要的是通过范型限定,可以检查出要克隆的对象是否支持序列化,这项检查是编译期完成的,不是在运行时抛出异常,这种方案明显优于使用Object类的clone方法克隆对象。

java对象复制_Java对象的复制三种方式相关推荐

  1. java数组初始_java数组初始化的三种方式

    第一种方式:通过给定数组长度来赋值: public static void main(String[] args) { // TODO Auto-generated method stub //第一种 ...

  2. java calendar格式化_java格式化日期的三种方式

    8月 29, 2014 | Nix.Huang 1)借助DateFormat类: public String toString(Date d) { SimpleDateFormat sdf = new ...

  3. Spring中把一个bean对象交给Spring容器管理的三种方式

    一.使用@Component,把bean对象依赖交给Spring容器 注意,该注解不能使用,则说明未添加依赖,需要去该项目pom.xml文件内引入依赖,若该项目只是作为一个存放工具类的子模块项目,没有 ...

  4. java定义数组_java中数组的三种定义方式_java中数组的定义及使用方法(推荐)...

    java中数组的三种定义方式 java中,数组是一种很常用的工具,今天我们来说说数组怎么定义 [java] view plain copy /** * 数组的三种定义方法 * 1.数组类型[] 数组名 ...

  5. java时间戳是什么类型_java 获取时间戳的三种方式

    java 获取时间戳的三种方式 CreationTime--2018年7月13日16点29分 Author:Marydon 1.实现方式 方式一:推荐使用 System.currentTimeMill ...

  6. java method 创建_java中创建对象的5种方式

    作为Java开发者,我们每天创建很多对象,但我们通常使用依赖管理系统,比如Spring去创建对象.然而这里有很多创建对象的方法. Java中有5种创建对象的方法,下面列出例子还有他们的字节码: 使用n ...

  7. java string分割_java 字符串分割的三种方法(总结)

    最近在项目中遇到一个小问题,一个字符串分割成一个数组,类似String str="aaa,bbb,ccc"; 然后以","为分割符,将其分割成一个数组,用什么方 ...

  8. java多线程的实现方式_JAVA多线程实现的三种方式

    最近在做代码优化时学习和研究了下JAVA多线程的使用,看了菜鸟们的见解后做了下总结. 1.JAVA多线程实现方式 JAVA多线程实现方式主要有三种:继承Thread类.实现Runnable接口.使用E ...

  9. java se 定时任务_Java实现定时任务的三种方法

    一.Quartz的特点 按作业类的继承方式来分,主要有以下两种: 作业类继承org.springframework.scheduling.quartz.QuartzJobBean类的方式作业类不继承o ...

  10. java json 解析_Java解析JSON的四种方式

    一.什么是JSON JSON是一种轻量级的数据交换格式,采用完全独立于编程语言的文本格式来存储和表示数据.简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言. 易于阅读和编写,同时也易于解析和 ...

最新文章

  1. python怎么限制输出精度_谈谈关于Python里面小数点精度控制的问题
  2. SAP Hybris的类型系统更改和ABAP的LOAD_PROGRAM_TABLE_MISMATCH
  3. HTML与CSS基础之伪类选择器(三)
  4. 全志A33-BootLoader的两个阶段:boot0和second boot
  5. opencv检测相交点_OpenCV特征点检测------ORB特征
  6. 空白世界地图打印版_洪恩识字卡1300字十字帖+绘本,可打印成册
  7. php使用webservivce_JWS服务开发使用指南
  8. 如何通过控制台访问openstack实例_如何通过seo提高网站设计的访问量
  9. Mysql 索引的学习
  10. python计算两点间距离_用python计算图像中两点之间的距离
  11. 2013第35周五杂记
  12. 类似QQ的可隐藏的便签工具SNOTE
  13. VLAN与三层交换机
  14. 纯css绘制简易对话气泡
  15. 东北大学计算机学院领导,计算机学院召开新一届全体干部大会
  16. 极米h6和 极米rs pro2区别,4k版极米h6和rspro2哪个好
  17. 网名闲话之“茶乡浪子”
  18. 单片机原理及应用之AT89S52
  19. .net调用百度api统计接口
  20. LeetCode500. 键盘行

热门文章

  1. 什么是生成艺术NFT,Art Blocks为什么能持续霸榜
  2. 快讯!分布式调度项目ElasticJob即将重新起航
  3. HDU - 5773 贪心 + LIS
  4. python学习(判断某年某月某日)
  5. 怎样开启无线热点服务器,在Ubuntu系统的电脑上开启无线热点全攻略
  6. [Unity3D] [学习] Unity3D的官方文档
  7. 体验服官网和平精英维护服务器,和平精英体验服怎么注册?和平精英体验服注册流程...
  8. 大数据可视化(一)数据可视化概述
  9. 简单的三点式腰背肌锻炼方法
  10. [NLP]——BPE、WordPiece、Unigram and SentencePiece