专栏首页Vi的技术博客Java基础系列(二十六):clone

Java基础系列(二十六):clone

Why Clone?

要想了解克隆的含义,我们首先来回想一下为一个包含对象引用的变量建立副本时会发生什么。原变量和副本都是同一个对象的引用。这说明,任何一个变量改变都会影响另一个变量。

Employee original = new Employee("Pony Ma",50000);
Employee copy = original;
copy.raiseSalary(10);   //这里original也会发生改变。

如果我们希望copy是一个全新的对象,它的初始状态和original相同,但是又不会相互影响。这时我们就可以使用clone方法来达到这种效果。

Employee copy = original.clone();
copy.raiseSalary(10);   //这时original就不会发生改变。

Shallow Clone & Deep Clone

clone方法是Object类的一个protected的方法,这说明你的方法并不能直接去调用这个方法。那我们应该如何去应用这个方法呢?Object源码中告诉了我们方法:

  1. 实现Cloneable接口
  2. 重写clone方法,并指定public修饰符。

为什么我们一定要去实现Cloneable接口,而不是直接去重写这个方法呢?我们通过源码可以发现这是一个空的接口,clone是从Object类继承的。这个接口只是作为一个标记,指示类设计者了解克隆继承。对象对于克隆也很"偏执",如果一个对象请求克隆,但没有实现这个接口,就会生成一个异常。

在Java中,Cloneable这样的接口叫做标记接口,标记接口不包括任何方法,它的唯一作用就是允许在类型查询的时候使用instanceof:

if (obj instanceof Cloneable){
    //TODO
}

Object类是如何实现clone呢?它对这个对象一无所知,所以只能逐个域的进行拷贝。如果对象中的所有数据域都是数值或其他基本类型,拷贝这些域没有任何问题,但是如果对象中包含子对象的引用,拷贝域就会得到相同子对象的另一个引用,这样一来,原对象和克隆对象仍然会去共享一些信息。这种Object类默认实现的clone方法称为浅拷贝(Shallow Clone)。

浅拷贝会有什么影响么?这要看具体情况,如果原对象和浅克隆对象共享的子对象是不可变的,那么这种共享就是安全的。如果子对象属于一个不可变的类,如String,就是这种情况。或者在对象的生命期中,子对象一直包含不变的常量 ,没有更改器方法会改变它,也没有方法会生成它的引用,这种情况同样是安全的。

不过子类对象通常是可变的,这时我们就需要定义深拷贝(Deep Clone),来克隆这个类的所有子对象。

具体实现方法如下:

public Employee clone() throws CloneNotSupportedException{
    //拷贝该对象
    Employee cloned = (Employee)super.clone();
    //拷贝该对象中的可变域
    cloned.hireDay = (Date) hireDay.clone();
    return cloned;
}

虽然我们已经学习了clone的两种用法,但是在实际的编码中还是尽量少用这个方法,它具有天生的不稳定性,仅仅了解即可。即使是Java的标准库中也只有5%的类实现了这个方法。

我们会在后面使用Java的对象串行化特性来实现克隆对象,虽然效率不高,但是很安全,而且很容易实现。

本文分享自微信公众号 - Vi的技术博客(viyoungblog),作者:ViYoung

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-08-24

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 「每日五分钟,玩转JVM」:对象哪里来

    众所周知,Java是一门面向对象的高级编程语言,那么现在问题来了,对象从哪来呢?有些人会说通过new关键字来创建一个对象,说的很好,本篇我们就来解密在new一个...

    Vi的技术博客
  • Spring Boot 2.x(九):遇到跨域不用慌

    只要协议,子域名,主域名,端口号这四项组成部分中有一项不同,就可以认为是不同的域,不同的域之间互相访问资源,就被称之为跨域。

    Vi的技术博客
  • 「每日五分钟,玩转JVM」:两种算法

    上篇文章,我们了解了GC 的相关概念,这篇文章我们通过两个算法来了解如何去确定堆中的对象实例哪些是我们需要去回收的垃圾对象。

    Vi的技术博客
  • 一张图搞定Java原型模式

    Tanyboye
  • 【前端知乎系列】ArrayBuffer 和 Blob 对象

    ArrayBuffer 对象与 Blob 对象大家或许不太陌生,常见于文件上传操作处理(如处理图片上传预览等问题)。

    pingan8787
  • 《C++ Primer》学习笔记:迭代器介绍

    《C++Primer》(第五版)中,3.4.1的例题中使用一个名为text的字符串向量存放文本文件中的数据,输出text中的内容,刚开始我这样写: #inclu...

    希希里之海
  • 【前端知乎】443- ArrayBuffer 与 Blob 对象详解

    ArrayBuffer 对象是 ES6 才纳入正式 ECMAScript 规范,是 JavaScript 操作二进制数据的一个接口。ArrayBuffer 对象...

    pingan8787
  • TIOBE 7 月编程语言榜:TypeScript 进入前 50 名

    TIOBE 7 月编程语言指数排行榜已经公布了,本月主角还是 TypeScript。

    用户1272076
  • Java--反射机制

    SuperHeroes
  • vue .sync修饰符的使用

    在有些情况下,我们可能需要对一个 prop 进行“双向绑定”。不幸的是,真正的双向绑定会带来维护上的问题,因为子组件可以修改父组件,且在父组件和子组件都没有明显...

    吴裕超

扫码关注云+社区

领取腾讯云代金券