必威体育Betway必威体育官网
当前位置:首页 > IT技术

java中clone方法的理解(深拷贝、浅拷贝)

时间:2019-07-17 10:42:07来源:IT技术作者:seo实验室小编阅读:53次「手机版」
 

clone

前言:

java中的clone一直是一个老生常谈的问题,另外关于克隆网上也有很多的写过这方面的问题。
我在这里记录一下我遇到的问题和使用clone的方法。

知识点一:什么是浅拷贝?

    我们这里说的浅拷贝是指我们拷贝出来的对象内部的引用类型变量和原来对象内部引用类型变量是同一引用(指向同一对象)。
    但是我们拷贝出来的对象和新对象不是同一对象。

    简单来说,新(拷贝产生)、旧(元对象)对象不同,但是内部如果有引用类型的变量,新、旧对象引用的都是同一引用。

知识点二:什么是深拷贝?

深拷贝:全部拷贝原对象的内容,包括内存的引用类型也进行拷贝

知识点三、java拷贝(clone)的前提:

1.首先我们需要知道Object类中一个clone()的方法,并且是protected关键字修饰的本地方法(使用native关键字修饰),我们完成克隆需要重写该方法。
注意:按照惯例重写的时候一个要将protected修饰符修改为public,这是JDK所推荐的做法,但是我测试了一下,
复写的时候不修改为public也是能够完成拷贝的。但是还是推荐写成public。


2.我们重写的clone方法一个要实现Cloneable接口。虽然这个接口并没有什么方法,但是必须实现该标志接口。
如果不实现将会在运行期间抛出:CloneNotSupportedException异常

3.Object中本地clone()方法,默认是浅拷贝

知识点四:浅拷贝案例:

拷贝类:

package cn.cupcat.java8;

/**
 * Created by xy on 2017/12/25.
 */
public class Person implements Cloneable{
    private String name;
    private int age;
    private int[] ints;

    public int[] getInts() {
        return ints;
    }

    public Person(String name, int age, int[] ints) {
        this.name = name;
        this.age = age;
        this.ints = ints;
    }

    public void setInts(int[] ints) {
        this.ints = ints;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     *  默认实现
     * */
    @Override
    public Object clone() throws CloneNotSupportedException {


        return  super.clone();
    }
}

测试类:

package cn.cupcat.java8;

import org.junit.Test;

/**
 * Created by xy on 2017/12/25.
 */
public class CloneTest  {

    @Test
    public void test() throws CloneNotSupportedException {
        int[] ints = {1,2,3};
        String name = "zhangxiangyang";
        int age = 23;
        Person person = new Person("zhangxiangyang",age,ints);
        System.out.print("一:克隆前:  age = "+ age + "... name = "+ name + " 数组:");
        for (int i : ints){
            System.out.print(i + " ");
        }

        System.out.println();


        //拷贝
        Person clonePerson = (Person) person.clone();

        int clonepersonage = clonePerson.getAge();
        String clonePersonName = clonePerson.getName();
        int[] ints1 = clonePerson.getInts();

        System.out.print("二:克隆后: age = "+ clonePersonAge + "... name = "+ clonePersonName + " 数组: ");
        for (int i : ints1){
            System.out.print(i + " ");
        }
        System.out.println();
        //修改:
        ints1[0] = 50;
        //修饰
        clonePerson.setName("666666666");



        age = person.getAge();
        name = person.getName();
        System.out.println();
        System.out.print("三:修改后原对象: age = "+ age + "... name = "+ name + "数组 ");
        for (int i : ints){
            System.out.print(i + " ");
        }
        System.out.println();
        System.out.println("四:person == clonePerson ? "+ (person == clonePerson ));
    }
}

结果为:

一:克隆前:  age = 23... name = zhangxiangyang 数组:1 2 3 
二:克隆后: age = 23... name = zhangxiangyang 数组: 1 2 3 

三:修改后原对象: age = 23... name = zhangxiangyang数组 50 2 3 
四:person == clonePerson ? false

总结:

1.通过四输出  person == clonePerson ? false 可以看出,克隆以后的对象已经和以前不是同一对象了。因为其引用是不同的。
2.通过分析一、二可以看出我们的拷贝成功执行了,并且拷贝的数据也都正确
3.通过分析三,我们修改了拷贝后对象的name、ints
        ints1[0] = 50;
        //修饰
        clonePerson.setName("666666666");
  发现原对象中的ints也跟着修改了,因此可以证明拷贝后的对象和原对象的ints数组指向了同一引用。
  而我们发现同时也修改了拷贝对象name属性,为什么那么原对象中的name属性没有发生改变呢?
  而且String类型也是引用类型呀? 别急,下面我会画图示意。
4. 通过以上总结,我们得出结论:clone()方法的默认实现是浅拷贝       

下面通过画图示意:

拷贝成功后:

这里写图片描述

修改拷贝对象过后:

这里写图片描述

ps:如果看不清楚图片,可以在标签窗口打开

知识点五:深拷贝案例:

该类只和浅拷贝在clone方法的实现上有区别,其他地方相同:

package cn.cupcat.java8;

/**
 * Created by xy on 2017/12/25.
 */
public class Person implements Cloneable{
    private String name;
    private int age;
    private int[] ints;

    public int[] getInts() {
        return ints;
    }

    public Person(String name, int age, int[] ints) {
        this.name = name;
        this.age = age;
        this.ints = ints;
    }

    public void setInts(int[] ints) {
        this.ints = ints;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     *  深拷贝
     * */
    @Override
    public Object clone() throws CloneNotSupportedException {
        Person person = new Person(name,age);
        int[] ints = new int[this.ints.length];
        System.arraycopy(this.ints,0,ints,0,ints.length);
        person.setInts(ints);

        return  person;
    }
}

该测试类和前拷贝测试类相同:

package cn.cupcat.java8;

import org.junit.Test;

/**
 * Created by xy on 2017/12/25.
 */
public class CloneTest  {

    @Test
    public void test() throws CloneNotSupportedException {
        int[] ints = {1,2,3};
        String name = "zhangxiangyang";
        int age = 23;
        Person person = new Person("zhangxiangyang",age,ints);
        System.out.print("克隆前:  age = "+ age + "... name = "+ name + " 数组:");
        for (int i : ints){
            System.out.print(i + " ");
        }

        System.out.println();


        //拷贝
        Person clonePerson = (Person) person.clone();

        int clonePersonAge = clonePerson.getAge();
        String clonePersonName = clonePerson.getName();
        int[] ints1 = clonePerson.getInts();

        System.out.print("克隆后: age = "+ clonePersonAge + "... name = "+ clonePersonName + " 数组: ");
        for (int i : ints1){
            System.out.print(i + " ");
        }
        System.out.println();
        //修改:
        ints1[0] = 50;
        //修饰可控后的对象
        clonePerson.setName("666666666");



        age = person.getAge();
        name = person.getName();
        System.out.println();
        System.out.print("修改后原对象: age = "+ age + "... name = "+ name + "数组 ");
        for (int i : ints){
            System.out.print(i + " ");
        }
        System.out.println();
        System.out.println("person == clonePerson ? "+ (person == clonePerson ));
    }
}

结果为:

一:克隆前:  age = 23... name = zhangxiangyang 数组:1 2 3 
二:克隆后: age = 23... name = zhangxiangyang 数组: 1 2 3 

三:修改后原对象: age = 23... name = zhangxiangyang数组 1 2 3 
四:person == clonePerson ? false

总结:

1.通过四可以看出完成了拷贝,并且克隆对象和原对象不是同一对象(没有同一引用)
2.通过一、二可以看出完成了拷贝并且数据正确
3.通过三,我们修改克隆以后的对象,打印原对象发现没有影响到原对象的数据,也就是说完成的深拷贝
  下面使用图解说明一下

画图说明:

拷贝完成后:

这里写图片描述

修改拷贝后对象后:

这里写图片描述

知识点六:总结

拷贝也算是我们经常使用的一个方法,但是如果是不明白其中原理的程序员可能还是会入坑的。下面总结几条使用建议:
 1.一定要实现Cloneable接口
 2.复写clone()方法,注意:默认是浅拷贝,这里需要将引用类型进行深拷贝处理
 3.特殊:String类虽然是引用类型,但是是final类,同时也有字符串常量池的存在,不必进行处理

相关阅读

Java Web理解基本的MVC框架(3分钟理解)

MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用于组织代码用一种业务逻辑

互信息的深度理解(总结的不错值得一看)

1.互信息的定义 正式地,两个离散随机变量 X 和 Y 的互信息可以定义为: 其中 p(x,y) 是 X 和 Y 的联合概率分布函数,而p(x

MODULE_DEVICE_TABLE宏的理解

在学习USB驱动时,会看到下面的代码:MODULE_DEVICE_TABLE(usb, id_table);MODULE_DEVICE_TABLE宏的定义如下: #define MODULE_DEVICE_

理解js中instanceof运算操作符

什么是instanceof 在js代码中,我们经常使用 instanceof 就是判断一个实例是否属于某种类型,instanceof 可以在继承关系中用来判断

Attention机制的小理解

1.传统的encode-decode模型 输入序列: 输出序列: 中间语义变量 所以对于,也就是         这就表明输入序列的每一个元素对于输

分享到:

栏目导航

推荐阅读

热门阅读