Java 对象拷贝工具类

article/2025/9/27 9:14:42

目录

1. Spring 中的对象拷贝

2. 本工具类中的对象拷贝

2.1 拷贝对象本身(单个)

2.2 拷贝对象本身(批量)

2.3 拷贝对象属性至其他类(单个)

2.4 拷贝对象属性至其他类(批量)

4. 工具类源码


1. Spring 中的对象拷贝

其实,在 Spring 中,也有类似的拷贝方法。他就是位于 org.springframework.beans.BeanUtils 工具类中的 copyProperties 方法。下面就简单演示下这个方法的使用效果。

为了方便演示,我们创建两个有部分相同属性的对象 Cat 类和 Dog 类(都有 name 和 age 字段)。

Cat 类如下:

@Data
public class Cat {private String name;private Integer age;private String color;}

Dog 类如下:

@Data
public class Dog {private String name;private Integer age;private String address;}

测试代码:

import org.springframework.beans.BeanUtils;public class Test {public static void main(String[] args) {// 实例化一个 Cat 对象并赋值属性Cat cat = new Cat();cat.setName("tom");cat.setAge(5);// 实例化一个 Dog 对象Dog dog = new Dog();// 将 cat 对象中的属性值拷贝至 dog 中BeanUtils.copyProperties(cat, dog);System.out.println("拷贝后:" + dog);}}

 测试效果:

可以看到,相同的 name 和 age 已经复制过去了。

2. 本工具类中的对象拷贝

上面我们演示了 Spring 下 BeanUtils 工具类中的对象属性拷贝,虽然他也可以成功拷贝对象中的属性,但对于我个人来说,还是有点不适应。

首先,Spring 去拷贝一个对象属性的时候,需要先创建好另外一个对象,然后再进行属性拷贝,这一步对象创建是明显可以放到工具方法中去的。

其次,如果只是本类复制的话,参数只需要传一个源对象的实例就应该够了,而Spring就算拷贝本类,也得传两个参数,即源实例对象和目标实例对象。

另外,Spring 的对象拷贝不支持批量拷贝,比如我们将 List<Cat> 属性拷贝后,生成一个 List<Dog> 中,只能自己循环去拷贝生成每个 Dog,然后添加到 List<Dog> 中。

于是,敝人针对个人习惯,编写了一个适合自己的编码习惯的对象拷贝工具类 BeanUtils(类名还是参照的 Spring),具体使用效果如下。

下面先做效果演示,工具类源码放在文章最后。

2.1 拷贝对象本身(单个)

比如,我们想复制一个对象本身(如 cat),那么直接使用下面这个方法就可以了。

Cat newCat = BeanUtils.copy(cat);

测试代码:

 测试效果:

从测试结果我们可以看到,源对象和复制对象的每个字段值已经拷贝过去了,但两个对象的内存 hashCode 并不相同,说明并不是同一个对象,也就说我们是进行深拷贝的,两个对象是互不影响的。

另外,我们这个工具类不但支持类对象本身属性拷贝,连父类属性拷贝也是支持的。

比如,Cat类去继承下面这个 Animal 类:

@Data
public class Animal {private Integer price;private Date birth;}
@Data
public class Cat extends Animal {private String name;private Integer age;private String color;}

我们再试试测试一下:

测试效果:

可以看到,我们的父类属性字段值也确实复制成功了。

2.2 拷贝对象本身(批量)

工具类中不仅支对单个对象拷贝的,对多个对象的拷贝也是支持的。

List<Cat> newCatList = BeanUtils.copyList(catList);

测试代码:

测试效果:

可以看到,批量属性复制也是OK的,拷贝后的集合中每个对象新生成的深拷贝对象。

2.3 拷贝对象属性至其他类(单个)

上面,我们演示了对象本身复制的效果,下面继续演示下拷贝同名字段到其他属性的效果。

Dog dog = BeanUtils.copy(cat, Dog.class);

我们把 Cat 中的同名字段属性拷贝到 Dog 中去,我们让 Dog 也去继承下 Anima 类。

@Data
public class Dog extends Animal {private String name;private Integer age;private String address;}

测试代码:

因为拷贝前后是两个完全不一样的对象了,所以这里就不再打印地址 hashCode 来进行说明是深拷贝了。

测试效果:

可以看到 cat 中的所有相同属性已经拷贝到 dog 中去了。

2.4 拷贝对象属性至其他类(批量)

同理,我们拷贝对象属性至其他类也是支持批量操作的。

List<Dog> dogs = BeanUtils.copyList(cats, Dog.calss);

测试代码:

测试效果:

可以看到,批量复制也是OK的。

至此,整个对象的拷贝的四个常用方法已经都已经支持了。

4. 工具类源码

下面就是整个工具类的源码 BeanUtils :

package com.zyq.utils.common;import java.lang.reflect.Field;
import java.util.*;/*** @author zyqok* @since 2022/07/18*/
@SuppressWarnings("unused")
public class BeanUtils {/*** 拷贝数据到新对象(单个)** @param source 源实例对象* @return 拷贝后的新实例对象*/public static <T> T copy(T source) {if (Objects.isNull(source)) {return null;}Class<?> c = source.getClass();List<Field> fields = getFields(c);return newInstance(source, c, fields);}/*** 拷贝数据到新对象(批量)** @param sourceList 源实例对象集合* @return 拷贝后的新实例对象集合*/public static <T> List<T> copyList(List<T> sourceList) {if (Objects.isNull(sourceList) || sourceList.isEmpty()) {return Collections.emptyList();}Class<?> c = getClass(sourceList);if (Objects.isNull(c)) {return Collections.emptyList();}List<Field> fields = getFields(c);List<T> ts = new ArrayList<>();for (T t : sourceList) {T s = newInstance(t, c, fields);if (Objects.nonNull(s)) {ts.add(s);}}return ts;}/*** 单个深度拷贝** @param source 源实例化对象* @param target 目标对象类(如:User.class)* @return 目标实例化对象*/public static <T> T copy(Object source, Class<T> target) {if (Objects.isNull(source) || Objects.isNull(target)) {return null;}List<Field> sourceFields = getFields(source.getClass());List<Field> targetFields = getFields(target);T t = null;try {t = newInstance(source, target, sourceFields, targetFields);} catch (Exception e) {e.printStackTrace();}return t;}/*** 批量深度拷贝(如果原集合中有null,则自动忽略)** @param sourceList 源实例化对象集合* @param target     目标对象类(如:User.class)* @return 目标实例化对象集合*/public static <T, K> List<K> copyList(List<T> sourceList, Class<K> target) {if (Objects.isNull(sourceList) || sourceList.isEmpty() || Objects.isNull(target)) {return Collections.emptyList();}Class<?> c = getClass(sourceList);if (Objects.isNull(c)) {return Collections.emptyList();}List<Field> sourceFields = getFields(c);List<Field> targetFields = getFields(target);List<K> ks = new ArrayList<>();for (T t : sourceList) {if (Objects.nonNull(t)) {try {K k = newInstance(t, target, sourceFields, targetFields);ks.add(k);} catch (Exception e) {e.printStackTrace();}}}return ks;}/*** 获取List集合中的类名** @param list 对象集合* @return 类名*/private static <T> Class<?> getClass(List<T> list) {for (T t : list) {if (Objects.nonNull(t)) {return t.getClass();}}return null;}/*** 实例化同源对象** @param source 源对象* @param c      源对象类名* @param fields 源对象属性集合* @return 同源新对象*/@SuppressWarnings("unchecked")private static <T> T newInstance(T source, Class<?> c, List<Field> fields) {T t = null;try {t = (T) c.newInstance();for (Field field : fields) {field.setAccessible(true);field.set(t, field.get(source));}} catch (Exception e) {e.printStackTrace();}return t;}/*** 目标实例化对象** @param source       原对实例化象* @param target       目标对象类* @param sourceFields 源对象字段集合* @param targetFields 目标对象属性字段集合* @return 目标实例化对象*/private static <T> T newInstance(Object source, Class<T> target, List<Field> sourceFields,List<Field> targetFields) throws Exception {T t = target.newInstance();if (targetFields.isEmpty()) {return t;}for (Field field : sourceFields) {field.setAccessible(true);Object o = field.get(source);Field sameField = getSameField(field, targetFields);if (Objects.nonNull(sameField)) {sameField.setAccessible(true);sameField.set(t, o);}}return t;}/*** 获取目标对象中同源对象属性相同的属性(字段名称,字段类型一致则判定为相同)** @param field  源对象属性* @param fields 目标对象属性集合* @return 目标对象相同的属性*/private static Field getSameField(Field field, List<Field> fields) {String name = field.getName();String type = field.getType().getName();for (Field f : fields) {if (name.equals(f.getName()) && type.equals(f.getType().getName())) {return f;}}return null;}/*** 获取一个类中的所有属性(包括父类属性)** @param c 类名* @return List<Field>*/private static List<Field> getFields(Class<?> c) {List<Field> fieldList = new ArrayList<>();Field[] fields = c.getDeclaredFields();if (fields.length > 0) {fieldList.addAll(Arrays.asList(fields));}return getSuperClassFields(c, fieldList);}/*** 递归获取父类属性** @param o         类名* @param allFields 外层定义的所有属性集合* @return 父类所有属性*/private static List<Field> getSuperClassFields(Class<?> o, List<Field> allFields) {Class<?> superclass = o.getSuperclass();if (Objects.isNull(superclass) || Object.class.getName().equals(superclass.getName())) {return allFields;}Field[] fields = superclass.getDeclaredFields();if (fields.length == 0) {return allFields;}allFields.addAll(Arrays.asList(fields));return getSuperClassFields(superclass, allFields);}}


http://chatgpt.dhexx.cn/article/t8DZLMnK.shtml

相关文章

Java表达式运算工具类

对于表达式的运算转换工具类&#xff0c;使用场景&#xff0c;例如前端文本框让用户输入了加减乘除运算&#xff0c;工具类智能转换。 资源下载&#xff1a;Java表达式工具类&#xff0c;用于加减乘除等智能转换-Java文档类资源-CSDN下载 例如&#xff1a; public static void…

阿里OSS工具类

参考博客&#xff1a;阿里OSS入门 bucket配置信息&#xff1a;alios.properties OSS配置文件&#xff1a;AliOSSConfig.java Data public class AliOSSConfig {private String endpoint;private String accessKeyId;private String accessKeySecret;private String bucketNam…

Guava Lists工具类

文章目录 01 概述02 Lists工具类03 文末 01 概述 Guava 是 Google 开源的一个 Java 工具库&#xff0c;里面有很多工具类&#xff0c;本文要讲的是里面的Lists工具类。 注意&#xff0c;使用Guava工具类库&#xff0c;必须先添加依赖&#xff1a; <dependency><gro…

JAVA之常用的工具类

目录 1 集合工具类 1.1 java.util.Collections 1.1.1 基本操作 1.1.2 转换线程安全集合 1.2 org.springframework.util.CollectionUtils 1.3 org.apache.commons.collections.CollectionUtils 1.4 org.apache.commons.lang.ArrayUtils 1.5 org.apache.commons.lang3.Ar…

常用的Java开发工具类

前言 在java的庞大体系中&#xff0c;其实有很多不错的小工具&#xff0c;也就是我们平常说的&#xff1a;轮子。 如果在我们的日常工作当中&#xff0c;能够将这些轮子用户&#xff0c;再配合一下idea的快捷键&#xff0c;可以极大得提升我们的开发效率。 今天我决定把一些…

工具类的详解

工具类网站 1、工具类 1.1 定义 工具类是为了提供一些通用的、某一非业务领域内的公共方法&#xff0c;不需要配套的成员变量&#xff0c;仅仅是作为工具方法被使用。所以将它做成静态方法最合适&#xff0c;不需要实例化&#xff0c;能够获取到方法的定义并调用就行。 1.2…

学习日记-安卓Package Manager和Package Installer

安装和卸载APK&#xff08;安卓应用程序包文件&#xff09;&#xff0c;运作原理。 什么是Package Manager&#xff08;包管理器&#xff09;和Package Installer&#xff08;程序安装包&#xff09;&#xff1f; APK文件保存在Android的哪个地方&#xff1f; APK文件安装过…

PackageInstaller (tv 修改安装app界面按钮及自动获取焦点)附源码分析

\packages\apps\PackageInstaller 一、一条真实的修改记录 TVOS基于的是一套板卡厂商原有的源码(mstar android8.0版本&#xff09;原生的这个app安装界面&#xff0c;存在俩个比较严重的用户体验问题&#xff0c; 1’、下面那俩按钮太小了&#xff0c;而且…

android packages/apps 加入工程,深入安卓Package Manager和Package Installer

我们每天都在安装和卸载APK(安卓应用程序包文件)&#xff0c;或许一天会有好几次&#xff0c;但是你有想过下面问题吗&#xff1f;什么是Package Manager(包管理器)和Package Installer(程序安装包)&#xff1f; APK文件保存在Android的哪个地方&#xff1f; APK文件安装过程的…

RK3568平台开发系列讲解(安卓篇)PackageInstaller(应用安装)流程介绍

文章目录 <font color=#0990d9>一、PackageInstaller入口<font color=#0990d9>二、InstallStart<font color=#0990d9>三、InstallStaging<font color=#0990d9>四、PackageInstallerActivity<font color=#0990d9>五、InstallInstalling<font c…

Android9.0 PM机制系列(一)PackageInstaller初始化解析

前言 包管理机制是Android中的重要机制&#xff0c;是应用开发和系统开发需要掌握的知识点之一。 包指的是Apk、jar和so文件等等&#xff0c;它们被加载到Android内存中&#xff0c;由一个包转变成可执行的代码&#xff0c;这就需要一个机制来进行包的加载、解析、管理等操作&…

PackageInstaller源码分析(一)

本篇博客分析PackageInstaller源码目的是分析Android权限机制&#xff0c;Android App的权限在应用被安装时&#xff0c;用户选择授予或者拒绝。所以&#xff0c;分析Android权限机制源码的第一步分析应用程序安装时的行为。   此次阅读源码旨在解决的问题&#xff1a;Andro…

A*B problem(FFT)

A*B problem&#xff08;FFT&#xff09; 设两个多项式\(A(x)\)和\(B(x)\)&#xff0c;它们的系数镜像反转一下&#xff0c;得到的多项式是\(A(x)\)和\(B(x)\)。那么\(C(x)A(x)*B(x)\)和\(C(x)A(x)*B(x)\)的系数也是镜像反转的。这个&#xff0c;&#xff0c;感性理解一下吧。 …

【kissfft】使用过程中的一些坑总结

API kissfft有两套API&#xff0c; 一个是在kiss_fftr.h中 另一个在kiss_fft.h中 区别 Basic API还是kiss_fft.h里的&#xff0c;kiss_fftr.h是在kiss_fft.h的基础上封装了一层。 Basic API只有fft没有见到ifft&#xff1f;&#xff1f; 利用频域数据的共轭对称性可以使用…

2020山东大学计算机组成原理课程设计报告

《计算机组成原理》 课程设计报告 微指令模型机实现 班级&#xff1a; 姓名&#xff1a; 学号&#xff1a; 小组成员&#xff1a; 完成日期&#xff1a;2020.10.16 一、计算机的功能和用途 通过该课程设计的学习&#xff0c;我们设计一台模型机&#xff0c;该模型机运行…

创建react应用程序_创建多版本React应用程序的6个步骤

创建react应用程序 The React team said that there are no new features in React 17, but react17.0.0-rc.0 comes with the power to lazy load and deep integrate multiple versions of React. This no-feature is larger than any feature, which is a stepping stone fo…

你真的懂package.json吗

点击蓝字 「前端小苑」关注我 作者 | MasonEast 编辑 | 桔子酱 前言 在Node.js中&#xff0c;模块是一个库或框架&#xff0c;也是一个Node.js项目。Node.js项目遵循模块化的架构&#xff0c;当我们创建了一个Node.js项目&#xff0c;意味着创建了一个模块&#xff0c;这个模块…

《Linux编程》上机作业 ·004【文件I/O操作】

注&#xff1a;前言、目录见 https://blog.csdn.net/qq_44220418/article/details/108428971 友情提醒&#xff1a;仅供参考理解&#xff0c;请勿直接复制粘贴 友情提醒&#xff1a;仅供参考理解&#xff0c;请勿直接复制粘贴 友情提醒&#xff1a;仅供参考理解&#xff0c;…

CPU比GPU训练神经网络快十几倍,英特尔:别用矩阵运算了

来源丨机器之心 神经网络训练通常是 GPU 大显身手的领域&#xff0c;然而莱斯大学和英特尔等机构对 GPU 的地位发起了挑战。 在深度学习与神经网络领域&#xff0c;研究人员通常离不开 GPU。得益于 GPU 极高内存带宽和较多核心数&#xff0c;研究人员可以更快地获得模型训练的结…

用于基于 CNT 的射频辐射热计开发研究的 CPX-VF 探针台

我们会不时强调我们的低温探针台如何用于有趣的研究。我们最新的应用重点是阿克伦大学领导的工作&#xff0c;并发表在上个月的IEEE 微波理论与技术汇刊上。与来自美国陆军和 Nano-C Inc.&#xff08;马萨诸塞州 Westwood 的纳米结构碳材料及其应用开发商&#xff09;的研究人员…