一、何为克隆

在Java的体系中,数据类型分为基本数据类型引用数据类型

基本数据类型包括byte,short,int,long,float,double,boolean,char 8种,其克隆可通过赋值运算实现,比如

  1. int a = 1;
  2. int b = a;

引用类型的克隆的实现方式有以下两种:

1)实现Cloneable接口,重写clone() 方法,修改clone() 方法的修饰符为public。其分为浅克隆深克隆

2)  实现Serializable接口,对实例进行序列化,通过二进制流反序列化。其为真正的克隆

注:若类没实现Cloneable接口,调用对象的clone方法会抛出CloneNotSupportedException异常

二、重写clone实现克隆

克隆就是获得当前对象的副本,其与当前对象是内存地址不同的两个对象。

浅克隆指的是当前对象的实例成员为引用类型时,副本的该实例成员只是复制了其引用值,指向同一个对象。

深克隆则是对该引用指向的对象进行克隆,然后引用指向了该副本,指向不同的对象。

浅克隆导致了一个问题就是,对实例或克隆副本做出的改变会影响彼此,从而导致错误的结果,下面的例子可以说明:

汽车类 Car,包含一个引用类型成员brand:

  1. /**
  2. * Car类
  3. * 实现Cloneable接口,重写clone方法
  4. * @author zhangyj
  5. *
  6. */
  7. public class Car implements Cloneable{
  8. //使用年限
  9. private int year;
  10. //品牌
  11. private Brand brand;
  12.  
  13. public int getYear() {
  14. return year;
  15. }
  16. public void setYear(int year) {
  17. this.year = year;
  18. }
  19. public Brand getBrand() {
  20. return brand;
  21. }
  22. public void setBrand(Brand brand) {
  23. this.brand = brand;
  24. }
  25.  
  26. //构造器
  27. public Car(int year, Brand brand) {
  28. this.year = year;
  29. this.brand = brand;
  30. }
  31.  
  32. /**
  33. * 浅克隆,调用父类的clone方法
  34. */
  35. @Override
  36. public Object clone() {
  37. Object obj = null;
  38. try {
  39. obj = super.clone();
  40. } catch (CloneNotSupportedException e) {
  41. e.printStackTrace();
  42. }
  43. return obj;
  44. }
  45.  
  46. /**
  47. * 深克隆,其引用类型成员也要调用clone
  48. * @return
  49. */
  50. public Car deepClone() {
  51. Car car = null;
  52. try {
  53. car = (Car)super.clone();
  54. car.setBrand((Brand)brand.clone());
  55. } catch (CloneNotSupportedException e) {
  56. e.printStackTrace();
  57. }
  58. return car;
  59. }
  60.  
  61. @Override
  62. public String toString() {
  63. return "[year=" + year + ", brand=" + brand + "]";
  64. }
  65. }
  66. /**
  67. * 品牌类
  68. * @author zhangyj
  69. *
  70. */
  71. class Brand implements Cloneable{
  72. //名称
  73. private String name;
  74.  
  75. public String getName() {
  76. return name;
  77. }
  78.  
  79. public void setName(String name) {
  80. this.name = name;
  81. }
  82.  
  83. @Override
  84. protected Object clone() {
  85. Object obj = null;
  86. try {
  87. obj = super.clone();
  88. } catch (CloneNotSupportedException e) {
  89. e.printStackTrace();
  90. }
  91. return obj;
  92. }
  93.  
  94. public Brand(String name) {
  95. this.name = name;
  96. }
  97.  
  98. @Override
  99. public String toString() {
  100. return "Brand [name=" + name + "]";
  101. }
  102. }

测试类 CarCloneTest :

  1. public class CarCloneTest {
  2. public static void main(String[] args) {
  3. Brand brand = new Brand("BMW");
  4. Car car = new Car(5, brand);
  5. Car carClone;
  6. carClone = (Car) car.clone(); //(1)
  7. //carClone = (Car) car.deepClone(); //(2)
  8.  
  9. System.out.println("******************************************");
  10. System.out.println("car "+car);
  11. System.out.println("carClone "+carClone);
  12.  
  13. System.out.println("******************************************");
  14. System.out.println("将car年限改为 7,品牌名称改为Banz");
  15. brand.setName("Banz");
  16. car.setYear(7);
  17.  
  18. System.out.println("******************************************");
  19. System.out.println("car "+car);
  20. System.out.println("carClone "+carClone);
  21. }
  22. }

浅克隆测试,运行(1)代码,得到结果如下:

  1. ******************************************
  2. car [year=5, brand=Brand [name=BMW]]
  3. carClone [year=5, brand=Brand [name=BMW]]
  4. ******************************************
    car年限改为 7,品牌名称改为Banz
  5. ******************************************
  6. car [year=7, brand=Brand [name=Banz]]
  7. carClone [year=5, brand=Brand [name=Banz]]

可见当修改car中brand对象的名称为Banz时,carClone也跟着改变,可见其引用的brand对象为同一个,显然这不是我们想要的,而深克隆就解决了这个问题。

深克隆测试,运行(2)代码,得到结果如下:

  1. ******************************************
  2. car [year=5, brand=Brand [name=BMW]]
  3. carClone [year=5, brand=Brand [name=BMW]]
  4. ******************************************
    car年限改为 7,品牌名称改为Banz
  5. ******************************************
  6. car [year=7, brand=Brand [name=Banz]]
  7. carClone [year=5, brand=Brand [name=BMW]]

三、序列化实现深克隆

序列化对象必须实现Serializable接口,使对象可序列化。

  1. public class Car implements Serializable{
  2. private static final long serialVersionUID = 7883197573658810857L;
  3. private int year; //使用年限
  4. private Brand brand; //品牌
  5.  
  6. public int getYear() {
  7. return year;
  8. }
  9. public void setYear(int year) {
  10. this.year = year;
  11. }
  12. public Brand getBrand() {
  13. return brand;
  14. }
  15. public void setBrand(Brand brand) {
  16. this.brand = brand;
  17. }
  18.  
  19. public Car(int year, Brand brand) {
  20. this.year = year;
  21. this.brand = brand;
  22. }
  23.  
  24. @Override
  25. public String toString() {
  26. return "[year=" + year + ", brand=" + brand + "]";
  27. }
  28. }
  29. class Brand implements Serializable{
  30. private static final long serialVersionUID = -6505899377489945908L;
  31. private String name;
  32.  
  33. public String getName() {
  34. return name;
  35. }
  36.  
  37. public void setName(String name) {
  38. this.name = name;
  39. }
  40.  
  41. public Brand(String name) {
  42. this.name = name;
  43. }
  44.  
  45. @Override
  46. public String toString() {
  47. return "Brand [name=" + name + "]";
  48. }
  49. }

克隆工具类 CloneUtil:

  1. public final class CloneUtil {
  2. private CloneUtil() { throw new AssertionError(); }
  3.  
  4. /**
  5. * 序列化实现深克隆
  6. * @param t 泛型对象
  7. * @return
  8. */
  9. @SuppressWarnings("unchecked")
  10. public static <T> T deepClone(T t) {
  11. ByteArrayOutputStream out = new ByteArrayOutputStream();
  12. ObjectOutputStream objOut;
  13. ObjectInputStream objIn;
  14. T tClone = null ;
  15. try {
  16. objOut = new ObjectOutputStream(out);
  17. objOut.writeObject(t); //将对象以二进制流形式写入
  18. ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
  19. objIn = new ObjectInputStream(in);
  20. tClone = (T)objIn.readObject(); //反序列化读取对象
  21. } catch (ClassNotFoundException e) {
  22. e.printStackTrace();
  23. } catch (IOException e) {
  24. e.printStackTrace();
  25. }
  26. return tClone;
  27. }
  28. }

测试类 CarCloneTest:

  1. public class CarCloneTest {
  2. public static void main(String[] args) {
  3. Brand brand = new Brand("BMW");
  4. Car car = new Car(5, brand);
  5. Car carClone;
  6. // carClone = (Car) car.clone(); //(1)
  7. // carClone = (Car) car.deepClone(); //(2)
  8. carClone = CloneUtil.deepClone(car);
  9.  
  10. System.out.println("******************************************");
  11. System.out.println("car "+car);
  12. System.out.println("carClone "+carClone);
  13.  
  14. System.out.println("******************************************");
  15. System.out.println("将car年限改为 7,品牌名称改为Banz");
  16. brand.setName("Banz");
  17. car.setYear(7);
  18.  
  19. System.out.println("******************************************");
  20. System.out.println("car "+car);
  21. System.out.println("carClone "+carClone);
  22. }
  23. }

运行测试程序,得到结果如下:

  1. ******************************************
  2. car [year=5, brand=Brand [name=BMW]]
  3. carClone [year=5, brand=Brand [name=BMW]]
  4. ******************************************
    car年限改为 7,品牌名称改为Banz
  1. ******************************************
    car [year=7, brand=Brand [name=Banz]] carClone [year=5, brand=Brand [name=BMW]]

可见通过反序列化对象进行克隆也能得到我们想要的结果。

以上!

【Java一看就懂】浅克隆和深克隆的更多相关文章

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

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

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

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

  3. Java的浅克隆与深克隆

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

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

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

  5. 保姆级别的RabbitMQ教程!一看就懂!(有安装教程,送安装需要的依赖包,送Java、Golang两种客户端教学Case)

    保姆级别的RabbitMQ教程!一看就懂!(有安装教程,送安装需要的依赖包,送Java.Golang两种客户端教学Case)   目录 什么是AMQP 和 JMS? 常见的MQ产品 安装RabbitM ...

  6. c#:浅克隆和深克隆,序列化和反序列化

    一.浅克隆和深克隆(浅复制和深复制)浅克隆和深克隆最典型的应用是数据集对象DataSet的Clone和Copy方法.Clone()方法用来复制DataSet的结构,但是不复制DataSet的数据,实现 ...

  7. 一看就懂的Android APP开发入门教程

    一看就懂的Android APP开发入门教程 作者: 字体:[增加 减小] 类型:转载   这篇文章主要介绍了Android APP开发入门教程,从SDK下载.开发环境搭建.代码编写.APP打包等步骤 ...

  8. mysql取出现在的时间戳和时间时间戳转成人类看得懂的时间

    mysql取出现在的时间戳和时间时间戳转成人类看得懂的时间,我们在mysql里面他封装了一个内置的时间戳转化的函数,比如我们现在的时间戳是:1458536709 ,"%Y-%m-%d&quo ...

  9. 一看就懂的ReactJs入门教程(精华版)

    一看就懂的ReactJs入门教程(精华版) 现在最热门的前端框架有AngularJS.React.Bootstrap等.自从接触了ReactJS,ReactJs的虚拟DOM(Virtual DOM)和 ...

随机推荐

  1. MacOS下SVN迁移Git踩坑记

    1. First Blood 之前在Windows环境下进行svn到git的迁移是很简单的,(参考官方文档:https://git-scm.com/book/zh/v1/Git-%E4%B8%8E%E ...

  2. jstree树形菜单

    final 用于声明属性.方法和类,分别表示属性不可变,方法不可重写,类不可继承.其实可以参考用easyui的tree 和 ztree参考: https://www.jstree.com/demo/ ...

  3. H3C无线路由器安装与设置

    一.电脑与路由器的连接利用一根cat5e网线一头连接到电脑上笔记本或台式机都可以,另一头连接到无线路由器的LAN口任意LAN口都可以二.设置无线路由器完成路由器安装与电脑连接后,接下首次使用就需要设置 ...

  4. JUnit4测试出错(一)

    log4j:WARN No appenders could be found for logger (org.springframework.test.context.junit4.SpringJUn ...

  5. R语言︱排序问题

    每每以为攀得众山小,可.每每又切实来到起点,大牛们,缓缓脚步来俺笔记葩分享一下吧,please~ --------------------------- 数据排序 1.sort(),rank(),or ...

  6. SMBus

    SMBus (System Management Bus,系统管理总线) 是1995年由Intel提出的,应用于移动PC和桌面PC系统中的低速率通讯.希望通过一条廉价并且功能强大的总线(由两条线组成) ...

  7. javaWeb学习之Listener监听

    ] 一.监听器Listener javaEE包括13门规范 在课程中主要学习 servlet技术 和 jsp技术 其中 servlet规范包括三个技术点:servlet  listener  filt ...

  8. java实现在线支付

    国内电子商务系统实现的基本流程如下: 客户在系统内下订单 -> 系统根据订单生成支付宝接口url -> 客户通过url使用支付宝(网上银行)付款 -> 支付宝将客户的付款完成信息发送 ...

  9. STL(set_pair)运用 CF#Pi D. One-Dimensional Battle Ships

    D. One-Dimensional Battle Ships time limit per test 1 second memory limit per test 256 megabytes inp ...

  10. 觉得OpenStack的网络复杂?其实你家里就有同样一个网络

    当你想了解OpenStack的Neutron网络,打开下面这张图的时候,心里一定是崩溃的,看起来这些模块连在一起很复杂,但其实和你家里的网络很像,看不出来?看我来慢慢解析. 其实这个网络的样子更像是我 ...