简介:
java克隆(clone)是java语言的特性之一,但在实际中应用比较少见。但有时候用克隆会更方便更有效率。
对于克隆(clone),java有一些限制:
1、被克隆的类必须自己实现cloneable 接口,以指示 object.clone() 方法可以合法地对该类实例进行按字段复制。cloneable 接口实际上是个标识接口,没有任何接口方法。
2、实现cloneable接口的类应该使用公共方法重写 object.clone(它是受保护的)。某个对象实现了此接口就克隆它是不可能的。即使 clone 方法是反射性调用的,也无法保证它将获得成功。
3、在java.lang.object类中克隆方法是这么定义的:
protected object clone()
throws clonenotsupportedexception
创建并返回此对象的一个副本。表明是一个受保护的方法,同一个包中可见。
按照惯例,返回的对象应该通过调用 super.clone 获得。
引题:
举个例子说吧,现在有一个对象比如叫foo,你需要在创建当前对象的一个副本作为存根你能怎么做?
假如你不用clone,那么你可以先new一个对象foo1:foo foo1=new foo(),然后用foo给foo1对象set值,这样就得到foo的副本foo1;除此之外,别无选择。
这样说,也许有人会觉得说的过于绝对了,不过事实如此啊。
要产生一个副本,那副本要不要内存?----当然要了,那就对了!既然需要内存,(不克隆的情况下)你不new还有什么办法呢?请大家时刻铭记对象是java运行时产生的,驻留在计算机内存中。
常见错误:
下面我澄清几个初学者容易犯迷糊的错误,同样的问题,产生foo对象的副本:
1、foo foo1=new foo();
foo1=foo;
然后就想当然的认为副本foo1生成了!
错误原因:foo1没错是申请了内存,但是执行foo1=foo后,foo1就不在指向刚申请的内存区域了,转而指向foo对象的内存区域,这时候,foo1、foo指向了同一内存区域。刚才new的操作制造一堆垃圾等着jvm回收。
2、foo foo1=foo;
错误原因:还是两个变量都指向了同一块内存。
3、有些老鸟更厉害一些:在foo中定义一个返回自身的方法:
public foo getinstance(){
return this;
}
然后,foo foo1=foo.getinstance();
错误原因:同上,主要还是没有重新开辟内存,this在对象里是什么?----就是对象自己的引用!那么getinstance()自然返回的就是对象自己,反正又是两个对象穿了一条裤子----***,哈哈。错得心服口服吧。为了节省篇幅,我在最后写个例子,留给那些对此有异议的人看。
引入克隆
看了这么多方法都不行,还很麻烦!干脆用克隆吧,简单明了。
废话不说了,看例子:
定义两个类clonefooa、clonefoob,然后写个测试类clonedemo分别克隆这两个类的对象,然后打印测试结果到控制台。
/**
* 简单类克隆实现
* 要实现克隆,必须实现cloneable接口,这是一个标识接口,没有接口方法
* 实现了 cloneable 接口,以指示 object.clone() 方法可以合法地对该类实例进行按字段复制。
* 按照惯例,实现此接口的类应该使用公共方法重写 object.clone(它是受保护的)。
*/
public class clonefooa implements cloneable {
private string stra;
private int inta;
public clonefooa(string stra, int inta) {
this.stra = stra;
this.inta = inta;
}
public string getstra() {
return stra;
}
public void setstra(string stra) {
this.stra = stra;
}
public int getinta() {
return inta;
}
public void setinta(int inta) {
this.inta = inta;
}
/**
* @return 创建并返回此对象的一个副本。
* @throws clonenotsupportedexception
*/
public object clone() throws clonenotsupportedexception {
//直接调用父类的clone()方法,返回克隆副本
return super.clone();
}
}
/**
* 深度克隆对象,当类存在聚合关系的时候,克隆就必须考虑聚合对象的克隆
*/
public class clonefoob implements cloneable {
private clonefooa fooa;
private double doub;
public clonefoob(double doub) {
this.doub = doub;
}
public clonefoob(clonefooa fooa, double doub) {
this.fooa = fooa;
this.doub = doub;
}
public clonefooa getfooa() {
return fooa;
}
public void setfooa(clonefooa fooa) {
this.fooa = fooa;
}
public double getdoub() {
return doub;
}
public void setdoub(double doub) {
this.doub = doub;
}
/**
* 克隆操作
*
* @return 自身对象的一个副本
* @throws clonenotsupportedexception
*/
public object clone() throws clonenotsupportedexception {
//先调用父类的克隆方法进行克隆操作
clonefoob clonefoob = (clonefoob) super.clone();
//对于克隆后出的对象clonefoob,如果其成员fooa为null,则不能调用clone(),否则出空指针异常
if (this.fooa != null)
clonefoob.fooa = (clonefooa) this.fooa.clone();
return clonefoob;
}
}
/**
* 测试类:分别克隆clonefooa和clonefoob类,并打印克隆前后的结果.
*/
public class clonedemo {
public static void main(string args[]) throws clonenotsupportedexception {
//clonefooa克隆前
clonefooa fooa1 = new clonefooa("fooa", 11);
system.out.println("clonefooa的对象克隆前对象fooa1值为: " + fooa1.getstra() + "," + fooa1.getinta());
//clonefooa克隆后
clonefooa fooa2 = (clonefooa) fooa1.clone();
system.out.println("clonefooa的对象克隆后对象fooa2值为: " + fooa2.getstra() + "," + fooa2.getinta());
//比较fooa1和fooa2内存地址
if (fooa1 == fooa2) system.out.println("比较fooa1和fooa2内存地址:相等!");
else system.out.println("比较fooa1和fooa2内存地址:不相等!");
system.out.println("-------------------------");
//clonefoob克隆前
clonefoob foob1 = new clonefoob(fooa1, new double("33"));
system.out.println("clonefoob的对象克隆前对象foob1值为: " + foob1.getfooa().getstra() + "," + foob1.getfooa().getinta() + " | " + foob1.getdoub());
//clonefoob克隆后
clonefoob foob2 = (clonefoob) foob1.clone();
system.out.println("clonefoob的对象克隆前对象foob2值为: " + foob2.getfooa().getstra() + "," + foob2.getfooa().getinta() + " | " + foob2.getdoub());
if (fooa1 == fooa2) system.out.println("比较foob1和foob2内存地址:相等!");
else system.out.println("比较foob1和foob2内存地址:不相等!");
}
}
运行结果:
clonefooa的对象克隆前对象fooa1值为: fooa,11
clonefooa的对象克隆后对象fooa2值为: fooa,11
比较fooa1和fooa2内存地址:不相等!
-------------------------
clonefoob的对象克隆前对象foob1值为: fooa,11 | 33.0
clonefoob的对象克隆前对象foob2值为: fooa,11 | 33.0
比较foob1和foob2内存地址:不相等!
process finished with exit code 0
反面教材:
最后,我给出我上面提出到最后要给出的反面例子。
随便写一个,在clonefooa 的基础上做了少许改动,内容如下:
public class clonefooa implements cloneable {
private string stra;
private int inta;
public clonefooa(string stra, int inta) {
this.stra = stra;
this.inta = inta;
}
public string getstra() {
return stra;
}
public void setstra(string stra) {
this.stra = stra;
}
public int getinta() {
return inta;
}
public void setinta(int inta) {
this.inta = inta;
}
/**
* @return 创建并返回此对象的一个副本。
* @throws clonenotsupportedexception
*/
public object clone() throws clonenotsupportedexception {
//直接调用父类的clone()方法,返回克隆副本
return super.clone();
}
/**
* @return 返回运行时的对象
*/
public clonefooa getinstance(){
return this;
}
public static void main(string args[]){
clonefooa fooa=new clonefooa("aa",11);
system.out.println(fooa.getstra()+""+fooa.getinta());
clonefooa fooa1=fooa.getinstance();
system.out.println(fooa1.getstra()+""+fooa1.getinta());
if(fooa==fooa1) system.out.println("fooa和fooa1内存地址相等!");
system.out.println("-------------------------");
//改变后fooa或者fooa1中任何一个,看看另外一个是否会改变
fooa1.setstra("bb");
system.out.println(fooa.getstra()+""+fooa.getinta());
system.out.println(fooa1.getstra()+""+fooa1.getinta());
if(fooa==fooa1) system.out.println("fooa和fooa1内存地址相等,改变fooa1后,fooa的值也跟着变化了");
}
}
运行结果:
aa11
aa11
fooa和fooa1内存地址相等!
-------------------------
bb11
bb11
fooa和fooa1内存地址相等,改变fooa1后,fooa的值也跟着变化了
process finished with exit code 0
======================================================
在最后,我邀请大家参加新浪APP,就是新浪免费送大家的一个空间,支持PHP+MySql,免费二级域名,免费域名绑定 这个是我邀请的地址,您通过这个链接注册即为我的好友,并获赠云豆500个,价值5元哦!短网址是http://t.cn/SXOiLh我创建的小站每天访客已经达到2000+了,每天挂广告赚50+元哦,呵呵,饭钱不愁了,\(^o^)/