为什么需要克隆
Object类中clone()方法产生的效果是:先在内存中开辟一块和原始对象一样的空间,然后原样拷贝原始对象中的内容。对基本数据类型,这样的操作是没有问题的,但对非基本类型变量,我们知道它们保存的仅仅是对象的引用,这也导致clone后的非基本类型变量和原始对象中相应的变量指向的是同一个对象。
Java代码实例:
public class Product implements Cloneable {
}
Java代码实例:
public class Attribute implements Cloneable {
}
public class Product implements Cloneable {
}
对象的序列化还有另一个容易被大家忽略的功能就是对象复制(Clone),Java中通过Clone机制可以复制大部分的对象,但是众所周知,Clone有深度Clone和浅度Clone,如果你的对象非常非常复杂,并且想实现深层 Clone,如果使用序列化,不会超过10行代码就可以解决。
虽然Java的序列化非常简单、强大,但是要用好,还有很多地方需要注意。比如曾经序列化了一个对象,可由于某种原因,该类做了一点点改动,然后重新被编译,那么这时反序列化刚才的对象,将会出现异常。 你可以通过添加serialVersionUID属性来解决这个问题(在Java序列化与反序列化学习(二):序列化接口说明 的标题三中有此说明)。如果你的类是个单例(Singleton)类,虽然我们多线程下的并发问题来控制单例,但是,是否允许用户通过序列化机制或者克隆来复制该类,如果不允许你需要谨慎对待该类的实现(让此类不用实现Serializable接口或Externalizable接口和Cloneable接口就行了)。
Java代码实例:
public class Attribute {
}
public class Product {
// 将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝
}
或者:
public static <T extends Serializable> T copy(T input) {
ByteArrayOutputStream baos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos);
oos.writeObject(input);
oos.flush();
byte[] bytes = baos.toByteArray();
bis = new ByteArrayInputStream(bytes);
ois = new ObjectInputStream(bis);
Object result = ois.readObject();
return (T) result;
} catch (IOException e) {
throw new IllegalArgumentException("Object can't be copied", e);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Unable to reconstruct serialized object due to invalid class definition", e);
} finally {
closeQuietly(oos);
closeQuietly(baos);
closeQuietly(bis);
closeQuietly(ois);
}
}
#########################注意######################
Bean复制的几种框架中(Apache BeanUtils、PropertyUtils,Spring BeanUtils,Cglib BeanCopier)都是相当于克隆中的浅克隆。
1)spring包和Apache中的 BeanUtils 采用反射实现
2)cglib包中的 Beancopier 采用动态字节码实现
公司内部对用到的bean属性复制做了下性能分析:
cglib BeanCopier 15ms
Spring BeanUtil 4031ms
apache BeanUtils 18514ms.
#########################注意######################
clone和BeanUtils.copyProperties对比:
Cloning is done by you. If the instance which you trying clone contains reference of another instance you have to write cloning code to that one too. What if the instances contains chain of references to other instances? So if you do cloning by yourself, there are chances that you might miss a small detail.
BeanUtils.copyProperties on otherhand take care of everything by itself. It reduces your effort.