序列化和反序列化的概念_序列化的概念

序列化和反序列化的概念

讨论了为什么Optional不可序列化以及如何处理(即将推出)之后,让我们仔细看看序列化。

总览

这篇文章介绍了序列化的一些关键概念。 它尝试精简地执行此操作,而不会涉及太多细节,包括将建议降至最低。 它没有叙述,更类似于Wiki文章。 主要信息来源是约书亚·布洛赫(Joshua Bloch)的出色著作《 有效的Java》 ,其中涉及到序列化的多个内容(第一版:54-57;第二版: 74-78 )。 在官方序列化规范中可以找到更多信息的方式

定义

通过序列化,实例可以被编码为字节流(称为序列化 ),并且这样的字节流可以被转换回实例(称为反序列化 )。

关键功能是两个进程不必由同一JVM执行。 这使得序列化成为一种在系统运行之间将对象存储在磁盘上或在不同系统之间传输它们以进行远程通信的机制。

语言外特征

序列化是一种有些奇怪的机制。 它将实例转换为字节流,反之亦然,与类的交互很少。 它既不调用访问器来获取值,也不使用构造函数创建实例。 为此,该类的所有开发人员所需要做的就是实现一个没有方法的接口。

Bloch将其描述为一种语言学特征 ,它是序列化中许多问题的根源。

方法

可以通过实现以下某些方法来自定义序列化过程。 它们可以是私有的,JVM将根据其签名来找到它们。 这些描述摘自Serializable的类注释 。

  • private void writeObject(java.io.ObjectOutputStream out) throws IOException
    负责为其特定类编写对象的状态,以便相应的readObject方法可以还原它。
  • private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException
    负责从流中读取并还原类字段。
  • private void readObjectNoData() throws ObjectStreamException
    在序列化流未将给定类列出为要反序列化的对象的超类的情况下,负责为其特定类初始化对象的状态。
  • ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException
    指定将此类的对象写入流时要使用的替代对象。
  • ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
    从流中读取此类的实例时,指定一个替换对象。

处理反序列化的语言外特性的一种好方法是将所有涉及的方法视为该类的附加构造函数。

(反)序列化涉及的对象流提供了以下有用的默认(反)序列化方法:

  • java.io.ObjectOutputStream.defaultWriteObject() throws IOException
    将当前类的非静态和非瞬态字段写入此流。
  • java.io.ObjectInputStream.defaultReadObject() throws IOException, ClassNotFoundException
    从此流中读取当前类的非静态和非瞬态字段。

不变量

不使用构造函数创建实例的一种效果是,在反序列化时不会自动建立类的不变式。 因此,尽管类通常会检查所有构造函数参数的有效性,但该机制不会自动应用于字段的反序列化值。

进行反序列化检查是一项额外的工作,很容易导致代码重复及其通常会引起的所有问题。 如果忘记或粗心地做,该类将打开漏洞或安全漏洞。

序列化表格

由infocux Technologies在CC-BY-NC 2.0下发布。

由infocux Technologies在CC-BY-NC 2.0下发布 。

可序列化类的字节流编码的结构称为其序列化形式 。 它主要由类字段的名称和类型定义。

序列化的表单具有一些不立即可见的属性。 尽管可以通过仔细定义表格来缓解某些有问题的问题,但它们通常仍然是班级未来发展的负担。

公开API

序列化表格的最重要属性是:

它是该类的公共API的一部分!

从部署可序列化类的那一刻起,必须假定已存在序列化实例。 通常期望系统支持使用同一系统的较早版本创建的实例的反序列化。 类的用户依赖于其序列化形式以及其记录的行为。

减少信息隐藏

信息隐藏的概念允许类在更改其实现方式的同时保留其记录的行为。 这包括其状态的表示,通常是隐藏的,可以根据需要进行调整。 由于捕获状态表示形式的序列化形式成为公共API的一部分,因此表示形式本身也是如此。

Serializable类只有有效地隐藏其行为的实施,同时暴露出该行为的界定国家使用它来实现它。

灵活性降低

因此,就像更改类的API(例如,通过更改或删除方法或更改其记录的行为)一样,使用它可能会破坏代码,更改序列化形式也是如此。 不难发现,如果固定领域,提高班级就变得更加困难。 如果需要,这大大降低了更改此类的灵活性。

在JDK可序列化中进行处理会使我们的维护成本急剧增加,因为这意味着该表示将一直冻结。 这限制了我们将来开发实现的能力,而我们无法轻松修复错误或提供增强功能的情况数量非常之多,而这种情况本来就很简单。 因此,尽管对您来说,这看起来像是一个“可序列化的实现”的简单问题,但不仅限于此。 解决早期的选择以使某些东西可序列化所消耗的工作量是惊人的。 布赖恩·格茨

增加测试工作量

如果更改了可序列化的类,则必须测试序列化和反序列化是否可以在系统的不同版本中工作。 这不是一件容易的事,并且会产生可衡量的成本。

类表示

from的序列化表示一个类,但并非所有表示都相等。

物理

如果一个类使用引用类型(即非基本类型)定义字段,则其实例包含指向这些类型的实例的指针。 这些实例又可以指向其他实例,依此类推。 这定义了互连实例的有向图。 实例的物理表示形式是从该实例可到达的所有实例的图形。

例如,考虑一个双向链表。 列表中的每个元素都包含在一个节点中,并且每个节点都知道上一个和下一个。 这基本上已经是列表的物理表示形式。 包含一打元素的列表将是13个节点的图形。 列表实例指向第一个列表节点和最后一个列表节点,从那里可以在两个方向之间遍历这十个节点。

序列化类实例的一种方法是简单地遍历图并序列化每个实例。 这有效地将物理表示形式写入字节流,这是默认的序列化机制。

虽然类的物理表示形式通常是实现细节,但是以这种方式对其进行序列化会暴露此隐藏信息。 序列化物理表示有效地将类绑定到该类,这使得将来很难更改它。 还有其他缺点,在有效Java (第2版的第297页)中进行了介绍。

逻辑上

类状态的逻辑表示通常更抽象。 通常从实施细节中将其删除,并且包含的​​信息较少。 在尝试表达此表示形式时,建议将两个方面都推到最大。 它应该尽可能地独立于实现,并且从某种意义上讲应该是最小的,因为遗漏了任何信息使得无法从中重新创建实例。

继续链接列表的示例,请考虑链接列表的实际含义:仅按特定顺序排列一些元素。 这些是否包含在节点中以及这些假想的节点如何链接都无关紧要。 因此,最小的逻辑表示将仅由那些元素组成。 (为了从流中正确地重新创建实例,有必要添加元素的数量。虽然这是多余的信息,但似乎并没有太大的伤害。)

因此,良好的逻辑表示形式只能捕获状态的抽象结构,而不能捕获表示状态的具体字段。 这意味着尽管改变前者仍然存在问题,但后者可以自由发展。 与序列化物理表示相比,这为类的进一步开发恢复了很大一部分灵活性。

序列化模式

至少有三种方法可以序列化一个类。 称呼所有这些模式都有些过头,因此该术语被宽松地使用。

默认序列化表格

这就像在声明中添加implements Serializable一样简单。 然后,序列化机制会将所有非临时字段写入流,并在反序列化时将流中存在的所有值分配给它们的匹配字段。

这是序列化类的最直接的方法。 这也是序列化所有尖锐的边缘都变得平淡无奇,并等待它们转而真正伤害您的地方。 序列化的形式捕获物理表示,并且绝对不检查不变量。

自定义序列化表格

通过实现writeObject一个类可以定义将哪些内容写入字节流。 匹配的readObject必须读取相应的流,并使用该信息将值分配给字段。

这种方法比默认形式具有更大的灵活性,可用于序列化类的逻辑表示。 有一些细节需要考虑,我只能建议阅读Effective Java中的相应项目(第1版中的项目55;第2版中的项目75)。

序列化代理模式

在这种情况下,要序列化的实例将替换为代理。 该代理是从字节流而不是原始实例写入和读取的。 这可以通过实现方法writeReplacereadResolve来实现。

在大多数情况下,这是迄今为止最好的序列化方法。 它值得自己的职位 ,它会很快得到它( 住宿 调整 )。

杂项

有关序列化的其他一些细节。

人工字节流

反序列化的快乐路径假定一个字节流是通过序列化同一类的实例创建的。 尽管在大多数情况下这样做是可以的,但是在安全关键代码中必须避免这样做。 这包括任何使用序列化进行远程通信的公共可访问服务。

取而代之的是,必须假设攻击者精心制作了流,以违反类的不变式。 如果不解决此问题,则可能导致系统不稳定,从而可能崩溃,破坏数据或受到攻击。

文献资料

Javadoc具有特殊的注释,用于记录类的序列化形式。 为此,它在文档中创建了一个特殊页面,其中列出了以下信息:

  • 标记@serialData可以注释方法,下面的注释应该用来记录字节流中写入的数据。 方法签名和注释显示在“ 序列化方法”下
  • 标记@serial可以注释字段,下面的注释应该描述字段。 然后,该字段的类型和名称以及注释会在“ 序列化字段”下列出

一个很好的例子是LinkedList的文档 。

翻译自: https://www.javacodegeeks.com/2015/01/concepts-of-serialization.html

序列化和反序列化的概念

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/338904.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言必学的12个排序算法:基数排序

# 基本思想基数排序(radix sort),同样时一种非比较的内部排序算法,主要基于多关键字排序的思想进行排序,它将单个关键字按照基数分成“多个关键字”进行排序。例如整数789是一个关键字,可以按照十进制位划分多关键字(十…

有没有code能改xml内容_Spring源码解析-applicationContext.xml加载和bean的注册

applicationContext文件加载和bean注册流程​ Spring对于从事Java开发的boy来说,再熟悉不过了,对于我们这个牛逼的框架的介绍就不在这里复述了,Spring这个大杂烩,怎么去使用怎么去配置,各种百度谷歌都能查到很多大牛教…

C语言数据类型转换

首先变量的数据类型是可以转换的。转换的方法有两种,一种是自动转换,另一种是强制转换。自动转换即当不同类型的数据进行混合运算时,编译系统将按照一定的规则自动完成。而强制类型转换是由程序员通过编程强制转换数据的类型。自动转换的规则…

java 类持久化_Java 持久化之 -- IO 全面整理(看了绝不后悔)

目录:一、java io 概述什么是IO?IO包括输入流和输出流,输入流指的是将数据以字符或者字节形式读取到内存 分为字符输入流和字符输入流输入流指的是从内存读取到外界 ,分为字符输入流和字节输出流Java IO即Java 输入输出系统。不管…

idea 找不到或无法加载主类_解决IDEA中Groovy项目no Groovy library is defined的问题

实验环境IDEA2019.1.2Groovy-2.5.8错误重现新建了一个Groovy工程,指定了groovy版本,如图新建了一个简单的Groovy Class,运行,出现如下错误错误的尝试(以下是试错过程,并不能解决问题,读者可以不要跟着操作&…

C语言标识符、关键字和注释

这一节主要讲解C语言中的几个基本概念。标识符定义变量时,我们使用了诸如“a”“abc”“mn123”这样的名字,它们都是程序员自己起的,一般能够表达出变量的作用,这叫做标识符(Identifier)。标识符就是程序员…

java 类.class_面试官:Java反射是什么?我回答不上来!

一.概念反射就是把Java的各种成分映射成相应的Java类。Class类的构造方法是private,由JVM创建。反射是java语言的一个特性,它允程序在运行时(注意不是编译的时候)来进行自我检查并且对内部的成员进行操作。例如它允许一个java的类获取他所有的成员变量和…

巧解C语言运算符的优先级和结合性

本篇文章我们从一个例子入手讲解,请看下面的代码:#include int main(){ int a 16, b 4, c 2; int d a b * c; int e a / b * c; printf( "d%d, e%d\n", d, e); return 0; }运行结果:d24, e81) 对于表达式a b * c&#xff0…

jms mdb_MDB!= JMS,反之亦然

jms mdb基本 消息驱动Bean(又称为MDB)只是另一个EJB,例如无状态,有状态或单例。 使用MessageDriven批注指定。 MDB用于异步消息处理 它们与无状态EJB 相似 ,因为它们都是由EJB容器池化的 但是,它们与无状…

C语言中的三目运算符是啥?有何用处?

一般来说,C语言中的三目运算符为a?b:c即有三个参与运算的量。由条件运算符组成条件表达式的一般形式为:表达式1? 表达式2:表达式3求值规则为:如果表达式1的值为真,则以表达式2 的值作为条件表达式的值,否…

status_code想要得到302却得到200_中考200天倒计时!教你高效规划!抓紧抢报预留座位!...

教育点击右上方蓝字关注金石教育金石教育青岛站推送青岛教育资讯,关注孩子成长关注教育主讲人——段莲1、金石教育首席学习规划师:在中考规划行业,段莲老师已经深入研究多年,了解最新的中考数据,并且能够把握住每个数据…

gui jfr_Java飞行记录器(JFR)

gui jfrJFR是一个Java分析器,它使您可以研究代码的运行时特征。 通常,您将使用探查器来确定代码的哪些部分导致大量内存分配或导致消耗过多的CPU。 有很多产品在那里。 过去,我使用过YourKit,OptimizeIt,JProfiler&am…

java 命名内部类_如何把java SWT程序中的匿名内部类改写成命名内部类?

展开全部//请看最后一个e69da5e6ba9062616964757a686964616f31333363386134private class ,其实命名内部类跟普通的java程序一样写,只要知道你自己的监听器要继承于哪个类,实现什么方法public class Level4Class extends ViewPart {private TableViewer …

C语言 | 递增运算符

如何灵活使用C语言递增运算符。 解题思路:自增运算符 使操作数的值加1,其操作数必须为可变左值(可简单地理解为变量)。对于自增就是加1这一点,读者应该不会有什么疑问。难点在于: 可以置于操作数前面&#…

数据结构实验之图论四:迷宫探索_迷宫搜索类的双向bfs问题(例题详解)

前言文章若有疏忽还请指正!更多精彩还请关注公众号:bigsai头条号:一直码农一直爽在搜索问题中,以迷宫问题最具有代表性,无论是八皇后的回溯问题,还是dfs找出口,bfs找最短次数等等题目的问题。在…

ngrok服务器搭建_利用暴露在外的API,无法检测的Linux恶意软件将矛头指向Docker服务器...

K8s已经成为一线大厂分布式平台的标配技术。你是不是还在惆怅怎么掌握它?来这里,大型互联网公司一线工程师亲授,不来虚的,直接上手实战,3天时间带你搭建K8s平台,快速学会K8s,点击下方图片可了解…

干货 | C语言系列3——常量,运算符,常用数学函数......

符号常量和const常量1.符号常量符号常量通俗来讲就是“替换”,又称为宏定义。格式如下:#define 标识符 常量宏定义可以定义单个变量为常量,也可以定义某个语句或片段。宏定义有一点需要特别注意,它只是“机械”替换,并…

javafx 浏览器_浏览器中的JavaFX

javafx 浏览器浏览器中的JavaFX屏幕截图 最近,Carl Dea和我启动了一个新项目,将JavaFX 8引入浏览器。 今天,我想介绍我们创建的前两个概念验证,以查看该想法是否完全可行。 对于不耐烦的人,这里是到PoC的链接。 但请注…

javafx 示例_JavaFX列表示例

javafx 示例这是使用JavaFX构建的示例列表应用程序。 该应用程序是待办事项列表。 该应用程序具有添加,更新和删除列表中项目的功能。 列表数据存储在HSQLDB关系数据库中。 该应用程序使用JDBC(Java数据库连接)API访问数据库。 该应用程序打包…

嵌入式C语言的7个硬核知识

1void 与 void*void表示的是无类型,不可以采用这个类型声明变量或常量,但是可以把指针定义为void类型,如void* ptr。void指针可以指向任意类型的数据,可用任意数据类型的指针对void指针赋值,比如int *ptrInt&#xff1…