当前位置: 首页 > news >正文

第八十六条:在实现serializable接口时要特别谨慎

要想使一个类的实例可被序列化,非常简单,只要在它的声明中加入"implements Serializable"字样即可。虽然使一个类可被序列化的直接开销低到甚至可以忽略不计,但是为了序列化而付出的长期开销往往是实实在在的

为实现Serializable而付出的最大代价是,一旦一个类被发布,就大大降低了"改变这个类的实现"的灵活性。 如果一个类实现了Serializable,它的字节流编码(或者说序列化形式,serialized form)就变成了它的导出的API的一部分。一旦这个类被广泛使用,往往必须永远支持这种序列化形式,就好像你必须要支持导出的API的所有其他部分一样。如果你不努力设计一个自定义的序列化形式(custom serialized form),而仅仅接受了默认的序列化形式,这种序列化形式将被永远地束缚在该类最初的内部表示法上。换句话说,如果你接受了默认的序列化形式,这个类中私有的和包级私有的实例域将都变成导出的API的一部分,这不符合"最低限度地访问域"的实践准则(见第13条),从而它就失去了作为信息隐藏工具的有效性

如果你接受了默认的序列化形式,并且以后又要改变这个类的内部表示法,结果可能导致序列化形式的不兼容。客户端程序企图用这个类的旧版本来序列化一个类,然后用新版本进行反序列化,结果将导致程序失败。在改变内部表示法的同时仍然维持原来的序列化形式(使用ObjectOutputStream.putFields和ObjectInputStream.readFields),这也是可能的,但是做起来比较困难,并且会在源代码中留下一些可以明显的隐患。因此,你应该仔细地设计一种高质量的序列化形式,并且在很长时间内都愿意使用这种形式(见第75,78条)。这样做将会增加开发的初始成本,但这是值得的。设计良好的序列化形式也许会给类的演变带来限制;但是设计不好的序列化形式则可能会使类根本无法演变。

序列化会使类的演变受到限制,这种限制的一个例子与流的唯一标识符(stream unique identifier)有关,通常它也被称为序列版本UID(serial version UID)。每个可序列化的类都有一个唯一标识号与它相关联。如果你没有在一个名为serialVersionUID的私有静态final的long域中显式地指定该标识号,系统就会自动地将一个复杂的过程作用在这个类上,从而在运行时产生该标识号。这个自动产生的值会受到类名称、它所实现的接口的名称、以及所有公有的和受保护的成员的名称所影响。如果你通过任何方式改变了这些信息,比如,增加了一个不是很重要的工具方法,自动产生的序列版本UID也会发生变化。因此,如果你没有声明一个显式的序列版本UID,兼容性将会遭到破坏,在运行时导致InvalidClassException异常。

实现Serializable的第二个代价是,它增加了出现Bug和安全漏洞的可能性。 通常情况下,对象是利用构造器来创建的;序列化机制是一种语言之外的对象创建机制(extralinguistic mechanism)。无论你是接受了默认的行为,还是覆盖了默认的行为,反序列化机制(deserialization)都是一个"隐藏的构造器",具备与其他构造器相同的特点。因为反序列化机制中没有显式的构造器,所以你很容易忘记要确保:反序列化过程必须也要保证所有"由构造器建立起来的约束关系",并且不允许攻击者访问正在构造过程中的对象的内部信息。依靠默认的反序列化机制,可以很容易地使对象的约束关系遭到破坏,以及遭受到非法访问(见第76条)。

实现Serializable的第三个代价是,随着类发行新的版本,相关的测试负担也增加了。 当一个可序列化的类被修订的时候,很重要的一点是,要检查是否可以"在新版本中序列化一个实例,然后在旧版本中反序列化",反之亦然。因此,测试所需要的工作量与"可序列化的类的数量和发行版本号"的乘积成正比,这个乘积可能会非常大。这些测试不可能自动构建,因为除了二进制兼容性(binary compatibility)以外,你还必须测试语义兼容性(semantic compatibility)。换句话说,你必须既要确保"序列化-反序列化"过程成功,也要确保结果产生的对象真正是原始对象的复制品。可序列化类的变化越大,它就越需要测试。如果在最初编写一个类的时候,就精心设计了自定义的序列化形式,测试的要求就可以有所降低,但是也不能完全没有测试。

实现Serializable接口并不是一个很轻松就可以做出的决定。它提供了一些实在的益处:如果一个类将要加入到某个框架中,并且该框架依赖于序列化来实现对象传输或者持久化,对于这个类来说,实现Serializable接口就非常有必要。更进一步来看,如果这个类要成为另一个类的一个组件,并且后者必须实现Serializable接口,若前者也实现了Serializable接口,它就会更易于被后者使用。 然而,有许多实际的开销都与实现Serializable接口有关。每当你实现一个类的时候,都需要权衡一下所付出的代价和带来的好处。根据经验,比如Date和BigInteger这样的值类应该实现Serializable,大多数的集合类也应该如此。代表活动实体的类,比如线程池(thread pool),一般不应该实现Serializable。

为了继承而设计的类(见第17条)应该很少实现Serializable,接口也应该很少会扩展它。如果违反了这条规则,扩展这个类或者实现这个接口的程序员就会背上沉重的负担。然而在有些情况下违反这条规则却是合适的。例如,如果一个类或者接口存在的目的主要是为了参与到某个框架中,该框架要求所有的参与者都必须实现Serializable,
那么,对于这个类或者接口来说,实现或者扩展Serializable就是非常有意义的。

为了继承而设计的类中真正实现了Serializable的有Throwable、Component和HttpServlet。因为Throwable实现了Serializable,所以RMI的异常可以从服务器端传到客户端。Component实现了Serializable,因此GUI可以被发送、保存和恢复。HttpServlet实现了Serializable,因此会话状态可以被缓存。

如果你实现了一个带有实例域的类,它是可序列化和可扩展的,你就应该担心这样一条告诫。

如果一个专门为了继承而设计的类不是可序列化的,就不可能编写出可序列化的子类。特别是,如果超类没有提供可访问的无参构造器,子类也不可能做到可序列化。因此,对于为继承而设计的不可序列化的类,你应该考虑提供一个无参构造器。

内部类不应该实现Serializable。它们使用编译器产生的合成域(synthetic field)来保存指向外围实例(enclosing instance)的引用,以及保存来自外围作用域的局部变量的值。"这些域如何对应到类定义中"并没有明确的规定,就好像没有指定匿名类和局部类的名称一样。因此,内部类的默认序列化形式是定义不清楚的。然而,静态成员类(static member class)却可以实现Serializable。

简而言之,千万不要认为实现Serializable会很容易。除非一个类在用了一段时间之后就会被抛弃,否则,实现Serializable就是个很严肃的承诺,必须认真对待。如果一个类是为了继承而设计的,则更加需要加倍小心。对于这样的类而言,在"允许子类实现Serializable"或"禁止子类实现Serializable"两者之间的一个折衷设计方案是,提供一个可访问的无参构造器。这种设计方案允许(但不要求)子类实现Serializable

所有文章无条件开放,顺手点个赞不为过吧!

                                                              

                                                            

相关文章:

第八十六条:在实现serializable接口时要特别谨慎

要想使一个类的实例可被序列化,非常简单,只要在它的声明中加入"implements Serializable"字样即可。虽然使一个类可被序列化的直接开销低到甚至可以忽略不计,但是为了序列化而付出的长期开销往往是实实在在的。 为实现Serializable…...

【Elasticsearch 中间件】Elasticsearch 客户端使用案例

文章目录 一、安装 Elasticsearch1.1 启动 Elasticsearch1.2 启动 Kibana 二、客户端代码2.1 导入依赖2.2 配置 application.yaml2.3 定义实体类2.4 连接 Elasticserach2.5 定义 Service 层接口2.6 实现 Service 层功能 三、测试项目3.1 添加数据3.2 搜索数据3.3 更新数据3.4 删…...

深入理解MySQL中的ONLY_FULL_GROUP_BY

在MySQL数据库管理中,ONLY_FULL_GROUP_BY是一个重要的SQL模式,它直接影响着GROUP BY语句的执行方式和结果。本文将从基础概念出发,逐步解析ONLY_FULL_GROUP_BY的工作原理、应用场景及应对策略。 什么是ONLY_FULL_GROUP_BY? ONLY…...

获得日志记录之外的新视角:应用程序性能监控简介(APM)

作者:来自 Elastic David Hope 日志记录领域即将发生改变。在这篇文章中,我们将概述从单纯的日志记录到包含日志、跟踪和 APM 的完全集成解决方案的推荐流程。 通过 APM 和跟踪优先考虑客户体验 企业软件开发和运营已成为一个有趣的领域。我们拥有一些非…...

如何避免缓存击穿?超融合常驻缓存和多存储池方案对比

作者:SmartX 解决方案专家 钟锦锌 很多运维人员都知道,混合存储介质配置可能会带来“缓存击穿”的问题,尤其是大数据分析、数据仓库等需要频繁访问“冷数据”的应用场景,缓存击穿可能会更频繁地出现,影响业务运行。除…...

口语笔记——祈使句用法

省略主语 (You give me) a cup of tea, please. 一杯茶(You wait for) another minute. 两等一分钟(You) keep quiet. 保持安静give me a break. 饶了我吧take your hand off. 把你的手拿开take this thing away 把这东西拿开never talk to strangers. 永远不要跟陌生人说话Do…...

SQL连续登录问题(详细案例分析)

如果要统计用户活跃度,那就涉及连续登录问题,接下来将举一个简单的例子来详细说明这个问题: 一、创建一些模拟数据 一些测试数据如下: deviceid1,2022-10-26,2022-10-26,2022-11-01 deviceid1,2022-10-26,2022-11-03,2022-11-0…...

Next.js 系统性教学:深入理解缓存与数据优化策略

更多有关Next.js教程,请查阅: 【目录】Next.js 独立开发系列教程-CSDN博客 目录 前言 1. 缓存的基本概念 1.1 缓存的作用 1.2 Next.js 中的缓存策略 2. Next.js 的缓存机制 2.1 请求记忆化(Request Memoization) 2.1.1 什…...

【PyTorch】(基础六)---- 搭建卷积神经网络

关于神经网络中激活函数、卷积层、池化层等底层原理,我不会在本文中详解,但是关于pytorch中如何使用对应的方法实现这些层的功能我会进行解释,如果你想要了解一些关于神经网络底层的知识,我十分推荐你去看一下吴恩达老师的深度学习…...

【JAVA项目】基于ssm的【美食推荐管理系统】

【JAVA项目】基于ssm的【美食推荐管理系统】 技术简介:采用JSP技术、B/S架构、SSM框架、MySQL技术等实现。 系统简介:美食推荐管理系统,在系统首页可以查看首页、热门美食、美食教程、美食店铺、美食社区、美食资讯、我的、跳转到后台等内容。…...

adb 常用命令笔记

adb connect <ip> #连接指定ip adb disconnect <ip> #断开连接指定ip adb devices #查看连接中的设备 adb install <flie> #安装apk adb uninstall <packageName> #卸载app adb -s install <flie> #指定设备安装 adb shell pm list package…...

[代码随想录Day32打卡] 理论基础 509. 斐波那契数 70. 爬楼梯 746. 使用最小花费爬楼梯

理论基础 题型 动归基础&#xff08;这一节就是基础题&#xff09;背包问题打家劫舍股票问题子序列问题 动态规划五部曲 确定dp数组及其下标的含义确定递推公式dp数组如何初始化遍历顺序打印dp数组 509. 斐波那契数 简单~ dp数组及下标含义&#xff1a; dp[i]表示第i各斐…...

android NumberPicker隐藏分割线或修改颜色

在 Android 中&#xff0c;可以通过以下几种方法隐藏 NumberPicker 的分割线&#xff1a; 使用 XML 属性设置 在布局文件中的 NumberPicker 标签内添加 android:selectionDividerHeight"0dp" 属性&#xff0c;将分割线的高度设置为 0&#xff0c;从而达到隐藏分割线…...

7-2 二分查找

输入n值(1<n<1000)、n个非降序排列的整数以及要查找的数x&#xff0c;使用二分查找算法查找x&#xff0c;输出x所在的下标&#xff08;0~n-1&#xff09;及比较次数。若x不存在&#xff0c;输出-1和比较次数。 输入格式: 输入共三行&#xff1a; 第一行是n值&#xff1…...

mid360使用cartorapher进行3d建图导航

1. 添加urdf配置文件&#xff1a; 添加IMU配置关节点和laser关节点 <!-- imu livox --> <joint name"livox_frame_joint" type"fixed"> <parent link"base_link" /> <child link"livox_frame" /> <o…...

Ubuntu安装grafana

需求背景&#xff1a;管理服务器&#xff0c;并在线预警&#xff0c;通知 需求目的&#xff1a; 及时获取服务器状态 技能要求&#xff1a; 1、ubuntu 2、grafana 3、prometheus 4、node 步骤&#xff1a; 一、grafana安装 1、准备系统环境&#xff0c;配置号网络 2、…...

Java版-图论-最短路-Floyd算法

实现描述 网络延迟时间示例 根据上面提示&#xff0c;可以计算出&#xff0c;最大有100个点&#xff0c;最大耗时为100*wi,即最大的耗时为10000&#xff0c;任何耗时计算出来超过这个值可以理解为不可达了&#xff1b;从而得出实现代码里面的&#xff1a; int maxTime 10005…...

可视化建模以及UML期末复习篇----UML图

这是一篇相对较长的文章&#xff0c;如你们所见&#xff0c;比较详细&#xff0c;全长两万字。我不建议你们一次性看完&#xff0c;直接跳目录找你需要的知识点即可。 --------欢迎各位来到我UML国&#xff01; 一、UML图 总共有如下几种&#xff1a; 用例图&#xff08;Use Ca…...

HTML常见标签列表,涵盖了多种用途的标签。

文档结构标签 <!DOCTYPE html>&#xff1a;声明文档类型&#xff0c;告诉浏览器使用HTML5标准。<html>&#xff1a;HTML文档的根元素。<head>&#xff1a;包含文档的元数据&#xff08;meta-data&#xff09;&#xff0c;如标题、字符集、样式表链接、脚本等…...

FPGA 16 ,Verilog中的位宽:深入理解与应用

目录 前言 一. 位宽的基本概念 二. 位宽的定义方法 1. 使用向量变量定义位宽 ① 向量类型及位宽指定 ② 位宽范围及位索引含义 ③ 存储数据与字节数据 2. 使用常量参数定义位宽 3. 使用宏定义位宽 4. 使用[:][-:]操作符定义位宽 1. 详细解释 : 操作符 -: 操作符 …...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

蓝桥杯 2024 15届国赛 A组 儿童节快乐

P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡&#xff0c;轻快的音乐在耳边持续回荡&#xff0c;小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下&#xff0c;六一来了。 今天是六一儿童节&#xff0c;小蓝老师为了让大家在节…...

【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表

1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

Keil 中设置 STM32 Flash 和 RAM 地址详解

文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

linux 下常用变更-8

1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行&#xff0c;YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID&#xff1a; YW3…...

Python如何给视频添加音频和字幕

在Python中&#xff0c;给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加&#xff0c;包括必要的代码示例和详细解释。 环境准备 在开始之前&#xff0c;需要安装以下Python库&#xff1a;…...

深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南

&#x1f680; C extern 关键字深度解析&#xff1a;跨文件编程的终极指南 &#x1f4c5; 更新时间&#xff1a;2025年6月5日 &#x1f3f7;️ 标签&#xff1a;C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言&#x1f525;一、extern 是什么&#xff1f;&…...

视觉slam十四讲实践部分记录——ch2、ch3

ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...

纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join

纯 Java 项目&#xff08;非 SpringBoot&#xff09;集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...