自定义枚举Serializable也是吗?

时间:2013-03-20 10:44:47

标签: java android

我理解Enum是可序列化的。因此,这样做是安全的。 (selectedCountry为enum Country

没有客户成员变量的原始枚举

public enum Country {
    Australia,
    Austria,
    UnitedState;
}

片段

@Override
public void onActivityCreated (Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    if (savedInstanceState != null) {
        selectedCountry = (Country)savedInstanceState.getSerializable(SELECTED_COUNTRY_KEY);
    }
}

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    savedInstanceState.putSerializable(SELECTED_COUNTRY_KEY, selectedCountry);
}

但是,如果我在自定义枚举类中有非可序列化成员怎么办?例如,

原始枚举客户成员变量

package org.yccheok;

import org.yccheok.R;

/**
 *
 * @author yccheok
 */
public enum Country {
    Australia(R.drawable.flag_au),
    Austria(R.drawable.flag_at),
    UnitedState(R.drawable.flag_us);

    Country(int icon) {
        this.icon = icon;
        nonSerializableClass = new NonSerializableClass(this.toString());
    }

    public int getIcon() {
        return icon;
    }

    public static class NonSerializableClass {
        public NonSerializableClass(String dummy) { this.dummy = dummy; }
        public String dummy;
    }

    private final int icon;

    public NonSerializableClass nonSerializableClass;
}

我测试了。有用。 (我通过在序列化之前和之后打印出成员变量的所有值来测试。它们在之前和之后是相同的)

但是,我不明白为什么会这样?由于readObject界面的要求,我没有提供正确的writeObjectSerializable

正如Effective Java 第75项中所述:考虑使用自定义序列化表单,如果我有自定义成员变量,是否需要提供自己的readObjectwriteObject在我的枚举?

3 个答案:

答案 0 :(得分:88)

它起作用的原因是Enum的序列化过程与其他类的序列化过程不同。来自official documentation

  

1.12枚举常数的序列化

     

枚举常量的序列化与普通的可序列化或可外部化的对象不同。枚举常量的序列化形式仅由其名称组成;常量的字段值不在表单中。要序列化枚举常量,ObjectOutputStream会写入枚举常量名称方法返回的值。要反序列化枚举常量,ObjectInputStream从流中读取常量名称;然后通过调用java.lang.Enum.valueOf方法获取反序列化的常量,将常量的枚举类型与接收的常量名称一起作为参数传递。与其他可序列化或可外部化的对象一样,枚举常量可以作为随后出现在序列化流中的反向引用的目标。

这意味着,所有自定义字段不会被序列化。在您的情况下,一切都运行正常,因为您的申请流程仍在运行,并且您获得了传递给Enum相同 savedInstanceState.putSerializable实例。

但是想象一下你的应用被杀的情况,因为Android没有足够的内存。用户下次打开应用时,您将获得 Enum实例,并且所有自定义字段都将丢失并由构造函数重新初始化。因此,枚举中的可变字段始终有效transient

答案 1 :(得分:10)

根据Serializable文档,根本不需要readObjectwriteObject,因此您的问题可能不完全正确。

Serializablemarker interface,并且没有任何方法。

我推荐你this answer,它提供了有关序列化实现的更多详细信息(这解释了为什么你不需要写和读函数)。

而且,正如Dianne Hackborn提到的hereParcelable对Android更有效。

如果您对Enum特别感兴趣,请参阅below paragraph

  

1.12 Enum常数的序列化

     

枚举常量的序列化与普通的可序列化或可外部化的对象不同。枚举的序列化形式   常数仅由其名称组成;常量的字段值是   不在表格中。要序列化枚举常量,   ObjectOutputStream写入枚举常量返回的值   名称方法。要反序列化枚举常量,ObjectInputStream会读取   来自流的常量名称;然后是反序列化的常量   通过调用java.lang.Enum.valueOf方法获得,传递   常量的枚举类型以及收到的常量名称为   参数。与其他可序列化或可外部化的对象一样,枚举   常量可以作为出现的反向引用的目标   随后在序列化流中。

     

无法自定义枚举常量序列化的过程:任何特定于类的writeObject,readObject,   readObjectNoData,writeReplace和readResolve方法定义   在序列化和反序列化期间忽略枚举类型。   同样,任何serialPersistentFields或serialVersionUID字段   声明也被忽略 - 所有枚举类型都有一个固定的   serialVersionUID为0L。记录可序列化的字段和数据   枚举类型是不必要的,因为类型没有变化   发送的数据。

所以,我不认为Enum是测试内部非序列化类工作的正确选择。

答案 2 :(得分:3)

枚举成员的序列化无效。 @Smironov回答时,nonSerializable字段永远不会被序列化。这是一个测试:

public enum Country {
Australia;

    public static class NonSerializableClass {
       public NonSerializableClass() {}
       public String dummy;
    }

    public NonSerializableClass nonSerializableClass;
}

将枚举写入序列化流的代码:

public class SerializationTestWrite {
    public static void main(String[] args) throws Exception{
        FileOutputStream f = new FileOutputStream("tmp");
        ObjectOutput s = new ObjectOutputStream(f);

        Country.Australia.nonSerializableClass = new Country.NonSerializableClass();
        Country.Australia.nonSerializableClass.dummy = "abc";

        s.writeObject(Country.Australia);
        s.flush();

        System.out.println(Country.Australia.nonSerializableClass.dummy);
    }
}    

写入虚拟字段的值为:abc

从序列化流中读取枚举的代码:

public class SerializationTestRead {
    public static void main(String[] args) throws Exception{
        FileInputStream in = new FileInputStream("tmp");
        ObjectInputStream so = new ObjectInputStream(in);
        Country readed = (Country) so.readObject();

        System.out.println(readed.nonSerializableClass);
    }
}

但在阅读时,字段nonSerializableClass的值为:null