自定义枚举是否也可以序列化?

2022-08-31 14:08:34

我理解是可序列化的。因此,这样做是安全的。(所选国家/地区是Enumenum 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;
}

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

但是,我不明白为什么它有效?由于我没有提供正确的读取对象写入对象,如可序列化接口所要求的。

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


答案 1

它之所以有效,是因为 的序列化过程不同于其他类的序列化过程。来自官方文档Enum

1.12 枚举常量的序列化

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

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

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


答案 2

根据可序列化的文档,并且根本不需要,因此您的问题可能不完全正确。readObjectwriteObject

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

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

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

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

1.12 枚举常量的序列化

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

无法自定义枚举常量序列化的过程:在序列化和反序列化过程中,将忽略枚举类型定义的任何特定于类的 writeObject、readObject、readObjectNoData、writeReplace 和 readResolve 方法。类似地,任何串行持久性字段或串行VersionUID字段声明也会被忽略 - 所有枚举类型都有一个固定的串行VersionUID为0L。记录枚举类型的可序列化字段和数据是不必要的,因为发送的数据类型没有变化。

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


推荐