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中的内存地址都是不同的,深拷贝成功。

posted @ 2021-09-09 14:12  轻风格走一走  阅读(155)  评论(0编辑  收藏  举报