美文网首页
clone方法使用需知

clone方法使用需知

作者: 睦月MTK | 来源:发表于2020-03-16 11:47 被阅读0次

    statement:本篇内容只是建立在我目前经验的基础之上,必然有不完善甚至是不正确的地方,请谨慎阅读,如果能指出错误与不足之处,更是不甚感激


    一、使用条件
    • 必须先实现Cloneable接口。如果不实现Cloneable接口,直接使用clone方法将会抛出CloneNotSupportedException异常。
    • 如果父类没有公有的clone方法,应该重写clone方法,并至少将其访问控制权限扩大为public

    二、什么样的类不需要重新覆盖clone方法
    • 不可变类以及事实不可变类
      像这样状态不会改变的类的对象有一个就可以了,完全没有需要一个克隆体的必要
    • 单例模式的类
    • 父类已经有了可靠的公有clone方法,且子类没有添加新的状态,如:
      • 子类没有添加任何新的字段
      • 子类仅仅添加了基础类型的字段
      • 子类新添加的引用类型的字段所指向的对象没有状态被改变的可能

    三、如何实现一个可靠的clone方法

    实现一个可靠的clone方法,大体需要遵循下列步骤:

    1. 调用super.clone获取克隆出来的对象
    2. 检查是否有引用类型的字段
      • 如果有,检查该引用类型的字段指向的对象的状态是否是有被改变可能的
        • 如果是,检查该字段是否是final修饰的
          • 如果是,应当考虑能否去掉final修饰,否则将无法进行深克隆
          • 如果不是,应当进行深克隆
        • 如果不是,直接返回super.clone克隆出来的对象
      • 如果没有,直接返回super.clone克隆出来的对象
    3. 判断该类是否是被设计用于继承的
      • 如果是,应当保持clone方法的访问权限为protected,返回类型为Object,同时抛出CloneNotSupportedException异常,并且不应当实现Cloneable接口,应当将是否具有克隆能力的选择权交由子类来决定
      • 如果不是,应当更改clone方法的访问权限为public,返回类型为当前类型,且不建议抛出CloneNotSupportedException异常,应当使用try-catch内部消化该异常,并抛出一个非受检异常。

    例子:

    @Override
    public Student clone(){
        try {
            return (Student) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
    

    注意:

    • clone过程也相当于一个构造对象的过程,虽然没有调用构造器。所以,也要注意不要提早让未完整的克隆对象发布,比如在将克隆对象调整到正确的状态之前,将该对象发布给另一个线程的某种方法,又或者是在clone内部调用了可能会被子类覆盖的新方法。要确保第二点,你应当对这些方法加上private或者final修饰以确保子类不会将其覆盖。
    • 其实作为一个克隆对象,并不是非得所有的状态和原对象相同,比如唯一序列号、创建时间等等,这些域应当有自己的值。

    四、深克隆

    Q--什么是深克隆?为什么要进行深克隆?
    A--因为Objec提供的clone方法只能够做到将字段的值复制到新对象中去,这个特点对于引用类型的字段来说就很糟糕了,这样会使得原体与克隆体和同一个对象关联,修改那个被关联对象的状态,原体和克隆体的状态会同时发生变化,这是不可容许的。所以就要有深克隆,深克隆负责在将一个完全独立于原对象,且又和原对象状态相同的对象交付给使用者。
    Q--如何进行深克隆?
    A--原理其实很简单,就是对引用类型的字段重新进行修改,修改为调用引用指向的对象的clone方法创建出来的对象。
    例子:

    public class Test implements Cloneable{
        private int[] arrInt = {1,2,3};
    
        @Override
        public Test clone(){
            try {
                Test studentClone = (Test) super.clone();
                studentClone.arrInt = arrInt.clone();
                return studentClone;
            } catch (CloneNotSupportedException e) {
                throw new AssertionError();
            }
        }
        
        public static void main(String[] args) {
            Test testOrigin = new Test();
            Test testClone = testOrigin.clone();
            System.out.println("testOrigin.arrInt == testClone.arrInt ====> "+ (testOrigin.arrInt.equals(testClone.arrInt)));
            System.out.println("testOrigin.arrInt equal testClone.arrInt ====> "+ (Arrays.equals(testOrigin.arrInt, testClone.arrInt)));
        }
    }
    
    /* result:
    testOrigin.arrInt == testClone.arrInt ====> false
    testOrigin.arrInt equal testClone.arrInt ====> true
    */
    

    注意:

    • 只要克隆的对象种存在状态可能变化的引用,就需要深克隆,所以深克隆中进行深克隆是很常见的事。

    五、总结

    一句话,没有绝对的必要不要去启用clone方法。作为替代,你可以尝试下“拷贝构造器”,像是这样

    public Test newInstance(Test test) {
        Test cloneTest = new Test();
        cloneTest.arrInt = Arrays.copyOf(arrInt, arrInt.length);
        return cloneTest;
    }
    

    当然实际上并不一定要以相同的类型返回,决定权完全在你手里。


    参考文档:

    相关文章

      网友评论

          本文标题:clone方法使用需知

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