为什么需要克隆

     在实际编程过程中,我们常常要遇到这种情况:有一个对象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. python2解决中文问题(无论是注释还是代码含有中文)

    interpreter:翻译器; 方式一:在第一行写上#coding=utf-8 方式二:在第一行写上#-*- coding:utf-8 -*-

  2. Java 定时任务 & 任务调度

    任务调度是指基于 给定时间点,给定时间间隔 或者 给定执行次数 自动执行任务. 方式1:通过 Thread 来实现 例如如下的代码,可以每隔 1000 毫秒做一次打印操作. public class ...

  3. iOS:基于Socket的第三方框架CocoaAsyncSocket的使用

    CocoaAsyncSocket无疑是目前封装得最完善的Socket库了:支持异步TCP/UDP,支持GCD,Objective-C接口封装,同时还有日志跟踪功能,使用此日志跟踪,程序员可以很方便的进 ...

  4. JS夯实基础:Javascript 变态题解析 (上)

    ["].map(parseInt) 1.知识点: Array/map Number/parseInt Global_Objects/parseInt JavaScript parseInt ...

  5. 【笔记】js 关于定时器的理解

    总所周知 js 里面的 setTimeout() 方法是用来设定某些功能在某段时间间隔之后执行的.但是今天看了高程对定时器的描述发现并不是这样. setTimeout(function(){ //.c ...

  6. 不输入sudo使用docker

    系统是debian系 安装: sudo apt install docker.io 将当前用户加入‘docker’组: sudo gpasswd -a ${USER} docker  刷新权限: su ...

  7. chrome浏览器 提示Adobe Flash Player未安装的解决方法

    最近遇到了个flash player设置的一个问题,记录一下,可能不同浏览器版本和设置不一样 浏览器版本:版本 61.0.3163.100(正式版本) (64 位) 打开需要flash player的 ...

  8. 解决Windows下,运行python工程下的模块找不的到问题

    1.首先在Windows下配置环境变量 找到python安装的目录,我是装在C:\Python27,将改路径添加到环境变量中 添加成功了,你可以在任意行下面执行 python 程序 当你在执行工程的时 ...

  9. Unity3D优化之合并网格

    原文地址点击这里

  10. Android Exception 7(attempt to re-open an already-closed object )

    07-23 18:16:17.641: W/SQLiteConnectionPool(28390): A SQLiteConnection object for database '/storage/ ...