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

作者:lylodlig 时间:2022-09-27 15:31:05 

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()方法。

来源:https://blog.csdn.net/lylodyf/article/details/52763720

标签:Java,clone,Cloneable,接口
0
投稿

猜你喜欢

  • Java使用Graphics2D绘制SVG和PNG的方法

    2021-11-13 01:03:51
  • C#中的数组用法详解

    2021-08-19 14:50:17
  • Java二维数组与稀疏数组相互转换实现详解

    2022-10-28 07:27:33
  • java语言基础之标识符和命名规则详解

    2023-04-21 16:50:18
  • Java日常练习题,每天进步一点点(58)

    2021-06-26 01:13:02
  • C#中is,as,using关键字的使用说明

    2023-07-01 22:54:42
  • java Unicode和UTF-8之间转换实例

    2023-04-05 01:56:00
  • 详解Android Gradle插件3.0挖坑日记

    2022-02-06 09:03:19
  • Android 根据手势顶部View自动展示与隐藏效果

    2022-01-11 07:30:20
  • 浅析12306售票算法(java版)

    2023-11-16 10:27:12
  • C#实现ini文件读写操作

    2022-02-26 11:01:19
  • UnityShader3实现彩光效果

    2021-09-02 03:48:53
  • 详解Java中的Lambda表达式

    2021-08-17 00:06:00
  • Android scrollToTop实现点击回到顶部(兼容PullTorefreshScrollview)

    2021-07-29 09:06:37
  • Spring Data JPA调用存储过程实例代码

    2023-11-25 00:13:15
  • C++数组指针和二维数组详情

    2022-03-31 21:11:07
  • 如何在android中使用html作布局文件

    2023-01-14 07:49:10
  • android使用PullToRefresh实现下拉刷新和上拉加载

    2023-08-06 11:06:58
  • Java中的clone方法详解_动力节点Java学院整理

    2023-07-31 15:37:33
  • SpringBoot整合Echarts实现用户人数和性别展示功能(详细步骤)

    2023-02-22 00:31:59
  • asp之家 软件编程 m.aspxhome.com