热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

EffectiveJava读书笔记(十):序列化

EffectiveJava读书笔记十序列化谨慎地实现Serializable接口考虑使用自定义的序列化形式保护性地编写readObject方法对于实例控制枚举类型优先于readRe

  • Effective Java 读书笔记十序列化
    • 谨慎地实现 Serializable 接口
    • 考虑使用自定义的序列化形式
    • 保护性地编写 readObject 方法
    • 对于实例控制枚举类型优先于 readResolve
    • 考虑用序列化代理代替序列化实例
    • 更多资料


Effective Java 读书笔记(十):序列化

谨慎地实现 Serializable 接口

实现 Serializable 接口是一个很严肃的承诺。

  1. 实现 Serializable 接口付出的最大代价是,一旦这个类被发布,就大大降低了“改变这个类的实现的灵活性“。
    • 其字节流编码(私有和包级私有域)变成了导出的 API 的一部分,不符合“最低限度地访问域”的实践准则。
  2. 序列版本 UID 可以手动指定,但若没有指定,则会根据类名称、接口名称、所有公有和受保护的域进行计算。
  3. 增加了出现 bug 和安全漏洞的可能性。
    • 反序列化机制是一个“隐藏的构造器”,使用默认的反序列化机制,很容易破坏“由真正的构造器建立起来的约束关系”。
  4. 随着类的新版本的发布,相关的测试负担也增加了,因为你必须确保二进制兼容性和语义兼容性。
  5. 为了继承而设计的类,应尽可能少地去实现 Serializable 接口。但是,最好提供一个无餐构造方法,允许子类去实现 Serializable 接口。
  6. 内部类不应该实现 Serializable 接口,因为其默认序列化形式是定义不清楚的。但是,静态成员类可以实现 Serializable 接口。

考虑使用自定义的序列化形式


  1. 如果一个对象的物理表示法等同于它的逻辑内容,可能就适合于使用默认的序列化形式。当一个对象的物理表示法与它的逻辑数据内容有实质性的区别时,使用默认序列化会有 4 个缺点:
    • 它使这个类的导出 API 永远地束缚在该类的内部表示法上了。
    • 消耗过多空间(导出了可以由其他字段推导出的字段)。
    • 消耗过多时间(做了无用功)。
    • 引起栈溢出(过深的递归遍历)。
  2. 即使你确定了使用默认序列化是合适的,通常还必须提供一个 readObject 方法以保证约束关系和安全性。
  3. 常见的逻辑内容和物理表示不同的有List、数组、Set,它们的逻辑内容都是“多个元素”,比如它们的 Json 序列化结果都是一样的。

通过定制 readObject 和 writeObject 可以定制序列化和反序列化。在方法中,先调用 defaultReadObject 或 defaultWriteObject 会有更好的兼容性(这两方法负责序列化和反序列化所有非 transient 实例域)。

讲一个域做成非 transient 时,一定要确保它的值时对象逻辑状态的一部分。

不管采用哪种序列化形式,都要为自己编写的每个可序列化类,声明一个显式的序列版本 UID(serial version UID)。这样可以避免 UID 称为潜在的不兼容根源,同时也减少了计算 UID 的性能开销。

保护性地编写 readObject 方法

readObject 相当于另一个公有构造器,如果构造方法里有做参数有效性检查,那么 readObject 里也要有。编写健壮 readObject 方法的指导方针如下:

  1. 对于对象引用域必须为私有的类,要保护性地拷贝这个域中的每个对象。不可变类的可变组件就属于这一类。
    • 没有保护性拷贝,容易被客户端拿到内部私有对象的引用。
  2. 对于任何约束条件,如果检查失败,抛出一个 InvalidObjectException 异常。这些检查动作应该放在所有保护性拷贝之后。
    • 不做检查,很容易通过伪造二进制数据流产生不合法的对象。
  3. 如果整个对象图在反序列化后必须进行验证,就应该使用 ObjectInputValidation 接口。
  4. 无论是直接方式,还是间接方式,都不要调用类中任何可被覆盖的方法。

对于实例控制,枚举类型优先于 readResolve


  1. 尽可能使用枚举来实施实例控制。
  2. 否则,若需要序列化 + 实例控制,就需要提供一个 readResolve 方法,并且确保该类所有实例域都是基本类型或是 transient 类型的。
  3. 对于一个 final 类,其 readResolve 方法应该是私有的。
  4. 对于非 final 类,其 readResolve 方法应该是 public 或 protected 或 package,子类覆盖时也必须覆盖 readResolve 方法,否则反序列化时容易有 ClassCastException。

考虑用序列化代理代替序列化实例

与 readResolve 相对的是 writeReplace,writeReplace 可以在序列化时,替换要序列化的对象。

序列化代理,就是使用一个代理类来对实例进行序列号,替代类本身的序列化功能。
1. 优点是安全,反序列化实例也是采用构造函数或静态工厂方法等常用方法构建,不必单独确保反序列化的实例一定要遵守类的约束条件;
2. 缺点是性能稍差、不能与可被客户端扩展到类兼容。

具体代码示例,可以看序列化代理模式。

更多资料


  1. 序列化代理模式

推荐阅读
  • 本文介绍了如何使用MATLAB调用摄像头进行人脸检测和识别。首先需要安装扩展工具,并下载安装OS Generic Video Interface。然后使用MATLAB的机器视觉工具箱中的VJ算法进行人脸检测,可以直接调用CascadeObjectDetector函数进行检测。同时还介绍了如何调用摄像头进行人脸识别,并对每一帧图像进行识别。最后,给出了一些相关的参考资料和实例。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
  • Java自带的观察者模式及实现方法详解
    本文介绍了Java自带的观察者模式,包括Observer和Observable对象的定义和使用方法。通过添加观察者和设置内部标志位,当被观察者中的事件发生变化时,通知观察者对象并执行相应的操作。实现观察者模式非常简单,只需继承Observable类和实现Observer接口即可。详情请参考Java官方api文档。 ... [详细]
  • 本文介绍了Java集合库的使用方法,包括如何方便地重复使用集合以及下溯造型的应用。通过使用集合库,可以方便地取用各种集合,并将其插入到自己的程序中。为了使集合能够重复使用,Java提供了一种通用类型,即Object类型。通过添加指向集合的对象句柄,可以实现对集合的重复使用。然而,由于集合只能容纳Object类型,当向集合中添加对象句柄时,会丢失其身份或标识信息。为了恢复其本来面貌,可以使用下溯造型。本文还介绍了Java 1.2集合库的特点和优势。 ... [详细]
  • 本文介绍了利用ARMA模型对平稳非白噪声序列进行建模的步骤及代码实现。首先对观察值序列进行样本自相关系数和样本偏自相关系数的计算,然后根据这些系数的性质选择适当的ARMA模型进行拟合,并估计模型中的位置参数。接着进行模型的有效性检验,如果不通过则重新选择模型再拟合,如果通过则进行模型优化。最后利用拟合模型预测序列的未来走势。文章还介绍了绘制时序图、平稳性检验、白噪声检验、确定ARMA阶数和预测未来走势的代码实现。 ... [详细]
  • 本文整理了Java中org.apache.solr.common.SolrDocument.setField()方法的一些代码示例,展示了SolrDocum ... [详细]
  • 本文由编程笔记#小编为大家整理,主要介绍了源码分析--ConcurrentHashMap与HashTable(JDK1.8)相关的知识,希望对你有一定的参考价值。  Concu ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • 展开全部下面的代码是创建一个立方体Thisexamplescreatesanddisplaysasimplebox.#Thefirstlineloadstheinit_disp ... [详细]
  • 有没有一种方法可以在不继承UIAlertController的子类或不涉及UIAlertActions的情况下 ... [详细]
  • Oracle10g备份导入的方法及注意事项
    本文介绍了使用Oracle10g进行备份导入的方法及相关注意事项,同时还介绍了2019年独角兽企业重金招聘Python工程师的标准。内容包括导出exp命令、删用户、创建数据库、授权等操作,以及导入imp命令的使用。详细介绍了导入时的参数设置,如full、ignore、buffer、commit、feedback等。转载来源于https://my.oschina.net/u/1767754/blog/377593。 ... [详细]
  • C语言的经典程序有哪些
    本篇内容介绍了“C语言的经典程序有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何 ... [详细]
author-avatar
mobiledu2502891987
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有