Felder in einer serialisierbaren Klasse müssen selbst entweder serialisierbar oder transient sein, auch wenn die Klasse nie explizit serialisiert oder deserialisiert wird. Das liegt daran, dass die meisten J2EE-Anwendungsframeworks unter Last Objekte auf die Festplatte leeren und ein angeblich serialisierbares Objekt mit nicht-transienten, nicht serialisierbaren Datenelementen Programmabstürze verursachen und Angreifern Tür und Tor öffnen könnte.
Diese Regel wirft ein Problem bei nicht serialisierbaren Feldern und bei Sammlungsfeldern auf, wenn diese nicht privat sind (da ihnen extern nicht serialisierbare Werte zugewiesen werden könnten) und wenn ihnen innerhalb der Klasse nicht serialisierbare Typen zugewiesen werden.
Beispiel für nicht konformen Code
public class Address {
//...
}
public class Person implements Serializable {
private static final long serialVersionUID = 1905122041950251207L;
private String name;
private Address address; // Noncompliant; Address isn't serializable
}
一个对象序列化时,按照Java默认的序列化规则,对象内的所有成员都要序列化,也就是说,这些Class都必须实现Serializable。
所以,你有两种改法,一是Address实现Serializable接口,二是对Person中的address成员加上
transient
标记,这样该成员就不会被序列化进去。如果 address 成员需要进行序列化的话,则Address类也需要实现Serializable接口。
如果 address 成员不需要进行序列化的话,可以加上transient关键字,则address成员不做序列化操作,值为null。如下:
当然还有其他方式:
比如实现Externalizable接口,重写readExternal(ObjectInput in)和writeExternal(ObjectOutput out)方法。
还有一个替代实现Externalizable接口方法,还是实现Serializable接口,添加writeObject(ObjectOutputStream obs)和readObject(ObjectInputStream ois)方法。
再说说为什么Address一定要实现Serializable,或者加上transient关键字Person才能进行序列化?
先看看不做处理,使用 ObjectOutputStream 来持久化对象,抛出的异常
看ObjectOutputStream源码:
从此可知, 如果被写对象类型是String、Array、Enum、Serializable,就可以进行序列化,否则将抛出NotSerializableException。且在序列化对象时,不仅会序列化当前对象本身,还会对该对象引用的其它对象也进行序列化。