zoukankan      html  css  js  c++  java
  • 内功心法 -- Java中的深拷贝和浅拷贝

    写在前面的话:读书破万卷,编码如有神
    --------------------------------------------------------------------
    这篇博客主要来谈谈"Java中的深拷贝和浅拷贝"的相关知识,主要内容包括:

    1.概述

    2.复制对象 or 复制引用

    3.深拷贝 or 浅拷贝

    --------------------------------------------------------------------

    1、概述                                                                                

      clone顾名思义就是克隆的意思,在Java语言中clone方法被调用会复制对象。所谓的复制对象,首先要分配一个和源对象同样大小的内存空间,在这个内存空间中创建一个新的对象。那么在Java语言中,有几种方式可以创建对象呢?  

      方法一: 使用new操作符创建一个对象

      方法二: 使用clone方法克隆一个对象

    那么上面这两种方式有什么相同和不同呢? new操作符的本意是分配内存,程序执行到new操作符时,首先去看看new操作符后面的数据类型,因为知道了数据类型,才能知道要分配多大的内存空间。分配完内存之后,再调用构造函数填充对象的各个域,这一步叫做对象的初始化,构造方法返回后,一个对象创建完毕,可以把它的引用(地址)发布到外部,在外部就可以使用这个引用操作这个对象了。而clone在第一步是和new操作符相似的,都是进行内存空间的分配,调用clone方法时分配的内存和源对象(即调用clone方法的对象)相同。然后再使用原对象中对应的各个域填充新对象的域,填充完成之后clone方法返回,一个新的相同的对象被创建,同样可以把这个新对象的引用发布到外部。

    2、复制对象 or 复制引用                                                         

    在Java中,以下类似的代码非常常见:

     1 public class Student implements Cloneable{
     2 
     3     private int StuId;
     4     private String StuName;
     5     private int StuAge;
     6     
     7     public Student(int stuId, String stuName, int stuAge) {
     8         super();
     9         StuId = stuId;
    10         StuName = stuName;
    11         StuAge = stuAge;
    12     }
    13     
    14     @Override
    15     protected Object clone() throws CloneNotSupportedException {
    16         return super.clone();
    17     }
    18 }
    View Code
     1 public class CloneTest {
     2     public static void main(String[] args) throws CloneNotSupportedException {
     3         Student stu1 = new Student(0,"zhangsan",20);        
     4         Student stu2 = stu1;
     5         
     6         System.out.println("stu1 = " + stu1);
     7         System.out.println("stu2 = " + stu2);
     8     }
     9 }
    10 
    11 运行结果:
    12 stu1 = Student@3c635421
    13 stu2 = Student@3c635421

    从运行的结果可以看出:打印的地址值是一样的,既然地址都是一样的,那么肯定是同一个对象。stu1和stu2只是引用而已,它们都指向了一个相同的对象Student(0,"zhangsan",20);可以把这种现象叫做引用的复制。执行完上面的代码之后,内存中的情况如下图:

    而下面的代码是真正的实现了克隆一个对象:

     1 public class CloneTest {
     2     public static void main(String[] args) throws CloneNotSupportedException {
     3         Student stu3 = new Student(1,"lisi",20);
     4         Student stu4 = (Student) stu3.clone();
     5         System.out.println("stu3 = " + stu3);
     6         System.out.println("stu4 = " + stu4);
     7     }
     8 }
     9 
    10 运行结果:
    11 stu3 = Student@7bc2f501
    12 stu4 = Student@3c635421

    从打印的结果可以看出,两个对象的地址是不同的,也就是说创建了新的对象,而不是把源对象的地址赋给了一个新的引用变量。

    执行完以上代码后,内存中的情况如下图:

    3、深拷贝 or 浅拷贝                                                                 

      在上面的示例代码中,Student中有三个成员变量,分别是StuId、StuName、StuAge,其中StuName是String类型,StuId和StuAge是int类型。由于StuId和StuAge是基本数据类型,那么对它们的拷贝没有什么疑议,直接将一个4字节的整数值拷贝过来就可以了。但是StuName是String类型,它只是一个引用,指向一个真正的String对象,那么对它的拷贝分为两种方式: 直接将源对象中的StuName的引用值拷贝给新对象的StuName字段,或者是根据原Student对象中的StuName指向的字符串对象创建一个新的相同的字符串对象,将这个新字符串对象的引用赋值给新拷贝的Student对象的StuName字段。这两种拷贝方式分别是浅拷贝深拷贝

    深拷贝和浅拷贝的原理如下图所示:

    下面通过代码进行验证,如果两个Student对象的StuName的地址值相同,说明两个对象的StuName都指向了同一个String对象,也就是浅拷贝;而如果两个对象的StuName的地址值不同,那么就说明指向不同的String对象,也就是在拷贝Student对象的时候,同时拷贝了StuName引用的对象,也就是深拷贝,代码如下:

     1 public class CloneTest {
     2     public static void main(String[] args) throws CloneNotSupportedException {
     3         Student stu1 = new Student(0,"zhangsan",20);        
     4         Student stu2 = (Student) stu1.clone();
     5         
     6         System.out.println("stu1 = " + stu1);
     7         System.out.println("stu2 = " + stu2);
     8         
     9         System.out.println("stu1.StuName == stu2.StuName : " + (stu1.getStuName()==stu2.getStuName()));
    10     }
    11 }
    12 
    13 运行结果:
    14 stu1 = Student@1df0a2a0
    15 stu2 = Student@2144c5bb
    16 stu1.StuName == stu2.StuName : true

    从运行结果可以看出,Object中默认的的clone方法执行的是浅拷贝

     

  • 相关阅读:
    linux 下使用scp命令传输文件
    yii2使用vendor文件夹下的的css文件
    yii2中使用定义在 params.php文件中的配置
    PHP Catchable fatal error: Argument 2 passed to IlluminateRoutingUrlGenerator::__construct()
    git 去除本地所有没有保存的修改
    学习修复Laravel The only supported ciphers are AES-128-CBC and AES-256-CBC
    [190308]Ubuntu 安装完之后,安装的软件小记
    swagger.yaml转换为swagger.json文件
    Linux sed -i 字符串替换
    [笔记]Laravel TDD 胡乱记录
  • 原文地址:https://www.cnblogs.com/xinhuaxuan/p/6370039.html
Copyright © 2011-2022 走看看