我们应该在什么时候实现Serializable接口?

212
public class Contact implements Serializable {
    private String name;
    private String email;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}
  1. 什么时候应该实现 Serializable 接口?
  2. 为什么要这样做?
  3. 它是否具有任何优势或安全性?

5
这里接受的答案是不完整和误导性的,因为它没有涉及到安全上的缺陷。请看《Effective Java》的第86条:“非常谨慎地实现Serializable”。在此处,Raedwald的答案说不要使用序列化是正确的。 - Nathan Hughes
3个回答

187
  1. 关于“序列化”的问题,来自这里

    它可以让你将一个或一组对象存储到磁盘上或通过有线或无线传输机制发送它们,然后在可能位于另一台计算机上的另一个时间,反向进行此过程:恢复原始的对象。基本机制是将对象压缩为一维的比特流,并将该比特流转换回原始的对象。

    就像星际迷航中的"传送器"一样,它的全部内容都是将复杂的东西变成一系列的1和0,然后将该1和0的序列(可能是在另一个地方,也可能是在另一个时间)重新构建为原始的复杂"东西"。

    因此,当您需要存储对象的副本、将它们发送到运行在同一系统或网络上的另一个进程时,请实现Serializable接口。

  2. 因为您想要存储或发送一个对象。

  3. 它使存储和发送对象变得容易。与安全无关。


7
在所有领域模型中实现可序列化接口是最佳实践吗? - theJava
13
这并不是最佳实践的问题,而是关于你是否需要一系列字节的问题。 - moinudin
7
使用JSON时,您无需实现该接口,只需发送该字符串即可。因此,我仍然不确定为什么要使用此接口,当您可以使用JSON时。 - CodeMonkey
1
@DarrellTeague 这不是针对JVM或版本的特定问题。它是专门设计成不会有这种情况发生的。你有关于Log4J问题的参考链接吗?我认为更有可能的是Java序列化的预期稳定性按设计工作,而Log4J开发人员误解了如何正确使用序列化。 - VGR
1
@DarrellTeague 当然可以,但这不是由于使用不同的JVM引起的,而是由于使用不同的类引起的。实际上,每个Swing组件的文档都说:“警告:此类的序列化对象将与未来的Swing版本不兼容。当前的序列化支持适用于短期存储或在运行相同Swing版本的应用程序之间进行RMI。”出现此警告是因为通常情况下,在Java SE中可序列化的类确实负责处理版本之间的序列化兼容性。 - VGR
显示剩余13条评论

109
这个问题的答案可能令人惊讶,实际上是“永远不要使用”,或者更现实地说,“只有在强制与旧代码进行互操作性时才使用”。这是 Joshua Bloch 在他的书 《Effective Java, 3rd Edition》 中的建议:

在你编写的任何新系统中都没有理由使用Java序列化

甲骨文公司的首席架构师Mark Reinhold在记录中表示,删除当前的Java序列化机制是一个长期的目标。

为什么Java序列化存在缺陷

Java提供了一个序列化方案,您可以通过使用Serializable接口选择加入。然而,该方案存在几个无法解决的缺陷,并应被Java语言设计者视为一次失败的实验。

  • 它基本上假装我们可以谈论一个对象的序列化形式。但是有无限多种序列化方案,导致无限多种序列化形式。通过强制使用一种方案,没有任何改变方案的方法,应用程序不能使用最适合它们的方案。
  • 它作为构造对象的另一种方式实现,绕过了构造函数或工厂方法执行的任何先决条件检查。除非编写棘手、容易出错且难以测试的额外反序列化代码,否则您的代码可能存在严重的安全漏洞。
  • 测试不同版本的序列化形式之间的互操作性非常困难。
  • 处理不可变对象很麻烦。

应该采用哪种方案

应该使用一种你可以明确控制的序列化方案,例如Protocol Buffers、JSON、XML或自定义的方案。


4
我不是那个水平上的专家,但我觉得你说得有道理。 - nightfury
3
我认为这是最好的答案! - jjanczur
1
我们需要的答案!谢谢。 - Aviral Verma
这个Sonar规则 https://rules.sonarsource.com/java/RSPEC-1948 怎么样?它声称大多数J2EE框架在重负载下会刷新到磁盘。对我来说听起来有点过时,但是有人知道更多吗? - Hannes Schneidermayer
1
迄今为止,我所知道的让您的应用在Tomcat重新启动后仍然生存的唯一方法是,在会话范围bean中实现所有存储项的序列化。对吧?我猜还有另一种方式,但是在几年前,那是合法的做法。 - horvoje
1
@horvoje,对此的答案是,不要使用老式的JEE功能和方法。Web服务器只需要来自客户端的令牌(或cookie)来指示会话。任何实际必要的会话数据都可以像任何其他数据一样持久化在数据库中。Web服务器中的任何存储都只是为了提高性能而进行的缓存。 - Raedwald

53
  1. 当您想将类的实例转换为一系列字节或认为Serializable对象可能引用您的类的实例时,请实现Serializable接口。

  2. Serializable类在您希望持久化它们的实例或通过网络发送它们时非常有用。

  3. Serializable类的实例可以很容易地传输。然而,序列化确实具有一些安全后果。请阅读Joshua Bloch的《Effective Java》。


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