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

问题描述:

以下代码为问题代码:

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

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
内部类的序列化过程和普通类是一样的。但是,需要注意的是,如果内部类是非静态的,则需要序列化其外部类的引用,以便在反序列化时能够正确地重建内部类实例。同时,如果内部类实现了 Serializable 接口,则需要确保其外部类也实现了 Serializable 接口,否则在序列化时会抛出 NotSerializableException 异常。 以下是一个示例代码: ```java import java.io.*; class Outer implements Serializable { private static final long serialVersionUID = 1L; private int outerValue; public Outer(int outerValue) { this.outerValue = outerValue; } public int getOuterValue() { return outerValue; } public void setOuterValue(int outerValue) { this.outerValue = outerValue; } class Inner implements Serializable { private static final long serialVersionUID = 1L; private int innerValue; public Inner(int innerValue) { this.innerValue = innerValue; } public int getInnerValue() { return innerValue; } public void setInnerValue(int innerValue) { this.innerValue = innerValue; } } } public class SerializationExample { public static void main(String[] args) throws Exception { Outer outer = new Outer(10); Outer.Inner inner = outer.new Inner(20); // 序列化 FileOutputStream fos = new FileOutputStream("data.ser"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(outer); oos.close(); // 反序列化 FileInputStream fis = new FileInputStream("data.ser"); ObjectInputStream ois = new ObjectInputStream(fis); Outer outer2 = (Outer) ois.readObject(); ois.close(); // 验证序列化结果 System.out.println("Outer value: " + outer2.getOuterValue()); System.out.println("Inner value: " + outer2.new Inner(0).getInnerValue()); } } ``` 在上面的示例中,我们定义了一个包含内部类的外部类 Outer,并进行了序列化和反序列化。注意,在序列化和反序列化时,我们都需要将外部类和内部类同时序列化和反序列化。最终输出的结果如下: ``` Outer value: 10 Inner value: 20 ``` 这表明内部类的序列化和反序列化过程都是成功的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值