为什么需要克隆

     在实际编程过程中,我们常常要遇到这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能会需要一个和A完全相同新对象B, 并且此后对B任何改动都不会影响到A中的值,也就是说,A与B是两个独立的对象,但B的初始值是由A对象确定的。在Java语言中,用简单的赋值语句是不 能满足这种需求的,要满足这种需求有很多途径。
 
    克隆的实现方式

   
  一、浅度克隆
 
  
 
    浅度克隆对于要克隆的对象,对于其基本数据类型的属性,复制一份给新产生的对象,对于非基本数据类型的属性,仅仅复制一份引用给新产生的对象,即新产生的对象和原始对象中的非基本数据类型的属性都指向的是同一个对象。
   
  浅度克隆步骤:
     
1、实现java.lang.Cloneable接口
     
 
 要clone的类为什么还要实现Cloneable接口呢?Cloneable接口是一个标识接口,不包含任何方法的!这个标识仅仅是针对Object类中clone()方法的,如果clone类没有实现Cloneable接口,并调用了Object的
clone()方法(也就是调用了super.Clone()方法),那么Object的clone()方法就会抛出
CloneNotSupportedException异常。
 
   
 2、重写java.lang.Object.clone()方法
     
 JDK
API的说明文档解释这个方法将返回Object对象的一个拷贝。要说明的有两点:一是拷贝对象返回的是一个新对象,而不是一个引用。二是拷贝对象与用new操作符返回的新对象的区别就是这个拷贝已经包含了一些原来对象的信息,而不是对象的初始信息。
 
 
   
观察一下Object类的clone()方法是一个native方法,native方法的效率一般来说都是远高于java中的非native方法。这也解
释了为什么要用Object中clone()方法而不是先new一个类,然后把原始对象中的信息赋到新对象中,虽然这也实现了clone功能。Object类中的clone()还是一个protected属性的方法,重写之后要把clone()方法的属性设置为public。
 
Object类中clone()方法产生的效果是:先在内存中开辟一块和原始对象一样的空间,然后原样拷贝原始对象中的内容。对基本数据类型,这样
的操作是没有问题的,但对非基本类型变量,我们知道它们保存的仅仅是对象的引用,这也导致clone后的非基本类型变量和原始对象中相应的变量指向的是同
一个对象。
 
Java代码实例:
public class Product implements Cloneable {
  
    private String name;
  
  
    public Object clone() {
  
     
  try {   
     
      return
super.clone();   
     
  } catch (CloneNotSupportedException e) {
  
     
      return
null;   
     
  }   
    }
  
}  
 
 
   二、深度克隆
   
 
   
 在浅度克隆的基础上,对于要克隆的对象中的非基本数据类型的属性对应的类,也实现克隆,这样对于非基本数据类型的属性,复制的不是一份引用,即新产生的对象和原始对象中的非基本数据类型的属性指向的不是同一个对象
 
   
  深度克隆步骤:
 
    要克隆的类和类中所有非基本数据类型的属性对应的类
     
  1、都实现java.lang.Cloneable接口
     
  2、都重写java.lang.Object.clone()方法
 
Java代码实例:
public class Attribute implements Cloneable {
  
    private String no;
  
     
 
    public Object clone() {
  
     
  try {   
     
      return
super.clone();   
     
  } catch (CloneNotSupportedException e) {
  
     
      return
null;   
     
  }   
    }
  
}   
  
public class Product implements Cloneable {
  
    private String name;
  
     
 
    private Attribute attribute;
  
  
    public Object clone() {
  
     
  try {   
     
      return
super.clone();   
     
  } catch (CloneNotSupportedException e) {
  
     
      return
null;   
     
  }   
    }
  
}   
 
 
   
  三、使用对象序列化和反序列化实现深度克隆
     
  所谓对象序列化就是将对象的状态转换成字节流,以后可以通过这些值再生成相同状态的对象。
 
对象的序列化还有另一个容易被大家忽略的功能就是对象复制(Clone),Java中通过Clone机制可以复制大部分的对象,但是众所周知,Clone有深度Clone和浅度Clone,如果你的对象非常非常复杂,并且想实现深层
Clone,如果使用序列化,不会超过10行代码就可以解决。
 
虽然Java的序列化非常简单、强大,但是要用好,还有很多地方需要注意。比如曾经序列化了一个对象,可由于某种原因,该类做了一点点改动,然后重新被编译,那么这时反序列化刚才的对象,将会出现异常。
你可以通过添加serialVersionUID属性来解决这个问题(在Java序列化与反序列化学习(二):序列化接口说明 的标题三中有此说明)。如果你的类是个单例(Singleton)类,虽然我们多线程下的并发问题来控制单例,但是,是否允许用户通过序列化机制或者克隆来复制该类,如果不允许你需要谨慎对待该类的实现(让此类不用实现Serializable接口或Externalizable接口和Cloneable接口就行了)
  
Java代码实例:
public class
Attribute {   
    private String no;
  
}
  
  
public class
Product {   
    private String name;
  
     
 
    private Attribute attribute;
  
  
    public Product clone() {
  
     
  ByteArrayOutputStream byteOut = null;
  
     
  ObjectOutputStream objOut = null;
  
     
  ByteArrayInputStream byteIn = null;
  
     
  ObjectInputStream objIn = null;
  
     
     
     
  try {   
//
将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝
     
      byteOut =
new ByteArrayOutputStream();  
 
     
      objOut =
new ObjectOutputStream(byteOut);  
 
     
     
objOut.writeObject(this);
  
  // 将流序列化成对象
     
      byteIn =
new ByteArrayInputStream(byteOut.toByteArray());
  
     
      objIn =
new ObjectInputStream(byteIn);
  
     
     
   
     
      return
(ContretePrototype) objIn.readObject();
  
     
  } catch (IOException e) {
  
     
      throw new
RuntimeException("Clone Object failed in IO.",e);
     
     
  } catch (ClassNotFoundException e) {
  
     
      throw new
RuntimeException("Class not found.",e);  
   
     
  } finally{
  
     
      try{
  
     
     
    byteIn = null;
  
     
     
    byteOut = null;
  
     
     
    if(objOut != null)
objOut.close();    
 
     
     
    if(objIn != null)
objIn.close();    
 
     
     
}catch(IOException e){    
 
     
      }
     
     
  }   
    }
  
 
#########################注意######################
Bean复制的几种框架中(Apache
BeanUtils、PropertyUtils,Spring BeanUtils,Cglib
BeanCopier)都是相当于克隆中的浅克隆。
1)spring包和Apache中的
BeanUtils   
 采用反射实现
 
  Spring: void
copyProperties(Object source, Object target,String[]
ignoreProperties)
   
Apache:void  copyProperties(Object dest, Object
orig)
2)cglib包中的
 Beancopier   采用动态字节码实现
 
 cglib: BeanCopier
create(Class source, Class target,boolean
useConverter)
 
 例如:
 
     
  BeanCopier copier
=BeanCopier.create(stuSource.getClass(), stuTarget.getClass(),
false);
     
    copier.copy(stuSource,
stuTarget, null);
 
公司内部对用到的bean属性复制做了下性能分析:
cglib   BeanCopier  
15ms
 
Spring  BeanUtil  
   4031ms
 
apache BeanUtils    
 18514ms.
 
原文地址:http://blog.sina.com.cn/s/blog_7ffb8dd501013q5c.html

Java对象的浅克隆和深克隆的更多相关文章

  1. 如何复制一个java对象(浅克隆与深度克隆)

    在项目中,有时候有一些比较重要的对象经常被当作参数传来传去,和C语言的值传递不同,java语言的传递都是引用传递,在任何一个地方修改了这个对象的值,就会导致这个对象在内存中的值被彻底改变.但是很多时候 ...

  2. Java对象的克隆和深浅问题

    Java实现克隆的方式 Java实现克隆的方式有如下两种, 推荐采用实现Cloneable接口的方式 实现Cloneable接口, 重写clone方法, 调用父类的clone方法 还有另一种方法, 不 ...

  3. 【Java一看就懂】浅克隆和深克隆

    一.何为克隆 在Java的体系中,数据类型分为基本数据类型和引用数据类型. 基本数据类型包括byte,short,int,long,float,double,boolean,char 8种,其克隆可通 ...

  4. 深入理解Java的浅克隆与深克隆

    前言 克隆,即复制一个对象,该对象的属性与被复制的对象一致,如果不使用Object类中的clone方法实现克隆,可以自己new出一个对象,并对相应的属性进行数据,这样也能实现克隆的目的. 但当对象属性 ...

  5. Java必备技能:clone浅克隆与深克隆

    介绍 一直以来只知道Java有clone方法,该方法属于Object的,对于什么是浅克隆与深克隆就比较模糊了,现在就来补充学习一下. 概念 浅拷贝(浅克隆)复制出来的对象的所有变量都含有与原来的对象相 ...

  6. Java的浅克隆与深克隆

    前言 克隆,即复制一个对象,该对象的属性与被复制的对象一致,如果不使用Object类中的clone方法实现克隆,可以自己new出一个对象,并对相应的属性进行数据,这样也能实现克隆的目的. 但当对象属性 ...

  7. java浅克隆和深克隆,序列化和反序列化实现深克隆(封装序列化和反序列化操作)

    本篇博客内容: 一.浅克隆(ShallowClone)和深克隆(DeepClone) 二.序列化和反序列化实现深克隆 三.封装序列化和反序列化操作 ObjectOutputStream + 内存流By ...

  8. Java的浅克隆和深克隆

    如何实现对象的克隆 (1)实现 Cloneable 接口并重写 Object 类中的 clone() 方法: (2)实现 Serializable 接口,通过对象的序列化和反序列化实现克隆,可以实现真 ...

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

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

随机推荐

  1. openresty记录响应body乱码问题

    问题背景 最近新上了一个功能,openresty通过syslog记录请求日志,然后由logstash推送至ES.测试上线时未发现这个问题,在日常查看日志的过程中,发现logstash推送有错误日志,错 ...

  2. latex 三个不同的图放在一行且每个图都有注释

    \begin{figure}[htbp] \begin{minipage}[t]{0.3\linewidth} \centering \includegraphics[width=.2.0.eps} ...

  3. nodejs vue SyntaxError:Block-scoped declarations (let,const,function,class) not yet supported

    下午在git上导入之前写的代码到本地环境,执行npm run dev报错. 在我之前的环境中是没有问题的,这个错误信息也没看懂是什么原因引起的, 猜测可能是nodejs版本太低,于是重新下载了一个wi ...

  4. Windows内存管理

    本博文很大程度上参考了,潘爱民先生的<Windows内核原理与实现>一书,在此对他表示感谢. 记得是在学C语言指针的时候,首次比较实际的使用内存寻址.也是在那个时候知道不能使用未初始化的指 ...

  5. 【甘道夫】Win7环境下Eclipse连接Hadoop2.2.0

    准备: 确保hadoop2.2.0集群正常执行 1.eclipse中建立javaproject,导入hadoop2.2.0相关jar包 2.在src根文件夹下拷入log4j.properties,通过 ...

  6. zookeeper伪分布式集群安装

    1.安装3个zookeeper 1.1创建集群安装的目录 1.2配置一个完整的服务 这里不做详细说明,参考我之前写的 zookeeper单节点安装 进行配置即可,此处直接复制之前单节点到集群目录 创建 ...

  7. 解决Xcode 6 编译Cocos2d-x iOS项目失败

    在Xcode 6 beta里编译Cocos2d-x iOS项目时可能会失败,提示如下错误: Undefined symbols for architecture i386: "_fwrite ...

  8. 《Java程序猿面试笔试宝典》之 什么是AOP

    AOP(Aspect-Oriented Programming.面向切面编程)是对面向对象开发的一种补充,它同意开发者在不改变原来模型的基础上动态地改动模型从而满足新的需求.比如.在不改变原来业务逻辑 ...

  9. C 输入一串数字,去掉当中含7的和能被7整除的数

    C 输入一串数字,去掉当中含7的和能被7整除的数,每一个数小于10000,数字个数小于100 输入样例:1,7,56,77,87,2,45,42,97,9977 输出:1,2,45 注意:输入个数不确 ...

  10. windows下安装UNO,配置AEROO_REPORT (Openoffice4已经升级为Python2.7.5版)

    来自:http://shine-it.net/index.php?topic=8019.msg22007 最近单位要上一个OE,但OE7一天一个新更新,不知何年到头. 闲着没事写一点心得,不敢称为教程 ...