美文网首页
[知识向] __ 关于深浅拷贝

[知识向] __ 关于深浅拷贝

作者: 葛木小舍先生丶 | 来源:发表于2019-05-05 17:09 被阅读0次
    • 前言

    拷贝操作,在我们的日常使用电脑的过程中的一种十分常见的情况.但在计算机语言里,拷贝的操作其实是分为两种的.那就是深拷贝与浅拷贝.

    而其实在任何编程语言里,都存在着深浅拷贝这两种概念的.java语言也不例外.今天就从java语言出发,记录一下关于深浅拷贝的学习.


    • 什么是深浅拷贝

    首先我们因当搞明白这两个名词的概念,在讲深浅拷贝之前,我们先来说一说拷贝.什么是拷贝,拷贝干了什么.
    以我们直观的理解,拷贝就是复制,就是将一个对象由一份变为两份

    但是这样直观的方式的实现,缺失有不同的方式的,那就是深浅拷贝两种不同的方式.

    我们一java语言的来理解它们,深浅拷贝必然是针对一个已有的对象进行的操作,而这个操作的目的,或者说实现的东西就如上面所说的直观解释.

    我们知道,JAVA语言是一门面向对象的编程语言,它除了基本数据类型之外,还存在着引用数据类型(类的实例对象).而在我们通常使用=来进行赋值操作的时候,对于基本数据类型,实际上是拷贝它的值,而对于对象而言,其实赋值的只是这个对象的引用,将原对象的引用传递过去.他们实际上是指向同一个对象的.

    而深浅拷贝就是以这为基础而进行区分的即:

    • 浅拷贝

    浅拷贝在拷贝操作的时候,只对基本数据类型进行拷贝,而在拷贝实例化对象(引用数据类型)的时候,知识进行了引用的传递,而不会新建一个对象来拷贝并保存原对象.实际上他们都指向的是同一个原对象即:.
    只对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,则为浅拷贝

    image.png
    • 深拷贝

    深拷贝则正好与浅拷贝相反,当在拷贝引用数据类型(实例化对象)时,新建了一个对象,并复制原对象中的所有属性和方法,将原对象保存在新对象中.使原对象和新建对象成为两个相同但完全独立的两个对象.
    对基本数据类型进行值传递,对引用数据类型,创建一个新对象,并复制原对象的内容,则为深拷贝

    image.png

    因此,其实在某种意义意义上拷贝操作并不是绝对的,因为他们在对基本数据类型拷贝的时候,操作都是一样的,所以都可称之为深拷贝,或浅拷贝.其主要区别在于后面关键的对象的拷贝上.

    并且值得我们注意的是,因为浅拷贝都是指向同一个原对象的,所以它是不会耗费储存空间的,但它具有一定的联动性,当原对象有所改变时,拷贝的对象(其实就是同一个)也一样会改变.(就和桌面快捷方式一样)

    而深拷贝会新建对象将原对象完全复制一份保存在新对象中,所以它是需要占用存储空间的.但它的好处就是两者相互不影响,都是相对独立的.(我们经常做的备份工作就是一种深拷贝操作)


    • Java中的clone()方法

    我们知道在java语言中,所有的类都继承自Object类,在Object类中,存在着一个clone方法.它被声明为protected(同一个类或子类中可用)所以我们可以在子类中使用它.

    而无论深浅拷贝,都需要重写clone方法来实现拷贝操作.

    image.png

    我们可以看到该方法十分简单.简单到我基本没怎么明白它的意思.

    我们再看看关于该方法的注释信息:

    image.png

    这段注释告诉我们如果调用这个方法的对象没有实现Cloneable接口将会报CloneNotSupportedException异常.

    image.png

    这段注释的意思我们可以大概明白,表示Object类自身不能实现接口,所以在对象上直接调用该方法时,会抛出一个运行时异常.

    image.png

    我们在看看这段注释,它给我们说明了,这个clone方法是默认的是一个浅拷贝的方法,它会新建一个实例对象并初始化其中的所有字段的内容,但该字段内容的本身是不会被克隆的.因此该方法是浅拷贝操作,而不是深拷贝操作.

    我们使用java语言来具体看看.
    先来看看object类的clone方法(浅拷贝)的例子.

    /**
     * object类中的clone方法.
     * 它实际上是一个浅拷贝操作.
     * 但它在拷贝一个对象的时候,却确实新建了一个实例化对象.
     * 但它是实际却是一次浅拷贝操作.
     */
    class ShallowCopy implements Cloneable{
        private String name;
        private int age;
    
        public String getName() {
            return name;
        }
        public int getAge() {
            return age;
        }
        // 构造方法.
        ShallowCopy(String name,int age){
            this.name=name;
            this.age=age;
        }
        // 创建一个输出方法
        void toimString(){
            System.out.println(this.name+this.age);
        }
        // clone方法是Object基类中提供的方法,修饰为protected,
        // 我们可以在任何继承它的类中重写实现该方法来完成clone操作.
        @Override
        // 重写object类中的clonr方法,使异常处理在方法体中进行.
        protected Object clone(){
            try {
                return super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    
    

    main方法中:

     public static void main(String[] args) {
            ShallowCopy xiaoshe = new ShallowCopy("xiaoshe", 3);
            xiaoshe.toimString();
            System.out.println(xiaoshe.hashCode());
            ShallowCopy xiaoshe2 = (ShallowCopy) xiaoshe.clone();
            xiaoshe2.toimString();
            System.out.println( xiaoshe2.hashCode());
            System.out.println((xiaoshe==xiaoshe2));
            System.out.println((xiaoshe.getName().equals(xiaoshe2.getName())));
        }
    

    我们观察下结果:

    image.png

    我们可以发现,clone方法确实新建了一个实例化对象来拷贝原对象(哈希值不同,==为false,equlas方法为true).那为什么会说object类中的clone方法实际是浅拷贝操作呢,创建一个新对象来储存原对象不是深拷贝的性质吗.我们再来看一个例子:

    在ShallShallowCopy类中添加一个inner成员变量(属性).并使用成员内部类的方式构建一个innerClass类.

      public innerClass inner;
    
        public class innerClass{
            innerClass(String inname,int inage){
                name=inname;
                age=inage;
            }
            void imString(){
                toimString();
            }
            void getname(){
                getName();
            }
            void getage(){
                getAge();
            }
        }
    

    main方法中(加入成员inner的构建和copy):

        xiaoshe.inner = xiaoshe.new innerClass("小舍",4);
        ShallowCopy xiaoshe3 = (ShallowCopy) xiaoshe.clone();
    

    输出测试:

            xiaoshe.inner.imString();
            System.out.println(xiaoshe.inner.hashCode());
            xiaoshe3.inner.imString();
            System.out.println(xiaoshe3.inner.hashCode());
            System.out.println((xiaoshe.inner == xiaoshe3.inner));
    

    结果:

    image.png

    我们从inner的输出可以发现在这个时候,这里只对ShallShallowCopy类拷贝了一次.而其中的inner对象和copy后的inner对象实际上还是指向的一个同一个对象,只是对它的引用进行了传递而没有进行拷贝操作.

    所以说深浅拷贝实际上并不是一个绝对的定义.而是根据情况来进行判断的,clone方法在copy基本数据类型时,就相当于深拷贝.只有在拷贝实例化对象时(引用数据类型)才会有两种不同的处理方式(深浅拷贝).

    我们再来看看深拷贝的实现.

    按照object类的clone方法的思路来考虑,我们只要让内部类innerClass也重写clone方法,让它自己也clone一份,那么实际上就解决了浅拷贝的问题.

    //让内部类继承Cloneable接口并重写clone方法.
       public class innerClass implements Cloneable{
            innerClass(String inname,int inage){
                name=inname;
                age=inage;
            }
            void imString(){
                toimString();
            }
            void getname(){
                getName();
            }
            void getage(){
                getAge();
            }
    
            @Override
            protected Object clone(){
                try {
                    return super.clone();
                } catch (CloneNotSupportedException e) {
                    e.printStackTrace();
                }
                return null;
            }
        }
    

    在类中重写clone方法,实例一个对象clone原对象,并调用其inner成员的clone方法对其inner对象进行拷贝.

    @Override
        protected Object clone(){
    
            try {
                ShallowCopy copy = (ShallowCopy) super.clone();
                copy.inner = (innerClass) this.inner.clone();
                return copy;
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return null;
        }
    

    结果:

    image.png

    可以看到,这里对inner也进行了一次拷贝,这里实际上对innerClass类来说是进行了一次浅拷贝,而对ShallShallowCopy类来说就是一次深拷贝.


    • 总结:
      现在我们大概搞清楚了深浅拷贝之间的概念了.在JAVA语言中,实际上深浅拷贝只是相对的,如果一个对象内部只有基本数据类型,那用clone()方法获取的就是这个对象的深拷贝,而如果其内部还有引用数据类型,那使用clone()方法就是一次浅拷贝的操作.

    更新时间:
    2019-5-5
    17:09

    相关文章

      网友评论

          本文标题:[知识向] __ 关于深浅拷贝

          本文链接:https://www.haomeiwen.com/subject/rcwmnqtx.html