Java的clone()方法使用详解

前言:

我们知道,在java的object类中,有这么一个方法clone(),这个方法有什么用呢?怎样才能正确地使用这个方法呢?

下面一一来进行阐述一下


clone()方法详解:

1>clone()方法的作用

顾名思义,clone()方法的作用就是克隆的意思,引入这个方法,这样就便于我们构建属于自己的一些本地对象副本。

这样我们就不用担心因为副本对象的引用而使原生的对象发生改变。这就是克隆所能带给我们的好处。


默认情况下,类是不具备克隆的能力的,需要再额外添加一些代码使其具备克隆能力。


2>如何使用clone()方法

I. 实现Cloneable 接口
II. 覆盖clone()
III. 在自己的clone()中调用super.clone()
IV. 在自己的clone()中捕获异常

参考示例如下:

package com.example.example_01;
/**
 *
 * 版权:XXX公司 版权所有
 *
 * 作者:will smith
 *
 * 版本:1.0
 *
 * 创建日期:2016/10/23
 *
 * 描述:
 *
 * 修订历史:
 *
 */

//实现Cloneable接口
public class CloneSample implements Cloneable{

    private int i;

    /**
     * 代码测试部分
     * @param args
     */
    public static void main(String[] args){
        CloneSample sample = new CloneSample(10);
        System.out.println("i: " + sample.i);
        CloneSample sampleCopy = (CloneSample) sample.clone();
        sampleCopy.i++;
        System.out.println("copy i: " + sampleCopy.i);
        System.out.println("i: " + sample.i);
    }


    public CloneSample(int i) {
        this.i = i;
    }

    //重写clone
    @Override
    public Object clone(){
        Object o = null;
        try {
            o = super.clone();   //调用父类的clone
        } catch (CloneNotSupportedException e) {    //异常捕获
            e.printStackTrace();
        }
        return o;
    }

}
 
  

运行结果如下:

代码分析:

由于CloneSample重写了clone()方法并实现了Cloneable接口,所以当调用CloneSample对象的clone()方法时,

就会克隆出一份CloneSample对象的副本,在克隆时,属性和方法都对照着拷贝,由于在拷贝属性时,其属性的

类型为 int ,所以当然是值传递,所以拷贝之后的副本和之前的副本就没有任何联系了。所以在对副本的属性 i 进行

++时,自然就不会影响原对象的属性 i 的值了。

若原对象的属性中含有引用类型,则其拷贝的就是句柄,这样会导致原对象属性的引用和副本对象属性的引用都指向

同一个对象,这样当副本对象修改引用类型属性的某个属性值时,就间接会影响原对象的引用类型属性的属性值,那么

如何才能真正达到两者完全分离呢?你看第三部分:深拷贝和浅拷贝。

3>深拷贝和浅拷贝

我们再来看一组调用clone()类的方法

package com.example.example_02;
/**
 *
 * 版权:XXX公司 版权所有
 *
 * 作者:will smith
 *
 * 版本:1.0
 *
 * 创建日期:2016/10/23
 *
 * 描述:用来比较深拷贝和浅拷贝
 *
 * 修订历史:
 *
 */

public class CloneSampleDiff implements Cloneable{

    Shallow shallow;
    Deep deep;

    public CloneSampleDiff() {
        shallow = new Shallow();
        deep = new Deep();
    }

    public static void main(String[] args){

        CloneSampleDiff sampleDiff = new CloneSampleDiff();
        CloneSampleDiff sampleDiffCopy = (CloneSampleDiff) sampleDiff.clone();

        /**
         * 改变之前的输出
         */
        System.out.println("sampleDiff.shallow.tempShallow: " + sampleDiff.shallow.tempShallow);
        System.out.println("sampleDiff.deep.tempDeep: " + sampleDiff.deep.tempDeep);

        System.out.println("sampleDiffCopy.shallow.tempShallow: " + sampleDiffCopy.shallow.tempShallow);
        System.out.println("sampleDiffCopy.deep.tempDeep: " + sampleDiffCopy.deep.tempDeep);


        /***
         * 改变tempClasstempClone属性
         */
        sampleDiffCopy.shallow.tempShallow++;
        sampleDiffCopy.deep.tempDeep++;

        System.out.println("====================================");

        /**
         * 改变之后的输出
         */
        System.out.println("sampleDiff.shallow.tempShallow: " + sampleDiff.shallow.tempShallow);
        System.out.println("sampleDiff.deep.tempDeep: " + sampleDiff.deep.tempDeep);

        System.out.println("sampleDiffCopy.shallow.tempShallow: " + sampleDiffCopy.shallow.tempShallow);
        System.out.println("sampleDiffCopy.deep.tempDeep: " + sampleDiffCopy.deep.tempDeep);



    }


    @Override
    public Object clone() {

        CloneSampleDiff o = null;
        try {

            o = (CloneSampleDiff) super.clone();

        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        /**
         * 注意观察这里
         */
        o.deep = (Deep) o.deep.clone();

        return o;
    }
}


/**
 * 实现了Cloneable
 */
class Shallow implements Cloneable{

    int tempShallow;

    public Shallow() {
        tempShallow = 10;
    }

    @Override
    public Object clone() {

        Object o = new Object();
        try {
            o = super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        return o;
    }
}

/**
 * 实现了Cloneable
 */
class Deep implements Cloneable{

    int tempDeep;

    public Deep() {
        tempDeep = 20;
    }

    @Override
    public Object clone(){
        Object o = new Object();
        try {
            o = super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return o;

    }
}

输出结果:

Java的clone()方法使用详解_第1张图片


代码分析:

在分析之前,我们先来大概看一下上述这个类:

其包含了两个属性,分别为:shallow和deep,而shallow和deep都分别具有了克隆的功能,

两者唯一的区别就是在CloneSampleDiff类的clone()方法中,对于deep属性,增加了一条语句:

o.deep = (Deep) o.deep.clone();

也就是这条语句造就了两者中的tempShallow和tempDeep属性值的变化有所差异,

也就是当对副本sampleDiff执行

sampleDiffCopy.shallow.tempShallow++;

其原来的对象sampleDiff.shallow.tempShallow也会一同变化,这就是我们所说的浅拷贝。


而当副本sampleDiff执行

sampleDiffCopy.deep.tempDeep++;

其原来的对象sampleDiffCopy.deep.tempDeep就没有发生变化,这也就真正达到了我们

拷贝的目的,这就是我们所说的深拷贝。


深拷贝和浅拷贝出现的原因在于:

拷贝时,若属性为主类型,则由于其是值传递,所以自然不会有影响。

若属性为对象,我们知道,其实质是句柄,那么在拷贝时,自然也就

拷贝句柄,这样拷贝的结果是:两个相同属性的句柄指向了一个对象。

自然,当我们用一个句柄去改变对象的值时,自然另一个句柄的所

指向的值也就发生变化。

为了真正达到深拷贝,需要满足两点:

第一:这个属性是具有克隆功能的

第二:在clone()方法中,再去调用对应属性的clone()方法予以赋值,如本示例中的deep属性:

@Override
public Object clone() {

    CloneSampleDiff o = null;
    try {

        o = (CloneSampleDiff) super.clone();

    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
    }

    /**
     * 注意观察这里
     */
    o.deep = (Deep) o.deep.clone();

    return o;
}


4>继承具有克隆功能的类

在第三部分中,我们知道,一个属性若要实现深拷贝,则需要让这个属性具有克隆的功能,

而要让这个属性具有克隆的功能,我们从第二部分可以知道:需要实现Cloneable接口及重写clone()方法。

但倘若一个类是继承一个已具有克隆功能的类的时候,此时,我们是否还有必要再去实现克隆功能呢?

下面,我们来看一下下面这段代码:

package com.example.example_03;

/**
 *
 * 版权:XXX公司 版权所有
 *
 * 作者:will smith
 *
 * 版本:1.0
 *
 * 创建日期:2016/10/23
 *
 * 描述:参考Java编程思想示例
 *
 * 修订历史:
 *
 */

class Person {}

class Hero extends Person {}

class Scientist extends Person
        implements Cloneable {
    public Object clone() {
        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
        // this should never happen:
        // It's Cloneable already!
            throw new InternalError();
        }
    }
}

class MadScientist extends Scientist {}

public class HorrorFlick {

    public static void main(String[] args) {
        Person p = new Person();
        Hero h = new Hero();
        Scientist s = new Scientist();
        MadScientist m = new MadScientist();
        // p = (Person)p.clone(); // Compile error
        // h = (Hero)h.clone(); // Compile error
        s = (Scientist)s.clone();
        m = (MadScientist)m.clone();
    }

}

代码分析:

若新建一个类,它的基础类会默认为Object,并默认为不具备克隆能力。只要不明确地添加克隆能力,这种能力便不会自动产生。

但我们可以在任何层添加它,然后便可从那个层开始向下具有克隆能力。



5>序列化和克隆

我们知道,其实序列化从某种意义上也可是实现克隆的一种方式,那有了序列化,为何还要克隆呢?

因为克隆性能更高,所需消耗的时间更短。



源码:

clone()方法详解示例源码


参考:

Java编程思想




关注微信公众号获取更多资讯


你可能感兴趣的:(Java)