经常听到有人说java中没有指针事实如此吗?nojava是有指针的只不过换了个名字而已也就是我们经常提到的引用我们知道在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 superclone();
}
}
//克隆
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Sheep sheep = new Sheep(); //先得到那只羊的实例
sheepsetName(我是真的); //给它做个记号
Systemoutprintln(sheepgetName() = + sheepgetName());
Sheep sheepClone = (Sheep)sheepclone(); //开始克隆
Systemoutprintln(sheepClonegetName() = + sheepClonegetName());
}
}
运行程序结果为
sheepgetName() = 我是真的
sheepClonegetName() = 我是真的
两只羊是一模一样的(哪怕那只羊瘸腿)让我们来看看代码首先要注意的是Sheep类实现了Cloneable接口(该接口属于javalang包默认已经导入了)该接口中并没有定义要实现的方法是个空接口起标志作用也就是说实现了这个接口的羊就不再是只普通的羊它是一只可以被克隆的羊再往下看有个clone方法返回Object类型的对象并抛出CloneNotSupportedException异常该方法覆写了父类 (Object)的clone方法并在最后调用了superclone()这也意味着无论clone类继承结构是什么样的superclone ()都会直接或间接调用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 superclone();
}
}
//羊圈
public class Sheepfold implements Cloneable {
public Sheep sheep;
public String name;
public Sheepfold() {
sheep = new Sheep();
}
public Object clone() throws CloneNotSupportedException {
return superclone();
}
}
//克隆
public class Main {
public static void main(String[] args) throws Exception {
Sheepfold fold = new Sheepfold();
foldname = 小羊圈;
foldsheepsetName(小羊);
Sheepfold fold = (Sheepfold)foldclone();
Systemoutprintln( foldname = + foldname);
Systemoutprintln( foldsheepgetName() = + foldsheepgetName());
foldname = 大羊圈;
foldsheepsetName(大羊);
Systemoutprintln(=====================================);
Systemoutprintln( foldname = + foldname);
Systemoutprintln(* foldsheepgetName() = + foldsheepgetName());
Systemoutprintln( foldname = + foldname);
Systemoutprintln(* foldsheepgetName() = + foldsheepgetName());
Systemoutprintln(=====================================);
}
}
在这个例子中有三个类Sheep和Sheepflod都实现了Cloneable接口并且覆写了Object类的clone方法说明这两个类是具有克隆能力的注意一点在Sheepflod中持有一个Sheep的实例并在Main类中对其进行克隆结果如下
请注意一下结果中带有*号的两条结果语句foldsheep和foldsheep的name都变为了大羊很奇怪是吗?在此之前我们只对foldsheep的name赋过值为什么foldsheep的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)superclone();
sheep = (Sheep)foldsheepclone();
return fold;
}