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()方法。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持。