java clone(浅复制和深复制)的前世今生

 

之前只是听说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也得到了克隆,从而实现了深度复制。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值