基于Spring的浅拷贝和深拷贝

浅拷贝

对当前对象进行克隆,并克隆该对象所包含的8种基本数据类型和String类型属性(拷贝一份该对象并重新分配内存,即产生了新的对象);但如果被克隆的对象中包含除8中数据类型和String类型外的其他类型的属性,浅克隆并不会克隆这些属性(即不会为这些属性分配内存,而是引用原来对象中的属性),

基于spring的浅拷贝

spring架构中提供了BeanUtils工具类

BeanUtils.copyProperties();

调用spring工具类中自带的方法,就可以实现浅拷贝
测试:

   @Data
   static
   class A{
       private String name;
       private Integer age;
       private Double money;
       private B1 bname;

       public A(String name,Integer age,Double money,String b1mp){
           this.name = name;
           this.age = age;
           this.money = money;
           this.bname = new B1(b1mp);
       }

       public A() {
       }
   }

   @Data
   static
   class B1{

       private String  mp;

       public B1(String mp) {
           this.mp = mp;
       }

       public B1() {
       }
   }

   public static void main(String[] args) {
       A a = new A("张三",12,12.5,"张三B1");
       A a1 = new A();
       BeanUtils.copyProperties(a,a1);
       a1.setName("李四");
       a1.setAge(19);
       a1.setMoney(19.8);
       a1.getBname().setMp("李四B1");
       System.out.println(a);
       System.out.println(a1);
   }

结果:

DeepBeanUtils.A(name=张三, age=12, money=12.5, bname=DeepBeanUtils.B1(mp=李四B1))
DeepBeanUtils.A(name=李四, age=19, money=19.8, bname=DeepBeanUtils.B1(mp=李四B1))

深拷贝

深拷贝会完全复制整个对象,包括这个对象所包含的内部对象。对于对象中的对象属性,会重新创建对象,分配对象应用。

基于spring实现的深拷贝
	/**
     * 深拷贝对象
     *
     * @param source          源对象
     * @param clazz           目标对象的类型
     * @param excludeProperties 不需要拷贝的属性名
     * @return 拷贝后的对象
     */
    public static <T> T deepCopy(Object source, Class<T> clazz, String... excludeProperties) {
        T target = null;
        try {
            //创建了一个目标对象 target
            target = clazz.getDeclaredConstructor().newInstance();
            //初始化了一个 HashMap 对象 map,用于存储已经深拷贝的源对象和目标对象的映射关系
            Map<Object, Object> map = new HashMap<>();
            deepCopyIterative(source, target, map, excludeProperties);
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            e.printStackTrace();
        }
        return target;
    }

    /**
     * 深拷贝对象
     *
     * @param source          源对象
     * @param clazz           目标对象的类型
     * @param map             存储已经拷贝过的对象的Map
     * @param excludeProperties 不需要拷贝的属性名
     * @return 拷贝后的对象
     */
    private static void deepCopyIterative(Object source, Object target, Map<Object, Object> map, String... excludeProperties) {
        if (source == null || target == null) {
            return;
        }
        //两个 LinkedList 对象,分别存储源对象和目标对象
        LinkedList<Object> sourceStack = new LinkedList<>();
        LinkedList<Object> targetStack = new LinkedList<>();
		//添加需要拷贝的对象
        sourceStack.push(source);
        targetStack.push(target);

        while (!sourceStack.isEmpty()) {
        	//取出要拷贝的对象
            Object currentSource = sourceStack.pop();
            Object currentTarget = targetStack.pop();
            //如果该属性被标记为排除属性,则不进行拷贝操作
            if (currentSource == null || map.containsKey(currentSource)) {
                continue;
            }
            //标记已经拷贝过的属性,避免循环引用
            map.put(currentSource, currentTarget);

            PropertyDescriptor[] sourcePds = BeanUtils.getPropertyDescriptors(currentSource.getClass());
            for (PropertyDescriptor sourcePd : sourcePds) {
                if (sourcePd.getReadMethod() != null && !"class".equals(sourcePd.getName())) {
                    PropertyDescriptor targetPd = BeanUtils.getPropertyDescriptor(currentTarget.getClass(), sourcePd.getName());
                    if (targetPd != null && targetPd.getWriteMethod() != null) {
                        String propertyName = sourcePd.getName();
                        if (Arrays.asList(excludeProperties).contains(propertyName)) {
                            continue;
                        }
                        try {
                            Object sourceValue = sourcePd.getReadMethod().invoke(currentSource);
                            if (sourceValue != null) {
                                Object targetValue = targetPd.getReadMethod().invoke(currentTarget);
                                //如果目标对象的属性值为 null,则根据属性类型进行赋值操作
                                if (targetValue == null) {
                                    if (targetPd.getPropertyType().isPrimitive() || Number.class.isAssignableFrom(targetPd.getPropertyType())) {
                                        targetValue = sourceValue;
                                    } else {
                                        targetValue = targetPd.getPropertyType().getDeclaredConstructor().newInstance();
                                    }
                                    targetPd.getWriteMethod().invoke(currentTarget, targetValue);
                                }
                                //将需要拷贝的对象添加到集合中
                                sourceStack.push(sourceValue);
                                targetStack.push(targetValue);
                            }
                        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | InstantiationException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

代码解释
这段代码的主要目的是实现对象的深度拷贝,其中使用了迭代的方法。

  1. 首先判断输入的源对象和目标对象是否为 null,若为 null 则直接返回。
  2. 定义两个 LinkedList 对象,sourceStack 和 targetStack,分别用于存储源对象和目标对象。
  3. 将源对象和目标对象分别压入 sourceStack 和 targetStack 栈中。
  4. 当 sourceStack 不为空时,执行以下操作:
    a. 从 sourceStack 和 targetStack 中弹出当前的源对象和目标对象。
    b. 如果当前源对象为空或已包含在 map 中,则跳过当前循环。
    c. 将当前源对象放入 map,以避免循环引用。
    d. 获取源对象的属性描述符数组。
    e. 对于源对象的每一个属性描述符:
      1. 检查是否有读取方法并且属性名不是 “class”。
      1. 获取目标对象中与源对象属性名相同的属性描述符。
      1. 检查目标对象属性描述符是否有写入方法。
      1. 判断源属性名是否在排除属性列表中,如果在则跳过当前循环。
      1. 尝试获取源对象的属性值;如果属性值不为 null,则进行以下操作:
        1. 获取目标对象的属性值。
        1. 如果目标对象的属性值为 null,则根据属性类型进行赋值操作。具体而言,如果目标属性类型是基本类型或 Number 类型的子类,则直接将源对象的属性值赋给目标对象;否则,使用目标属性类型的构造方法创建一个新实例赋给目标对象。
        1. 将源对象的属性值压入 sourceStack,将目标对象的属性值压入 targetStack。
        1. 如果在执行上述操作过程中发生异常,打印异常堆栈信息。

这段代码的核心思想是使用栈实现对象深度拷贝的迭代过程,遍历源对象的每个属性,并将属性值复制到目标对象。在复制过程中,可以选择排除某些属性,以便某些属性不会被复制到目标对象。

测试

  @Data
   static
   class A{
       private String name;
       private Integer age;
       private Double money;
       private B1 bname;

       public A(String name,Integer age,Double money,String b1mp){
           this.name = name;
           this.age = age;
           this.money = money;
           this.bname = new B1(b1mp);
       }

       public A() {
       }
   }

   @Data
   static
   class B1{

       private String  mp;

       public B1(String mp) {
           this.mp = mp;
       }

       public B1() {
       }
   }

   public static void main(String[] args) {
       A a = new A("张三",12,12.5,"张三B1");
       A a1 = deepCopy(a, A.class);
       a1.setName("李四");
       a1.setAge(19);
       a1.setMoney(19.8);
       a1.getBname().setMp("李四B1");
       System.out.println(a);
       System.out.println(a1);
   }

结果

DeepBeanUtils.A(name=张三, age=12, money=12.5, bname=DeepBeanUtils.B1(mp=张三B1))
DeepBeanUtils.A(name=李四, age=19, money=19.8, bname=DeepBeanUtils.B1(mp=李四B1))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值