Java对象的拷贝与克隆

news2024/12/24 8:16:14

Java对象的拷贝与克隆

    在日常开发中,我们经常需要给对象进行赋值,通常会调用其 set/get 方法,有些时候,为了简化代码,我们会采用第三方工具类进行属性拷贝。但是面对如此多的拷贝工具和方法,其性能差异如何不得而知,下面我就对几种属性拷贝工具和方法进行性能分析。

    比如我们经常在代码中会对一个数据结构封装成 DO、SDO、DTO、VO 等,而 这些 Bean 中的大部分属性都是一样的,所以使用属性拷贝类工具可以帮助我们节省 大量的 set 和 get 操作。

属性拷贝

    我所知道的属性拷贝方法大致分为三种:1.原生get/set方法 2.属性拷贝工具类 3. 序列化再反序列化

  1. set/get方法这里就不做介绍了。

  2. 属性拷贝工具类。

    目前使用比较广泛的属性拷贝工具类有:

    1. Spring BeanUtils

    2. Apache BeanUtils
      还有其他属性拷贝的工具类这里就不再赘述了,原理都差不多。只不过实现的功能有所差异。

      性能对比:

      首先定义两个类:

      	@Data
      	public static class Student {
      		private Integer id;
      		private String name;
      		private Integer age;
      		private Date birthday;
      	}
      
      	@Data
      	public static class StudentDTO {
      		private String name;
      		private Integer age;
      		private Date birthday;
      	}
      

      进行拷贝测试性能:

      	public static void main(String[] args) {
      		Student student = new Student();
      		student.setId(1);
      		student.setName("独孤求败");
      		student.setAge(18);
      		student.setBirthday(new Date());
      		
              StopWatch watch = new StopWatch();
      		watch.start();
      		
              int size = 1000000;
      		for (int i = 0; i < size; i++) {
      			StudentDTO dto = new StudentDTO();
      			BeanUtils.copyProperties(student, dto);
      		}
      		watch.stop();
      		System.out.println(watch.prettyPrint());
      	}
      

      结果如下:

      工具类执行1000次耗时执行10000次耗时执行100000次耗时执行1000000次耗时执行10000000次耗时
      Spring BeanUtils132ms178ms386ms2315ms18976ms
      Apache BeanUtils140ms314ms699ms4399ms40302ms

      结论:

      ​ 由此可见,Sping的属性拷贝工具类是最快的,因为 Apache BeanUtils 力求做得完美 , 在代码中增加了非常多的校验、兼容、日志打印等代码,过度的包装导致性能下降严重。如果是追求性能的话建议不要使用Apache BeanUtils

  3. JSON序列化再反序列化实现拷贝

    序列化和反序列化也能实现拷贝和克隆的功能,常见的JSON序列化方式有:

    • 阿里的Fastjson

    • Google的Gson

      进行拷贝功能测试:

      	public static void main(String[] args) {
      		Student student = new Student();
      		student.setId(1);
      		student.setName("独孤求败");
      		student.setAge(18);
      		student.setBirthday(new Date());
      		StopWatch watch = new StopWatch();
              Gson gson = new Gson();
              
      		watch.start();
      		int size = 1000;
      		for (int i = 0; i < size; i++) {
                  
      			StudentDTO dto = JSON.parseObject(JSON.toJSONString(student), StudentDTO.class);
                  
                  // StudentDTO dto = gson.fromJson(gson.toJson(student), StudentDTO.class);
                  
      		}
      		watch.stop();
          }
      

      结果如下:

      序列化工具执行1000次耗时执行10000次耗时执行100000次耗时执行1000000次耗时执行10000000次耗时
      Fastjson94ms121ms195ms691ms5684ms
      Gson81ms251ms1073ms7867ms72722ms

      结论:

      ​ 可以看出,阿里的Fastjson表现还是不错的,当数据量非常大时,Fastjson与Gson的效率有非常明显的差别。但是,Fastjson漏洞频发,导致使用Fastjson的公司不得不经常加班修复漏洞,希望后面Fastjson漏洞能少一点吧。

    最后从整体上看一下两类属性拷贝的性能差距

    工具类执行1000次耗时执行10000次耗时执行100000次耗时执行1000000次耗时执行10000000次耗时
    Spring BeanUtils132ms178ms386ms2315ms18976ms
    Apache BeanUtils140ms314ms699ms4399ms40302ms
    Fastjson94ms121ms195ms691ms5684ms
    Gson81ms251ms1073ms7867ms72722ms

    ​ 从下图中能更加直观的看出几种属性拷贝的执行效率差别。不过,最高效的还是直接调用get/set方法,所谓的大道至简应该就是这个道理吧。

在这里插入图片描述

对象克隆(拷贝)

在Java语言中,拷贝一个对象时,有浅拷贝与深拷贝两种。

浅拷贝:只拷贝源对象的地址,所以新对象与老对象共用一个地址,当该地址变化时,两个对象也会随之改变。

深拷贝:拷贝对象的所有值,即使源对象发生任何改变,拷贝的值也不会变化。

浅拷贝这里就不介绍了,主要介绍一下几种深拷贝方式。常用的深拷贝方式有几种:

  1. 重写Object中的clone()方法
  2. Apache Commons Lang序列化
  3. JSON序列化

重写clone

​ 调用重写的Object中的clone()方法,实际上是调用C++的本地函数进行拷贝,所以其拷贝效率非常高。但是有几点需要注意。

  • 拷贝对象需要实现Cloneable接口,并重写clone()方法。

  • 拷贝对象的属性如果是对象乐行,拷贝的仍然是原属性对象。

    比如:

    	@Data
    	public static class Student implements Serializable, Cloneable {
    		private Integer id;
    		private String name;
    		private Integer age;
    		private Date birthday;
    
    		@Override
    		public Student clone() {
    			try {
    				Student clone = (Student) super.clone();
    				// TODO: copy mutable state here, so the clone can't change the internals of the original
    				return clone;
    			} catch (CloneNotSupportedException e) {
    				throw new AssertionError();
    			}
    		}
    	}
    

    ​拷贝之后,新的对象中的属性对象仍然是原对象中的属性对象。所以对象属性需要特殊处理。但是,有些对象不用处理,比如String和基本类型的包装类,因为他们都是不可变类,每次进行运算时都会指向新的对象,所以不用担心修改后会影响克隆对象的属性值。

Apache Commons Lang序列化

​ Apache Commons Lang序列化主要使用org.apache.commons.lang3包下SerializationUtils.clone()方法进行序列化克隆。序列化克隆后生成的属性对象都是新的,与原对象没有关系。此种方法也有几点需要注意:

  • 克隆对象需要实现Serializable接口。

  • 序列化效率比较低。

    	public static void main(String[] args) {
            
    		Student student = new Student();
    		student.setId(1);
    		student.setName("独孤求败");
    		student.setAge(18);
    		student.setBirthday(new Date());
    
    		Student student1 = SerializationUtils.clone(student);
    		System.out.println(student1);
    
    	}
    

JSON序列化

​ JSON序列化通过把对象序列化为json字符串,再把json字符串反序列化成对象实现。上面属性拷贝的时候已经讲过了,主流的JSON工具有阿里的FastJson和Google的Gson,这里就不再赘述。

下面通过简单的测试验证几种克隆的效率:

public static void main(String[] args) {
		Student student = new Student();
		student.setId(1);
		student.setName("独孤求败");
		student.setAge(18);
		student.setBirthday(new Date());
		StopWatch watch = new StopWatch();
		Gson gson = new Gson();

		watch.start();
		for (int i = 0; i < 100000; i++) {
			Student student1 = student.clone();
         // Student student1 = SerializationUtils.clone(student);
         // Student student1 = gson.fromJson(gson.toJson(student), Student.class);
         // Student student1 = JSON.parseObject(JSON.toJSONString(student), Student.class);
		}
		watch.stop();
		System.out.println(watch.getTotalTimeMillis() + "ms");
	}

测试结果:

拷贝方法执行1000次执行10000次执行100000次执行1000000次执行10000000次
原生clone0ms0ms4ms14ms75ms
Apache SerializationUtils96ms320ms1148ms7880ms76159ms
Gson111ms315ms1069ms7345ms70489ms
FastJson113ms121ms203ms514ms3552ms

1 - Made with DesignCap

    综上所述,原生clone和FastJson序列化方式进行拷贝比较好,如果对象属性是基本类型或者String和基本类型的包装类这种不可变类,就可以用原生clone方法进行克隆,否则则可以用FastJson序列化再反序列化进行克隆。当然,上面讲到的属性拷贝也可以用,但是性能却没有那么好。

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

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

相关文章

yo!这里是STL::unordered系列简单模拟实现

目录 前言 相关概念介绍 哈希概念 哈希冲突与哈希函数 闭散列 框架 核心函数 开散列 框架 核心函数 哈希表&#xff08;开散列&#xff09;的修改 迭代器实现 细节修改 unordered系列封装 后记 前言 我们之前了解过map和set知道&#xff0c;map、set的底层结构是…

亚信科技斩获“鼎新杯”多项大奖!AntDB数据库在信创赛道再创佳绩

近日&#xff0c;第二届“鼎新杯”数字化转型应用大赛全国总决赛在北京落下帷幕&#xff0c;亚信科技成功收获一等奖1项、二等奖1项、三等奖3项、行业标杆奖1项。 “两江协同创新区智慧园区项目&#xff08;二期&#xff09;”斩获两项殊荣 在“行业数字化融合方向-智慧园区”…

Java并发工具-4-并发框架(ExecutorForkJoin)

一 Executor 并发框架介绍 1 整体结构介绍 executor [ɪɡˈzekjətə(r)] 执行者 execute [ˈeksɪkjuːt] 执行 从 JDK 1.5 开始&#xff0c;java 中将工作单元和执行机制做了分离&#xff0c;于是 Executor 并行框架出现。 什么是工作单元&#xff08;或称为任务&#xff…

Leetcode 第 369 场周赛题解

Leetcode 第 369 场周赛题解 Leetcode 第 369 场周赛题解题目1&#xff1a;2917. 找出数组中的 K-or 值思路代码复杂度分析 题目2&#xff1a;2918. 数组的最小相等和思路代码复杂度分析 题目3&#xff1a;2919. 使数组变美的最小增量运算数思路代码复杂度分析 题目4&#xff1…

合并两个有序链表OJ

合并两个有序链表OJ 文章目录 合并两个有序链表OJ一、题目及要求二、思路分析三、代码实现 一、题目及要求 二、思路分析 其次&#xff0c;题目里说了新链表是通过拼接原来的结点形成的&#xff0c;所以说我们不需要开辟新的空间。 三、代码实现 if (list1 NULL) {return li…

在字节4年,一个27岁女软件测试工程师的心路历程

个人经验分享 简单的先说一下&#xff0c;坐标深圳&#xff0c;18届本科毕业&#xff0c;算上在字节的面试&#xff0c;一共有面试了5家公司&#xff08;不想请假&#xff0c;所以只是每个晚上去其他公司面试&#xff0c;面试的公司就比较少&#xff09; 其中面试成功的有3家&…

vue基础知识十八:说说你对keep-alive的理解是什么?

一、Keep-alive 是什么 keep-alive是vue中的内置组件&#xff0c;能在组件切换过程中将状态保留在内存中&#xff0c;防止重复渲染DOM keep-alive 包裹动态组件时&#xff0c;会缓存不活动的组件实例&#xff0c;而不是销毁它们 keep-alive可以设置以下props属性&#xff1a…

js各种简单事件处理(整理)

**## 获取当天昨天日期** // 当天日期 const today new Date();// 格式化当天日期为 YYYY-MM-DD 格式 const formattedToday today.toISOString().slice(0, 10);// 昨天日期 const yesterday new Date(); yesterday.setDate(yesterday.getDate() - 1);// 格式化昨天日期为 Y…

2023年11月在线IDE流行度最新排名

点击查看最新在线IDE流行度最新排名&#xff08;每月更新&#xff09; 2023年11月在线IDE流行度最新排名 TOP 在线IDE排名是通过分析在线ide名称在谷歌上被搜索的频率而创建的 在线IDE被搜索的次数越多&#xff0c;人们就会认为它越受欢迎。原始数据来自谷歌Trends 如果您相…

广和通5G模组FM650助力阿里云打造无影魔方Pro

随着云基础设施的完善及云电脑体验的不断优化&#xff0c;越来越多的个人和企业选择无影云电脑进行办公。基于云原生的云网端技术架构&#xff0c;无影云电脑相比传统PC&#xff0c;具有弹性、安全、保障个人数据等产品优势。 10月31日&#xff0c;阿里云在杭州云栖大会上宣布…

易货:一种古老而新颖的交易方式

在当今快速发展的经济环境中&#xff0c;易货模式正逐渐引起人们的关注。这种古老而新颖的交易方式&#xff0c;不仅为企业提供了新的商业机会&#xff0c;还为消费者带来了更多的选择。本文将详细介绍易货模式的概念、优势以及如何实现易货交易&#xff0c;并探讨这种模式未来…

精解括号匹配问题与极致栈设计:揭开最大栈和最小栈的奥秘

目录 括号匹配问题最小栈最大栈 最大栈和最小栈是极致栈的两个重要变种。最大栈用于存储当前匹配的最大值&#xff0c;而最小栈用于存储当前匹配的最小值。 括号匹配问题 这个问题我们来看力扣20题的描述&#xff1a; 给定一个只包括 ‘(’&#xff0c;‘)’&#xff0c;‘{’…

如何修改文件的修改日期?

如何修改文件的修改日期&#xff1f;文件的修改日期指的是文件最近一次被修改的日期和时间。当文件内容被修改、编辑或更新时&#xff0c;系统会自动更新文件的修改日期。这个日期记录了文件内容的实际修改时间&#xff0c;可以帮助用户了解文件的更新情况以及文件版本的管理。…

[云原生案例2.2 ] Kubernetes的部署安装 【单master集群架构 ---- (二进制安装部署)】网络插件部分

文章目录 1. Kubernetes的网络类别2. Kubernetes的接口类型3. CNI网络插件 ---- Flannel的介绍及部署3.1 简介3.2 flannel的三种模式3.3 flannel的UDP模式工作原理3.4 flannel的VXLAN模式工作原理3.5 Flannel CNI 网络插件部署3.5.1 上传flannel镜像文件和插件包到node节点3.5.…

2023年度API安全状况详解

随着云计算和移动应用的快速发展&#xff0c;API&#xff08;应用程序接口&#xff09;已成为不可或缺的技术组成部分。然而&#xff0c;API的广泛使用也带来了安全风险。本文将探讨2023年的API安全状况&#xff0c;并介绍了一些应对这些安全挑战的最佳实践。 引言 随着全球互联…

改进YOLOv5:结合ICCV2023|动态蛇形卷积,构建不规则目标识别网络

🔥🔥🔥 提升多尺度、不规则目标检测,创新提升 🔥🔥🔥 🔥🔥🔥 捕捉图像特征和处理复杂图像特征 🔥🔥🔥 👉👉👉: 本专栏包含大量的新设计的创新想法,包含详细的代码和说明,具备有效的创新组合,可以有效应用到改进创新当中 👉👉👉: �…

安装ubuntu-20.04.6-desktop版本、根据ISO文件制作U盘启动盘

前言 本文简述&#xff0c;安装Ubuntu20.04.6的过程&#xff0c;包括制作U盘启动盘、安装。 下载Ubuntu镜像 去官网下载桌面版ubuntu-20.04.6镜像&#xff0c;下载完后文件名是ubuntu-20.04.6-desktop-amd64.iso&#xff0c;这里有个问题amd64.iso能安装在intel处理器的电脑…

计算机网络学习笔记(五):运输层(待更新)

5.1 概述 5.1.1 TCP协议的应用场景 TCP为应用层协议提供可靠传输&#xff0c;发送端按顺序发送&#xff0c;接收端按顺序接收&#xff0c;其间发送丢包、乱序&#xff0c;TCP负责重传和排序。下面是TCP的应用场景。 多次交互&#xff1a;客户端程序和服务端程序需要多次交互才…

ai实景直播矩阵式引流---技术开发搭建(剪辑、矩阵、直播)

目前我们的短视频矩阵剪辑分发系统更新&#xff1a; 无人直播更新&#xff1a; 1、新增文案引流&#xff1a;已接入混元数据大模型&#xff0c;千帆数据大模型&#xff0c;星火数据大模型&#xff0c;盘古数据大模型&#xff0c;通义数据大模型&#xff0c;ChatGPT数据大模型…

muduo源码剖析之TimerQueue类

简介 TimerQueue ​ 通过timerfd实现的定时器功能&#xff0c;为EventLoop扩展了一系列runAt&#xff0c;runEvery&#xff0c;runEvery等函数TimerQueue中通过std::set维护所有的Timer&#xff0c;也可以使用优先队列实现 muduo的TimerQueue是基于timerfd_create实现&#…