热门标签 | 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. 序列化代理模式

推荐阅读
  • 深入解析CAS机制:全面替代传统锁的底层原理与应用
    本文深入探讨了CAS(Compare-and-Swap)机制,分析了其作为传统锁的替代方案在并发控制中的优势与原理。CAS通过原子操作确保数据的一致性,避免了传统锁带来的性能瓶颈和死锁问题。文章详细解析了CAS的工作机制,并结合实际应用场景,展示了其在高并发环境下的高效性和可靠性。 ... [详细]
  • CTF竞赛中文件上传技巧与安全绕过方法深入解析
    CTF竞赛中文件上传技巧与安全绕过方法深入解析 ... [详细]
  • vue引入echarts地图的四种方式
    一、vue中引入echart1、安装echarts:npminstallecharts--save2、在main.js文件中引入echarts实例:  Vue.prototype.$echartsecharts3、在需要用到echart图形的vue文件中引入:   importechartsfrom"echarts";4、如果用到map(地图),还 ... [详细]
  • 本文将深入探讨 iOS 中的 Grand Central Dispatch (GCD),并介绍如何利用 GCD 进行高效多线程编程。如果你对线程的基本概念还不熟悉,建议先阅读相关基础资料。 ... [详细]
  • 2020年9月15日,Oracle正式发布了最新的JDK 15版本。本次更新带来了许多新特性,包括隐藏类、EdDSA签名算法、模式匹配、记录类、封闭类和文本块等。 ... [详细]
  • 在多线程并发环境中,普通变量的操作往往是线程不安全的。本文通过一个简单的例子,展示了如何使用 AtomicInteger 类及其核心的 CAS 无锁算法来保证线程安全。 ... [详细]
  • 本文总结了一些开发中常见的问题及其解决方案,包括特性过滤器的使用、NuGet程序集版本冲突、线程存储、溢出检查、ThreadPool的最大线程数设置、Redis使用中的问题以及Task.Result和Task.GetAwaiter().GetResult()的区别。 ... [详细]
  • MySQL Decimal 类型的最大值解析及其在数据处理中的应用艺术
    在关系型数据库中,表的设计与SQL语句的编写对性能的影响至关重要,甚至可占到90%以上。本文将重点探讨MySQL中Decimal类型的最大值及其在数据处理中的应用技巧,通过实例分析和优化建议,帮助读者深入理解并掌握这一重要知识点。 ... [详细]
  • 本文深入探讨了NoSQL数据库的四大主要类型:键值对存储、文档存储、列式存储和图数据库。NoSQL(Not Only SQL)是指一系列非关系型数据库系统,它们不依赖于固定模式的数据存储方式,能够灵活处理大规模、高并发的数据需求。键值对存储适用于简单的数据结构;文档存储支持复杂的数据对象;列式存储优化了大数据量的读写性能;而图数据库则擅长处理复杂的关系网络。每种类型的NoSQL数据库都有其独特的优势和应用场景,本文将详细分析它们的特点及应用实例。 ... [详细]
  • 在Django中提交表单时遇到值错误问题如何解决?
    在Django项目中,当用户提交包含多个选择目标的表单时,可能会遇到值错误问题。本文将探讨如何通过优化表单处理逻辑和验证机制来有效解决这一问题,确保表单数据的准确性和完整性。 ... [详细]
  • C++ 开发实战:实用技巧与经验分享
    C++ 开发实战:实用技巧与经验分享 ... [详细]
  • 如何精通编程语言:全面指南与实用技巧
    如何精通编程语言:全面指南与实用技巧 ... [详细]
  • Spring框架的核心组件与架构解析 ... [详细]
  • 2018年9月21日,Destoon官方发布了安全更新,修复了一个由用户“索马里的海贼”报告的前端GETShell漏洞。该漏洞存在于20180827版本的某CMS中,攻击者可以通过构造特定的HTTP请求,利用该漏洞在服务器上执行任意代码,从而获得对系统的控制权。此次更新建议所有用户尽快升级至最新版本,以确保系统的安全性。 ... [详细]
  • 通过将常用的外部命令集成到VSCode中,可以提高开发效率。本文介绍如何在VSCode中配置和使用自定义的外部命令,从而简化命令执行过程。 ... [详细]
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社区 版权所有