1. 项目场景:
我在将一个对象序列化存到redis队列中,发现总是报序列化异常的错误
2022-07-05 21:20:45.522 ERROR 3177 --- [nio-8095-exec-2] SerializeUtil工具类 : serialize处发生一个异常,参数value:SendMsgReq(token=c7cc8f376ffd482ba9b1fc934e28f6d5, requestId=388a5924D0R85Lsx29Ev7ru63DOXoQ86, eventCode=registerAccountMs, params={telephone=17806169286, telphoneEnd=9286, smsCode=527535}, eventVarPackage=[ActDriveReq(actCode=act_t2ehn28, actName=null, actVariable={smsCode=527535, telephone=17806169286}, sendTarget=null, sendContent=null)])
java.io.NotSerializableException: com.mintrust.ms.service.impl.SmsSendTranService
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at com.mintrust.commonutil.utils.SerializeUtil.serialize(SerializeUtil.java:26)
at com.mintrust.ms.service.impl.MsServiceImpl.unifyMsgSend(MsServiceImpl.java:174)
而我在序列化对象的时候明明是将对象实现了Serializable序列化接口的,也添加了序列化ID,为啥报没有序列化Service服务类的异常呢? 我真的是百思不得其解,我只是序列化了一个对象而已,难不成我将Service类进行序列化?
在序列化Service类之后出现了新的问题:
jar包里的类真的没有办法继续序列化了…
2. 问题发现
后来经过排查代码发现原来是同事用了双括号来构建了Map,通过断点可以看到:
咦,我这里明明定义的是一个Map,可是这里的SmsSendTranService代表了个啥?
正常来说这里应该是这样:
这才是我们想要的Map啊,为啥给我整一个SmsSendTranService$1 ?
后来通过查看同事的代码,发现了这样的写法:
通过查阅资料得出:
这里利用了内部类语法,这种方式比先new出对象然后再进行依次add要方便、简洁许多。该方法称之为“双括号初始化”(double brace initialization)。
显然这是在HashMap的构造器中写了一个匿名内部类,这个匿名内部类含有一个实例初始化块,初始化块的内容是三个add()函数,向被初始化的this指向的HashMap中添加了三个元素。
3.效率问题
利用双大括号初始化集合从效率上来说可能不如标准的集合初始化步骤。原因在于使用双大括号初始化会导致内部类文件的产生,而这个过程就会影响代码的执行效率。
假设有一个Test1类,里面有1000个list,都是使用双括号初始化,编译后如下:
Test1$1.class
Test1$2.class
...
Test1$1000.class
这也就是为啥第一次传参的时候出现了SmsSendTranService$1的问题,我要的是一个实现了序列化接口的Map,他直接给我传了一个内部类过来,在序列化内部类的时候,肯定会也序列化他的外部类,所以这就是为啥我报的那第一个错,没有序列化Service的原因。
我真的给我整无语了…
4.解决方法
后来我是通过将这个对象转化为String类型的JSON串,然后又转成了对象,绕了一圈,来躲掉这个中招的引用,
5.总结建议
虽然双括号语法看起来,更简洁,但是一定要慎用!!!尤其是在调别人接口的时候…