服务热线:13616026886

技术文档 欢迎使用技术文档,我们为你提供从新手到专业开发者的所有资源,你也可以通过它日益精进

位置:首页 > 技术文档 > JAVA > 新手入门 > 基础入门 > 查看文档

详细讲述java中的克隆

  经常听到有人说java中没有指针。事实如此吗?no,java是有指针的,只不过换了个名字而已,也就是我们经常提到的引用。我们知道,在java中一切都是对象,那么我们如何操控对象?如何在成千上万的对象中找到我们所需的那个对象呢?又是如何让对象按照我们的意思来完成任务的呢?

  object o = new object();

  这是java中最常见的语句了,在这句话中做了三件事。首先声明一个object类型的变量o,在内存中为对象划分一块地址new object(),将声明的变量指向内存中的对象。如此一来,我们就可以通过o来操纵对象了。就好像孩子们玩的遥控飞机,在空中飞行的是飞机,而使它做出优美动作的却是孩子们手中的摇控器。

  "克隆"是如今听到的较多的词汇,听说已经将某只羊克隆了好几份了。但愿这种技术不要在人身上实验。java中也有"克隆",与现实世界的克隆一样,将一个实际存在的对象拷贝几份。如下:

//倒霉的羊
public class sheep implements cloneable{
private string name;
public void setname(string arg) {
name = arg;
}
public string getname() {
return name;
}
public object clone() throws clonenotsupportedexception {
return super.clone();
}
}
//克隆
public class main {
public static void main(string[] args) throws clonenotsupportedexception {
sheep sheep = new sheep(); //先得到那只羊的实例
sheep.setname("我是真的"); //给它做个记号
system.out.println("sheep.getname() = " + sheep.getname());
sheep sheepclone = (sheep)sheep.clone(); //开始克隆
system.out.println("sheepclone.getname() = " + sheepclone.getname());
}
}

  运行程序结果为:

  sheep.getname() = 我是真的

  sheepclone.getname() = 我是真的

  两只羊是一模一样的(哪怕那只羊瘸腿)。让我们来看看代码。首先要注意的是sheep类实现了cloneable接口(该接口属于java.lang包,默认已经导入了),该接口中并没有定义要实现的方法,是个空接口,起标志作用。也就是说,实现了这个接口的羊就不再是只普通的羊,它是一只可以被克隆的羊。再往下看,有个clone方法,返回object类型的对象,并抛出clonenotsupportedexception异常。该方法覆写了父类 (object)的clone方法,并在最后调用了super.clone(),这也意味着无论clone类继承结构是什么样的,super.clone ()都会直接或间接调用object类的clone()方法。看看jdk帮助文档会发现,object类的clone()是一个native方法,我们知道,native方法的效率一般来说都是远高于java中的非native方法。这也说明了new一个对象,然后将原对象中的数据导入到新创建的对象中去的做法是多么愚蠢。必须说明的是object中的clone方法是protected的,所以要使用clone就必须继承object类(默认)。并且为了可以使其它类调用该方法,必须将其作用域设置为public.

  以上只是一个简单clone的实现。明天说说"影子clone"和"深度clone".

  夜,深了。何为影子clone?先看一下例子。

//倒霉的羊
public class sheep implements cloneable{
private string name;
public void setname(string arg) {
name = arg;
}
public string getname() {
return name;
}
public object clone() throws clonenotsupportedexception {
return super.clone();
}
}
//羊圈
public class sheepfold implements cloneable {
public sheep sheep;
public string name;
public sheepfold() {
sheep = new sheep();
}
public object clone() throws clonenotsupportedexception {
return super.clone();
}
}
//克隆
public class main {
public static void main(string[] args) throws exception {
sheepfold fold = new sheepfold();
fold.name = "小羊圈";
fold.sheep.setname("小羊");
sheepfold fold2 = (sheepfold)fold.clone();
system.out.println(" fold2.name = " + fold2.name);
system.out.println(" fold2.sheep.getname() = " + fold2.sheep.getname());
fold2.name = "大羊圈";
fold2.sheep.setname("大羊");
system.out.println("=====================================");
system.out.println(" fold2.name = " + fold2.name);
system.out.println("* fold2.sheep.getname() = " + fold2.sheep.getname());
system.out.println(" fold.name = " + fold.name);
system.out.println("* fold.sheep.getname() = " + fold.sheep.getname());
system.out.println("=====================================");
}
}

  在这个例子中有三个类,sheep和sheepflod都实现了cloneable接口,并且覆写了object类的clone方法,说明这两个类是具有克隆能力的。注意一点,在sheepflod中持有一个sheep的实例,并在main类中对其进行克隆,结果如下:

  请注意一下结果中带有"*"号的两条结果语句。fold2.sheep和fold.sheep的name都变为了"大羊",很奇怪是吗?在此之前,我们只对fold2.sheep的name赋过值。为什么fold.sheep的name也变为了"大羊"呢?原因很简单,因为它们是指向同一个对象的不同引用。从中可以看出,调用object类中clone()方法时,首先在内存中划分一块同原对象相同的空间,然后将原对象的内容原样拷贝至新对象。我们知道,java中有基本数据类型,对于基本数据类型,这样的操作是没有问题的,但对非基本类型变量,它们保存的仅仅是对象的引用,这也是为什么clone后非基本类型变量和原对象中的变量指向同一个对象的原因。可能你已经注意到,程序中用到了string类型,即对象,为什么没有出现引用指向同一地址的情况?这是因为string是一个不可更改的类(immutable class),每次给它赋值时,都会产生一个新的string对象。如 string str = "a"; str += "b";在这两句代码中,当执行str += "b"时,实际上是重新成生了一个值为"ab"的 string对象,即重新分配了一块内存空间。以上clone方法通常被称为"影子clone"."影子clone"给我们留下了一个问题,即多个引用指向同一个对象。如何解决该问题呢?答案为"深度clone".把上面的例子改成深度clone很简单,只需将sheepfold的clone()方法改为如下即可:很简单,只需将sheepfold的clone()方法改为如下即可:

public object clone() throws clonenotsupportedexception {
sheepfold fold = (sheepfold)super.clone();
sheep = (sheep)fold.sheep.clone();
return fold;
}

扫描关注微信公众号