Effective Java(3rd)-Item86 非常谨慎地实现Serializable

  允许对类的实例进行序列化可以非常简单,只需将单词implements Serializable添加到其声明中即可。因为这很容易做到,所以有一个普遍的误解,认为序列化只需要程序员付出很少的努力。事实要复杂得多。虽然使类可序列化的即时成本可以忽略不计,但长期成本通常是巨大的。
  实现Serializable的一个主要成本是,一旦类的实现被释放,它就会降低更改该类实现的灵活性。当类实现Serializable时,其字节流编码(或序列化形式)成为其导出API的一部分。一旦广泛分发了一个类,通常就需要永远支持序列化的表单,就像需要支持导出API的所有其他部分一样。如果您不努力设计自定义序列化表单,而只是接受默认值,则序列化表单将永远绑定到类的原始内部表示。换句话说,如果您接受默认的序列化表单,类的私有和包私有实例字段将成为其导出API的一部分,并且最小化字段访问的实践(item15)将失去其作为信息隐藏工具的有效性。

  如果您接受默认的序列化表单,然后更改类的内部表示,则会导致序列化表单中不兼容的更改。试图使用类的旧版本序列化实例并使用新版本反序列化实例的客户机(反之亦然)将经历程序失败。在维护原始序列化表单的同时,可以更改内部表示(使用ObjectOutputStream.putFields和ObjectInputStream.readFields),但是这很困难,并且会在源代码中留下明显的缺陷。如果您选择使类可序列化,您应该仔细设计一个高质量的序列化表单,以便长期使用(第87、90项)。这样做会增加开发的初始成本,但是这样做是值得的。即使是设计良好的序列化形式,也会限制类的演化;设计不良的序列化表单可能会造成严重后果。
  可串行化性对演化施加的约束的一个简单示例涉及流惟一标识符(通常称为串行版本)
uid。每个可序列化的类都有一个与之关联的惟一标识号。如果您没有通过声明一个名为serialVersionUID的静态final长字段来指定这个数字,系统在运行时通过对类的结构应用加密哈希函数(SHA-1)自动生成它。这个值受类的名称、它实现的接口及其大多数成员(包括编译器生成的合成成员)的影响。如果你改变了这些的任何,比如,通过添加一个方便的方法,生成的串行版本UID发生了变化。如果您未能声明串行版本UID,兼容性将被破坏,从而在运行时导致InvalidClassException异常。
  实现Serializable的第二个代价是增加了bug和安全漏洞的可能性(item85 )。通常,对象是用构造函数创建的;序列化是一种用于创建对象的超语言机制。无论您接受默认行为还是覆盖它,反序列化都是一个“隐藏构造函数”,与其他构造函数具有相同的所有问题。由于没有与反序列化关联的显式构造函数,因此很容易忘记必须确保它保证构造函数建立的所有不变量,并且不允许攻击者访问正在构造的对象的内部。赖默认的反序列化机制可以很容易地让对象暴露于不变的破坏和非法访问(item88 )。

  实现Serializable的第三个成本是,它增加了与发布类的新版本相关的测试负担。当一个可序列化的类被修改时,重要的是检查是否可以在新版本中序列化一个实例,并在旧版本中反序列化它,反之亦然。因此,所需的测试量与可序列化类的数量和版本的数量成正比,这两个数量可能很大。您必须确保序列化-反序列化过程成功,并确保它生成原始对象的忠实副本。如果在第一次编写类时仔细设计了自定义序列化表单,那么测试的需求就会减少 (Items 87, 90).
  实现Serializable并不是一个轻松的决定。如果一个类要参与一个依赖于Java序列化来进行对象传输或持久性的框架,这是非常重要的。此外,它还极大地简化了将类作为必须实现Serializable的另一个类中的组件的使用。然而,与实现Serializable相关的成本很多。每次设计一个类时,都要权衡利弊。历史上,像BigInteger和Instant这样的值类实现了序列化,集合类也实现了序列化。表示活动实体(如线程池)的类很少应该实现Serializable。
  为继承而设计的类(item19)应该很少实现 可序列化的,接口很少应该扩展它。违反此规则会给扩展类或实现接口的任何人带来很大的负担。有时违反规则是适当的。例如,如果一个类或接口的存在主要是为了参与一个要求所有参与者实现Serializable的框架,那么类或接口实现或扩展Serializable可能是有意义的。

  为继承而设计的实现序列化的类包括
Throwable和Component。Throwable实现了Serializable,因此RMI可以将异常从服务器发送到客户机。Component实现了序列化
gui可以被发送、保存和恢复,但是即使在Swing和AWT的鼎盛时期,这个工具在实践中也很少使用。
  如果您使用实例字段实现了一个既可序列化又可扩展的类,那么需要注意几个风险。如果实例字段值上有任何不变量,关键是要防止子类覆盖finalize方法,该类可以通过覆盖finalize并声明它为final来实现这一点。否则,该类将容易受到终结器攻击(item8)。最后,如果类的实例字段初始化为默认值(整数类型为0,布尔值为false,对象引用类型为null),那么必须添加readObjectNoData方法:

image.png

  这个方法是在Java 4中添加的,以涵盖一个涉及到将可序列化超类添加到现有可序列化类[serializable, 3.5]的特殊情况。
  关于不实现Serializable的决定,有一个警告。如果为继承而设计的类不可序列化,则可能需要额外的工作来编写可序列化的子类。此类的常规反序列化要求超类具有可访问的无参数构造函数[Serialization, 1.10]。如果不提供这样的构造函数,子类将被迫使用序列化代理模式( item90 )。

  内部类(内部类(第24项)不应该实现Serializable。)不应该实现Serializable。它们使用编译器生成的合成字段存储对封闭实例的引用,并存储来自封闭范围的局部变量的值。这些字段与类定义的对应关系,以及匿名类和本地类的名称都是未指定的。因此,内部类的默认序列化形式定义不正确。但是,静态成员类可以实现Serializable。
  总而言之,实现Serializable的简单性是似是而非的。除非类只在受保护的环境中使用,在这种环境中,版本永远不必互操作,服务器永远不会暴露给不可信的数据,否则实现
Serializable是一个严肃的承诺,应该非常谨慎地做出。如果类允许继承,则需要格外小心。
本文写于2019.7.24,历时1天

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容