之前只是听说clone,很高级的样子,一直没有用过,感觉日常开发好像用不到似的,谁知道这段时间的开发,发现clone非常好用,就专门学习了一下。
前世
先说说clone的由来吧,只知道它的用法,而不知道为什么要这样用,总有是死记硬背的错觉。废话不多说,大家都知道,在java里面,使用一个对象的话,需要先new一个对象出来,并赋给一个引用,这个引用指向了该对象,我们对引用的操作,其实也就是操作对象了,就相当于操作空调的时候,用原装的遥控器可以,用带红外线的手机也可以操作,空调就相当于对象,遥控器和手机就相当于指向该对象的引用,所以,不管引用怎么变,只要都指向同一个对象,那么引用代表的结果都会变得。
很好的例子就是,有一个void类型的方法,他的作用是修改方法的参数,也就是方法体里面是对参数中的对象的引用进行修改,那么该方法的参数里面的该对象也就在作用域范围内改变了,这样就容易造成混乱,都这样的操作的话,多传递几个方法,改着改着自己都不知道改成什么样了,所以讲道理,是不允许这样操作的。所以可以先把该对象保存一个“本地副本”,这样再进行修改,就省心多了,于是clone就应运而生了,即给对象创建一个本地副本。
今生
缩小了说,clone其实就是对象的“复制(赋值)”,把一个对象赋给另一个相同的对象,从而使更改不影响前一个对象,即可以操作相同值的不同对象。例如:我们自定义的一个类对象Test,想讲test1所指向的对象复制一份给test2所指对象,而且使两个引用互不影响,这时候就可以用clone来实现(这也是java中创建对象的一种方式,另一种方式是new)。为什么要这样做呢,举个再直白点的例子,对新建的HashMap来clone,产生另一个互不影响的另一个hashmap,这样就可以保证里面的数值保持不变,就省了添加数值。
对hashmap克隆的时候,可以直接使用clone方法,因为Hashmap里面已经实现了clone方法,如下图所示:
所以,要使自己建的类也有克隆能力,也要实现clone方法,也就是让自己的类变成“可克隆的”,即cloneable,让类具有克隆能力,需要同时具备一下两点:
1、类要实现Cloneable接口。
该接口没有方法,是一个标记,该方法”可克隆“,不实现的话,会抛出CloneNotSupportedException异常。如下图:
public class Paas{
private String pname;
private String pdepartment;
private Saas saas;
public Object clone(){
Object result = null;
try {
result = (Paas) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return result;
}public String getPname() {
return pname;
}public void setPname(String pname) {
this.pname = pname;
}public String getPdepartment() {
return pdepartment;
}public void setPdepartment(String pdepartment) {
this.pdepartment = pdepartment;
}public Saas getSaas() {
return saas;
}public void setSaas(Saas saas) {
this.saas = saas;
}
}
2、要重新实现clone方法,调用super.clone方法,并设置成public。这样用super.clone可以使该对象可以实现克隆,设置成public可以在任何地方使用该clone方法。完整例子如下所示:
Pass.java
public class Paas implements Cloneable{
private String pname;
private String pdepartment;
private Saas saas;
public Object clone(){
Object result = null;
try {
result = (Paas) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return result;
}public String getPname() {
return pname;
}public void setPname(String pname) {
this.pname = pname;
}public String getPdepartment() {
return pdepartment;
}public void setPdepartment(String pdepartment) {
this.pdepartment = pdepartment;
}public Saas getSaas() {
return saas;
}public void setSaas(Saas saas) {
this.saas = saas;
}
}
Sass.java
public class Saas {
private String sname;
private String sdepartment;
public String getSname() {
return sname;
}public void setSname(String sname) {
this.sname = sname;
}public String getSdepartment() {
return sdepartment;
}public void setSdepartment(String sdepartment) {
this.sdepartment = sdepartment;
}
}
测试类,TestClone.java
public class TestClone{
public static void main(String[] args) {
Paas paas = new Paas();
paas.setPname("安元");
paas.setPdepartment("BI");Paas paasClone = (Paas) paas.clone();
System.out.println(paas);
System.out.println(paasClone);System.out.println(paas.getSaas());
System.out.println(paasClone.getSaas());}
}
运行结果:
Paas@1d56ce6a
Paas@5197848c
Saas@17f052a3
Saas@17f052a3
由运行结果的前两个可以看出,克隆后的passClone和paas指向了两个不同的对象。 但是paas中引用的对象sass却仍然是指向了相同的对象。这就涉及到了常说的浅复制和深复制。
浅复制
顾名思义,浅复制就是只克隆了对象的”表面“信息,而对象里面引用的对象,以及引用对象的引用对象,均无法赋值。以上方法实现的就是浅复制。
深复制
要实现深复制,就需要对对象里面的引用对象,以及引用对象的引用对象也进行一次克隆,层层递进,只要需要克隆的对象,都需要实现clone方法。修改代码如下,来实现深复制:
Paas.java
public class Paas implements Cloneable{
private String pname;
private String pdepartment;
private Saas saas;
public Object clone(){
Paas result = null;
try {
result = (Paas) super.clone();
Saas saas = (Saas) this.saas.clone();
result.setSaas(saas);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return result;
}public String getPname() {
return pname;
}public void setPname(String pname) {
this.pname = pname;
}public String getPdepartment() {
return pdepartment;
}public void setPdepartment(String pdepartment) {
this.pdepartment = pdepartment;
}public Saas getSaas() {
return saas;
}public void setSaas(Saas saas) {
this.saas = saas;
}
Saas.java
public class Saas implements Cloneable{
private String sname;
private String sdepartment;
public Object clone(){
Saas result = null;
try {
result = (Saas) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return result;
}public String getSname() {
return sname;
}public void setSname(String sname) {
this.sname = sname;
}public String getSdepartment() {
return sdepartment;
}public void setSdepartment(String sdepartment) {
this.sdepartment = sdepartment;
}
}
同样的测试类
public class TestMq{
public static void main(String[] args) {
Paas paas = new Paas();
paas.setPname("安元");
paas.setPdepartment("BI");Saas saas = new Saas();
saas.setSname("启业云");
saas.setSdepartment("bi");
paas.setSaas(saas);
Paas paasClone = (Paas) paas.clone();
System.out.println(paas);
System.out.println(paasClone);
System.out.println(paas.getSaas());
System.out.println(paasClone.getSaas());}
}
运行结果:
Paas@1d56ce6a
Paas@5197848c
Saas@17f052a3
Saas@2e0fa5d3
由上面的运行结果可以看出,paas中的saas也得到了克隆,从而实现了深度复制。