Java:哪些可以序列化,哪些不能序列化?

32

如果Serializable接口只是用于传递Java类的元数据的标记接口-我有点困惑:

阅读了Java序列化算法的过程(从底部到顶部的元数据,然后从顶部到底部的实际实例数据),我无法真正理解哪些数据不能通过该算法处理。

简而言之:

  1. 什么数据可能导致NotSerializableException
  2. 我应该如何知道不应该为我的类添加implements Serializable子句?

简而言之:我认为从几个答案的结合中所读到的是,基本的Java类型是可序列化的,但如果你将它们放在一个类中,那么该类(或其父类之一)需要被标记为Serializable。如果任何一个类(或父类)没有明确标记为Serializable,则它就不是可序列化的。如果你将一个类标记为Serializable,那么它的所有成员都需要遵循这些相同的规则。 - Andrew
然而,如果一个类被标记为Serializable,则该Serializable类的任何成员类实例(仅包含Serializable类型作为其成员的其他类,等等)也将是Serializable,无需显式声明。这是我从自己的代码中获得的结论。 - Andrew
1
@Andrew 关于“成员类”的最后一条注释 - 每个应该(并且可以...)被序列化的类都需要标记为Serializable。任何其他选项至少都是非常糟糕的做法。 - MordechayS
9个回答

16

首先,如果您不计划序列化类的任何实例,则根本没有必要考虑对其进行序列化。只需实现您需要的内容,不要为了序列化而试图使您的类可序列化。

如果您的对象引用(传递或直接)到任何非可序列化对象,并且此引用未标记为transient关键字,则您的对象将无法序列化。

通常,对于那些在反序列化后或其他地方无法重用的对象,对它们进行序列化是没有意义的。这可能是因为对象的状态只在此处和现在有意义(例如,如果它引用正在运行的线程),或者因为它使用某些资源,如套接字、数据库连接或类似资源。许多对象并不代表数据,因此不应该可序列化。


14
当谈论到NotSerializableException时,它会在您想要序列化一个未被标记为Serializable的对象时抛出 - 就是这样。但是,如果您扩展了不可序列化的类并添加了Serializable接口,那么就没有问题了。
没有不能被序列化的数据。

2
唯一真正理解我的意思的人是:我不想知道何时使用“可序列化”接口。我只想了解哪些数据是可序列化的,哪些数据不是。如果你们中的一些人(@JB Nizet等)能够添加一个小的解释:如果任何数据最终都由基元组成 - 而这些基元是可序列化的 - 那么为什么Java开发人员创建了这个接口? - MordechayS
2
Serializable是一个标记接口,用于区分您想要序列化的对象和您不想要序列化的对象。有许多类型的对象在序列化时没有意义,例如Connection / File等类型的对象。这些表示与特定机器绑定的资源,在序列化时没有任何意义。因此需要使用标记接口。 - Gautam
@Michal Borek,你说得很好,没有不能序列化的数据,那么为什么我们需要Serializable属性,为什么Serializable属性不能是隐式行为(我只是好奇)。 - Shankar S
1
@ShankarS 这是因为您可能希望禁止类的序列化。我当时的意思是,只要提供适当的序列化机制,就可以序列化任何对象。在某些情况下,您可能需要提供自定义序列化程序,在某些情况下,隐式序列化就足够了。Java是一种显式语言,通常需要声明显而易见的内容,例如本地变量的类型(这 tends to change in newer versions - the "var" keyword,但仍然是Java构建的方式)。 - Michal Borek

6

如果您的Serializable类中包含未序列化的内容,将会抛出此异常。您可以使用transient关键字来避免该异常。

常见的无法序列化的内容包括Swing组件和线程。如果您考虑一下,这是有道理的,因为您永远无法反序列化它们并使其有意义。


5
所有的原始数据类型和类都直接继承自Serializable
class MyClass extends Serializable{
}

或者间接地,
class MyClass extends SomeClass{
}

SomeClass实现了Serializable接口。

可以被序列化。实现Serializable接口的类中所有字段都会被序列化,除非该字段被标记为transient。如果一个序列化类中包含了一个不可序列化的字段(非基本类型并且没有实现Serializable接口),那么将抛出NotSerializableException异常。

对于第二个问题的回答:正如@JB Nizet所说,只有当你打算将一个类的实例写入某个流中时,才需要将该类标记为Serializable,否则永远不要将一个类标记为Serializable。


4

2

NotSerializable 异常是在你的可序列化对象中有某些标记为可序列化的内容时抛出的。其中一个这样的情况可能是:

class Super{}
class Sub implements Serializable
{
Super super;

这里没有将super声明为可序列化,因此会抛出NotSerializableException异常。


2
阅读完Java序列化算法的过程后(元数据从底部到顶部,然后实际实例数据从顶部到底部),我真的无法理解哪些数据不能通过该算法处理。
答案是某些系统级别的类,例如Thread、OutputStream及其子类,它们不可序列化。在Oracle文档中有很好的解释:http://www.oracle.com/technetwork/articles/java/javaserial-1536170.html 以下是摘要:
另一方面,某些系统级别的类,例如Thread、OutputStream及其子类以及Socket不可序列化。事实上,如果它们可以被序列化,这是没有任何意义的。例如,在我的JVM中运行的线程将使用我的系统内存。持久化它并尝试在您的JVM中运行它根本没有任何意义。

0
更实际的是,除非它的类实现了Serializable接口,否则没有任何对象可以通过Java内置机制进行序列化。然而,仅仅是成为这样一个类的实例并不足够:为了成功地序列化一个对象,它持有的所有非瞬态引用必须为null或者指向可序列化的对象。请注意,这是一个递归条件。原始值、null和瞬态变量不是问题。静态变量不属于单个对象,因此它们也不会造成问题。
一些常见的类是可靠的序列化安全的。字符串可能是最显著的例子,但所有基本类型的包装类也是安全的。原始类型的数组是可靠的可序列化的。如果它们的所有元素都可以序列化,那么引用类型的数组也可以被序列化。

如果一个可序列化的类具有非可序列化类的对象引用,但该引用为null,会发生什么?您能否更详细地解释一下在序列化时如何处理null? - Akhil Dad

0
在Java中,我们将对象(已经实现了“ Serializable 接口”的Java类的实例)进行序列化。因此,很明显,如果一个类没有实现“ Serializable 接口”,它就无法被序列化(在这种情况下会抛出“ NotSerializableException”)。
Serializable接口只是一个“标记接口”,可以说它只是一个类上的标记,告诉JVM该类可以序列化。
那么如何知道我不应该为我的类添加实现Serializable的语句呢?
这完全取决于您的需求。
1.如果您想将对象存储在数据库中,可以将其序列化为一系列字节,然后将其作为持久数据存储在数据库中。
2.您可以将您的对象序列化以供其他在不同机器上运行的JVM使用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接