我理解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
界面的要求,我没有提供正确的writeObject
和Serializable
。
正如Effective Java 第75项中所述:考虑使用自定义序列化表单,如果我有自定义成员变量,是否需要提供自己的readObject
和writeObject
在我的枚举?
答案 0 :(得分:88)
它起作用的原因是Enum
的序列化过程与其他类的序列化过程不同。来自official documentation:
1.12枚举常数的序列化
枚举常量的序列化与普通的可序列化或可外部化的对象不同。枚举常量的序列化形式仅由其名称组成;常量的字段值不在表单中。要序列化枚举常量,ObjectOutputStream会写入枚举常量名称方法返回的值。要反序列化枚举常量,ObjectInputStream从流中读取常量名称;然后通过调用java.lang.Enum.valueOf方法获取反序列化的常量,将常量的枚举类型与接收的常量名称一起作为参数传递。与其他可序列化或可外部化的对象一样,枚举常量可以作为随后出现在序列化流中的反向引用的目标。
这意味着,所有自定义字段不会被序列化。在您的情况下,一切都运行正常,因为您的申请流程仍在运行,并且您获得了传递给Enum
的相同 savedInstanceState.putSerializable
实例。
但是想象一下你的应用被杀的情况,因为Android
没有足够的内存。用户下次打开应用时,您将获得新 Enum
实例,并且所有自定义字段都将丢失并由构造函数重新初始化。因此,枚举中的可变字段始终有效transient
。
答案 1 :(得分:10)
根据Serializable文档,根本不需要readObject
和writeObject
,因此您的问题可能不完全正确。
Serializable
是marker interface,并且没有任何方法。
我推荐你this answer,它提供了有关序列化实现的更多详细信息(这解释了为什么你不需要写和读函数)。
而且,正如Dianne Hackborn提到的here,Parcelable对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