序列化是一种将对象的状态信息转换为字节流的过程,这种转换可以使得对象在 JVM 之间传递,或者写入到磁盘中持久保存。Java 提供了两个接口用来进行对象的序列化,分别是 Serializable 和 Externalizable。下面我们会根据四个方面,对两者进行详细的比较:
1. 自动化:
Serializable 接口仅仅是一个标记接口,不包含任何方法。当一个类实现了 Serializable 接口后,就可以使用一个 ObjectOutputStream 来写入一个对象,然后通过一个 ObjectInputStream 来读回这个对象。整个过程是 JVM 自动完成的,你的类仅仅需要提供一个 serialVersionUID 就可以了。
Externalizable 接口会提供两个方法:writeExternal() 和 readExternal()。这两个方法可以让开发者自定义序列化和反序列化的过程。实现了 Externalizable 接口的类在序列化时,不会自动序列化,而是需要程序员自己去实现具体的序列化过程。
2. 透明性:
因为 Serializable 的序列化过程是 JVM 自动完成的,所以它有很好的透明性。Serializable 可以序列化任何实现了 Serializable 接口的对象,而无需开发者实现任何方法。但是这种自动化也带来了一些问题,因为 JVM 是通过递归的方式把对象以及对象的引用对象写入到流中,这可能会导致把整个对象图都序列化。
而在 Externalizable 的序列化过程中,开发者必须显示地去调用父类和成员变量的序列化方法,从而可以自主地决定序列化的过程,具有更大的灵活性。
3.性能:
Serializable 因为其自动化、透明化的特性,对性能的影响可能会更大。不过从 Java 1.4 开始,Serializable 对象在序列化时,可以选择仅序列化非 transient 字段,这样在一定程度上可以提高效率。
与此相反,Externalizable 因为其允许开发人员自定义序列化过程,因此在处理大型对象或复杂对象图形时,可能会获得更好的性能。
4. 控制:
Serializable 对象允许开发者使用 transient 关键字去声明不需要序列化的字段。所以我们可以在某种程度上控制哪些字段需要序列化。
而 Externalizable 接口允许我们更加深入地控制序列化的过程是怎样的。如果你的类有特殊的序列化需求,或者你需要避免序列化一些可以从其他对象中获取的信息,那么 Externalizable 将是一个好的选择。
如果再更深入地讨论一下这两个接口,对比如下:
1. 默认行为:
Serializable 的默认行为是保存对象的所有字段,无论它们是基本类型或对象引用。只有标记为 “transient” 的字段将被忽略。
相反,Externalizable 中没有默认行为。writeExternal()和readExternal() 被期望完全处理对象的所有状态自身。这样做的好处是可以避免一些冗余,例如避免序列化那些易于从其他数据重新计算或重建的字段。
2.继承:
实现了 Serializable 的类,可以通过它们的父类继承序列化行为,自动包括基类字段在序列化中。
Externalizable 则不包含任何继承的概念,子类必须负责它们的整个状态,包括调用父类的保存和恢复方法。否则,只有子类的某些字段会被存储和访问。
3. 兼容性:
使用 Serializable,如果添加了新的字段,反序列化过程能够完全兼容写在旧版本中的对象,新添加的字段将会被设置为其默认值。但是,对于实现了 Externalizable 的对象,如果在类中添加了新的字段,这些字段默认不会序列化,除非我们在 wirteExternal()
方法中显式地处理这些新增字段。
4. 在类库中的使用:
在 java 类库自己的代码中,Serializable 是最通用的序列化接口,被广泛的用在各种标准的数据结构中,例如各种 Collection 类型。而 Externalizable 通常只在需要进一步优化序列化性能和效率的时候使用。
Serializable 和 Externalizable 接口都为 Java 类提供了序列化能力。相比之下,Serializable 提供的是一种自动且完全的序列化机制,对开发者非常友好。而 Externalizable 可以提供更大的灵活性和控制,但使用起来更复杂一些。在选择哪个接口的时候,要基于你的使用场景和需求。在许多情况下,Serializable 已经足够,但当需要更细粒度的控制序列化过程时,Externalizable 是必不可少的。