精华内容
下载资源
问答
  • java对象copy工具比较
    千次阅读
    2020-02-22 17:03:07
    import java.lang.reflect.Field;
    import java.util.HashMap;
    import java.util.Map;
    
    
    public class ObjectCopy {
        /**
         * 复制对象值 不复制对象地址
         *
         * @param src
         * @param dest
         */
        public static void copy(Object src, Object dest) {
    
            Map<String, Object> srcMap = new HashMap<String, Object>();
            Field[] srcFields = src.getClass().getDeclaredFields();
            for (Field fd : srcFields) {
                try {
                    srcMap.put(fd.getName(), fd.get(src)); //获取属性值
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            Field[] destFields = dest.getClass().getDeclaredFields();
            for (Field fd : destFields) {
                if (srcMap.get(fd.getName()) == null) {
                    continue;
                }
                try {
                    fd.set(dest, srcMap.get(fd.getName())); //给属性赋值
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    
    
    更多相关内容
  • Java copy对象工具

    2018-07-27 13:47:52
    Java CopyUtil工具类,可以进行对象的深copy,比如:对象里面包含对象对象里面包含Map,List,Set...等复杂类型的属性都可以copycopy后的对象与原有对象没有联系,即改变原有对象内容,不会改变copy后的对象里面的...
  • java对象Copy

    2019-08-10 03:11:41
    NULL 博文链接:https://sivan-0222.iteye.com/blog/2211077
  • Java 对象深拷贝工具

    多人点赞 2022-07-18 17:32:04
    比如(1)当我们更新一个对象的时候,如果要记录对象属性的前后变化,那么在更新对象之前,我们应该首先将对象拷贝暂存起来,且这个时候的拷贝一定是深拷贝(内存地址不同的两个对象),因为Java存在对象引用,将一...

    目录

    1. 使用场景

    1.1 场景一

    1.2 场景二

    2. Spring 中的对象拷贝

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

    3.1 拷贝对象本身(单个)

    3.2 拷贝对象本身(批量)

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

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

    4. 工具类源码


    1. 使用场景

    我们在Java编码中,有时候可能会经常遇到对象拷贝的场景。

    1.1 场景一

    当我们更新一个对象的时候,如果要记录对象属性的前后变化,那么在更新对象之前,我们应该首先将对象拷贝暂存起来,且这个时候的拷贝一定是深拷贝(内存地址不同的两个对象),因为Java存在对象引用,将一个对象赋值给另外一个对象,他是浅拷贝的(两个不同变量名,但实际内存地址一样的两个对象)的话,也就是说当我们去更新完成属性值的时候,其实是设置的同一个对象,那么这个时候就会导致更新前后无变化的情况。

    1.2 场景二

    又比如,当我们从数据库中查询出一个实体对象的时候,这个对象往往对应的是和数据库字段 一 一 对应的实体,但这个实体往往又不会满足我们的页面需求。比如我们查询学生课程表的时候,我们数据库往往只是存的一个 id 对应关系,但页面往往是展示的名称,那么这个名称字段我们的表对应的实体类是不应该存在的,这个时候,我们应该创建一个对应的 VO (View Object)类,把额外的需要字段定义在这里面,同时可以去继承原表实体类,这样的一个对象就满足了。到时候,我们把原表实体对应的字段值拷贝到 VO 对象中后,再设置其他表额外的字段,这样就可以返回给前端页面进行展示了。

    综上:所以,对象拷贝还是挺有用途的,但如果我们拷贝对象的时候,去一个一个字段挨着进行取值拷贝的话,难免代码看上去不够优雅。于是,搞一个对象拷贝工具类还是很有必要的。

    2. 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 已经复制过去了。

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

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

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

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

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

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

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

    3.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;
    
    }
    

    我们再试试测试一下:

    测试效果:

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

    3.2 拷贝对象本身(批量)

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

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

    测试代码:

    测试效果:

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

    3.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 中去了。

    3.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);
        }
    
    }
    

    展开全文
  • 由于在项目中经常需要使用到Java对象拷贝和属性复制,如DTO、VO和数据库Entity之间的转换,因此本文对需要用到的相关方法、工具类做一个汇总,包括浅拷贝和深拷贝,方便在需要用到时作为参考。 浅拷贝(Shadow ...

    前言

    由于在项目中经常需要使用到Java的对象拷贝和属性复制,如DTO、VO和数据库Entity之间的转换,因此本文对需要用到的相关方法、工具类做一个汇总,包括浅拷贝和深拷贝,方便在需要用到时作为参考。

    浅拷贝(Shadow Copy)

    手动复制

    手动new对象,并设置相应字段的值,在字段较少时比较方便。另外就是由于是手动赋值,安全性较高,不容易出错,并且性能最好。

    比如有如下一个类:

    public class User {
        
        private String name;
        private int age;
        private Address address;
    
        //getter and setters
    }
    复制代码

    复制的时候只需简单地创建新的对象并赋值:

    User newUser = new User();
    newUser.setName(oldUser.getName());
    newUser.setAge(oldUser.getAge());
    newUser.setAddress(oldUser.getAddress());
    复制代码

    Object类的clone()方法

    这个方法需要实现Cloneable接口(浅拷贝)。要实现深拷贝,如果类中的字段类型是可变类型,也需要重写可变类型的clone方法。同样以User类为例:

    @Getter
    @Setter
    public class User implements Cloneable {
    
        private String name;
        private int age;
        private Address address;
    
        @Override
        public User clone() {
            try {
                User newUser = (User) super.clone();
                //实现深拷贝需要如下手动set
                Address address = newUser.getAddress();
                Address newAddress = address.clone();
                newUser.setAddress(newAddress);
                return newUser;
            } catch (CloneNotSupportedException e) {
                throw new AssertionError();
            }
        }
    
    }
    复制代码

    Address类:

    @Getter
    @Setter
    public class Address implements Cloneable {
        private String province;
        private String city;
    
        @Override
        public Address clone() {
            try {
                return (Address) super.clone();
            } catch (CloneNotSupportedException e) {
                throw new AssertionError();
            }
        }
    }
    复制代码

    使用如下:

    User newUser = oldUser.clone();
    复制代码

    Apache BeanUtils 性能较差

    pom文件中引入如下依赖:

    <dependency>
        <groupId>commons-beanutils</groupId>
        <artifactId>commons-beanutils</artifactId>
        <version>1.9.4</version>
    </dependency>
    复制代码

    使用如下:

    User newUser = new User();
    BeanUtils.copyProperties(newUser, oldUser);//复制字段名、类型相同的
    复制代码

    User newUser = (User) BeanUtils.cloneBean(oldUser);
    复制代码

    Apache PropertyUtils

    PropertyUtils用法跟BeanUtils相同,这里需要注意的是PropertyUtils不支持类型转换功能。

    使用BeanUtils.copyProperties方法时,BeanUtils会调用默认的转换器(Convertor),在八个基本类型间进行转换,不能转换则抛出异常。

    使用PropertyUtils.copyProperties方法时,若两同名属性不是同一类型,则直接抛出 java.lang.IllegalArgumentException: argument type mismatch 异常。

    我们新建一个UserDto来进行测试:

    @Getter
    @Setter
    public class UserDto {
    
        private String name;
        private Long age;//此处与User类不同,User类为int
        private Address address;
    
    }
    复制代码

    测试如下:

    UserDto dto = new UserDto();
    //以下正常执行
    BeanUtils.copyProperties(dto, oldUser);
    //以下方法会抛出IllegalArgumentException异常
    PropertyUtils.copyProperties(dto, oldUser);
    复制代码

    若UserDto类中age类型改为Integer,则不会报错,PropertyUtils会自动将int类型转为Integer。

    Spring BeanUtils

    spring中也有BeanUtils.copyProperties方法,这里需要注意的时入参列表跟apache的BeanUtils.copyProperties方法相反,如下所示:

    BeanUtils.copyProperties(source, target);
    复制代码

    测试如下:

    User newUser = new User();
    BeanUtils.copyProperties(oldUser, newUser);
    复制代码

    另外spring中的BeanUtils.copyProperties方法比apache的性能要好,而且在spring项目中自带该工具类,推荐在spring项目中使用。

    Cglib BeanCopier 性能较好

    cglib的BeanCopier由于使用到了字节码生成技术,在运行时生成相应的字节码,而不是使用Java的反射,因此性能要比Spring的BeanUtils,Apache的BeanUtils和PropertyUtils要好,推荐使用。

    使用时需要在pom文件中引入如下依赖:

    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>3.2.0</version>
    </dependency>
    复制代码

    测试如下:

    final BeanCopier copier = BeanCopier.create(User.class, UserDto.class, false);
    UserDto dto = new UserDto();
    copier.copy(oldUser, dto, null);
    复制代码

    这里需要注意的是如果有字段类型不同需要手动开启并指定Converter,不然同名字段属性不同不会进行拷贝,如以上例子oldUser中的age(int类型)不会拷贝到dto中的age(Long类型),dto中的age改为Integer类型也不会拷贝。

    MapStruct(浅拷贝和深拷贝)性能较好

    MapStruct由于是在编译时生成相应的拷贝方法,因此性能很好,理论上拷贝速度是最快的。这里注意运行前需先进行 mvn compile

    pom文件中引入相应依赖:

    ...
    <properties>
        <org.mapstruct.version>1.4.2.Final</org.mapstruct.version>
    </properties>
    ...
    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>${org.mapstruct.version}</version>
        </dependency>
    </dependencies>
    ...
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source> <!-- depending on your project -->
                    <target>1.8</target> <!-- depending on your project -->
                    <annotationProcessorPaths>
                        <!-- 使用lombok需要加入以下path,并且需要放在最前面,不然不会生成相应的setter方法 -->
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>1.18.22</version>
                        </path>
                        <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>${org.mapstruct.version}</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
        </plugins>
    </build>
    复制代码

    创建mapper:

    import org.mapstruct.Mapper;
    import org.mapstruct.factory.Mappers;
    
    @Mapper
    public interface UserMapper {
    
        UserMapper INSTANCE = Mappers.getMapper( UserMapper.class);
    
        UserDto convert(User user);
    
    }
    复制代码

    使用如下:

    UserDto userDto = UserMapper.INSTANCE.convert(oldUser);
    复制代码

    mapstruct默认是浅拷贝,如果需要深拷贝,需要在mapper上加注解 ``@Mapper(mappingControl = DeepClone.class)` ,如下所示:

    import org.mapstruct.Mapper;
    import org.mapstruct.control.DeepClone;
    import org.mapstruct.factory.Mappers;
    
    @Mapper(mappingControl = DeepClone.class)
    public interface UserMapper {
    
        UserMapper INSTANCE = Mappers.getMapper( UserMapper.class);
    
        UserDto convert(User user);
    
    }
    复制代码

    但是以上的 DeepClone.class 会导致同名字段在不同类型之间的自动转换失效,如果age从int转换为Long,会编译不通过,提示 Consider to declare/implement a mapping method: "Long map(int value)". 可自定义注解如下:

    @Retention(RetentionPolicy.CLASS)
    @MappingControl( MappingControl.Use.MAPPING_METHOD )
    @MappingControl( MappingControl.Use.BUILT_IN_CONVERSION )
    public @interface CustomDeepClone {
    }
    复制代码

    在mapper上加注解 ``@Mapper(mappingControl = CustomDeepClone.class)` ,即可实现深拷贝并保证同名字段在不同类型之间的自动转换生效。

    深拷贝(Deep Copy)

    Java原生序列化和反序列化

    类需要实现Serializable接口,如下所示:

    @Getter
    @Setter
    public class User implements Serializable {
        
        private static final long serialVersionUID = 1L;
        
        private String name;
        private int age;
        private Address address;
    
    }
    
    @Getter
    @Setter
    public class Address implements Serializable {
    
        private static final long serialVersionUID = 1L;
    
        private String province;
        private String city;
    
    }
    复制代码

    将对象序列化为bytes并从bytes反序列化:

            User oldUser = ...;
    
            //序列化为bytes
            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            ObjectOutputStream oout = new ObjectOutputStream(bout);
            oout.writeObject(oldUser);
            oout.close();
            byte[] bytes = bout.toByteArray();
            bout.close();
    
            //从bytes反序列化为object
            ObjectInputStream oin = new ObjectInputStream(new ByteArrayInputStream(bytes));
            User newUser = (User) oin.readObject();
            oin.close();
    复制代码

    Apache SerializationUtils

    apache SerializationUtils使用的也是Java原生的序列化和反序列化,来实现对象的深拷贝,因此类也需要实现Serializable接口。

    pom文件中引入如下依赖:

    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.10</version>
    </dependency>
    复制代码

    使用如下:

    User newUser = SerializationUtils.clone(oldUser);
    复制代码

    Json序列化和反序列化

    • Gson

    引入Gson依赖:

    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>2.8.9</version>
    </dependency>
    复制代码

    使用如下:

    User oldUser = ...;
    Gson gson = new Gson();
    User newUser = gson.fromJson(gson.toJson(oldUser), User.class);
    //不同类之间的深拷贝
    UserDto userDto = gson.fromJson(gson.toJson(oldUser), UserDto.class);
    复制代码

    该方法也支持同名字段不同类型之间的转换,如将age字段在User和UserDto类中分别为int和Long,可拷贝成功。

    • Jackson

    引入Jackson依赖:

    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.13.1</version>
    </dependency>
    复制代码

    使用如下:

    User oldUser = ...;
    ObjectMapper mapper = new ObjectMapper();
    User newUser = mapper.readValue(mapper.writeValueAsBytes(oldUser), User.class);
    UserDto userDto = mapper.readValue(mapper.writeValueAsBytes(oldUser), UserDto.class);
    复制代码

    Jackson同样支持同名字段不同类型之间的转换,由于Spring项目一般已经依赖了Jackson,推荐使用Jackson来实现对象的深拷贝。

    Dozer

    引入dozer依赖:

    <dependency>
        <groupId>net.sf.dozer</groupId>
        <artifactId>dozer</artifactId>
        <version>5.4.0</version>
    </dependency>
    复制代码

    使用如下:

    User oldUser = ...;
    Mapper mapper = new DozerBeanMapper();
    User newUser = mapper.map(oldUser, User.class);
    UserDto userDto = mapper.map(oldUser, UserDto.class);
    复制代码

    总结

    以上总结了Java中进行对象属性复制、浅拷贝或深拷贝的各个方法工具类,可供使用时作为参考。至于在项目中具体使用哪个工具类,则需要根据业务情况、项目原先使用的依赖库等进行衡量,权衡性能和使用的方便性、安全性(避免出错)等,来选择合适的工具。文中有何错漏之处欢迎指出,


    作者:枫葉也
    链接:https://juejin.cn/post/7051166519811637278
     

    展开全文
  • Java对象拷贝以及常用对象拷贝工具

    千次阅读 2021-04-10 22:49:18
    许久没有更新过,最近因为这个问题引发线上bug,特再次整理汇总!...Java语言中对象拷贝分为深拷贝和浅拷贝以及对象简单的引用拷贝(也就是通常使用的对象赋值)。 1.1 引用拷贝 引用拷贝即对象的赋值操作,就

    许久没有更新过,最近因为这个问题引发线上bug,特再次整理汇总!!!

    1. 对象拷贝

    Java语言中对象拷贝分为深拷贝和浅拷贝以及对象简单的引用拷贝(也就是通常使用的对象赋值)。

    1.1 引用拷贝

    引用拷贝即对象的赋值操作,就是通常使用的 obj = new Object() 操作,这种方式不会重新在堆内存开辟一个空间去创建这个对象,只是在栈中新增加了一个引用指向原本的对象。

    1.2 浅拷贝

    浅拷贝则会在堆内存中重新创建一个对象,但是它内部的属性不会重新去创建,而是直接引用原对象属性的地址。Java语言Object的克隆方法默认是浅拷贝。下面的Student类有个特殊的地方就是它的属性类型都是不可变的,即class类通过final进行了修饰,即使对studentB进行属性的修改,也不会对studentA有什么影响,因为不可变对象每进行修改都是重新创建了一个对象(常量池没有创建只是会进行引用的重新指向)。但是若有个Date类型的属性,那么对studentB Date属性进行修改时就会对原有对象studentA的属性值改变。

    // 需要实现Cloneable接口
    public class Student implements Cloneable {
    
        private Long id;
        private String name;
        private Integer age;
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }
    
    public class CopyDemo {
        public static void main(String[] args) throws CloneNotSupportedException {
            Student studentA = new Student();
            studentA.setId(1L);
            studentA.setName("tom");
            studentA.setAge(14);
            Student studentB = (Student) studentA.clone();
        }
    }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H49NFYcx-1618066137512)(./img/1.png)]

    1.3 深拷贝

    深拷贝和浅拷贝一样都会重新创建一个对象,和浅拷贝不同的是对象的属性也都是重新去创建的,没有引用原对象属性的地址。要通过clone方法的方式去实现深拷贝必须手动去实现,非不可变对象一定要进行对象的重新创建

    package com.tianqb.object.copy;
    
    import java.util.Date;
    
    public class Student implements Cloneable {
    
        private Long id;
        private String name;
        private Integer age;
        private Date created;
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            Student target = (Student) super.clone();
            if (null != this.created) {
                target.setCreated((Date) this.created.clone());
            }
            return target;
        }
    }
    

    2. 对象拷贝中常用的工具类

    2.1 Apache BeanUtils#copyProperties

    特点:

    • 浅拷贝,属性还是指向原本对象的引用
    • 字段名称相同,类型不同无法进行赋值
    • 基本类型字段和引用对象可以映射

    2.2 SpringUtils#copyProperties

    特点:

    • 特性同Apache,效率比Apache高
    • 参数位置同Apache不同

    2.3 序列化(JSON)

    特点:

    • 性能较低,耗时(序列化-反序列化)
    • 深拷贝
    • 基本类型字段和引用对象可以映射
    • 字段名称相同,类型不同可以赋值(如Long -> String)

    2.4 MapStruct(推荐)

    特点:

    • 灵活(可自主配置字段映射关系)
    • 可以配置多对一映射关系
    • 效率高,准确(编译器代码生成,源码就是get、set方法)
    • 深拷贝

    使用:

    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>1.3.1.Final</version>
    </dependency>
    
    @Mapper
    public interface StudentMapper {
     
        StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);
      
        @Mapping(source = "no", target = "number")
        @Mapping(source = "createDate", target = "createDate", dateFormat = "yyyy-MM-dd")
        StudentDO dtoToDo(StudentDTO studentDTO);
    }
    
    
    
    StudentDO studentDO = StudentMapper.INSTANCE.dtoToDo(studentDTO);
    

    注意点:
    由于是编译期间生成get、set代码,假如项目中使用了lombok工具,就会出现编译失败找不到具体的get、set方法的问题,原因是lombok也是编译期间进行代码的生成,但是在mapstruct编译的时候lombok还没有进行代码的生成,因此编译出错,解决的方式如下,需要在maven配置文件中添加两个插件,即lombok和maspstruct。

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
        <configuration>
            <source>1.8</source> <!-- depending on your project -->
            <target>1.8</target> <!-- depending on your project -->
            <annotationProcessorPaths>
                <path>
                    <groupId>org.mapstruct</groupId>
                    <artifactId>mapstruct-processor</artifactId>
                    <version>1.3.1.Final</version>
                </path>
                <!-- other annotation processors -->
            </annotationProcessorPaths>
        </configuration>
    </plugin>
    
    展开全文
  • 本号主要是Java常用关键技术点,通用工具类的分享;以及springboot+springcloud+Mybatisplus+druid+mysql+redis+swagger+maven+docker等集成框架的技术分享;datax、kafka、flink等大数据处理框架的技术分享。文章会...
  • Java对象映射工具类封装

    千次阅读 2021-11-18 14:11:34
    文章主要记录一下工作中封装的对象转换工具类 /** * Spring的BeanUtils只能映射类型相同的属性,无法满足将Date转字符串,枚举类型转换等需求 * 所以将Spring的BeanUtils源码复制出来,自己扩展了映射逻辑 */ ...
  • Java对象属性拷贝工具对比分析

    千次阅读 2022-03-26 18:47:57
    Java对象属性拷贝工具详解1. 对象属性拷贝概述2. 对象属性拷贝工具2.1 拷贝工具对比2.2 拷贝工具验证3. 实现案例3.1 集合对象拷贝验证3.2 对象拷贝验证 1. 对象属性拷贝概述 在开发中经常遇到对象属性拷贝功能,而...
  • 简单好用的java bean对象深拷贝工具

    千次阅读 2020-12-02 23:23:37
    简单好用的java bean对象拷贝工具。 支持字段注解,注解错误抛出异常。 github仓库 简单的拷贝class A copy to new class A 你只需要简单的这样的代码: A a = new A(); A a2 = Copier.copy(a); // clone new ...
  • NULL 博文链接:https://ollevere.iteye.com/blog/1323205
  • NULL 博文链接:https://loven-11.iteye.com/blog/952161
  • java反射机制创建对象实现:java 深度拷贝 -超完美,只使反射机制,不使用其它封装好的深度拷贝的工具
  • 对象克隆、复制工具

    2018-07-31 16:25:37
    对象复制工具,基于cglib BeanCopier 实现对实体对象、持久化对象、代理对象的克隆和复制, 避免重复克隆和复制,避免无限循环引用,(校验hashCode) 可指定实体对象和集合属性的克隆深度
  • 常见的属性赋值工具Frameworks that ease bean mapping.dOOv - Provides fluent API for typesafe domain model validation and mapping. It uses annotations, code generation and a type safe DSL to make bean ...
  • 这是一个BeanUtil.Merge方法,JAVA合并对象属性,把对象的非空属性合成到目标对象上。 作为对之前文章《BeanUtils.copyProperties忽略null值/只拷贝非null属性》的完善和提升 解决方案 BeanUtil源码,。if(default...
  • Java对象复制

    万次阅读 2022-02-11 11:26:57
    Java对象复制(拷贝)
  • Java 对象copy之BeanCopier工具类的使用

    万次阅读 2018-11-23 15:20:01
    业务系统中经常需要两个对象进行属性的拷贝,不能否认逐个的对象拷贝是最快速最安全的做法,但是当数据对象的属性字段数量超过程序员的容忍的程度,代码因此变得臃肿不堪,使用一些方便的对象拷贝工具类将是很好的...
  • Java中,工具类定义了一组公共方法,这篇文章将介绍Java中使用最频繁及最通用的Java工具类。以下工具类、方法按使用流行度排名,参考数据来源于Github上随机选取的5万个开源项目源码。 一. org.apache.commons.io....
  • 实用的对象copy工具

    2019-07-01 11:15:27
    package ... import org.springframework.util.CollectionUtils; import java.util.ArrayList; import java.util.List; ... * notes 对象工具类 * author xiaoming.chen * create 2018/11/2 000...
  • 1.引言大家在做java开发时,肯定会遇到api层参数对象传递给服务层,或者把service层的对象传递给dao层,他们之间又不是同一个类型对象,但字段又是一样,如果还是用普通的get、set方式来处理话,比较繁琐,.......
  • Bean,List copy工具

    千次阅读 2021-07-07 09:00:48
    在开发中很多地方都需要两个list互相拷贝,spring的BeanUtils只能完成浅层次copy,以下代码是在spring的BeanUtils的基础上扩展而来。。。 import org.springframework.beans.BeanUtils; import java.util....
  • 在web开发中,经常遇到...而使用hibernate,它的修改是基于对象的,如果用户修改的字段非常多,但是我们并不知道用户到底修改了那个字段,这时候就需要一个一个的去判断,因此非常耗时,所以我写了个工具类来帮助大家!
  • Java对象的复制三种方式

    千次阅读 2021-02-12 09:36:34
    Java对象的复制三种方式概述在实际编程过程中,我们常常要遇到这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能 会需要一个和A完全相同新对象B,并且此后对B任何改动都不会影响到A中的值,也...
  • JAVA复制对象和集合的工具类 因为BeanUtils.copyProperties(Object source, Object target),只能复制对象,不能复制数组和集合,所以决定写个工具类来解决 public class CopyUtils { /** * 复制对象 */ public ...
  • Dozer 工具类 import java.util.Collection; import java.util.List; import org.dozer.DozerBeanMapper; import com.google.common.collect.Lists; /** * 简单封装Dozer, 实现深度转换Bean<->Bean的...
  • Java 如何优雅的拷贝对象属性

    千次阅读 2021-02-12 10:27:19
    场景在 Java 项目中,经常遇到需要在对象之间拷贝属性的问题。然而,除了直接使用 Getter/Stter 方法,我们还有其他的方法么?当然有,例如 Apache Common Lang3 的 BeanUtils,然而 BeanUtils 却无法完全满足吾辈的...
  • Java List的两种copy方式

    千次阅读 2022-03-02 16:21:47
    copy后的两个对象互不影响,可理解为深层拷贝 测试入口 public static void main(String[] args) { TestDto a = new TestDto("a"); TestDto b1 = new TestDto("b1"); TestDto b = new TestDto("b", b1); List...
  • import org.springframework.beans.BeanUtils; BeanUtils.copyProperties(源对象, 目标对象);
  • JAVA对象任意深度克隆clone工具类分享 源代码下载地址:http://www.zuidaima.com/share/1550463408114688.htm

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 85,759
精华内容 34,303
关键字:

java对象copy工具比较