背景
如果不关注对象引用传递,这必将带来的是悲剧,你将一个对象从一个模块交到了另一个模块而浑然不知,而当另一个模块在修改该对象的内容同时也默默的影响到之前的模块,这是很糟糕的,问题也很难找。
我们如何在编码中警觉到这一点?
我们如何在编码中警觉到这一点?以减少最后成为这种问题的受害者时候的尴尬呢?
准则一:B模块需要使用到A模块中的某对象值,B模块内对该对象值修改,你期望这会影响到A模块的该对象内容吗?如果否,那就请用对象拷贝。一般为否。
准则二:当你的模块对外暴露方法接口提供对象,如果外界修改该对象内容,你期望这会影响到你自身模块的该对象内容吗?如果否,那就请用对象拷贝。一般为否。
对象拷贝
浅拷贝
拷贝对象自身,但不拷贝对象内所引用的对象
class Student implements Cloneable
{
public int age;
public String name;
public Teacher teacher;
/**
* 重写clone()方法
* @return
*/
public Object clone() {
//浅拷贝
try {
// 直接调用父类的clone()方法
return super.clone();
} catch (CloneNotSupportedException e) {
return null;
}
}
}
深拷贝
除了拷贝对象自身,连同对象内所引用的对象也进行拷贝
一.嵌套clone方式
接上面浅拷贝,在自身对象clone执行的时候,也嵌套执行所引用对象的clone方法,并将clone对象设置到自身对象中。
@Override
public Object clone() throws CloneNotSupportedException
{
Student student = (Student) super.clone();
// 将引用的对象teacher也clone下
student.setTeacher((Teacher) (student.getTeacher().clone()));
return student;
}
二.序列化方式
/**
* 深度拷贝 要求data对象及引用对象都实现了Serializable接口才可以用
* @param data
*/
public static <T> T deepCopy(T data) throws Exception {
// 将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(data);
// 将流序列化成对象
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (T) ois.readObject();
}
对比
从实现角度而言,方式二更加简便,只需要实现Serializable,方式一,需要实现了Cloneable,还有clone方法。效率还不知道。