Java中的clone()和Cloneable接口实例

 更新时间:2021年11月03日 09:39:35   作者:lylodlig  
这篇文章主要介绍了Java中的clone()和Cloneable接口实例,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

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接口。

Cloneable和clone()的总结

1.Cloneable 的用途

Cloneable和Serializable一样都是标记型接口,它们内部都没有方法和属性,implements Cloneable表示该对象能被克隆,能使用Object.clone()方法。如果没有implements Cloneable的类调用Object.clone()方法就会抛出CloneNotSupportedException。

2.克隆的分类

(1)浅克隆(shallow clone),浅拷贝是指拷贝对象时仅仅拷贝对象本身和对象中的基本变量,而不拷贝对象包含的引用指向的对象。

(2)深克隆(deep clone),深拷贝不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象。

举例区别一下:对象A1中包含对B1的引用,B1中包含对C1的引用。浅拷贝A1得到A2,A2中依然包含对B1的引用,B1中依然包含对C1的引用。深拷贝则是对浅拷贝的递归,深拷贝A1得到A2,A2中包含对B2(B1的copy)的引用,B2中包含对C2(C1的copy)的引用。

3.克隆的举例

要让一个对象进行克隆,其实就是两个步骤:

1.让该类实现java.lang.Cloneable接口;

2.重写(override)Object类的clone()方法。

public class Wife implements Cloneable {  
    private int id;  
    private String name;  
    public int getId() {  
        return id;  
    }  
    public void setId(int id) {  
        this.id = id;  
    }  
    public String getName() {  
        return name;  
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
    public Wife(int id,String name) {  
        this.id = id;  
        this.name = name;  
    }  
    @Override  
    public int hashCode() {//myeclipse自动生成的  
        final int prime = 31;  
        int result = 1;  
        result = prime * result + id;  
        result = prime * result + ((name == null) ? 0 : name.hashCode());  
        return result;  
    }  
    @Override  
    public boolean equals(Object obj) {//myeclipse自动生成的  
        if (this == obj)  
            return true;  
        if (obj == null)  
            return false;  
        if (getClass() != obj.getClass())  
            return false;  
        Wife other = (Wife) obj;  
        if (id != other.id)  
            return false;  
        if (name == null) {  
            if (other.name != null)  
                return false;  
        } else if (!name.equals(other.name))  
            return false;  
        return true;  
    }  
    @Override  
    public Object clone() throws CloneNotSupportedException {  
        return super.clone();  
    }  
    /** 
     * @param args 
     * @throws CloneNotSupportedException  
     */  
    public static void main(String[] args) throws CloneNotSupportedException {  
        Wife wife = new Wife(1,"wang");  
        Wife wife2 = null;  
        wife2 = (Wife) wife.clone();  
        System.out.println("class same="+(wife.getClass()==wife2.getClass()));//true  
        System.out.println("object same="+(wife==wife2));//false  
        System.out.println("object equals="+(wife.equals(wife2)));//true  
    }  
}  

4.浅克隆的举例

public class Husband implements Cloneable {  
    private int id;  
    private Wife wife;  
    public Wife getWife() {  
        return wife;  
    }  
    public void setWife(Wife wife) {  
        this.wife = wife;  
    }  
    public int getId() {  
        return id;  
    }  
    public void setId(int id) {  
        this.id = id;  
    }  
    public Husband(int id) {  
        this.id = id;  
    }  
    @Override  
    public int hashCode() {//myeclipse自动生成的  
        final int prime = 31;  
        int result = 1;  
        result = prime * result + id;  
        return result;  
    }  
    @Override  
    protected Object clone() throws CloneNotSupportedException {  
        return super.clone();  
    }  
    @Override  
    public boolean equals(Object obj) {//myeclipse自动生成的  
        if (this == obj)  
            return true;  
        if (obj == null)  
            return false;  
        if (getClass() != obj.getClass())  
            return false;  
        Husband other = (Husband) obj;  
        if (id != other.id)  
            return false;  
        return true;  
    }  
    /** 
     * @param args 
     * @throws CloneNotSupportedException  
     */  
    public static void main(String[] args) throws CloneNotSupportedException {  
        Wife wife = new Wife(1,"jin");  
        Husband husband = new Husband(1);  
        Husband husband2 = null;  
        husband.setWife(wife);  
        husband2 = (Husband) husband.clone();  
        System.out.println("husband class same="+(husband.getClass()==husband2.getClass()));//true  
        System.out.println("husband object same="+(husband==husband2));//false  
        System.out.println("husband object equals="+(husband.equals(husband)));//true  
        System.out.println("wife class same="+(husband.getWife().getClass()==husband2.getWife().getClass()));//true  
        System.out.println("wife object same="+(husband.getWife()==husband2.getWife()));//true  
        System.out.println("wife object equals="+(husband.getWife().equals(husband.getWife())));//true  
    }  
}  

5.深克隆的举例

如果要深克隆,需要重写(override)Object类的clone()方法,并且在方法内部调用持有对象的clone()方法;注意如下代码的clone()方法

public class Husband implements Cloneable {  
    private int id;  
    private Wife wife;  
    public Wife getWife() {  
        return wife;  
    }  
    public void setWife(Wife wife) {  
        this.wife = wife;  
    }  
    public int getId() {  
        return id;  
    }  
    public void setId(int id) {  
        this.id = id;  
    }  
    public Husband(int id) {  
        this.id = id;  
    }  
    @Override  
    public int hashCode() {//myeclipse自动生成的  
        final int prime = 31;  
        int result = 1;  
        result = prime * result + id;  
        return result;  
    }  
    @Override  
    protected Object clone() throws CloneNotSupportedException {  
        Husband husband = (Husband) super.clone();  
        husband.wife = (Wife) husband.getWife().clone();  
        return husband;  
    }  
    @Override  
    public boolean equals(Object obj) {//myeclipse自动生成的  
        if (this == obj)  
            return true;  
        if (obj == null)  
            return false;  
        if (getClass() != obj.getClass())  
            return false;  
        Husband other = (Husband) obj;  
        if (id != other.id)  
            return false;  
        return true;  
    }  
    /** 
     * @param args 
     * @throws CloneNotSupportedException  
     */  
    public static void main(String[] args) throws CloneNotSupportedException {  
        Wife wife = new Wife(1,"jin");  
        Husband husband = new Husband(1);  
        Husband husband2 = null;  
        husband.setWife(wife);  
        husband2 = (Husband) husband.clone();  
        System.out.println("husband class same="+(husband.getClass()==husband2.getClass()));//true  
        System.out.println("husband object same="+(husband==husband2));//false  
        System.out.println("husband object equals="+(husband.equals(husband)));//true  
        System.out.println("wife class same="+(husband.getWife().getClass()==husband2.getWife().getClass()));//true  
        System.out.println("wife object same="+(husband.getWife()==husband2.getWife()));//false  
        System.out.println("wife object equals="+(husband.getWife().equals(husband.getWife())));//true  
    }  
}  

但是也有不足之处,如果Husband内有N个对象属性,突然改变了类的结构,还要重新修改clone()方法。

解决办法:可以使用Serializable运用反序列化手段,调用java.io.ObjectInputStream对象的 readObject()方法。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 解读HttpServletRequestWrapper处理request数据流多次读取问题

    解读HttpServletRequestWrapper处理request数据流多次读取问题

    在Java Web开发中,获取HTTP请求参数是常见需求,本文详细讨论了通过POST方式获取参数的两种主要方法:使用request.getParameter()适用于application/x-www-form-urlencoded和multipart/form-data内容类型;而对于application/json类型的数据
    2024-10-10
  • SpringBoot中的统一异常处理详细解析

    SpringBoot中的统一异常处理详细解析

    这篇文章主要介绍了SpringBoot中的统一异常处理详细解析,该注解可以把异常处理器应用到所有控制器,而不是单个控制器,借助该注解,我们可以实现:在独立的某个地方,比如单独一个类,定义一套对各种异常的处理机制,需要的朋友可以参考下
    2024-01-01
  • Autowired的注入过程源码解析

    Autowired的注入过程源码解析

    这篇文章主要为大家介绍了Autowired的注入过程源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09
  • SpringBoot + MyBatis-Plus构建树形结构的几种方式

    SpringBoot + MyBatis-Plus构建树形结构的几种方式

    在实际开发中,很多数据都是树形结构,本文主要介绍了SpringBoot + MyBatis-Plus构建树形结构的几种方式,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-08-08
  • MyBatis的<foreach>以及java代码的批处理方式

    MyBatis的<foreach>以及java代码的批处理方式

    这篇文章主要介绍了MyBatis的<foreach>以及java代码的批处理方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-08-08
  • RocketMQ生产者调用start发送消息原理示例

    RocketMQ生产者调用start发送消息原理示例

    这篇文章主要为大家介绍了RocketMQ生产者调用start发送消息原理示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • Java中HashMap集合的6种遍历方式详解

    Java中HashMap集合的6种遍历方式详解

    这篇文章主要介绍了Java中HashMap集合的6种遍历方式详解,HashMap 基于哈希表的 Map 接口实现,是以 key-value 存储形式存在,即主要用来存放键值对,HashMap 的实现不是同步的,这意味着它不是线程安全的,我们来看一下其遍历方式,需要的朋友可以参考下
    2023-12-12
  • Java实例讲解Comparator的使用

    Java实例讲解Comparator的使用

    我们先介绍一下comparator,我们后文称为比较器。比较器实际上是一个实现了comparator interface是一个实例,在Arrays.sort方法我们可以提供这样一个实例,即使被排序的object没有实现comparable接口,我们也可以排序了
    2022-11-11
  • 详解Spring学习总结——Spring实现AOP的多种方式

    详解Spring学习总结——Spring实现AOP的多种方式

    这篇文章主要介绍了详解Spring学习总结——Spring实现AOP的多种方式,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-01-01
  • 利用IDEA社区版创建SpringBoot项目的详细图文教程

    利用IDEA社区版创建SpringBoot项目的详细图文教程

    大家应该都知道Idea社区版本,默认是不能创建SpringBoot项目的,下面这篇文章主要给大家介绍了关于利用IDEA社区版创建SpringBoot项目的详细图文教程,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2023-04-04

最新评论