由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. javascript-this,call,apply,bind简述2

    上节我们一起研究了this这个小兄弟,得出一个结论,this指向调用this所在函数(或作用域)的那个对象或作用域.不太理解的朋友可以看看上节的内容,这次我们主要探讨一下call(),apply(), ...

  2. SAP Basis常用事务代码

    事务码 描述(中英文)     SBIT Menu 菜单     SBTA Test background processing 后台处理测试     SBTU Background processi ...

  3. CRM 2013 Reporting Extensions for SSRS 安装及问题解决

    说明一下 Reporting Extensions for SSRS 安装过程. 安装目录在安装目录下 SrsDataConnector 下.如果是CRM 2013安装中运行,可以跳过此步. 此外在说 ...

  4. Objective-C的IO流

                                                                                     

  5. 比较好用的web打印控件——Lodop

    前一段时间公司一项目比较特殊,客户要求打印单必须是淘宝上卖的那种三联打印单.如果还是使用原来系统自带的打印的话,就会造成无法打印出来理想的效果,于是找了下相关的打印控件,比较网络上比较流行的几款插件, ...

  6. SQL 递归查询(根据指定的节点向上获取所有父节点,向下获取所有子节点)

    --------------------01.向上查找所有父节点-----------------WITH TEMP AS (SELECT * FROM CO_Department WHERE ID= ...

  7. ASP.NET 使用Ajax(转)

    之前在Ajax初步理解中介绍了对Ajax的初步理解,本文将介绍在ASP.NET中如何方便使用Ajax,第一种当然是使用jQuery的ajax,功能强大而且操作简单方便,第二种是使用.NET封装好的Sc ...

  8. cocos2d-x之事件传递

    bool HelloWorld::init() { if ( !Layer::init() ) { return false; } Size size=Director::getInstance()- ...

  9. openssl c_rehash

    一.简介 c_rehash 为文件创建一个符号连接,并将此符号连接的名称设为文件的hash值,作用是让openssl在证书目录中能够找到证书. 二.语法 c_rehash [-old] [-h] [- ...

  10. 【转载】chromium浏览器开发系列第一篇:如何获取最新chromium源码

    背景:     最近摊上一个事儿,领导非要让写一篇技术文章,思来想去,自己接触chrome浏览器时间也不短了,干脆就总结一下吧.于是乎,本文顺理成章.由于有些细节必需描述清楚,所以这次先讲如何拿到ch ...