Java深拷贝和浅拷贝原理

源头

最近项目中想要实现一个功能,根据一个统计的java模版,统计的参数基本初始化都为0,针对每次不同的查询条件,查询出来的不同记录,然后修改部分值,但是发现每次查询出来的不同记录会影响到模版类数据的变化。

问题复现

/**
 * 统计不同部门人数
 */
@Data
public class CountEmpModel {
    // 部门名称
    private String department;
    // 男性人数
    private Integer male = 0;
    // 女性人数
    private Integer female = 0;
    // 不同岗位的人数
    private CountPostModel countPostModel;
}

/**
 * 统计不同岗位人数
 */
@Data
public class CountPostModel {
    // 岗位id
    private String postId;
    // 岗位名称
    private String postName;
    // 岗位人数
    private Integer postNum;
}

操作:

public static void main(String[] args) {
    // 初始化模版countEmpTemplate
    CountEmpModel countEmpTemplate = new CountEmpModel();
    CountPostModel countPostModel = new CountPostModel();
    countPostModel.setPostId(null);
    countPostModel.setPostName(null);
    countPostModel.setPostNum(0);
    countEmpTemplate.setFemale(0);
    countEmpTemplate.setMale(0);
    countEmpTemplate.setDepartment(null);
    countEmpTemplate.setCountPostModel(countPostModel);
    // 创建查询后返回前端的对象countEmpModel1
    CountEmpModel countEmpModel1 = new CountEmpModel();
    // 使用模版类初始化countEmpModel1
    BeanUtil.copyProperties(countEmpTemplate, countEmpModel1);
    // 查询到值之后,赋值给返回前端的对象countEmpModel1
    countEmpModel1.setFemale(100);
    countEmpModel1.setMale(100);
    countEmpModel1.setDepartment("科技部门");
    CountPostModel countPostModel1 = countEmpModel1.getCountPostModel();
    countPostModel1.setPostId("1111");
    countPostModel1.setPostName("岗位1");
    countPostModel1.setPostNum(30);
    // 查看模版类是否发生变化
    System.out.println(countEmpTemplate.getDepartment());
    System.out.println(countEmpTemplate.getFemale());
    System.out.println(countEmpTemplate.getMale());
    System.out.println(countEmpTemplate.getCountPostModel().getPostId());
    System.out.println(countEmpTemplate.getCountPostModel().getPostName());
    System.out.println(countEmpTemplate.getCountPostModel().getPostNum());
}

结果:模版类发生了变化
image

到底什么是浅拷贝和深拷贝

  • 浅拷贝:针对引用类型,目标对象拷贝的只是源对象的地址,无论目标对象还是源对象改变,他们都会一起改变。
  • 深拷贝:将源对象复制一份给目标对象,二者没有任何关系。
  • 注意:浅拷贝还是深拷贝都是针对引用类型,而基本数据类型和String类型是不会发生变化的。

BeanUtils.copyProperties是浅拷贝还是深拷贝?

上面的实验看出BeanUtils.copyProperties是浅拷贝。

如何实现深拷贝?

/**
 * 深拷贝
 * @param source
 * @param <S>
 */
public static <S extends Serializable, T extends Serializable> void copyBeanByDeep(S source, T dest) {
    ObjectOutputStream obs = null;
    ObjectInputStream os = null;
    try {
        //写入字节流
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        obs = new ObjectOutputStream(out);
        obs.writeObject(source);
        //分配内存,写入原始对象,生成新对象
        ByteArrayInputStream is = new ByteArrayInputStream(out.toByteArray());
        os = new ObjectInputStream(is);
        S s = (S) os.readObject();
        BeanUtils.copyProperties(s, dest);
    } catch (IOException e) {
        throw new RuntimeException(e);
    } catch (ClassNotFoundException e) {
        throw new RuntimeException(e);
    } finally {
        try {
            if (obs != null) {
                obs.close();
            }

            if (os != null) {
                os.close();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

测试深拷贝

public static void main(String[] args) {
    // 初始化模版countEmpTemplate
    CountEmpModel countEmpTemplate = new CountEmpModel();
    CountPostModel countPostModel = new CountPostModel();
    countPostModel.setPostId(null);
    countPostModel.setPostName(null);
    countPostModel.setPostNum(0);
    countEmpTemplate.setFemale(0);
    countEmpTemplate.setMale(0);
    countEmpTemplate.setDepartment(null);
    countEmpTemplate.setCountPostModel(countPostModel);
    // 创建查询后返回前端的对象countEmpModel1
    CountEmpModel countEmpModel1 = new CountEmpModel();
    // 使用模版类初始化countEmpModel1
    CopyUtils.copyBeanByDeep(countEmpTemplate, countEmpModel1);
    // BeanUtil.copyProperties(countEmpTemplate, countEmpModel1);
    // 查询到值之后,赋值给返回前端的对象countEmpModel1
    countEmpModel1.setFemale(100);
    countEmpModel1.setMale(100);
    countEmpModel1.setDepartment("科技部门");
    CountPostModel countPostModel1 = countEmpModel1.getCountPostModel();
    countPostModel1.setPostId("1111");
    countPostModel1.setPostName("岗位1");
    countPostModel1.setPostNum(30);
    // 查看模版类是否发生变化
    System.out.println(countEmpTemplate.getDepartment());
    System.out.println(countEmpTemplate.getFemale());
    System.out.println(countEmpTemplate.getMale());
    System.out.println(countEmpTemplate.getCountPostModel().getPostId());
    System.out.println(countEmpTemplate.getCountPostModel().getPostName());
    System.out.println(countEmpTemplate.getCountPostModel().getPostNum());
}

结果:模版类没有受到影响
image

posted @ 2023-05-18 13:26  sunpeiyu  阅读(28)  评论(0编辑  收藏  举报