双括号初始化(double brace initialization)踩坑记录

tech2022-09-29  68

场景:系统中有两个服务A和B,A服务中需要调用服务B中的批量删除的接口

服务B批量删除接口的入参

public class BatchOpVO implements Serializable { private String opIds; }

 服务B的方法

public ServiceResponse delete(BatchOpVO batchOpVO ) { return ServiceResponse.success(manager.delete(batchOpVO)); }

 在服务A中的调用

@PostMapping(path = "/{op_id:\\d+}/actions/delete") public ServiceResponse delete(@PathVariable("op_id") String opId) { BatchOpVO batchOpVO = new BatchOpVO(){{ setOpIds(opId); }});   return serviceB.delete(batchOpVO) }

然后测试服务A的删除接口,调用失败,抛出异常

Caused by: java.lang.UnsupportedOperationException -    at com.taobao.pandora.service.sharedclass.LazyClassCacheMap.entrySet(LazyClassCacheMap.java:126) -    at com.taobao.hsf.com.caucho.hessian.io.MapSerializer.writeObject(MapSerializer.java:95) -    at com.taobao.hsf.com.caucho.hessian.io.Hessian2Output.writeObject(Hessian2Output.java:421) -    at com.taobao.hsf.com.caucho.hessian.io.UnsafeSerializer$ObjectFieldSerializer.serialize(UnsafeSerializer.java:293) -    ... 175 more

序列化异常,第一想到的是,maven二方依赖包版本不同导致的,查看了使用的版本,重新将服务B deploy,服务A再更新依赖,做了一切保证版本同步的操作,依然报错,这种删除的接口以前也写过很多次了,想到唯一不同的就是初始化BatchOpVO的写法,我试着修改的初始化方式

在服务A中的调用(修改后的)

@PostMapping(path = "/{op_id:\\d+}/actions/delete") public ServiceResponse delete(@PathVariable("op_id") String opId) { BatchOpVO batchOpVO = new BatchOpVO(); batchOpVO.setOpIds(opId);   return serviceB.delete(batchOpVO) }

测试通过,那这两种方式有什么区别呢

搜到一篇关于讲双括号初始化的文章,才了解到,这样的创建方式,创建的是该类的匿名子类的实例,自然也就序列化失败了

上面的文章中还提到,这种方式的效率问题,这种方式会产生匿名类的class文件,效率会低,感觉有道理,但评论中又有人说

双花括号是节省效率的,List 自增机制对效率的影响尤为重要,以 ArrayList为例,初始默认空间为 10,每次自增为其 1.5 倍 + 1,就是 16、25...,所用空间较少时,ArrayList需频繁的自我复制来进行自增。当使用双花括号,取 ArrayList某个较大的自增临界点进行测试时,会发现在自增前使用 add方法,效率比在双花括号外 add 快 1/3 左走,但当空间增加到触发自增时,由双花括号内触发的自增效率,比双花括号外触发的自增要慢很多很多。当所用空间较少,ArrayList 频繁自增,由双花括号内触发的自增,损耗性能较大,当所用空间越来越多,双花括号内使用 add 方法,节省性能越多。若使用双花括号当自增达到某一临界值时,add 所节省的性能便会远远大于自增所消耗的性能,当然这个临界值会很大,所以当所占空间较少时,使用带有自我复制进行自增的集合来测试双花括号是不准确的。您若用真实实体对象就轻松多了

我看得不是很明白,但是最后说可以用真实的对象测试,我试了下,效率上基本没什么差别

测试代码

Long start = System.currentTimeMillis(); for (int i=0;i<10000000;i++){ BlogVo blogVo = new BlogVo(){{ setTitle("标题"); setContent("内容"); }}; } System.out.println(System.currentTimeMillis() - start); Long start = System.currentTimeMillis(); for (int i=0;i<10000000;i++){ BlogVo vo = new BlogVo(); vo.setTitle("标题"); vo.setContent("内容"); } System.out.println(System.currentTimeMillis() - start);

 

最新回复(0)