为什么需要克隆

     在实际编程过程中,我们常常要遇到这种情况:有一个对象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. 解决Hue/hiveserver2报错:java.io.IOException: Job status not available

    sql是:select count(distinct col) from db.table; 排查过程中遇到过几个不同的报错: 1. beeline -u jdbc:hive2://0.0.0.0:1 ...

  2. Linux中文乱码问题终极解决方法

    方法一: 修改/root/.bash_profile文件,增加export LANG=zh_CN.GB18030该文件在用户目录下,对于其他用户,也必须相应修改该文件. 使用该方法时putty能显示中 ...

  3. 如何监控Oracle

    介绍了DBA每天在监控Oracle数据库方面的职责,讲述了如何通过shell脚本来完成这些重复的监控工作.本文首先回顾了一些DBA常用的Unix命令,以及解释了如何通过Unix Cron来定时执行DB ...

  4. JVM调优思路

    一.jvm内存调优 (Gc  和 Full gc) hotspot -Xms40m  最小堆内存 -Xmx512m 最大值内存 -verboose:gc -XX:PrintGCDetails -XX: ...

  5. DIV强制不换行

    一.单个DIV:1.用nobr元素 <html> <head> </head> <body> <div><nobr>不换行不换行 ...

  6. SecureRandom产生强随机数简介

    SecureRandom是强随机数生成器,主要应用的场景为:用于安全目的的数据数,例如生成秘钥或者会话标示(session ID),弱随机数生成器会产生严重的安全问题,而使用SecureRandom这 ...

  7. solr 自聚类实现

    参考官网:https://lucene.apache.org/solr/guide/6_6/result-clustering.html 最近用到solr自聚类的,先简单介绍如下: 1.配置文件 主要 ...

  8. 【云计算】IBM开放云架构

    IBM 的开放云架构 通过改变业务和社会运行的方式,云计算开启了丰富的创新途径.开发人员现在正将记录系统与参与性系统相结合,一种新的基于云的应用程序风格正在出现:交互系统.这些应用程序要可持续发展,云 ...

  9. poj 2236 Wireless Network 【并查集】

    Wireless Network Time Limit: 10000MS   Memory Limit: 65536K Total Submissions: 16832   Accepted: 706 ...

  10. C# 5 in a Nutshell - Delegate

    1. What is delegate in C#? A delegate is an object that knows how to call a method.A delegate type d ...