zoukankan      html  css  js  c++  java
  • java中的浅拷贝和深拷贝解析

    任何编程语言中,其实都有浅拷贝和深拷贝的概念,java中也不例外。在对一个现有的对象进行拷贝操作的时候,是有浅拷贝和深拷贝之分的,他们在实际使用中,区别很大,如果对其进行混淆,可能会引发一些难以排查的问题。

    本文就在java中的浅拷贝和深拷贝做一个详细的解说。

    什么是浅拷贝和深拷贝?

    首先需要明白,浅拷贝和深拷贝都是针对一个已有对象的操作。那先来看看浅拷贝和深拷贝的概念。

    在java中,除了基本数据类型(元类型)之外,还存在类的实例对象这个引用数据类型。而一般使用【=】号做赋值操作的时候。对于基本数据类型,实际上拷贝的它的值,但是对于对象而言,其实赋值的只是这个对象的引用,将原对象的引用传递过去,他们实际上还是指向同一个对象。

    而浅拷贝和深拷贝就是在这个基础之上做的区分,如果在拷贝这个对象的时候,只对基本数据类型进行了拷贝,而对引用数据类型只是进行了引用的传递,而没有真实的创建一个新的对象,则认为是浅拷贝。反之,在对引用数据类型进行拷贝的时候,创建了一个新的对象,并且复制其内的成员变量,则认为是深拷贝。

    所以到现在,就应该了解了,所谓浅拷贝和深拷贝,只是在拷贝对象的时候,对类的实例对象这种引用数据类型的不同操作而已。

    总结来说:

    1、浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递的拷贝,此为浅拷贝。

    febeaT.md.png

    2、深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。

    feb1MR.md.png

    从内存角度区分浅拷贝和深拷贝的区别

    • 数据分为基本数据类型和引用数据类型。基本数据类型:数据直接存储在栈;引用数据类型:存储在栈中的是对象的引用地址,真实的对象数据存放在堆内存里。
    • 浅拷贝:对于基本数据类型:直接复制数据值;对于引用数据类型:只是复制了对象的引用地址,新旧对象指向同一个内存地址,修改其中一个对象的值,另一个对象的值随之改变。
    • 深拷贝:对于基础数据类型:直接复制数据值;对于引用数据类型:开辟新的内存空间,在新的内存空间里复制一个一模一样的对象,新老对象不共享内存,修改其中一个对象的值,不会影响另一个对象的值。
    • 深拷贝相比较于浅拷贝速度较慢并且花销较大。

    浅拷贝和深拷贝案例分析

    • 浅拷贝实现Cloneable,深拷贝是通过实现Serializable读取二进制流

    深拷贝实现

    首先Person对象实现Serializable接口,然后自定义深拷贝方法deepClone()

    /**
    * 深拷贝
    * @return Person 注意 要实现序列化接口
    */
    public Person deepClone(){
    	try {
                //输出(序列化)
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(baos);
                oos.writeObject(this);
                //输入(反序列化)
                ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
                ObjectInputStream ois=new ObjectInputStream(bais);
                Person person=(Person) ois.readObject();
                return person;
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }        
    }
    

    接下来验证一下深拷贝是否成功:

    @Test
    public void testProtoType(){
    	Person person1=new Person();
        person1.setUsername("wcc");
        person1.setAge(20);
        //初始化list 并为其加入数据
    	person1.setList(new ArrayList<>());
        person1.getList().add("aaa");
        person1.getList().add("bbb");
        System.out.println(person1);
    
        //深拷贝
        Person person=person1.deepClone();
        person.setUsername("hzw");
        //给person中的list添加一条数据
        person.getList().add("ccc");
        System.out.println("person:"+person);
        System.out.println("person1:"+person1);
        boolean flag1=person1==person;
        System.out.println("person1和person的引用地址是否相同:"+flag1);
        boolean flag2=person1.getList()==person.getList();
        System.out.println("person1和person的list引用地址是否相同:"+flag2);
    }
    

    输出结果如下:

    Person{username='wcc', age=20, list=[aaa, bbb]}
    person:Person{username='hzw', age=20, list=[aaa, bbb, ccc]}
    person1:Person{username='wcc', age=20, list=[aaa, bbb]}
    person1和person的引用地址是否相同:false
    person1和person的list引用地址是否相同:false
    

    由结果可得出:深拷贝person所得到的list内存地址和原来的person1中的内存地址都是不同的,深拷贝成功。

  • 相关阅读:
    HTML DOM教程 14HTML DOM Document 对象
    HTML DOM教程 19HTML DOM Button 对象
    HTML DOM教程 22HTML DOM Form 对象
    HTML DOM教程 16HTML DOM Area 对象
    ubuntu 11.04 问题 小结
    VC6.0的 错误解决办法 小结
    boot.img的解包与打包
    shell里 截取字符串
    从零 使用vc
    Imagemagick 对图片 大小 和 格式的 调整
  • 原文地址:https://www.cnblogs.com/gesh-code/p/15246798.html
Copyright © 2011-2022 走看看