java提高(15)—java深浅拷贝

释放双眼,带上耳机,听听看~!
00:00
00:00

#java深浅拷贝

一、前言

为什么会有深浅拷贝这个概念?

我觉得主要跟JVM内存分配有关,对于基本数据类型,只存在栈内存,所以它的拷贝不存在深浅拷贝这个概念。而对于对象而言,一个对象的创建会在内存中分配两块空间,一个在栈内存存对象的引用指针,一个在堆内存存放对象。这个时候会有一个问题,你拷贝的只是这个引用指针还是拷贝两块内存一起拷贝,这个时候就会有深浅拷贝一说。
还有之前我认为Arrays.copyOf()是深度拷贝,亲测后发现原来它也是浅拷贝。下面进行具体说明。

二、数据类型

数据分为基本数据类型(int, boolean, double, byte, char等)和对象数据类型。
基本数据类型的特点:直接存储在栈(stack)中的数据.
引用数据类型的特点:在栈内存存储对象引用,真实的数据存放在堆内存里
引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。

java提高(15)---java深浅拷贝

三、什么是浅拷贝和深拷贝

首先需要明白,深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型的。那先来看看浅拷贝和深拷贝的概念。
在 Java 中,除了基本数据类型(元类型)之外,还存在 类的实例对象 这个引用数据类型。而一般使用 =号做赋值操作的时候。对于基本数据类型,实际上是拷贝的它的值,但是对于对象而言,其实赋值的只是这个对象的引用,将原对象的引用传递过去,他们实际上还是指向的同一个对象。

浅拷贝:如果在拷贝这个对象的时候,只对基本数据类型进行了拷贝,而对引用数据类型只是进行了引用的传递,而没有真实的创建一个新的对象。

深拷贝:在对引用数据类型进行拷贝的时候,创建了一个新的对象,并且复制其内的成员变量。

深拷贝和浅拷贝的示意图大致如下:
java提高(15)---java深浅拷贝

java提高(15)---java深浅拷贝

具体接下来代码演示。

四、代码演示

1、浅拷贝

Person

  1. public class Person {
  2. public String name;
  3. public Integer age;
  4. public String sex;
  5. /**
  6. * 提供get和set方法和全参构造函数
  7. */
  8. }

Test

  1. public static void main(String[] args) throws Exception {
  2. Person person = new Person(\"小小\",3,\"\");
  3. //将person值赋值给person1
  4. Person person1 = person;
  5. System.out.println(person);
  6. System.out.println(person1);
  7. person1.setName(\"小小她爸\");
  8. System.out.println(\"person name为:\"+person.getName());
  9. System.out.println(\"person1 name为:\"+person.getName());
  10. }

查看运行结果
java提高(15)---java深浅拷贝

从图片中我们可以很明显看出,它们指向的内存地址是一致的,同样我改变person1的属性值时发现person的属性值也改变了。

说明:对于对象用\"=\" 赋值 其实只是引用指针的复制,这两个引用还是指向同一个对象。

2、深拷贝

如果要实现深拷贝就会比较复杂点

Student

  1. /**
  2. * 如果对象要实现深拷贝 那么实体需要做两步
  3. * 1、实体实现Cloneable接口
  4. * 2、重写 clone()方法
  5. */
  6. public class Student implements Cloneable {
  7. public String name;
  8. public Integer age;
  9. public String sex;
  10. //这也是个实体
  11. public Address address;
  12. /**
  13. * 提供get和set方法和全参构造函数
  14. */
  15. @Override
  16. protected Object clone() throws CloneNotSupportedException {
  17. return super.clone();
  18. }
  19. }

Test

  1. public static void main(String[] args) throws Exception {
  2. Student student = new Student(\"小小\", 3, \"\", null);
  3. //将person值赋值给person1
  4. Student student1 = (Student) student.clone();
  5. System.out.println(student);
  6. System.out.println(student1);
  7. student1.setName(\"小小她爸\");
  8. System.out.println(\"person name为:\" + student.getName());
  9. System.out.println(\"person1 name为:\" + student1.getName());
  10. }

java提高(15)---java深浅拷贝

这里可以已经是两个不同的对象了。但是这里需要注意的是,如果对象中含有对象,这个对象还是浅拷贝。

Address

  1. public class Address {
  2. public String city;
  3. public int phone;
  4. /**
  5. * 提供get和set方法和全参构造函数
  6. */
  7. }

Test

  1. public static void main(String[] args) throws Exception {
  2. Address address = new Address(\"杭州\", 1888888888);
  3. Student student2 = new Student(\"小小\", 3, \"\", address);
  4. //将person值赋值给person1
  5. Student student3 = (Student) student2.clone();
  6. address.setCity(\"北京天安门\");
  7. System.out.println(\"person2 city为:\" + student2.getAddress().getCity());
  8. System.out.println(\"person3 city为:\" + student3.getAddress().getCity());
  9. }

java提高(15)---java深浅拷贝

我们发现虽然Student是实现了深拷贝,但Address却还是浅拷贝,那如何让Adress也实现深拷贝呢。
Address修改

  1. public class Address implements Cloneable {
  2. public String city;
  3. public int phone;
  4. /**
  5. * 提供get和set方法和全参构造函数
  6. */
  7. @Override
  8. protected Object clone() throws CloneNotSupportedException {
  9. return super.clone();
  10. }

Student修改

  1. //修改clone方法
  2. @Override
  3. protected Object clone() throws CloneNotSupportedException {
  4. Student s = (Student) super.clone();
  5. s.address = (Address) address.clone();
  6. return s;
  7. }

java提高(15)---java深浅拷贝

弊端: 这里我们Person 类只有一个 Address 引用类型,而 Address 类没有,所以我们只用重写 Address 类的clone 方法,但是如果 Address 类也存在一个引用类型,
那么我们也要重写其clone 方法,这样下去,有多少个引用类型,我们就要重写多少次,如果存在很多引用类型,那么代码量显然会很大,所以这种方法不太合适。
所以还有另一种实现深拷贝方法。

序列化实现深拷贝

  1. //序列化实现深拷贝
  2. public Object deepClone() throws Exception{
  3. // 序列化
  4. ByteArrayOutputStream bos = new ByteArrayOutputStream();
  5. ObjectOutputStream oos = new ObjectOutputStream(bos);
  6. oos.writeObject(this);
  7. // 反序列化
  8. ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
  9. ObjectInputStream ois = new ObjectInputStream(bis);
  10. return ois.readObject();
  11. }
  12.  //因为序列化产生的是两个完全独立的对象,所有无论嵌套多少个引用类型,序列化都是能实现深拷贝的。

五、Arrays.copyOf()

之前我误以为Arrays.copyOf()为深拷贝,那只是因为我用的是基本数据类型作为数组,而基本数据类型上面已经说过它没有深浅拷贝这个概念,可以把他理解成只有深拷贝。

  1. public static void main(String[] args) {
  2. //1、基本数据类型
  3. int[] a = {0, 1, 2, 3};
  4. // Arrays.copyOf拷贝
  5. int[] copy = Arrays.copyOf(a, a.length);
  6. a[0] = 1;
  7. System.out.println(Arrays.toString(copy));
  8. System.out.println(Arrays.toString(a));
  9. //2、对象数组
  10. Student[] stuArr = {new Student(\"小小\", 3, \"\"),new Student(\"小小爸\", 29, \"\"),new Student(\"小小妈\", 27, \"\")};
  11. // Arrays.copyOf拷贝
  12. Student[] copyStuArr = Arrays.copyOf(stuArr, stuArr.length);
  13. copyStuArr[0].setName(\"小小爷爷\");
  14. System.out.println(Arrays.toString(stuArr));
  15. System.out.println(Arrays.toString(copyStuArr));
  16. }

运行结果:

java提高(15)---java深浅拷贝

可以明显看出,对于基本数据类型只有深拷贝,而对于数组对象而言,明显存在深浅拷贝,而且可以看出Arrays.copyOf()为浅拷贝

  1. 只要自己变优秀了,其他的事情才会跟着好起来(少将2

点点赞赏,手留余香

给TA打赏
共0人
还没有人赞赏,快来当第一个赞赏的人吧!
    站长资讯

    测试客户端连接12c ASM实例

    2020-11-9 3:46:03

    站长资讯

    rabbitmq学习(七) —— springboot下的可靠使用

    2020-11-9 3:46:05

    0 条回复 A文章作者 M管理员
    欢迎您,新朋友,感谢参与互动!
      暂无讨论,说说你的看法吧
    个人中心
    购物车
    优惠劵
    今日签到
    私信列表
    搜索