欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-06-19 13:31:40
目录clone()和cloneable接口cloneable和clone()的总结1.cloneable 的用途2.克隆的分类3.克隆的举例4.浅克隆的举例5.深克隆的举例clone()和clonea...

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

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