java基础:浅拷贝与深拷贝(实际开发中遇到的深拷贝问题)

文章讨论了Java中的深拷贝和浅拷贝概念,包括Cloneable接口和序列化在深拷贝中的应用。同时,解释了Java方法参数传递的原理,指出即使是对象作为参数,也是值传递,即传递对象引用的副本。文中通过一个具体的代码示例展示了因直接修改引用参数导致的数据异常问题,提出了两种修复方案,一种是使用JSON序列化实现深拷贝,另一种是创建新对象存储筛选数据。

1、java中深浅拷贝的区别?

  • 浅拷贝:只对基本数据类型进行拷贝,对于引用数据类型只是进行了引用的传递,没有真实的创建一个新的对象,则是浅拷贝;(参数引用传递中的改变源对象情况)
    在这里插入图片描述

  • 深拷贝:在对引用数据类型拷贝过程中,创建了一个新的对象,并且复制了对象内的所有成员变量,则是深拷贝(参数引用传递中的不改变源对象情况)

在这里插入图片描述

1.1 java浅拷贝的实现?

Cloneable 接口,并覆写 clone() 方法,clone()方法实现的是对当前对象进行拷贝。

1.2 java深拷贝的实现

  • Cloneable 接口,并覆写 clone() 方法
    需要拿到拷贝自己后产生的新的对象,然后对新的对象的引用类型再调用拷贝操作,实现对引用类型成员变量的深拷贝。
    实现两次clone,第一次得到当前对象浅拷贝,然后再对新的对象引用类型调用拷贝实现深拷贝
  • 序列化对象,再反序列化回来,然后就可以得到一个新的对象,

1.3 什么是序列化?(serialization涉及到IO部分)

将对象写入到io流中,反序列化是从io流中恢复对象;

2、java方法中的参数传递?(都是传递副本,值传递,java中没有引用传递)

问:当一个对象当作参数传递到一个方法之后,此方法可以改变对象属性,并且可以返回变化后的对象,那么这里是值传递还是对象传递。。
答:java中只有值传递参数

  • 如果参数类型是基本数据类型,值传递,改变副本的值不会改变原始数据的值;
  • 如果参数类型是引用数据类型,传过来的是对象地址副本,如果方法中没有改变地址的值,方法内针对对象的改变会影响到传入的参数;(浅拷贝)
  • 如果参数类型是引用数据类型,函数中改变了副本的地址,比如new一个,也就是副本指向一个新的地址,此时方法中针对对象的改变不会影响到传入参数对象;(深拷贝)

3. 开发中遇到的数据拷贝问题

刚工作的时候,业务代码开发过程中遇到过这种直接修改了引用数据类型参数导致的数据异常问题。

有问题的代码如下:

  public SettingsExtVo filterRateByCity(SettingsExtVo settingsExtVo,String cityCode){

    if (cityCode == null){
      return settingsExtVo;
    }

    List<GroupExtVO> groups = settingsExtVo.getGroups();

//    SettingsExtVo settingsExtVoNew = new SettingsExtVo();

    // 定义一个新的List<GroupExtVO>

    List<GroupExtVO> groupExtVOList = com.google.api.client.util.Lists.newArrayList();

    // 遍历groups筛选出满足城市条件的group

    groups.forEach(groupExtVO -> {

      if (groupExtVO.getCondition() != null){

        Map<String,String> condition = groupExtVO.getCondition();

        if (condition.get("cityGuid") != null){

          List<String> cityList = Arrays.asList(condition.get("cityGuid").split(","));

          if (cityList.contains(cityCode)){
            groupExtVOList.add(groupExtVO);
          }

        }

      }

    });

    if (CollectionUtils.isEmpty(groupExtVOList)){

      groups.forEach(groupExtVO1 -> {

        if (groupExtVO1.getCondition() == null){

          groupExtVOList.add(groupExtVO1);

        }
      });
    }
    
// 修改引用传参数,值传递传的是对象地址值,此时对地址对应的对象进行了修改。
    settingsExtVo.setGroups(groupExtVOList);

    return settingsExtVo;
  }

上述代码功能主要是对一个对象中的list列表数据进行筛选,并且返回筛选后的数据。

代码中对SettingsExtVo的实例对象进行了修改,而该对象是本地缓存的数据对象,通过该方法对本地缓存的对象进行修改,这就导致后续程序使用该缓存对象的时候出现数据丢失的问题。

修复后的代码逻辑(使用json序列化,深拷贝出一个全新的对象)

  /**
   * 根据头盔城市筛选出对应的评价问题
   *
   * @param settingsExtVo
   * @param cityCode
   * @return SettingsExtVo
   */
  public SettingsExtVo filterRateByCity(SettingsExtVo settingsExtVo,String cityCode){


    // 利用Json序列化实现对象深拷贝
    String jsonString = JsonUtils.json2String(settingsExtVo);
    SettingsExtVo settingsExtVoNew = JsonUtils.json2Object(jsonString,SettingsExtVo.class);


    List<GroupExtVO> groups = settingsExtVo.getGroups();

    // 定义一个新的List<GroupExtVO>

    List<GroupExtVO> groupExtVOList = Lists.newArrayList();

    // 遍历groups筛选出满足城市条件的group

    groups.forEach(groupExtVO -> {

      if (groupExtVO.getCondition() != null && cityCode != null){

        Map<String,String> condition = groupExtVO.getCondition();

        if (condition.get("cityGuid") != null){

          List<String> cityList = Arrays.asList(condition.get("cityGuid").split(","));

          if (cityList.contains(cityCode)){
            groupExtVOList.add(groupExtVO);
          }

        }

      }

    });

    if (CollectionUtils.isEmpty(groupExtVOList)){

      groups.forEach(groupExtVO1 -> {

        if (groupExtVO1.getCondition() == null){

          groupExtVOList.add(groupExtVO1);

        }
      });
    }

    settingsExtVoNew.setGroups(groupExtVOList);

    return settingsExtVoNew;
  }

第二种修复方案是不修改引用参数对象,利用参数对象做筛选,但是新建一个对象来存储筛选的数据,并且也返回这个新创建的对象;

  /**
   * 按照group的匹配规则来筛选评价问题
   * @param settingsExtVo
   * @param cityCode
   * @return
   */
  public List<GroupExtVO> filterRateByCondition(SettingsExtVo settingsExtVo,String cityCode,String npsType){

    List<GroupExtVO> groups = settingsExtVo.getGroups();

    // 定义一个新的List<GroupExtVO>
    List<GroupExtVO> groupExtVOList = Lists.newArrayList();

    //遍历groups筛选出满足头盔城市条件的group
    groups.forEach(groupExtVO -> {

      if (groupExtVO.getCondition() != null && cityCode != null){
        Map<String,String> condition = groupExtVO.getCondition();

        if (condition.get("cityGuid") != null){
          List<String> cityList = Arrays.asList(condition.get("cityGuid").split(","));

          if (cityList.contains(cityCode) && groupExtVO.getDisplay().equals(true)){
            groupExtVOList.add(groupExtVO);

          }
        }

      }

    });

    // 按头盔城市筛选为空,则再次按照nps模式进行筛选

    if (CollectionUtils.isEmpty(groupExtVOList)){

      groups.forEach(groupExtVO -> {

        if (groupExtVO.getCondition() != null && Objects.nonNull(npsType)){

          if (groupExtVO.getCondition().get("npsType").equals(npsType)){
            groupExtVOList.add(groupExtVO);
          }

        }
      });
    }
    //  如果按照已有规则筛选都为空,则筛选无规则group
    if (CollectionUtils.isEmpty(groupExtVOList)){

      groups.forEach(groupExtVO1 -> {

        if (Objects.isNull(groupExtVO1.getCondition())){

          groupExtVOList.add(groupExtVO1);

        }
      });

    }

    return groupExtVOList;
  }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

maligebilaowang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值