在 Java 中有一个特殊的接口 Serializable,很长一段时间只知道其与类的序列化和反序列化有关,有的人习惯给实体类实现这个方法并定义一个常量 serialVersionUID 有些人在编码过程中从来不使用 Serializable. 这两种截然不同的做法,都不曾听说有哪一种会遇到什么问题,然而这本身就是一种令人困扰的话题,究竟要不要去实现 Serializable?

关于这个问题,最早是在 V 站看过一篇讨论,同意需要实现的占相对多数,也有不少人认为完全不需要去实现 Serializable.

首先,实现 Serializable 与否究竟有什么差异

我认为,如果一个类不实现 Serializable 接口,直接的影响是无法使用 Java 原生的序列化方法对这个类进行序列化和反序列化。下面是将 Java 类写入到磁盘的例子

1
2
3
4
5
6
7
SimpleUser user = new SimpleUser();
user.setId(1L);
user.setFullName("李新宇");

FileOutputStream fo = new FileOutputStream("/tmp/user");
ObjectOutputStream so = new ObjectOutputStream(fo);
so.writeObject(user);

序列化后的 /tmp/user 文件如下

1
2
��sr%com.yuxisoft.meiyu.pojo.bo.SimpleUserIx���FdfullNametLjava/lang/String;LidtLjava/lang/Long;LversiontLjava/lang/Integer;xpt	李新宇srjava.lang.Long;��̏#�Jvaluexrjava.lang.Number���
��xpp%

序列化的前提是 SimpleUser 类实现了 Serializable 接口,否则会抛出 java.io.NotSerializableException 异常

serialVersionUID 的值可以改变吗

一般情况下不可以改变,会造成之前序列化好的对象无法反序列化,异常信息为 java.io.InvalidClassException. 当然,如果不打算兼容以前的序列化产物,表名版本差异,可以对其进行更新

只实现 Serializable 接口而不定义 serialVersionUID 字段会怎样

如果不显式地定义 serialVersionUID 字段,jvm 会基于包名,类名,继承关系,非私有的方法和属性,以及参数,返回值等自动计算这个属性的值。因此,一旦类有修改,计算得到的 serialVersionUID 的值也必然发生改变,反序列化以前的序列化产物时同样会抛出 java.io.InvalidClassException,因此实现 Serializable 接口,基本都要定义 serialVersionUID 字段

不实现 Serializable 接口的理由是什么

Java 原生的序列化和反序列化在实际项目中使用的频率很低甚至没有,在现在的 Java Web 项目中,一般都采用 Json 对 Java 对象进行封装和传输、解析,基本不会有对象直接以流的形式直接写入文件,然后读取的场景,实际开发过程中不实现 Serializable 完全没有任何问题

实现 OR 不实现,怎么选择

需要看情况,首先是确定项目中有没有使用 Java 原生序列化或者依赖的其它框架需要使用 Java 原生序列化的场景,如果没有,大可以不实现,但是实现了就一定要定义 serialVersionUID 字段。在写和不写都可以的情况下,看下团队其他成员的习惯,大家保持统一,目前来看实现这个 Serializable 接口要比不实现在某种程度上要政治正确,但我认为团队内部也要理性,可能大家为了保持代码风格统一去写或者不写,但个人要正确地理解这一接口,做到心中有数即可。