• Java中的克隆(CLONE)


    解读克隆

    编程过程中我们常常遇到如下情况: 假设有一个对象object,在某处又需要一个跟object一样的实例object2,强调的是object和object2是两个独立的实例,只是在 开始的时候,他们是具有相同状态的(属性字段的值都相同),跟同卵双胞胎很相似。遇到这种情况的做法一般是,重新new一个对象object2,将 object的字段值赋予object2,可以说这种方法很土,我们可以使用clone方法克隆出一个跟object一样的object2,很高效。

    误区:有人认为直接将对象object的引用赋予object2就解决问题了,即:object2=object; 这样的话两个引用仍然指向的是同一个对象,不是两个对象。

    Java中跟克隆有关的两个类分别是Cloneable接口和Object类中的clone方法,通过两者的协作来实现克隆。首先看一下java api doc中关于Cloneable接口和Object类中的clone方法的描述:

    java.lang.Cloneable 接口(以下源引JavaTM 2 Platform Standard Ed. 5.0 API DOC)

    此类实 现了 Cloneable 接口,以指示 Object.clone() 方法可以合法地对该类实例进行按字段复制。 如果在没有实现 Cloneable 接口的实例上调用 Object 的 clone 方法,则会导致抛出 CloneNotSupportedException异常。 

    按照惯例,实现此接口的类应该使用公共方法重写 Object.clone(它是受保护的)。请参阅 Object.clone(),以获得有关重写此方法的详细信息。 

    注意,此接口不包含 clone 方法。因此,因为某个对象实现了此接口就克隆它是不可能的。即使 clone 方法是反射性调用的,也无法保证它将获得成功。

    解读:Cloneable接口没有任何方法, 仅是个标志接口(tagging interface),若要具有克隆能力,实现Cloneable接口的类必须重写从Object继承来的clone方法,并调用Object的 clone方法(见下面Object#clone的定义),重写后的方法应为public 的。For example(标准写法):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    class CloneClass implements Cloneable{
     
     public int aInt;
     
    //重写clone方法
     
     public Object clone(){
     
      CloneClass o = null;
     
      try{
     
       o = (CloneClass)super.clone();
     
      }catch(CloneNotSupportedException e){
     
       e.printStackTrace();
     
      }
     
      return o;
     
     }
     

    protected native Object clone() throws CloneNotSupportedException(以下源引JavaTM 2 Platform Standard Ed. 5.0 API DOC)

    创建并返回此对象的一个副本。“副本”的准确含义可能依赖于对象的类。一般来说,对于任何对象 x,如果表达式: 

    x.clone() != x是正确的,则表达式: x.clone().getClass() == x.getClass()将为 true,但这些不是绝对条件。

    一般情况下是: x.clone().equals(x)将为 true,但这不是绝对条件。 

    按照惯例,返回的对象应该通过调用 super.clone  获得。如果一个类及其所有的超类(Object 除外)都遵守此约定,则 x.clone().getClass() == x.getClass()。 

    按照惯例,此方法返回的对象应该独立于该对象(正被克隆的对象)。要获得 此独立性,在 super.clone 返回对象之前,有必要对该对象的一个或多个字段进行修改。这通常意味着要复制包含正在被克隆对象的内部“深层结构”的所有可变对象,并使用对副本的引用替 换对这些对象的引用。如果一个类只包含基本字段或对不变对象的引用,那么通常不需要修改 super.clone 返回的对象中的字段。 

    Object 类的 clone 方法执行特定的克隆操作。首先,如果此对象的类不能实现接口 Cloneable,则会抛出 CloneNotSupportedException。注意:所有的数组都被视为实现接口 Cloneable。否则,此方法会创建此对象的类的一个新实例,并像通过分配那样,严格使用此对象相应字段的内容初始化该对象的所有字段;这些字段的内 容没有被自我克隆。所以,此方法执行的是该对象的“浅表复制”,而不“深层复制”操作。 

    Object 类本身不实现接口 Cloneable,所以在类为 Object 的对象上调用 clone 方法将会导致在运行时抛出异常 。

    解读:Object中的clone方法为 native,这也解释了上面提到的“高效”。实现Cloneable接口的类通过super.clone()调用来实现克隆能力(标准写法)。上文提到 克隆是按属性字段复制的,如果对象的属性值为可变的引用(不可变:如基本类型,final类型的,可变:如对象的引用,数组引用),那原对象跟克隆后的对 象将无法保持独立,因为他们的某个属性共同引用了一个对象,这就是“浅表复制”,这不是我们想要的,正常我们需要的是“深层复制 ”,即对对象的属性进行进一步的clone,for example:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class A implements Cloneable {
        public String a[];
        public A(){
            a=new String[2];
        }
     
        public Object clone() {
            A o = null;
            try {
                o = (A) super.clone();
     o.a=(String[])a.clone();//属性a的克隆,实现A类的深度克隆
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return o;
        }
    }

      

    如果把public String a[];改为public B a[],如果我们想要得到彻底的拷贝,需要类B也要实现Cloneable接口,特别在遇到复杂类型时,比如Vector等,要注意容器中的对象,至于您 是否需要深度复制以及复制多少层,都是根据具体需要来定的。

    关联知识

    Java修饰符访问权限一览

    T1:修饰符的访问权限

    this和super用法 

    this关键字代表对象自身,在程序中主要的使用用途有以下几个方面:

    l  使用this关键字引用成员变量

    l  使用this关键字在自身构造方法内部引用其它构造方法

    l  使用this关键字代表自身类的对象

    l  使用this关键字引用成员方法

    super关键字,使用super关键字可以在子类中引用父类中的内容。主要的使用形式有以下几种:

    l  在子类的构造方法内部引用父类的构造方法

    l  在子类中调用父类中的成员方法

    l  在子类中调用父类中的成员变量

  • 相关阅读:
    2021/3/16
    2021/3/15
    plist
    百度小程序更新管理
    uni-app v-for v-modal
    小程序中不能使用字符串模板吗
    条件编译
    百度app 和小程序版本关系
    uni-app 全局变量的几种实现方式
    canvas 换行处理
  • 原文地址:https://www.cnblogs.com/liuzy2014/p/4821553.html
Copyright © 2020-2023  润新知