【Java内存模型】--- 深入探讨出现浅拷贝的原因+Map的putAll()方法会进行深拷贝吗???

本文源码地址:https://github.com/nieandsun/NRSC-STUDY

1 先看两段代码

2.1当value为基本数据类型或字符串时

@Test
public void MapDemo1() {

    Map<String, String> map = new HashMap<>();
    System.err.println("map加入内容前=" + System.identityHashCode(map));
    map.put("热火", "wade");
    System.err.println("map加入内容后=" + System.identityHashCode(map));


    Map<String, String> copyMap1 = new HashMap<>();
    System.err.println("copyMap2加入内容前=" + System.identityHashCode(copyMap1));
    copyMap1 = map;
    System.err.println("copyMap2加入内容后=" + System.identityHashCode(copyMap1));


    Map<String, String> copyMap2 = new HashMap<>();
    System.err.println("copyMap1加入内容前=" + System.identityHashCode(copyMap2));
    copyMap2.putAll(map);
    System.err.println("copyMap1加入内容后=" + System.identityHashCode(copyMap2));


    map.put("热火", "james");
    System.out.println("直接修改的====" + System.identityHashCode(map) + "===" + map);
    System.out.println("不想修改的====" + System.identityHashCode(copyMap1) + copyMap1);
    System.out.println("不想修改的====" + System.identityHashCode(copyMap2) + copyMap2);
}

代码运行结果和结论如下:
在这里插入图片描述


2.2 当value为非字符串非基本数据类型的包装类型的引用数据类型时

    @Test
    public void MapDemo2() {
        Company company1 = new Company("heat", "wade");
        Map<String, Company> map = new HashMap<>();

        System.err.println("map加入内容前=" + System.identityHashCode(map));
        map.put("热火", company1);
        System.err.println("map加入内容后=" + System.identityHashCode(map));

        Map<String, Company> copyMap1 = new HashMap<>();
        System.out.println("copyMap1加入内容前=" + System.identityHashCode(copyMap1));
        copyMap1 = map;
        System.out.println("copyMap1加入内容后=" + System.identityHashCode(copyMap1));

        Map<String, Company> copyMap2 = new HashMap<>();
        System.err.println("copyMap2加入内容前=" + System.identityHashCode(copyMap2));
        copyMap2.putAll(map);
        System.err.println("copyMap2加入内容后=" + System.identityHashCode(copyMap2));

        company1.setCompanyCode("1001");
        System.out.println("直接修改的====" + System.identityHashCode(map) + "===" + map);
        System.out.println("不想修改的====" + System.identityHashCode(copyMap1) + "===" + copyMap1);
        System.out.println("不想修改的====" + System.identityHashCode(copyMap2) + "===" + copyMap2);
    }

代码运行结果和结论如下:
在这里插入图片描述

通过上面两段代码可以看到:
(1)当value为基本数据类型或字符串时 ------> Map的putAll()方法进行了深拷贝
(2)当value为非字符串的引用数据类型时 —> Map的putAll()方法并没有进行深拷贝


2 从JVM内存模型的角度来深入的聊一聊原因

2.1 当value为基本数据类型或字符串时

其基本过程如下:
在这里插入图片描述


2.2 当value为非字符串的引用数据类型时

其基本过程如下:
在这里插入图片描述


小结+深入探讨出现浅拷贝的原因

首先应该知道Java有8中基本数据类型(byte、short、int、long、float、double、char、boolean),其它的统称为引用数据类型。

其次在Java语言里万事万物皆对象。

再其次Java里的对象肯定都是由基本数据类型+引用数据类型的变量组成的,但是由于节省内存等方面的考虑,对象里真正存放的数据为基本类型的变量对应的数据、基本类型的包装类型的变量对应的数据和String类型的变量对应的数据(当然对于String类型严格意义上来讲应该并不是这样,只是我们可以这样认为);其他引用数据类型的变量 并不会存放数据本身,而是存放该引用数据类型变量的地址,如下图所示:
在这里插入图片描述
由此便可以知道,开发中之所以出现浅拷贝,是因为你拷贝的对象中有对象、数组、集合等引用数据类型。


3 如何进行深拷贝

3.1 真实开发中可能碰到的浅拷贝的栗子即解决方案

在实际开发中,for循环的层数肯定不能过多,因为数据量一上来,很容器造成接口响应时间过长的问题,为了达到这个目的,我往往的做法是将某一层的for循环先转成Map,以此来压缩for循环的层数,这时候非常容易出现的一个浅拷贝的bug就是在其他for循环里修改预先转成的Map的值,然后将修改后的值作为for循环里的返回值。

针对上面我说的这种情况,其实有两种解决方法,
(1)第一种就是死活不能改预先转成的Map,而是在for循环里将每次循环从Map里拿出的对象取出,然后新建一个对象,将从Map里拿出的对象的属性全部复制给新建的对象,然后拿着新建的对象进行操作
(2)第二种也是死活不能改预先转成的Map,而是在for循环里,首先将预先取出的Map对象,进行深拷贝一份,然后拿着该深拷贝的Map进行本次for循环里的操作,这样每次for循环都是操作的深拷贝的Map,每次循环修改的对象,肯定也不会是一个对象 —》 当然这种方式应该比(1)更耗性能。


3.2 本文Map< String, Company >属性的深拷贝

其实到这里我想人人应该都能解决这个问题了,我这里给个栗子,代码如下:

@Test
public void MapDemo2_1() {
    Company company1 = new Company("heat", "wade");
    Map<String, Company> map = new HashMap<>();

    System.out.println("map加入内容前=" + System.identityHashCode(map));
    map.put("热火", company1);
    System.out.println("map加入内容后=" + System.identityHashCode(map));


    Map<String, Company> copyMap2 = new HashMap<>();
    System.err.println("copyMap2加入内容前=" + System.identityHashCode(copyMap2));
    //(1)从map中取出company1
    Company company1_1 = map.get("热火");
    //(2)新建一个Company对象 --- JVM会在堆中为其分配一个新的地址
    Company deepCopyCompany = new Company();
    //(3)将从map中取出的Company对像的属性复制给新的Company对象
    BeanUtils.copyProperties(company1_1, deepCopyCompany);
    //(4)将新的对象给copyMap2 ---> 完成深拷贝的过程
    copyMap2.put("热火", deepCopyCompany);
    System.err.println("copyMap2加入内容后=" + System.identityHashCode(copyMap2));

    //修改company1--->可以发现map中Company对象的值会变化,但copyMap2的不会
    company1.setCompanyCode("1001");
    System.out.println("直接修改的====" + System.identityHashCode(map) + map);
    System.out.println("不想修改的====" + System.identityHashCode(copyMap2) + copyMap2);
}

测试结果如下:
在这里插入图片描述

发布了189 篇原创文章 · 获赞 187 · 访问量 39万+

猜你喜欢

转载自blog.csdn.net/nrsc272420199/article/details/103398339
今日推荐