当前位置: 首页 > 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. 详细解释 : 操作符 -: 操作符 …...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

AI Agent与Agentic AI:原理、应用、挑战与未来展望

文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例&#xff1a;使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例&#xff1a;使用OpenAI GPT-3进…...

(转)什么是DockerCompose?它有什么作用?

一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用&#xff0c;而无需手动一个个创建和运行容器。 Compose文件是一个文本文件&#xff0c;通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

JVM虚拟机:内存结构、垃圾回收、性能优化

1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用

一、方案背景​ 在现代生产与生活场景中&#xff0c;如工厂高危作业区、医院手术室、公共场景等&#xff0c;人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式&#xff0c;存在效率低、覆盖面不足、判断主观性强等问题&#xff0c;难以满足对人员打手机行为精…...

怎么让Comfyui导出的图像不包含工作流信息,

为了数据安全&#xff0c;让Comfyui导出的图像不包含工作流信息&#xff0c;导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo&#xff08;推荐&#xff09;​​ 在 save_images 方法中&#xff0c;​​删除或注释掉所有与 metadata …...

Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案

在大数据时代&#xff0c;海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构&#xff0c;在处理大规模数据抓取任务时展现出强大的能力。然而&#xff0c;随着业务规模的不断扩大和数据抓取需求的日益复杂&#xff0c;传统…...

Chrome 浏览器前端与客户端双向通信实战

Chrome 前端&#xff08;即页面 JS / Web UI&#xff09;与客户端&#xff08;C 后端&#xff09;的交互机制&#xff0c;是 Chromium 架构中非常核心的一环。下面我将按常见场景&#xff0c;从通道、流程、技术栈几个角度做一套完整的分析&#xff0c;特别适合你这种在分析和改…...

9-Oracle 23 ai Vector Search 特性 知识准备

很多小伙伴是不是参加了 免费认证课程&#xff08;限时至2025/5/15&#xff09; Oracle AI Vector Search 1Z0-184-25考试&#xff0c;都顺利拿到certified了没。 各行各业的AI 大模型的到来&#xff0c;传统的数据库中的SQL还能不能打&#xff0c;结构化和非结构的话数据如何和…...