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 接口中并没有给我们定义任何方法
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接口。