本节我们会讨论 Cloneable 接口,这个接口指示一个类提供了一个安全的 clone() 方法。
(相关资料图)
Object 类提供的 clone() 方法是 “浅拷贝”,并没有克隆对象中引用的其他对象,原对象和克隆的对象仍然会共享一些信息。深拷贝指的是:在对象中存在其他对象的引用的情况下,会同时克隆对象中引用的其他对象,原对象和克隆的对象互不影响。
介绍克隆要了解克隆的具体含义,先来回忆为一个包含对象引用的变量建立副本时会发生什么。原变量和副本都是同一个对象的引用(见图 6-1)。这说明,任何一个变量改变都会影响另一个变量。
Employee original = new Employee("John Public", 50000);Employee copy = original;copy.raiseSalary(lO); // oops-also changed original
如果希望 copy 是一个新对象,它的初始状态与 original 相同,但是之后它们各自会有自己不同的状态,这种情况下就可以使用 clone() 方法。
Employee copy = original.clone();copy.raiseSalary(lO); // OK original unchanged
不过并没有这么简单。clone() 方法是 Object 的一个 protected 方法,这说明你的代码不能直接调用这个方法。只有 Employee 类可以克隆 Employee 对象(Object 类不可以克隆 Employee 类)。这个限制是有原因的。想想看 Object 类如何实现 clone()。Object 类它对于这个对象一无所知,所以只能逐个域地进行拷贝。如果对象中的所有数据域都是数值或其他基本类型,拷贝这些域没有任何问题、但是如果对象包含子对象的引用,拷贝域就会得到相同子对象的另一个引用,这样一来,原对象和克隆的对象仍然会共享一些信息。
class Employee { // instance fields private String name; private double salary; private Date hireDay; // constructor public Employee(String n, double s, int year, int month, int day) { this.name = n; this.salary = s; this.hireDay = new Date(year, month, day); } // a method public String getName() { return name; } // more methods}
图 6-2 显示了使用 Object 类的 clone() 方法克隆一个 Employee 对象会发生什么。可以看到,默认的克隆操作是 “浅拷贝”,并没有克隆对象中引用的其他对象。
浅拷贝会有什么影响吗?这要看具体情况。
如果原对象和浅克隆对象共享的子对象是不可变的,那么这种共享就是安全的。如果子对象属于一个不可变的类,如 String,就是这种情况。或者在对象的生命周期中,子对象一直包含不变的常量,没有更改器方法会改变它,也没有方法会生成它的引用,这种情况下同样是安全的(子对象虽然是可变的,但是在在对象的生命周期中,子对象的数据域没有发生改变)。不过,通常子对象都是可变的,必须重新定义 clone() 方法来建立一个深拷贝,同时克隆所有子对象。在这个例子中,hireDay 域是一个 Date,这是可变的,所以它也需要克隆。(出于这个原因,这个例子使用 Date 类型的域而不是 LocalDate 来展示克隆过程。如果 hireDay 是不可变的 LocalDate 类的一个实例,就无需我们做任何处理了。)对于每一个类,需要确定:
默认的 clone() 方法是否满足要求;是否可以在可变的子对象上调用 clone() 方法来修补默认的 clone() 方法;是否不该使用 clone() 方法实际上第 3 个选项是默认选项。如果选择第 1 项或第 2 项,类必须:
实现 Cloneable 接口;重新定义 clone() 方法,并指定 public 访问修饰符。Cloneable 接口Cloneable 接口的出现与接口的正常使用并没有关系。具体来说,Cloneable 接口没有指定 clone() 方法,clone() 方法是从 Object 类继承的。Cloneable 接口只是作为一个标记,指示类设计者了解克隆过程。对象对于克隆很 “偏执”,如果一个对象请求克隆,但没有实现 Cloneable 接口,就会生成一个受检异常(CloneNotSupportedException 异常)。
如果在一个对象上调用 clone() 方法,但这个对象的类并没有实现 Cloneable 接口,Object 类的 clone() 方法就会拋出一个 CloneNotSupportedException。
注释:Cloneable 接口是 Java 提供的一组标记接口(tagging interface)之一。应该记得:
Comparable 等接口的通常用途是确保一个类实现一个或一组特定的方法。标记接口不包含任何方法,标记接口唯一的作用就是允许在类型查询中使用 instanceof:if (obj instanceof Cloneable) {}
建议你自己的程序中不要使用标记接口。
即使 clone() 的默认(浅拷贝)实现能够满足要求,还是需要实现 Cloneable 接口,将 clone() 方法重新定义为 public, 再调用 super.clone()。下面给出一个例子:
class Employee implements Cloneable { // raise visibility level to public, change return type public Employee clone() throws CloneNotSupportedException { return (Employee) super.clone(); }}
与 Object.clone() 提供的浅拷贝相比,前面看到的 clone() 方法并没有为它增加任何功能。这里只是让这个方法是公有的。要建立深拷贝,还需要做更多工作,克隆对象中可变的实例域。下面来看创建深拷贝的 done() 方法的一个例子:
class Employee implements Cloneable { // ... public Employee clone() throws CloneNotSupportedException { // call Object.clone() Employee cloned = (Employee) super.clone(); // clone mutable fields cloned.hireDay = (Date) hireDay.clone(); return cloned; }}
如果在一个对象上调用 clone() 方法,但这个对象的类并没有实现 Cloneable 接口,Object 类的 clone() 方法就会拋出一个 CloneNotSupportedException。当然,Employee 和 Date 类实现了 Cloneable 接口,所以不会抛出这个异常。不过, 编译器并不了解这一点,因此,我们声明了这个异常。
捕获这个异常是不是更好一些?
public Employee clone() { try { Employee cloned = (Employee) super.clone(); // ... return cloned; } catch (CloneNotSupportedException e) { return null; } // this won"t happen, since we are Cloneable}
捕获异常这非常适用于 final 类。否则,最好还是保留 throws 说明符。这样就允许子类在不支持克隆时选择抛出一个 CloneNotSupportedException()。
子类的克隆必须当心子类的克隆。例如,一旦为 Employee 类定义了 clone() 方法,任何人都可以用它来克隆 Manager 对象(Manager 类是 Employee 类的子类)。Employee 克隆方法能完成工作吗?这取决于 Manager 类的域。在这里是没有问题的,因为 bonus 域是基本类型。但是 Manager 可能会有需要深拷贝或不可克隆的域。不能保证子类的实现者一定会修正 clone() 方法让它正常工作。出于这个原因, 在 Object 类中 clone() 方法声明为 protected。不过,如果你希望类用户调用 clone(),就不能这样做。
参考资料《Java核心技术卷一:基础知识》(第10版)第 6 章:接口、lambda 表达式与内部类 6.2.3 对象克隆
标签:
精彩推荐
Object类提供的clone()方法是“浅拷贝”,并没有克隆对象中引用的其他对象,原对象和克隆的对象仍然会共...
1、一、2019个人所得税纳税标准工资个税的计算公式为:应纳税额=(工资薪金所得-“五险一金”-扣除数)×...
从魏牌高山的产品定位来看,这款车型的价格预计和岚图梦想家(参数|询价)插混版相当,起价在36万元左右。...
1、《英语高手的学习秘诀》是江苏少年儿童出版社出版的图书,作者是钟琬婷本文关于英语高手的学习秘诀的...
欧冠四强产生2席!皇马AC米兰晋级切尔西出局明晨全部出炉,欧冠,曼城,皇马,利物浦,切尔西队,ac米兰,那不...
天空下着小雨,但难阻广州队球迷在训练场边呐喊、鼓掌。4月18日下午,广州队举行训练基地球迷开放日活动...
1、其实大学里,看的就是你自己了,大学里一般都是自学的,黄河科技学院不仅是全国第一所实施专科学历教育...
https: www bilibili com video BV11c411W7ML ?spm_id_from=333 999 0 0&vd_source=724c4cb677efe6e2616dc9b110d68de4++++++++++++++++++++++
一、广西壮族自治区玉林市天气预报1、容县气象台2023年4月19日4时3分发布雷电黄色预警信号。2、目前雷雨...
原标题:收入态势回稳向上支出保持较高强度——解读一季度财政收支数据新华社北京4月18日电 新华社记者...
在今日凌晨进行的欧冠1 4决赛次回合的一场较量中,AC米兰1-1战平那不勒斯,总比分2-1挺进四强。赛后在...
原标题:抚平“大地伤疤”筑牢生态根基——来自我省矿山生态修复的观察与思考江西日报全媒体记者 杨碧...
黑龙江省铁力市发布道路结冰黄色预警
1、主要为中小企业客户提供 "网络营销平台、交易服务平台、商机管理平台、效果评估平台和增值服务平台...
1、这也是痛经的伴随症状。2、痛经是指周期性腹痛,甚至腰骶部疼痛,严重时伴有恶心呕吐、腹泻、头晕乏...
近年,国内科技成果转化日趋活跃。据科技部火炬中心数据显示,2022年全年,全国共登记技术合同772507项...
媒体探访北京起火医院:着火痕迹明显
1、羊奶和牛奶的区别在于:蛋白质含量、脂肪含量、糖类、维生素含量、矿物质含量。2、蛋白质含量的区别...
lunac品牌目前的办公地在上海,在互联网上开设了官方旗舰店lunac旗舰店,让广大网民在网上也能买到与lunac实体店
中国—国际可再生能源署合作研讨会在京召开---4月18日,中国—国际可再生能源署(简称IRENA)合作研讨会...
资讯News
08-15
07-07
11-03
11-03
11-03
11-03
11-03
11-03
11-03
11-03
11-03
11-03
聚焦Policy
当好农民工的“护薪人” 近日,罗某等7名农民工在收到被拖欠的工资后,纷纷打电话向江西省南昌市...
“通讯录里所有人都知道我欠钱了” □ 本报记者 韩丹东 □ 本报见习记者 张守坤 ...
大连宝马车撞人案肇事司机被判死刑 本报讯 记者韩宇 10月29日,辽宁省大连市中级人民法院一审...
医院财务迷上网络赌博输光5000万元公款 □ 本报记者 马维博 □ 本报通讯员 汪宇堂 曹...
辊环车削 雕琢毫厘(工匠绝活) 【绝活看点】 23年来,雷虎始终扎根一线,改进钢材轧制工艺...
交警严查超标电动自行车挪用“白牌” 截至昨晚6时,处罚电动自行车违法行为共计6585笔;下一步将...
明起寒潮来袭 北方气温普降10℃以上 中央气象台预计,本周日北京平原地区最低气温降至-4℃左右...
多种蔬菜价格降幅达五成 包括菠菜、蒿子秆等 预计本月中旬蔬菜恢复供需平衡 本报讯(记者...
北京周日最低气温或达-4℃ 本报讯(记者 赵婷婷)北京青年报记者昨天从中央气象台获悉,新一股...
昌平一家四口确诊新冠肺炎 天通北苑第二社区升级为中风险地区 朝阳两涉疫校区及16所学校停课 ...