推荐一部好电影《致命魔术》。(此处为植入广告)
推荐理由:涉及人性。画面不错,剧情跌宕,亦魔亦幻(此处的”魔“为魔术的”魔“)。虽然女猪脚不尽如人意,但是男猪脚比较帅。而且看完后有利于理解克隆,当然理解了克隆也利于观影!
首先,简单客观地解释下几个关键的名词(我们约定A表示原来的对象,P表示A引用的对象;AC表示克隆后的A对象):
浅克隆:复制克隆对象的基本信息及其对其他对象的引用。在改变AC对象的P对象时,那么也会改变A对象的P对象。
深克隆:深克隆也会复制对象的基本信息以及其对其他对象的引用,但是,改变AC对象的引用P对象时,不会引起A对象的P对象。
从前面浅克隆的定义上看,改变AC的P就能改变A的P,这样显得这种克隆更加像深克隆(都刨到别人祖坟了,够深的!)。但是,换个角度来看,这种克隆只是浅显的将一个对象拷贝出来了,并没有真正的去对这个对象进行深入地剖析,即没有剥离两者之间的依赖,使得A和AC更像一个对象的不同命名,因此,反而显得浅显了。深克隆的技术含量也较之浅克隆高点。
为了方便理解,我将浅克隆形象化为一对连体双胞胎,而将深克隆形象化为一对同卵双胞胎;或者也可将浅克隆理解为镜像,而深克隆则是复制了一个真正具有独立行为能力的实体。
下面详细对它们进行阐述:
克隆
实现克隆的类都必须实现Cloneable接口,而且一般需要重写Object类里的clone()方法。我们首先看看Object类中对clone()方法的注释与声明:
/**
* Creates and returns a copy of this object. The precise meaning
* of "copy" may depend on the class of the object. The general
* intent is that, for any object {@code x}, the expression:
* <blockquote>
* <pre>
* x.clone() != x</pre></blockquote>
* will be true, and that the expression:
* <blockquote>
* <pre>
* x.clone().getClass() == x.getClass()</pre></blockquote>
* will be {@code true}, but these are not absolute requirements.
* While it is typically the case that:
* <blockquote>
* <pre>
* x.clone().equals(x)</pre></blockquote>
* will be {@code true}, this is not an absolute requirement.
* <p>
* By convention, the returned object should be obtained by calling
* {@code super.clone}. If a class and all of its superclasses (except
* {@code Object}) obey this convention, it will be the case that
* {@code x.clone().getClass() == x.getClass()}.
* <p>
* By convention, the object returned by this method should be independent
* of this object (which is being cloned). To achieve this independence,
* it may be necessary to modify one or more fields of the object returned
* by {@code super.clone} before returning it. Typically, this means
* copying any mutable objects that comprise the internal "deep structure"
* of the object being cloned and replacing the references to these
* objects with references to the copies. If a class contains only
* primitive fields or references to immutable objects, then it is usually
* the case that no fields in the object returned by {@code super.clone}
* need to be modified.
* <p>
* The method {@code clone} for class {@code Object} performs a
* specific cloning operation. First, if the class of this object does
* not implement the interface {@code Cloneable}, then a
* {@code CloneNotSupportedException} is thrown. Note that all arrays
* are considered to implement the interface {@code Cloneable} and that
* the return type of the {@code clone} method of an array type {@code T[]}
* is {@code T[]} where T is any reference or primitive type.
* Otherwise, this method creates a new instance of the class of this
* object and initializes all its fields with exactly the contents of
* the corresponding fields of this object, as if by assignment; the
* contents of the fields are not themselves cloned. Thus, this method
* performs a "shallow copy" of this object, not a "deep copy" operation.
* <p>
* The class {@code Object} does not itself implement the interface
* {@code Cloneable}, so calling the {@code clone} method on an object
* whose class is {@code Object} will result in throwing an
* exception at run time.
*
* @return a clone of this instance.
* @exception CloneNotSupportedException if the object's class does not
* support the {@code Cloneable} interface. Subclasses
* that override the {@code clone} method can also
* throw this exception to indicate that an instance cannot
* be cloned.
* @see java.lang.Cloneable
*/
protected native Object clone() throws CloneNotSupportedException;
虽然过长,但是我觉得还是很有必要看看的。从前面的注释中可以看出:x.clone() != x 但是 x.clone().getClass() == x.getClass() 。这可以看成克隆的精确描述。从x.clone() != x 看,觉得这个镜像也不简单,镜子里面的世界和镜子外面的世界原来也不是同一个,开始有一点魔幻的味道了。注释里还有一句话值得我们关注:Note that all arrays are considered to implement the interface Cloneable and that the return type of the clone method of an array type T[] is T[] where T is any reference or primitive type.所有的数组都实现了Cloneable接口,返回的是一个数组类型。这个大家可以验证一下,反正我验证是有的。这段注释里还有很多地方值得我们去研究(比如提到了深克隆和浅克隆),我都好不容易贴出来了,大家自己去看看吧!
clone()方法会抛出CloneNotSupportedException,这是为什么呢?这是因为Object类没有实现Cloneable接口。身为万物之祖,Object也有很多不会的啊!
浅克隆
要想做到AC的属性和A一样其实并不难,最简单的办法就是AC = A;而且也能保证改变AC的P会引起A的P改变。这样不就可以了吗?为什么还要用克隆呢?你似乎忘了,在克隆里我们讲过,AC和A需满足两个条件:x.clone() != x和x.clone().getClass() == x.getClass()。如果直接AC = A,很明显AC == A返回的是true。至于具体原因就涉及到克隆的作用了,等会的克隆的用途会详细说明。
浅克隆的实现并不难,下面看一个示例:
class Sword{
String name;
float weight;
public Sword(String name, float weight){
this.name = name;
this.weight = weight;
} // end constructor
} // end class Sword
class Hero implements Cloneable{
String name;
int energy; // hero的战斗值
Sword s;
public Hero(String name, int energy, Sword s){
this.name = name;
this.energy = energy;
this.s = s;
} // end constructor
public void kill(){
System.out.println("战斗值为" + energy + "的" + name + "挥动着重为"
+ s.weight + "斤的" + s.name + "要开杀戒了!");
} // end kill
/**
* 重写Object的clone方法。
*/
public Object clone(){
Hero h = null;
try {
h = (Hero)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
} // end try-catch
return h;
} // end clone
} // end class Hero
public class ShallowClone{
/**
* 主函数。
* @param args
*/
public static void main(String[] args) {
// 声明一个Sword对象
Sword s = new Sword("绝世好剑", 58.3f);
// 声明一个Hero
Hero h1 = new Hero("步惊云", 1000, s);
h1.kill();
// 克隆
Hero h2 = (Hero) h1.clone();
// 改变h2的s的一些属性
h2.s.name = "草雉剑";
h2.s.weight = 23.4f;
h1.kill();
if( !(h1 == h2)){
System.out.println("从哲学的角度讲:此时的" +
h1.name + "已经不是从前的" + h1.name + "了!");
}else{
System.out.println("娃哈哈,我" + h1.name + "还是" + h1.name + "!");
} // end if-else
} // end main
} // end class ShallowClone
这段代码的运行结果是什么呢?请看:
战斗值为1000的步惊云挥动着重为58.3斤的绝世好剑要开杀戒了!
战斗值为1000的步惊云挥动着重为23.4斤的草雉剑要开杀戒了!
从哲学的角度讲:此时的步惊云已经不是从前的步惊云了!
是的,正如我们所说的h1的s对象的name和weight也改变了。而且其实现也是很简单。当然对这一块比较熟悉的朋友会非常气愤地指出,这里有一些基本的常识错误:绝世好剑和草雉剑根本就不是这个重量,步惊云也得不到草雉剑!但是,("made in China".equals("everything is possible")) == true(支持国产,再次植入广告!)。好吧,我们回到浅克隆,这里实现浅克隆的代码相当简单,直接super.clone()就可以了。
网上有一种说法,说浅克隆是不正确的克隆。我觉得不管正不正确,当我们要克隆的对象只有基本数据类型和String等属性时,直接浅克隆就可以了。运用之妙,存乎一心!
深克隆
前面讲了,深克隆就是将克隆的对象和原来的对象独立开来。那么怎么实现呢?
在上面的代码上修改了一点:
class Sword implements Cloneable{
String name;
float weight;
public Sword(String name, float weight){
this.name = name;
this.weight = weight;
} // end constructor
public Object clone(){
try {
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
} // end try-catch
return null;
} // end clone
} // end class Sword
class Hero implements Cloneable{
String name;
int energy; // hero的战斗值
Sword s;
public Hero(String name, int energy, Sword s){
this.name = name;
this.energy = energy;
this.s = s;
} // end constructor
public void kill(){
System.out.println("战斗值为" + energy + "的" + name + "挥动着 重为" + s.weight + "斤的" + s.name + "要开杀戒了!");
} // end kill
/**
* 重写Object的clone方法。
*/
public Object clone(){
Hero h = null;
try {
h = (Hero)super.clone();
h.s = (Sword) s.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
} // end try-catch
return h;
} // end clone
} // end class Hero
public class DeepClone{
/**
* 主函数。
* @param args
*/
public static void main(String[] args) {
// 声明一个Sword对象
Sword s = new Sword("绝世好剑", 58.3f);
// 声明一个Hero
Hero h1 = new Hero("步惊云", 1000, s);
h1.kill();
// 克隆
Hero h2 = (Hero) h1.clone();
// 改变h2的s的一些属性
h2.s.name = "草雉剑";
h2.s.weight = 23.4f;
h1.kill();
if(! (h1 == h2)){
System.out.println("从哲学的角度讲:此时的" +
h1.name + "已经不是从前的" + h1.name + "了!");
}else{
System.out.println("娃哈哈,我" + h1.name + "还是" + h1.name + "!");
} // end if-else
} // end main
} // end class DeepClone
认真观察就会发现,代码的变动并不是很大,只是Sword类也实现了Cloneable接口,在Hero中也对hero对象的sword进行了克隆。这样就实现了深克隆。那么这段代码的结果是不是我们希望看到的呢:
战斗值为1000的步惊云挥动着重为58.3斤的绝世好剑要开杀戒了!
战斗值为1000的步惊云挥动着重为58.3斤的绝世好剑要开杀戒了!
从哲学的角度讲:此时的步惊云已经不是从前的步惊云了!
看吧,h1并没有因为克隆后的h2改变了s的name和weight而跟着发生了改变,圆满完成了我们的预期目标。
关于深克隆还有另一种方式:使用Serializable。大家可以去关注一下,这里就不讨论了。
克隆的用途
我们知道了深克隆和浅克隆,那么克隆到底有什么用呢?
答案很简单:有需求就有市场。我们要克隆是因为我们需要一个和已知对象一样的对象(这个我觉得看了《致命魔术》后肯定理解得更深)。当我们需要一个对象的副本但又不想影响原来的对象时,我们可以考虑使用克隆。
个人觉得克隆为程序员提供了对对象更加灵活的操纵力。我觉得大家在理解的基础上然后提出自己的见解就可以了。
总结
最近看《Effective Java》,里面专门提到了:谨慎地覆盖clone。而且里面也提到了用copy constructor(克隆构造器)或者copy factory(克隆工厂)更加地安全。网上有很多解说的,但是我觉得这个版本不错,大家去看看吧:http://www.slideshare.net/fmshaon/effective-java-override-clone-method-judiciously
最后,还有一件事,《致命魔术》真的不错!
晚安!
- 大小: 35.6 KB
分享到:
相关推荐
pandas whl安装包,对应各个python版本和系统(具体看资源名字),找准自己对应的下载即可! 下载后解压出来是已.whl为后缀的安装包,进入终端,直接pip install pandas-xxx.whl即可,非常方便。 再也不用担心pip联网下载网络超时,各种安装不成功的问题。
基于java的大学生兼职信息系统答辩PPT.pptx
基于java的乐校园二手书交易管理系统答辩PPT.pptx
tornado-6.4-cp38-abi3-musllinux_1_1_i686.whl
Android Studio Ladybug 2024.2.1(android-studio-2024.2.1.10-mac.dmg)适用于macOS Intel系统,文件使用360压缩软件分割成两个压缩包,必须一起下载使用: part1: https://download.csdn.net/download/weixin_43800734/89954174 part2: https://download.csdn.net/download/weixin_43800734/89954175
有学生和教师两种角色 登录和注册模块 考场信息模块 考试信息模块 点我收藏 功能 监考安排模块 考场类型模块 系统公告模块 个人中心模块: 1、修改个人信息,可以上传图片 2、我的收藏列表 账号管理模块 服务模块 eclipse或者idea 均可以运行 jdk1.8 apache-maven-3.6 mysql5.7及以上 tomcat 8.0及以上版本
tornado-6.1b2-cp38-cp38-macosx_10_9_x86_64.whl
Android Studio Ladybug 2024.2.1(android-studio-2024.2.1.10-mac.dmg)适用于macOS Intel系统,文件使用360压缩软件分割成两个压缩包,必须一起下载使用: part1: https://download.csdn.net/download/weixin_43800734/89954174 part2: https://download.csdn.net/download/weixin_43800734/89954175
matlab
基于java的毕业生就业信息管理系统答辩PPT.pptx
随着高等教育的普及和毕业设计的日益重要,为了方便教师、学生和管理员进行毕业设计的选题和管理,我们开发了这款基于Web的毕业设计选题系统。 该系统主要包括教师管理、院系管理、学生管理等多个模块。在教师管理模块中,管理员可以新增、删除教师信息,并查看教师的详细资料,方便进行教师资源的分配和管理。院系管理模块则允许管理员对各个院系的信息进行管理和维护,确保信息的准确性和完整性。 学生管理模块是系统的核心之一,它提供了学生选题、任务书管理、开题报告管理、开题成绩管理等功能。学生可以在此模块中进行毕业设计的选题,并上传任务书和开题报告,管理员和教师则可以对学生的报告进行审阅和评分。 此外,系统还具备课题分类管理和课题信息管理功能,方便对毕业设计课题进行分类和归档,提高管理效率。在线留言功能则为学生、教师和管理员提供了一个交流互动的平台,可以就毕业设计相关问题进行讨论和解答。 整个系统设计简洁明了,操作便捷,大大提高了毕业设计的选题和管理效率,为高等教育的发展做出了积极贡献。
这个数据集来自世界卫生组织(WHO),包含了2000年至2015年期间193个国家的预期寿命和相关健康因素的数据。它提供了一个全面的视角,用于分析影响全球人口预期寿命的多种因素。数据集涵盖了从婴儿死亡率、GDP、BMI到免疫接种覆盖率等多个维度,为研究者提供了丰富的信息来探索和预测预期寿命。 该数据集的特点在于其跨国家的比较性,使得研究者能够识别出不同国家之间预期寿命的差异,并分析这些差异背后的原因。数据集包含22个特征列和2938行数据,涉及的变量被分为几个大类:免疫相关因素、死亡因素、经济因素和社会因素。这些数据不仅有助于了解全球健康趋势,还可以辅助制定公共卫生政策和社会福利计划。 数据集的处理包括对缺失值的处理、数据类型转换以及去重等步骤,以确保数据的准确性和可靠性。研究者可以使用这个数据集来探索如教育、健康习惯、生活方式等因素如何影响人们的寿命,以及不同国家的经济发展水平如何与预期寿命相关联。此外,数据集还可以用于预测模型的构建,通过回归分析等统计方法来预测预期寿命。 总的来说,这个数据集是研究全球健康和预期寿命变化的宝贵资源,它不仅提供了历史数据,还为未来的研究和政策制
基于微信小程序的高校毕业论文管理系统小程序答辩PPT.pptx
基于java的超市 Pos 收银管理系统答辩PPT.pptx
基于java的网上报名系统答辩PPT.pptx
基于java的网上书城答辩PPT.pptx
婚恋网站 SSM毕业设计 附带论文 启动教程:https://www.bilibili.com/video/BV1GK1iYyE2B
基于java的戒烟网站答辩PPT.pptx
基于微信小程序的“健康早知道”微信小程序答辩PPT.pptx
Capital Bikeshare 数据集是一个包含从2020年5月到2024年8月的自行车共享使用情况的数据集。这个数据集记录了华盛顿特区Capital Bikeshare项目中自行车的租赁模式,包括了骑行的持续时间、开始和结束日期时间、起始和结束站点、使用的自行车编号、用户类型(注册会员或临时用户)等信息。这些数据可以帮助分析和预测自行车共享系统的需求模式,以及了解用户行为和偏好。 数据集的特点包括: 时间范围:覆盖了四年多的时间,提供了长期的数据观察。 细节丰富:包含了每次骑行的详细信息,如日期、时间、天气条件、季节等,有助于深入分析。 用户分类:数据中区分了注册用户和临时用户,可以分析不同用户群体的使用习惯。 天气和季节因素:包含了天气情况和季节信息,可以研究这些因素对骑行需求的影响。 通过分析这个数据集,可以得出关于自行车共享使用模式的多种见解,比如一天中不同时间段的使用高峰、不同天气条件下的使用差异、季节性变化对骑行需求的影响等。这些信息对于城市规划者、交通管理者以及自行车共享服务提供商来说都是非常宝贵的,可以帮助他们优化服务、提高效率和满足用户需求。同时,这个数据集也