由Object类定义的绝大部分方法在本书其他部分讨论。而一个特别值得关注的方法是clone( )。clone( )方法创建调用它的对象的一个复制副本。只有那些实现Cloneable接口的类能被复制。
  
  Cloneable接口没有定义成员。它通常用于指明被创建的一个允许对对象进行位复制(也就是对象副本)的类。如果试图用一个不支持Cloneable接口的类调用clone(
)方法,将引发一个CloneNotSupportedExcepti

on异常。当一个副本被创建时,并没有调用被复制对象的构造函数。副本仅仅是原对象的一个简单精确的拷贝。

  
  复制是一个具有潜在危险的操作,因为它可能引起不是你所期望的副作用。例如,假如被复制的对象包含了一个称为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。

浅复制和深复制来说一下,首先需要了解对象深、浅复制的概念:

深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底。

此处,写一个深浅复制的例子:

public class Prototype implements Cloneable, Serializable {

  • private static final long serialVersionUID = 1L;
  • private String string;
  • private SerializableObject obj;
  • /* 浅复制 */
  • public Object clone() throws CloneNotSupportedException {
  • Prototype proto = (Prototype) super.clone();
  • return proto;
  • }
  • /* 深复制 */
  • public Object deepClone() throws IOException, ClassNotFoundException {
  • /* 写入当前对象的二进制流 */
  • ByteArrayOutputStream bos = new ByteArrayOutputStream();
  • ObjectOutputStream oos = new ObjectOutputStream(bos);
  • oos.writeObject(this);
  • /* 读出二进制流产生的新对象 */
  • ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
  • ObjectInputStream ois = new ObjectInputStream(bis);
  • return ois.readObject();
  • }
  • public String getString() {
  • return string;
  • }
  • public void setString(String string) {
  • this.string = string;
  • }
  • public SerializableObject getObj() {
  • return obj;
  • }
  • public void setObj(SerializableObject obj) {
  • this.obj = obj;
  • }
  • }
  • class SerializableObject implements Serializable {
  • private static final long serialVersionUID = 1L;
  • }
 
要实现深复制,需要采用流的形式读入当前对象的二进制输入,再写出二进制数据对应的对象。

使用clone( )和Cloneable接口的更多相关文章

  1. Cloneable接口和Object的clone()方法

    为什么要克隆 为什么要使用克隆,这其实反映的是一个很现实的问题,假如我们有一个对象: public class SimpleObject implements Cloneable { private ...

  2. Java对象克隆(Clone)及Cloneable接口、Serializable接口的深入探讨

    Java对象克隆(Clone)及Cloneable接口.Serializable接口的深入探讨 Part I 没啥好说的,直接开始Part II吧. Part II 谈到了对象的克隆,就不得不说为什么 ...

  3. (转)Java对象克隆(Clone)及Cloneable接口、Serializable接口的深入探讨

    原文地址:http://blog.csdn.net/kenthong/article/details/5758884 Part I 没啥好说的,直接开始Part II吧. Part II 谈到了对象的 ...

  4. java开发——Cloneable接口、clone()方法和深浅拷贝

    1.实现Cloneable接口表明该类的对象是允许克隆的. 2.允许克隆的意思是:可以调用clone()方法. 3.深拷贝还是浅拷贝,取决于如何重写Object的clone()方法. 4.原对象和克隆 ...

  5. Java 深拷贝、浅拷贝及Cloneable接口

    Cloneable接口是一个空接口,仅用于标记对象,Cloneable接口里面是没有clone()方法,的clone()方法是Object类里面的方法!默认实现是一个Native方法 protecte ...

  6. java 的对象拷贝(有深浅拷贝两种方式,深拷贝实现的两种方式(逐层实现cloneable接口,序列化的方式来实现))

    Java提高篇--对象克隆(复制)(转自:http://www.cnblogs.com/Qian123/p/5710533.html#_label0)   阅读目录 为什么要克隆? 如何实现克隆 浅克 ...

  7. java对象 深度克隆(不实现Cloneable接口)和浅度克隆

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt128 为什么需要克隆: 在实际编程过程中,我们常常要遇到这种情况:有一个对象 ...

  8. Java中的Cloneable接口理解

    Cloneable接口是一个标记接口,也就是没有任何内容,定义如下: 这里分析一下这个接口的用法,clone方法是在Object种定义的,而且是protected型的,只有实现了这个接口,才可以在该类 ...

  9. java Comparable 和 Cloneable接口

    Comparable接口定义了compareTo方法,用于比较对象. 例如,在JavaAPI中,Integer.BigInteger.String以及Date类定义如下 Cloneable接口 Clo ...

随机推荐

  1. New Valid Tracking Metric Now Available in Seller Central

    On July 7, Amazon added Valid Tracking Rate as a new metric in Seller Central. This metric shows the ...

  2. 再说Play!framework http://hsfgo.iteye.com/blog/806974

    这篇帖子的内容我本来想发到 http://www.iteye.com/topic/806660这里的主贴里去的,想挽回被隐藏的命运,但我写完本贴的内容,却发现为时已晚.好吧,我承认,上一个贴的标题容易 ...

  3. css超出2行部分省略号...

    今天做东西,遇到了这个问题,百度后总结得到了这个结果. 首先,要知道css的三条属性. overflow:hidden; //超出的文本隐藏 text-overflow:ellipsis; //溢出用 ...

  4. 养只爬虫当宠物(Node.js爬虫爬取58同城租房信息)

    先上一个源代码吧. https://github.com/answershuto/Rental 欢迎指导交流. 效果图 搭建Node.js环境及启动服务 安装node以及npm,用express模块启 ...

  5. Android Activity/Service/Broadcaster三大组件之间互相调用

    我们研究两个问题,1.Service如何通过Broadcaster更改activity的一个TextView.(研究这个问题,考虑到Service从服务器端获得消息之后,将msg返回给activity ...

  6. C# 线程基础

    1. 线程的基本概念 简单的讲进程就是程序分配在内存当中,等待处理器进行处理,请记住线程会消耗大量的操作系统资源.多个线程共享一个物理处理器将导致处理器忙于处理管理这些进程,而无法运行程序. 使用线程 ...

  7. 终端环境之tmux

    今天继续介绍我的终端环境,tmux. why tmux? 用一个工具的第一问自然还是为什么要用.其实当时使用tmux的原因很简单.工作中经常需要长时间的编译.总想要下班后要关机的情况下,(肯定有人问我 ...

  8. java多线程系列2-线程控制

    前面的文章已经介绍了有关线程的调度,接下来介绍如何使用方法对线程进行控制 1.线程休眠 public static void sleep(long millis) /* * 线程休眠 * public ...

  9. 【读书笔记】iOS-对象初始化

    一,分配对象. 分配是一个样的对象诞生的过程.最美好的时刻,是从操作系统获得一块内存并将其指定为存放对象的实例变量的位置.向某个类发送alloc消息的结果,就是为该类分配一块足够大的内存,以存放该内的 ...

  10. 用nginx-gridFS读取MongoDB的图片及文件(为什么你老是配不成功?)

    最近在部署公司服务器的nginx + mongodb + gridfs环境: 搜索了N多文档,基本上都一样,期间遇到很多问题: 下面是整理的一份搭建文档: 摘要 nginx-gridfs是一个ngin ...