A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

如题,平时遇到的不多,这个接口没什么了解,只知道大概是克隆的意思。哪个同学详细说说他们的意义和使用。

5 个回复

倒序浏览

本帖最后由 钱兆元 于 2012-2-1 23:12 编辑

先给个JDK的解释:
1.此类实现了 Cloneable 接口,以指示 Object.clone() 方法可以合法地对该类实例进行按字段复制。   
2.  
3.如果在没有实现 Cloneable 接口的实例上调用 Object 的 clone 方法,则会导致抛出 CloneNotSupportedException 异常。   
4.  
5.按照惯例,实现此接口的类应该使用公共方法重写 Object.clone(它是受保护的)。请参阅 Object.clone(),以获得有关重写此方法的详细信息。   
6.  
7.注意,此接口不 包含 clone 方法。因此,因为某个对象实现了此接口就克隆它是不可能的。即使 clone 方法是反射性调用的,也无法保证它将获得成功。   


我们平时需要对某个对象进行操作,但是这个对象被两个地方操作,却都要使用最原始时候的对象。这个时候就需要Cloneable接口出现了。并且如果我们对象里面的属性还有非基本类型对象的话也需要实现此接口直到没有属性是非基本类型对象为止。

clone的用法:

  1 声明实现Cloneable接口,这然使用如super.clone()方法时,会发生抛出cloneNotSupportedException。

  2 调用super.clone拿到一个对象,如果父类的clone实现没有问题的话,在该对象的内存存储中,所有父类定义的field都已经clone好了,该类中的primitive和不可变类型引用也克隆好了,可变类型引用都是浅copy。

  3 把浅copy的引用指向原型对象新的克隆体。

评分

参与人数 1技术分 +2 收起 理由
admin + 2

查看全部评分

回复 使用道具 举报
java的类型分为两大类,一类为primitive,如int,另一类为引用类型,如String,Object等等。
java引用类型的存储,java的引用类型都是存储在堆上的。
Java代码public class B { int a; String b; public B(int a, String b) { super(); this.a = a; this.b = b; } }   public class B {
    int a;
    String b;

    public B(int a, String b) {
        super();
        this.a = a;
        this.b = b;
    }
}


简单的说就是clone一个对象实例。使得clone出来的copy和原有的对象一模一样。
插一个简单使用clone的例子,如果一个对象内部有可变对象实例的话,public API不应该直接返回该对象的引用,以防调用方的code改变该对象的内部状态。这个时候可以返回该对象的clone。
一般而言,我们要的clone应该是这样的。copy和原型的内容一样,但是又是彼此隔离的。即在clone之后,改变其中一个不影响另外一个。


Object的clone以及为什么如此实现
Object的clone的行为是最简单的。以堆上的内存存储解释的话(不计内务内存),对一个对象a的clone就是在堆上分配
一个和a在堆上所占存储空间一样大的一块地方,然后把a的堆上内存的内容复制到这个新分配的内存空间上。


java为什么如此实现clone呢?
1 效率和简单性,简单的copy一个对象在堆上的的内存比遍历一个对象网然后内存深copy明显效率高并且简单。
2 不给别的类强加意义。如果A实现了Cloneable,同时有一个引用指向B,如果直接复制内存进行深copy的话,意味着B在意义上也是支持Clone的,但是这个是在使用B的A中做的,B甚至都不知道。破坏了B原有的接口。
3 有可能破坏语义。如果A实现了Cloneable,同时有一个引用指向B,该B实现为单例模式,如果直接复制内存进行深copy的话,破坏了B的单例模式。
4 方便且更灵活,如果A引用一个不可变对象,则内存deep copy是一种浪费。Shadow copy给了程序员更好的灵活性。

如何clone
clone三部曲。
1 声明实现Cloneable接口。
2 调用super.clone拿到一个对象,如果父类的clone实现没有问题的话,在该对象的内存存储中,所有父类定义的field都已经clone好了,该类中的primitive和不可变类型引用也克隆好了,可变类型引用都是浅copy。
3 把浅copy的引用指向原型对象新的克隆体。
给个例子。
Java代码class User implements Cloneable {
            String name;
            int age;

            @Override
            public User clone() throws CloneNotSupportedException {
                    return (User) super.clone();
            }
    }

    class Account implements Cloneable {
            User user;
            long balance;

            @Override
            public Account clone() throws CloneNotSupportedException {
                    Account account = null;

                    account = (Account) super.clone();
                    if (user != null) {
                            account.user = user.clone();
                    }

                    return account;
            }
    }


clone也是有缺点的。
1 手工维护clone的调用链。
2 如果class的field是个final的可变类,就不行了。三部曲的第三步没有办法做了。

其他的选择
可以用原型构造函数,或者静态copy方法来手工制作一个对象的copy。
好处是即使class的field为final,也不会影响该方法的使用。不好的地方是所有的primitive赋值都得自己维护。



评分

参与人数 1技术分 +2 收起 理由
admin + 2

查看全部评分

回复 使用道具 举报
clone( )方法创建调用它的对象的一个复制副本。只有那些实现Cloneable接口的类能被复制。

Cloneable接口没有定义成员。它通常用于指明被创建的一个允许对对象进行位复制(也就是对象副本)的类。如果试图用一个不支持Cloneable接口的类调用clone( )方法,将引发一个CloneNotSupportedException异常。当一个副本被创建时,并没有调用被复制对象的构造函数。副本仅仅是原对象的一个简单精确的拷贝。

复制是一个具有潜在危险的操作,因为它可能引起不是你所期望的副作用。例如,假如被复制的对象包含了一个称为obRef的引用变量,当副本创建时,副本中的obRef如同原对象中的obRef一样引用相同的对象。如果副本改变了被obRef引用的对象的内容,那么对应的原对象也将被改变。这里是另一个例子。如果一个对象打开一个I/O流并被复制,两个对象将可操作相同的流。而且,如果其中一个对象关闭了流,而另一个对象仍试图对I/O流进行写操作的话,将导致错误。

由于复制可能引起问题,因此在Object内,clone( )方法被说明为protected。这就意味着它必须或者被由实现Cloneable的类所定义的方法调用,或者必须被那些类显式重载以便它是公共的。让我们看关于下面每一种方法的例子。

下面的程序实现Cloneable接口并定义cloneTest( )方法,该方法在Object中调用clone( )方法:

// Demonstrate the clone() method.



class TestClone implements Cloneable {

  int a;

  double b;



  // This method calls Object's clone().

  TestClone cloneTest() {

    try {

      // call clone in Object.

      return (TestClone) super.clone();

    } catch(CloneNotSupportedException e) {

      System.out.println("Cloning not allowed.");

      return this;

    }

  }

}



class CloneDemo {

  public static void main(String args[]) {

    TestClone x1 = new TestClone();

    TestClone x2;



    x1.a = 10;

    x1.b = 20.98;



    x2 = x1.cloneTest(); // clone x1



    System.out.println("x1: " + x1.a + " " + x1.b);

    System.out.println("x2: " + x2.a + " " + x2.b);

  }

}

这里,方法cloneTest( )在Object中调用clone( )方法并且返回结果。注意由clone( )方法返回的对象必须被强制转换成它的适当类型(TestClone)。

下面的例子重载clone( )方法以便它能被其类外的程序所调用。为了完成这项功能,它的存取说明符必须是public,如下所示:

// Override the clone() method.



class TestClone implements Cloneable {

  int a;

  double b;



  // clone() is now overridden and is public.

  public Object clone() {

    try {

      // call clone in Object.

      return super.clone();

    } catch(CloneNotSupportedException e) {

      System.out.println("Cloning not allowed.");

      return this;

    }

  }

}



class CloneDemo2 {

  public static void main(String args[]) {

    TestClone x1 = new TestClone();

    TestClone x2;



    x1.a = 10;

    x1.b = 20.98;



    // here, clone() is called directly.

    x2 = (TestClone) x1.clone();



    System.out.println("x1: " + x1.a + " " + x1.b);

    System.out.println("x2: " + x2.a + " " + x2.b);

  }

}

由复制带来的副作用最初一般是比较难发现的。通常很容易想到的是类在复制时是很安全的,而实际却不是这样。一般在没有一个必须的原因的情况下,对任何类都不应该执行Cloneable。

评分

参与人数 1技术分 +2 收起 理由
admin + 2

查看全部评分

回复 使用道具 举报
cloneable接口是一个标志性的接口,实现他可以不实现任何方法。使用了它就标志这个类可以复制,如果想实现深层次的克隆,那就要重载clone()方法。
若需要修改一个对象,同时不想改变调用者的对象,就用克隆,
先用implements Cloneble实现Cloneble接口,
再实现clone();

这是里面的一段(非完整的例子):
public class Snake implements Cloneable {

...........................

public Object clone() {
Object o = null;
try {
o = super.clone();
} catch (CloneNotSupportedException e) {}
return o;

}


copy书上一段给你看看,希望有帮助把。。
当制作实例变量的一个引用时,原始实例变量和副本实力变量在内存中引用的均是同一个存储空间,这就意味着但对其中一个实例变量操作时就会影响到这个对象的副本。例如下面的程序代码。在执行时将会看到所画的两条线重合在一起,都是改变后的LineFigure1。
LineFigure lineFigure1 = new LineFigure();
LineFigure lineFigure2 = lineFigure1;
lineFigure1.draw(g);
…code for changing lineFigure1
lineFigure2.draw(g);
如果想让LineFigure2成为一个新的对象,就要用到Clone( )方法。Clone的后对象和原对象的存储空间不同,改变其中一个并不会对另一个产生任何影响。例如下面代码所示:
…………….
LineFigure2 = lineFigure1.clone();
…………….
如果LineFigure2中没有对其他对象的引用,事情就到此结束了,但LineFigure类中的实例变量StartPoint和EndPoint都是一个对象,即使简单地实现了对LineFigure1地Clone,可LineFigure1和LineFigure2的StartPoint和EndPoint实例变量引用的是同一个变量,要想完全的实现将LineFigure2和LineFigure1分离开,就要实现Cloneable接口,并重写Clone方法。Cloneable接口是Java提供的少数几个标签化接口之一。平常所说的标签化接口就是不提供任何方法的接口。在图形白板程序中,也用到了Cloneable接口,例如LineFigure类中的代码:
public class LineFigure extends Figure implements Cloneable{
public Object clone(){
LineFigure lineFigure = new LineFigure();
lineFigure.startPoint = (Point)startPoint.clone();
lineFigure.endPoint = (Point)endPoint.clone();
lineFigure.firstDraw = firstDraw;
return lineFigure;
}
………………..
}

评分

参与人数 1技术分 +2 收起 理由
admin + 2

查看全部评分

回复 使用道具 举报
当需要修改对象属性,又不想影响原来的属性值,这时候就应该使用clone了,前提是
该类必须实现Cloneable接口,实现接口是用来说明你的对象是允许复制的,如果不实现该接口
当调用clone()方法时,则会出现CloneNotSupportedException异常。

评分

参与人数 1技术分 +1 收起 理由
admin + 1

查看全部评分

回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马