Java中的clone()和Cloneable接口

clone顾名思义就是克隆,即,复制一个相等的对象,但是不同的引用地址。

我们知道拿到一个对象的地址,只要提供相应的方法就可以修改这个对象,但是如果我们想要得到这个对象去修改它,又想保留这个对象原来的属性,这是就可以使用clone(),它会复制一个内容相同的对象而具有不同内存地址。

Cloneable接口,就是我们要使用clone()必须实现的接口,不然会抛出异常。

public class Bean implements Cloneable {
    private String a;

    public Bean(String a) {
        this.a = a;
    }

    public String getA() {
        return a;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public boolean equals(Object o) {
        if(o instanceof Bean){
            Bean bean = (Bean) o;
            return bean.getA().equals(a);
        }
        return false;
    }
}

在Cloneable 接口中并没有给我们定义任何方法

这里需要重写clone()方法

protected native Object clone() throws CloneNotSupportedException;

protected Object clone() throws CloneNotSupportedException {
        if (!(this instanceof Cloneable)) {
            throw new CloneNotSupportedException("Class " + getClass().getName() +
                                                 " doesn't implement Cloneable");
        }

        return internalClone();
    }

它是Object类里面的native方法,它是protected的,根据需要可以写为public,可以看到如果不实现Cloneable接口将会抛出CloneNotSupportedException 异常。


测试一下

try {

            Bean a = new Bean("lzy");
            Bean b = a;
            Bean c = (Bean) a.clone();

            Log.i(TAG, "onCreate: " + (a == b));         //true
            Log.i(TAG, "onCreate: " + (a.equals(b)));    //true

            Log.i(TAG, "onCreate: " + (a == c));         //false
            Log.i(TAG, "onCreate: " + (a.equals(c)));    //true


        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
可以看到克隆出来的类的地址是不同的,而内容是相同的。


下面修改一下,在Bean加一个成员变量ChildBean

public class ChildBean implements Cloneable {
    private String c;
    public String getC() {
        return c;
    }
    public ChildBean(String c) {
        this.c = c;
    }
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    @Override
    public boolean equals(Object o) {
        if (o instanceof ChildBean) {
            ChildBean bean = (ChildBean) o;
            return bean.getC().equals(c);
        }
        return false;
    }
}
public class Bean implements Cloneable {
    private String a;
    private ChildBean childBean;

    public Bean(String a, ChildBean childBean) {
        this.a = a;
        this.childBean = childBean;
    }
    public String getA() {
        return a;
    }

    public ChildBean getChildBean() {
        return childBean;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof Bean) {
            Bean bean = (Bean) o;
            return bean.getA().equals(a);
        }
        return false;
    }
}
Bean a = new Bean("lzy", new ChildBean("child"));
            Bean b = a;
            Bean c = (Bean) a.clone();

            Log.i(TAG, "onCreate: " + (a.getChildBean() == b.getChildBean()));         //true
            Log.i(TAG, "onCreate: " + (a.getChildBean().equals(b.getChildBean())));    //true

            Log.i(TAG, "onCreate: " + (a.getChildBean() == c.getChildBean()));         //true
            Log.i(TAG, "onCreate: " + (a.getChildBean().equals(c.getChildBean())));    //true
测试发现有一个结果不是我们所预期的,这意味着并没有真正克隆ChildBean,只是克隆的它的内存地址,导致两个具有相同的内存地址,这也就是浅克隆,此时我们需要的是深克隆,需要按照下面方法修改,重写clone()方法

  @Override
    public Object clone() throws CloneNotSupportedException {
        Bean bean = (Bean) super.clone();
        bean.childBean = (ChildBean) bean.childBean.clone();
        return bean;
    }
但是这样做如果有很多层的类,那每一层都需要去重写,显得很麻烦。所以我们可以用下面的工具类来实现

public class BeanUtil {
    public static <T> T cloneTo(T src) throws RuntimeException {
        ByteArrayOutputStream memoryBuffer = new ByteArrayOutputStream();
        ObjectOutputStream out = null;
        ObjectInputStream in = null;
        T dist = null;
        try {
            out = new ObjectOutputStream(memoryBuffer);
            out.writeObject(src);
            out.flush();
            in = new ObjectInputStream(new ByteArrayInputStream(memoryBuffer.toByteArray()));
            dist = (T) in.readObject();
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (out != null)
                try {
                    out.close();
                    out = null;
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            if (in != null)
                try {
                    in.close();
                    in = null;
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
        }
        return dist;
    }
}
Bean a = new Bean("lzy", new ChildBean("child"));
        Bean b = BeanUtil.cloneTo(a);

        Log.i(TAG, "onCreate: " + (a.getChildBean() == b.getChildBean()));         //false
        Log.i(TAG, "onCreate: " + (a.getChildBean().equals(b.getChildBean())));    //true

这样就可以很轻松的得到我们预期的结果,但是记得每一个类都要去 实现Serializable接口









  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值