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

目录
  • clone()和Cloneable接口
  • Cloneable和clone()的总结
    • 1.Cloneable 的用途
    • 2.克隆的分类
    • 3.克隆的举例
    • 4.浅克隆的举例
    • 5.深克隆的举例

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

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

(0)

相关推荐

  • java Clone接口和深拷贝详解

    对于数组的拷贝,如果是简单类型的话是深拷贝,如果是引用类型的话是浅拷贝,但是因为java是面向对象的,在回答面试官问题的时候,我们可以不用说的这么细,可以直接说浅拷贝. 代码示例1 class Person implements Cloneable{//如果想克隆自定义类,那么需要在自定义类上实现Cloneable接口 public int age; /*疑问:为什么这个接口是空接口呢?这是一个面试问题. 空节课:也把它叫做标记接口.其实就是这个意思:只要一个类实现了这个接口,那么就标记这个类是

  • 详解Java中clone的写法

    Cloneable这个接口设计得十分奇葩,不符合正常人的使用习惯,然而用这个接口的人很多也很有必要,所以还是有必要了解一下这套扭曲的机制.以下内容来自于对Effective Java ed 2. item 11的整理.  Cloneable接口 首先,Cloneable接口中并没有方法.它的存在意义一是让程序员注明当前对象可以clone,二是改变父类Object类中clone方法的行为:如果某个类实现了Cloneable,那么它的父类Object的clone方法可以调用,否则会抛出CloneNo

  • Java中的数组复制(clone与arraycopy)代码详解

    JAVA数组的复制是引用传递,而并不是其他语言的值传递. 1.clone protectedObjectclone() throwsCloneNotSupportedException创建并返回此对象的一个副本."副本"的准确含义可能依赖于对象的类.这样做的目的是,对于任何对象x,表达式: x.clone()!=x为true,表达式: x.clone().getClass()==x.getClass()也为true,但这些并非必须要满足的要求.一般情况下: x.clone().equa

  • 详解java中的深拷贝和浅拷贝(clone()方法的重写、使用序列化实现真正的深拷贝)

    1.序列化实现 public class CloneUtils { @SuppressWarnings("unchecked") public static <T extends Serializable> T clone(T object){ T cloneObj = null; try { ByteArrayOutputStream out = new ByteArrayOutputStream(); ObjectOutputStream obs = new Objec

  • 浅析Java中clone()方法浅克隆与深度克隆

    现在Clone已经不是一个新鲜词语了,伴随着"多莉"的产生这个词语确实很"火"过一阵子,在Java中也有这么一个概念,它可以让我们很方便的"制造"出一个对象的副本来,下面来具体看看Java中的Clone机制是如何工作的?      1. Clone&Copy 假设现在有一个Employee对象,Employee tobby =new Employee("CMTobby",5000),通 常我们会有这样的赋值Employ

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

    目录 clone()和Cloneable接口 Cloneable和clone()的总结 1.Cloneable 的用途 2.克隆的分类 3.克隆的举例 4.浅克隆的举例 5.深克隆的举例 clone()和Cloneable接口 clone顾名思义就是克隆,即,复制一个相等的对象,但是不同的引用地址. 我们知道拿到一个对象的地址,只要提供相应的方法就可以修改这个对象,但是如果我们想要得到这个对象去修改它,又想保留这个对象原来的属性,这是就可以使用clone(),它会复制一个内容相同的对象而具有不同

  • Java中的clone方法实例详解

    Java中对象创建 clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象.所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象.那么在java语言中,有几种方式可以创建对象呢? 1 使用new操作符创建一个对象 2 使用clone方法复制一个对象 那么这两种方式有什么相同和不同呢? new操作符的本意是分配内存.程序执行到new操作符时, 首先去看new操作符后面的类型,因为知道了类型,才能知道要分配多大的内存空间.分配完内存之

  • Java 中的 clone( ) 和 new哪个效率更高

    对象创建的几种方法: 使用new关键字 使用clone方法 反射机制 反序列化 以上四种都可以产生java对象 1,3都会明确的显式的调用构造函数 2是在内存上对已有对象的影印 所以不会调用构造函数 4是从文件中还原类的对象 也不会调用构造函数 何为clone()? 拷贝对象返回的是一个新的对象,而不是一个对象的引用地址: 拷贝对象已经包含原来对象的信息,而不是对象的初始信息,即每次拷贝动作不是针对一个全新对象的创建. clone()和new那个更快? 利用clone,在内存中进行数据块的拷贝,

  • java 中序列化与readResolve()方法的实例详解

    java 中序列化与readResolve()方法的实例详解 readResolve方法是作用是什么?这个方法跟对象的序列化相关(这样倒是解释了为什么 readResolve方法是private修饰的). 怎么跟对象的序列化相关了? 下面我们先简要地回顾下对象的序列化.一般来说,一个类实现了 Serializable接口,我们就可以把它往内存地写再从内存里读出而"组装"成一个跟原来一模一样的对象.不过当序列化遇到单例时,里边就有了个问题:从内存读出而组装的对象破坏了单例的规则.单例是要

  • Java中io流解析及代码实例

    IO流 Java中IO流分为两种,字节流和字符流,顾名思义字节流就是按照字节来读取和写入的,字符刘是按照字符来存取的:常用的文件读取用的就是字符流,在网络通信里面用的就是字节流 下面这张图是Java中IO流的总体框架: 字节流 Java中字节流一般都是以stream结尾的,输入的字节流叫InputStream,输出字节流叫OutputStream;InputStream和OutputStream是表示自己输入/输出的所有类的超类,是抽象类(abstract) 常用的字节流有: 1.FileInp

  • Java中final关键字详解及实例

    final在Java中可以声明成员变量.方法.类以及本地变量.一旦你将引用声明作final,你将不能改变这个引用了,如果你试图将变量再次初始化的话,编译器会报编译错误.  final的含义在不同的场景下有细微的差别,但总体来说,它指的是"不可变". 1. final变量 凡是对成员变量或者本地变量(在方法中的或者代码块中的变量称为本地变量)声明为final的都叫作final变量.final变量经常和static关键字一起使用,作为常量.用final关键字修饰的变量,只能进行一次赋值操作

  • Java中的Comparable和Comparator接口

    目录 一. Comparable接口 1. Comparable简介 2. 为什么要实现Comparable接口 3. Comparable的实际应用 二. Comparator接口 1. Comparator简介 2. Comparator接口的实际运用 三. Comparable和Comparator的比较 一. Comparable接口 1. Comparable简介 Comparable是排序接口. 若一个类实现了Comparable接口,就意味着该类支持排序. 实现了Comparabl

  • java 中Excel转shape file的实例详解

    java  中Excel转shape file的实例详解 概述: 本文讲述如何结合geotools和POI实现Excel到shp的转换,再结合前文shp到geojson数据的转换,即可实现用户上传excel数据并在web端的展示功能. 截图: 原始Excel文件 运行耗时 运行结果 代码: package com.lzugis.geotools; import com.lzugis.CommonMethod; import com.vividsolutions.jts.geom.Coordina

  • JAVA 中解密RSA算法JS加密实例详解

    JAVA 中解密RSA算法JS加密实例详解 有这样一个需求,前端登录的用户名密码,密码必需加密,但不可使用MD5,因为后台要检测密码的复杂度,那么在保证安全的前提下将密码传到后台呢,答案就是使用RSA非对称加密算法解决 . java代码 需要依赖 commons-codec 包 RSACoder.Java import org.apache.commons.codec.binary.Base64; import javax.crypto.Cipher; import java.security.

  • java中判断字段真实长度的实例(中文2个字符,英文1个字符)

    实例如下: public class Char_cn { public static void main(String[] args) { // TODO Auto-generated method stub String haha = "我叫兜兜abcd"; int true_num = String_length(haha); System.out.println("true" + true_num); int false_num = haha.length()

随机推荐