匿名内部类方式构建对象导致序列化失败

问题描述:

以下代码为问题代码:

public class ItemDO implements Serializable {

    private static final long serialVersionUID=-463144769925355007L;
    
    ...
    
    private Map<String,String> langAndTitleMap;
    
    ...
}
public class ItemMultiLangDecorator implements ItemDecorator {
    
    ...

     @Override
    public ItemDO getItemDO() throws IcException {
        ...
        
        if(titleMultiLangFieldMeta!=null){
            itemDO.setLangAndTitleMap(new HashMap<String, String>(){{put(titleMultiLangFieldMeta.getFieldLang(),itemDO.getTitle());}});
        }else{
            itemDO.setLangAndTitleMap(new HashMap<String, String>(){{put(LanguageConstants.EN,itemDO.getTitle());}});
        }
        
        ...
    }

    ...
} 

在应用发布之后,日志出现ItemDO类序列化报错异常,如下:

java.io.NotSerializableException: com.taobao.item.manager.decorator.ItemMultiLangDecorator
        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.taobao.item.util.CloneUtil.deepCloneBySerialize(CloneUtil.java:29)
        at com.taobao.item.domain.ItemUpdateDO.mergeDbItem(ItemUpdateDO.java:320)

问题原因

虽然序列化的是ItemDO类,但是报错却是ItemMultiLangDecorator类,表面上看ItemDO里面并无任何对ItemMultiLangDecorator引用,但是由于在构造HashMap实例时候采用了匿名内部类构造的方式,即{{ ... }}形式,查阅资料得知道java会在匿名内部类的实例都持有一个外部封装类实例的隐式引用,也就导致构建Map实例拥有所在外部类ItemMultiLangDecorator实例引用,而恰好外部类并没有实现序列化接口。

本人也在本地写了一个Demo再次重现该问题。

public class Inner implements Serializable{

    private static final long    serialVersionUID= -463144769925355007L;
    
    private String name;

    public void setName(String name) {
        this.name = name;
    }
}
public class Outer {

    public Inner create(){
        return new Inner(){
            {
                setName("Hello");
            }
        };
    }

}
public class Main {

    public static void main(String[] args) {
        Inner inner=new Outer().create();

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = null;
        try {
            oos = new ObjectOutputStream(baos);
            oos.writeObject(inner);
            oos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

运行后会抛出Outer类序列化错误,在IDE中断点,可以看到Inner类持有外部类实例引用,如图:
匿名内部类方式构建对象导致序列化失败

上一篇:项目经理如何提高软件的质量


下一篇:Spring MVC能响应HTTP请求的原因?