• Cloneable接口和Object的clone()方法


    为什么要克隆

    为什么要使用克隆,这其实反映的是一个很现实的问题,假如我们有一个对象:

    复制代码
    public class SimpleObject implements Cloneable
    {
        private String str;
        
        public SimpleObject()
        {
            System.out.println("Enter SimpleObject.constructor()");
        }
    
        public String getStr()
        {
            return str;
        }
    
        public void setStr(String str)
        {
            this.str = str;
        }
        
        public Object clone() throws CloneNotSupportedException
        {
            return super.clone();
        }
    }
    复制代码

    现在我写一段程序:

    复制代码
    public static void main(String[] args)
    {
        SimpleObject so0 = new SimpleObject();
        so0.setStr("111");
        System.out.println("so0.getStr():" + so0.getStr());
        SimpleObject so1 = so0;
        so1.setStr("222");
        System.out.println("so0.getStr():" + so0.getStr());
        System.out.println("so1.getStr():" + so1.getStr());
    }
    复制代码

    运行结果其实很明显:

    so0.getStr():111
    so0.getStr():222
    so1.getStr():222

    Java底层使用C/C++实现的,"="这个运算符,如果左右两边都是对象引用的话,在Java中表示的将等号右边的引用赋值给等号左边的引用,二者指向的还是同一块内存,所以任何一个引用对内存的操作都直接反映到另一个引用上。

    但是,现在我想拿这个so0的数据进行一些操作,不想改变原来so0中的内容,这时候就可以使用克隆了,它允许在堆中克隆出一块和原对象一样的对象,并将这个对象的地址赋予新的引用,这样,显然我对新引用的操作,不会影响到原对象。

    当然,理解克隆,最好还是对Java内存区域有比较好的理解。

    Cloneable接口和Object的clone()方法

    Java中实现了Cloneable接口的类有很多,像我们熟悉的ArrayList、Calendar、Date、HashMap、Hashtable、HashSet、LinkedList等等。

    还是那句话,对于不熟悉的接口、方法,第一反应一定是查询JDK API。

    1、Cloneable接口

    三句话总结:

    (1)此类实现了Cloneable接口,以指示Object的clone()方法可以合法地对该类实例进行按字段复制

    (2)如果在没有实现Cloneable接口的实例上调用Object的clone()方法,则会导致抛出CloneNotSupporteddException

    (3)按照惯例,实现此接口的类应该使用公共方法重写Object的clone()方法,Object的clone()方法是一个受保护的方法

    2、Object的clone()方法

    创建并返回此对象的一个副本。对于任何对象x,表达式:

    (1)x.clone() != x为true

    (2)x.clone().getClass() == x.getClass()为true

    (3)x.clone().equals(x)一般情况下为true,但这并不是必须要满足的要求

    克隆实例

    把上面例子的main函数修改一下:

    复制代码
    public static void main(String[] args) throws Exception
    {
        SimpleObject so0 = new SimpleObject();
        so0.setStr("111");
        SimpleObject so1 = (SimpleObject)so0.clone();
        
        System.out.println("so0 == so1?" + (so0 == so1));
        System.out.println("so0.getClass() == so1.getClass()?" + (so0.getClass() == so1.getClass()));
        System.out.println("so0.equals(so1)?" + (so0.equals(so1)));
            
        so1.setStr("222");
        System.out.println("so0.getStr():" + so0.getStr());
        System.out.println("so1.getStr():" + so1.getStr());
    }
    复制代码

    看一下运行结果:

    Enter SimpleObject.constructor()
    so0 == so1?false
    so0.getClass() == so1.getClass()?true
    so0.equals(so1)?false
    so0.getStr():111
    so1.getStr():222

    得到三个结论:

    1、克隆一个对象并不会调用对象的构造方法,因为"Enter SimpleObject.constructor()"语句只出现了一次

    2、符合JDK API的clone()方法三条规则

    3、so1对于SimpleObject对象str字段的修改再也不会影响到so0了

    浅克隆和深克隆

    浅克隆(shallow clone)和深克隆(deep clone)反映的是,当对象中还有对象的时候,那么:

    1、浅克隆,即很表层的克隆,如果我们要克隆对象,只克隆它自身以及它所包含的所有对象的引用地址

    2、深克隆,克隆除自身对象以外的所有对象,包括自身所包含的所有对象实例

    这两个概念应该很好理解,就不写代码了。多提一句,所有的基本数据类型,无论是浅克隆还是深克隆,都会进行原值克隆,毕竟它们都不是对象,不是存储在堆中的。

    那其实Object的clone()方法,提供的是一种浅克隆的机制,如果想要实现对对象的深克隆,在不引入第三方jar包的情况下,可以使用两种办法:

    1、先对对象进行序列化,紧接着马上反序列化出

    2、先调用super.clone()方法克隆出一个新对象来,然后在子类的clone()方法中手动给克隆出来的非基本数据类型(引用类型)赋值,比如ArrayList的clone()方法:

    复制代码
    public Object clone() {
    try {
        ArrayList<E> v = (ArrayList<E>) super.clone();
        v.elementData = Arrays.copyOf(elementData, size);
        v.modCount = 0;
        return v;
    } catch (CloneNotSupportedException e) {
        // this shouldn't happen, since we are Cloneable
        throw new InternalError();
    }
    }
    复制代码
  • 相关阅读:
    对象状态序列化到字节流中
    操作EXCEL完毕后,关闭EXCEL进程
    ORACLE多表查询优化(引)
    再谈需要分析一
    动态添加table,动态添加控件
    ref传参时出错
    SqlServer2000下实现行列转换
    调用结构属性、方法或公共字段的区别
    拆箱存在的隐患
    鼠标悬停图片,滑动显示文字
  • 原文地址:https://www.cnblogs.com/szlbm/p/5504649.html
Copyright © 2020-2023  润新知