匿名內部類方式構建物件導致序列化失敗

zypronet發表於2018-07-11

問題描述:

以下程式碼為問題程式碼:

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類持有外部類例項引用,如圖:
image.png


相關文章