java 中的 clone()

1、为何引入

  JAVA缺少拷贝构造函数,当需要通过实例构造实例的情况下就需要clone()。常见的设计模式Prototype,是需要clone的常见例子。

2、基本语法

  只要类扩展了java.lang.Cloneable接口,就默认具备了clone功能,直接调用colne(),就可以实现实例的拷贝。该接口只是一个标志,用来告诉编译器,该类需要实现clone,于是编译器就照做。

3、函数原形(在Object类定义)

  1. protected native Object clone() throws CloneNotSupportedException;

  注意,如果不实现Cloneable,无论类如何定义,即使重载了clone函数依然会有CloneNotSupportedException,就像第二条所述,因为你没有告诉编译器;记住照做就好。

4、使用范围

  还记得定义中的protected?正常情况下我们只能类本身或它的子类来调用clone()方法;除非我们重载clone()的时候修改了他的protected定义。但我感觉如果修改他的修饰符不如保持它的好,因为这样对结构的封装有影响。如果别的类需要使用的话,还是写一个函数来间接调用的好。

5、默认clone()的效果


  默认的效果,其实可以理解成是对对象所占内存镜像的完全拷贝;这一点也从native的定义可见一斑,他的实现很可能与下面描述类似
  1. target=malloc(sizeof(src));
  2. memcpy(src,target,sizeof(src))
  由于JAVA处理所有的类实例是通过引用完成的;所以,对实例而言实际的效果就是:新拷贝出的实例与原有实例分别拥有一个reference,两个reference指向同一个实例。

6、改变clone()效果


  前面5条说的拷贝,通常叫做“浅拷贝”、“影子拷贝”。与之相对有“深拷贝”,所要达到的目的就是:不是通过reference指向同一个实例来达到“状态一致”;二是两个reference分别指向不同的实例,而这两个不同的实例具有“相同的状态”。听上去有些拗口,其实这个定义或许就是递归的定义吧。

  定义是递归的,那么我们程序也应当对应的是递归的,那么需要我们深cpy的话,就递归的调用里面各个实例的clone()吧。不过,还记得上面的 CloneNotSupportedException吗?如果使用的类没有进行相应声明我们的递归就终止于异常了。于是,如果要使用深度cpy,还是写一个try吧,切记!

7、不要忽视对static变量的影响


  effective c++中曾提及,在拷贝构造函数时不要忽视对static变量的影响。一个简单的例子,我们设计了一个简单的变量来记录类当前的实例个数,就一定不要忘记了在拷贝构造函数中counter++的调用。

8、误解


  对第5条,默认clone效果的总结,可以归纳为:基本变量,得到的是一个副本;类的实例得到的是指向同一个实例的不同reference而已,并没有创建副本。

  对于基本变量的数组,以及常用的String,我们认识的往往不清晰。

  对于基本数组,始终记住,我们创建数组时是这样使用的 new int[100],所以依然只是得到一个热ference而已。

  对于String的误解根源于对String API的不理解。我们总是以为,a=b.clone()后,如果a b的修改不会影响另一方就是深度copy了。看一下String API吧,他的任何操作都会新建立一个字符串,而不是在原来字符串上进行修改,于是无论怎样a b之间的String实例都不会相互影响。但并由此认为这就是深拷贝。
  不妨再看回来,两者之间的String实例不会相互影响,那么对于String类型就没有必要这么麻烦,进行深copy了。这其实有点操作系统copyOnWrite的思想。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值