什么是拷贝、影子拷贝、深度拷贝,不是本文要讨论的。如需了解,以下两个连接还是不错滴。
http://liran-email.iteye.com/blog/550249
http://www.ibm.com/developerworks/cn/java/l-jpointer/index.html
1.clone的优点
a. 获得一个对象的拷贝(此处指深层拷贝)使用赋值操作符“=”是不能完成的;
b. 无需调用构造函数即可获得对象的拷贝(当然,拷贝对象和被克隆对象之间是否影响取决于深克隆还是浅克隆),一定程度上可以提高执行效率。
2.clone的缺点
以下将根据一个具体的例子来说明这个问题,当然这里指的是深层拷贝。
Car.java -- 父类,没有公开clone方法
package com.clonedemo.test;
public class Car {
private String type; // 型号
private String manufacturer; //制造商
private Engine engine; // 引擎
public Car(String type, String manufacturer, Engine engine) {
super();
this.type = type;
this.manufacturer = manufacturer;
this.engine = engine;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getManufacturer() {
return manufacturer;
}
public void setManufacturer(String manufacturer) {
this.manufacturer = manufacturer;
}
public Engine getEngine() {
return engine;
}
public void setEngine(Engine engine) {
this.engine = engine;
}
// car common methods such as drive,start,stop definition, omitted
// ...
}
RaceCar.java -- 继承自Car类,为其子类,提供公开的clone方法
package com.clonedemo.test;
public class RaceCar extends Car implements Cloneable {
private String speeder;
// other variables, omitted
// ...
public RaceCar(String type, String manufacturer, Engine engine,
String speeder) {
super(type, manufacturer, engine);
this.speeder = speeder;
}
@Override
protected Object clone() throws CloneNotSupportedException {
RaceCar racecar = (RaceCar) super.clone();
racecar.speeder = speeder;
return racecar;
}
public String getSpeeder() {
return speeder;
}
public void setSpeeder(String speeder) {
this.speeder = speeder;
}
// race car methods definition, omitted
// ...
}
Engine.java -- 引擎类,没有公开clone方法
package com.clonedemo.test;
public class Engine {
private String type; // 引擎型号
private Integer power; // 马力
public Engine(String type, Integer power) {
super();
this.type = type;
this.power = power;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Integer getPower() {
return power;
}
public void setPower(Integer power) {
this.power = power;
}
}
CloneTester.java -- 测试类
package com.clonedemo.test;
public class CloneTester {
/**
* @param args
* @throws CloneNotSupportedException
*/
public static void main(String[] args) throws CloneNotSupportedException {
RaceCar r1 = new RaceCar("BMW-X7", "BMW", new Engine("RR-X7", 500),
"speeder-001");
RaceCar r2 = (RaceCar) r1.clone();
r2.setManufacturer("GE");
System.out.println("R1 Manufacturer: " + r1.getManufacturer());
System.out.println("R2 Manufacturer: " + r2.getManufacturer());
r2.getEngine().setType("RR-X8");
System.out.println("R1 Engine Type: " + r1.getEngine().getType());
System.out.println("R2 Engine Type: " + r2.getEngine().getType());
}
}
输出结果
R1 Engine Type: RR-X8
R2 Engine Type: RR-X8
R1 Manufacturer: BMW
R2 Manufacturer: GE
观察输出结果可以发现,R1和R2的引擎引用的是同一个对象,原因是我们没有为RaceCar的父类实现公开的clone。
但为什么同是对象类型(String)的Manufacturer却不是指向同一个对象呢? 事实上,你会发现基本类型int,double等对应的Integer,Double等对象在这种情况下和String类型一样,实现了深度克隆的效果。原因在于,这些对象被设计为不可更改的类(immutable class),即一旦这个类初始化,那么类中的函数都不能改变自身的值,而是返回修改后的对象。当执行r2 = r1.clone()后,r1的manufacturer和r2的manufacturer指向的是同一个String对象,这可以通过如下代码证实:
RaceCar r1 = new RaceCar("BMW-X7", "BMW", new Engine("RR-X7", 500), "speeder-001");
RaceCar r2 = (RaceCar) r1.clone();
System.out.println(r1.getManufacturer() == r2.getManufacturer());
输出: true
当执行r2.setManufacturer("GE");时,r2的manufacturer指向新的字符串对象"GE",所以我们看到以上的结果。
好了,问题来了。当存在继承关系时,父类没有公开的clone方法,而子类需要深层拷贝时,子类的clone方法是否安全呢?
显而易见,如果子类的clone方法依赖父类的clone就会出问题,除非保证父类公开了clone方法并正确的实现了它,否则就会出现示例的情况;
此外,当我们为Car类实现clone方法时,是否要依赖Engine类提供公开的且正确的clone呢?如果Engine类是一个final类呢?对于前一个问题,如果依赖,那么Engine不能是一个final类,因为如果是final类,就没法提供公开的clone方法(无法实现Cloneable接口);如果Engine类是final,则Car类的clone就无法依赖Engine的clone,原因同上。
如果不为Car类提供行为良好的clone,那么子类RaceCar就不能依赖于父类的clone,而要自己实现行为正确的clone,就本例而言,可以这样:
@Override
protected Object clone() throws CloneNotSupportedException {
RaceCar racecar = (RaceCar) super.clone();
racecar.setEngine(new Engine(getEngine().getType(), getEngine().getPower()));
racecar.speeder = speeder;
return racecar;
}
输出结果
R1 Engine Type: RR-X7
R2 Engine Type: RR-X8
R1 Manufacturer: BMW
R2 Manufacturer: GE
总结如下:
java中的clone约束是很弱的,因为没有规定一定要实现,但全部都实现又没有必要,因此在使用clone方法进行深层复制时,应该慎重,尤其当存在继承关系时。一个不错的做法是,先调用super.clone,然后对结果对象(super.clone返回对象)的所有域重新赋值(内容为原对象副本),像这样:
@Override
protected Object clone() throws CloneNotSupportedException {
RaceCar racecar = (RaceCar) super.clone();
racecar.setType(getType());
racecar.setManufacturer(getManufacturer());
racecar.setEngine(new Engine(getEngine().getType(), getEngine().getPower()));
racecar.speeder = speeder;
return racecar;
}
3.替代方案
(1)提供一个拷贝构造函数(如果你用过C++就不会陌生)
public RaceCar(RaceCar raceCar);
(2)提供一个静态工厂方法,当然名字可以改变,比如deepCopy等
public RaceCar newInstance(RaceCar raceCar);
(3)使用序列化
如何实现此处不再赘述,资料有很多,本文提供的连接也提及,可以参考。
ps:示例代码的clone是protected的,因为文件都放在同一包中,所以访问没问题,实际中也许要改为public
分享到:
相关推荐
darts-clone-java 用Java编写的Double-ARray Trie System克隆。 该库基于称为“快速高效”库的 。入门设置要使用Maven添加依赖项,请使用以下命令: < dependency> < groupId>...
语音克隆模型
详细描述了java基础中的数组与方法的应用技术,以及面向对象的过程思想,有助于java初学者的入门学习。
jquery-clone-tableheader table 头部抬头固定 用于 多数据出现滚动条,看不到标题
(实战)[re:Invent 2018]-001:赛道分析-(致敬1024) 配套 train and evaluation log
matlab 鱼群算法 亲测保证可以运行 鱼群算法 鱼群算法
Facebook-login-page-clone-main-源码.rar
android-java-organizze-clone
kokoa-clone-2020-源码.rar
Java Clone(类的复制)实例代码 Java Clone(类的复制)实例代码
比nero更完美的刻录CD软件---CLONE_CD
Jlink-clone解决办法,替换文件
spotify-clone-源码.rar
Netflix-Clone-源码.rar
flow-clone-源码.rar
gojek-clone-源码.rar
twitter-clone, 一种基于 Ruby on Rails的Twitter 开源克隆 twitter克隆使用 Ruby on Rails http://twitter-clone-rails.herokuapp.com/的Twitter的开源克隆。正在启动git clone https://github.co
github-clone-all 克隆您的所有 Github 存储库,包括私有存储库。 目前限制为 100 个存储库(最多一个请求),请随时发送 PR 以支持更多。目录安装这个模块在上可用。 $ npm install -g github-clone-all 这个包需要...
git-clone-repos 通过shell命令克隆多个git仓库 安装 npm i git-clone-repos 用法 const clone = require ( 'git-clone-repos' ) ; ( async ( ) => { await clone ( [ 'git@github....
用 Java 语言编写的 初学者可学习的clone()