在功能模块的实现中,需要对一个变量进行深度拷贝,该变量是一个存储二维Map的对象,之前同事写了一个用序列化进行深度拷贝的方法。
在一般的业务情况下,没有发现问题,但是在进行性能测试时,对象大小会达到1~2MB,在进行对象深度拷贝时,会有异常抛出java.io.OptionalDataException,导致对象拷贝失败,影响程序的正确性。
错误日志如下:
[E] 05/31 09:58:10,247 RouteService(1517) - [ROU]动态路由copy异常! e.toString = java.io.OptionalDataException
e.getStackTrace = [
java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1373), java.io.ObjectInputStream.readObject(ObjectInputStream.java:373), java.util.HashMap.readObject(HashMap.java:1402), sun.reflect.GeneratedMethodAccessor8.invoke(Unknown Source), sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43), java.lang.reflect.Method.invoke(Method.java:498), java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1058), java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1909), java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1808), java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353), java.io.ObjectInputStream.readObject(ObjectInputStream.java:373),
com.qsd.common.utils.CopyMapUtil.clone(CopyMapUtil.java:28), ==============ObjectInputStream.readObject
com.qsd.route.main.RouteService.updateRouteTable(RouteService.java:1515), com.qsd.route.main.RouteService.updateQuantumRoute(RouteService.java:667), com.qsd.route.main.RouteCalculationThread.action(RouteCalculationThread.java:124), com.qsd.common.utils.PersistentThread.run(PersistentThread.java:43)]
于是对又重新复习了一下对象克隆的基本概念,和深度拷贝的方法
1.对象克隆的基本概念,在《Java核心技术-卷一》(原书第九版),第六章 接口与内部类,6.2对象克隆中讲的非常清楚
1)如果被拷贝对象中的所有数据域都属于数值或者基本类型,用Object集成来的clone方法进行拷贝,没有任何问题;
2)如果被拷贝对象中,数据域中包含子对象引用,拷贝的结果会使得两个域引用同一个子对象--因此原始对象和克隆对象共享这个子对象域,没有实现深度拷贝!
2.关于深度拷贝,在网上查了一些资料,有不少同学认为,序列化是一个不错的深度拷贝的实现方式,包括《Java核心技术-卷二》序列化章节,也提到了序列化可以进行对象深度拷贝。但是进行了一些代码分析和调整后,异常还是会出现,所以没有办法,只能使用自定义clone()方法的办法,用循环进行对象的深度拷贝了。需要做的有两步:
1)修改要拷贝对象的类,添加集成克隆接口(implements Cloneable),并添加clone()方法
如果类的成员变量都是Java基本类型,那么clone()方法非常简单,Class1 clone()方法如下:
public Class1 clone() throws CloneNotSupportedException {
return (Class1) super.clone();
}
如果类的成员变量包含其他类ClassName2的对象,那么就需要给这个类添加clone()方法,一次类推。Class1 clone()方法如下:
public Class1 clone() throws CloneNotSupportedException {
Class1 cloned = (Class1) super.clone();
cloned.obj2 = (Class2) obj2.clone();// obj2是Class1的城域变量,类型是Class2
return cloned;
}
2)在遍历要拷贝对象的集合时,调用clone()方法,实现集合的深度拷贝