68

我知道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;
}

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

但是,我不明白它为什么起作用?由于我没有按照界面的要求提供适当的readObjectand 。writeObjectSerializable

正如 Effective Java Item 75: Consider using a custom serialized form中指出的那样,如果我的枚举中有自定义成员变量,我是否需要提供自己的readObjectand ?writeObject

4

3 回答 3

111

它起作用的原因是Enum's 的序列化过程与其他类的序列化过程不同。从官方文档

1.12 枚举常量的序列化

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

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

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

于 2013-03-20T11:28:33.313 回答
10

根据Serializable文档,readObject根本writeObject不需要,所以您的问题可能并不完全正确。

Serializable是一个标记接口,没有任何方法。

我向您推荐这个答案,它提供了有关序列化实现的更多详细信息(这解释了为什么您不需要写入和读取功能)。

而且,正如Dianne Hackborn在这里提到的, Parcelable对于 Android 来说效率更高。

如果您对 Enum 特别感兴趣,请参阅以下段落

1.12 枚举常量的序列化

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

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

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

于 2013-03-20T10:54:12.910 回答
4

枚举成员的序列化不起作用。当@vmironov 回答时,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

于 2013-03-20T11:59:26.573 回答