java流与文件——对象流和序列化

【0】README

0.1) 本文描述转自 core java volume 2, 旨在理解 java流与文件——对象流和序列化 的相关知识;
0.2) for source code , please visit https://github.com/pacosonTang/core-java-volume/blob/master/coreJavaAdvanced/chapter1/ObjectStreamTest.java and https://github.com/pacosonTang/core-java-volume/blob/master/coreJavaAdvanced/chapter1/SerialCloneTest.java


【1】对象流和序列化

1.1)problem + solution

  • 1.1.1)problem: 当你需要存储相同类型的数据时, 使用固定长度的记录格式是一个不错的选择。在面向对象程序中创建的对象很少全部都具有相同的类型; (如父类指针指向子类对象)
  • 1.1.2)solution : Java的 对象序列化(Object serialization)是非常通用的机制; 它可以将对象写出到流中, 并在之后将其读回;

1.2)保存数据对象 的步骤:

  • step1)打开一个 ObjectOutputStream 对象:
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(“employee.dat”));
  • step2)为保存对象,直接使用 ObjectOutputStream 的 writeObject方法;
 Employee harry = new Employee("Harry Hacker", 50000, 1989, 10, 1);Manager carl = new Manager("Carl Cracker", 80000, 1987, 12, 15);
oos.writeObject(harry);
oos.writeObject(carl);
  • step3)为将对象读回,需要获得一个 ObjectInputStream 对象:
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream(“employee.dat”));
  • step4) 用readObject方法以这些对象被写出时的顺序获取他们:
    Employee e1 = (Employee)ois.readObject();

这里写图片描述

  • 1.2.1)但是,对希望在对象流中存储或恢复的所有类都应该进行一次修改, 这些类必须实现 Serializable 接口:

Attention)

  • A1) Serializable 接口 没有任何方法, 你不需要做任何改动, 这一点和 Cloneable 接口很相似;
  • A2)你只有在写出对象时 才能用 writeObject/readObject 方法; 对于基本类型,使用writeInt/readInt 或writeDouble/readDouble 这样的方法(对象流类都实现了 DataInput/DataOutput 接口)
  • A3)在后台,是ObjectOutputStream 在浏览对象的所有域, 并存储它们的内容;

1.3)有一种case 需要考虑: 当一个对象被多个对象共享时, 作为它们各自状态的一部分,会发生什么呢?(如两个经理共用一个秘书harry, 上面的源代码就是这种 case )

 Employee harry = new Employee("Harry Hacker", 50000, 1989, 10, 1);Manager carl = new Manager("Carl Cracker", 80000, 1987, 12, 15);carl.setSecretary(harry);Manager tony = new Manager("Tony Tester", 40000, 1990, 3, 15);tony.setSecretary(harry);
  • 1.3.1)solution: 每个对象都是用一个序列号保存的, 这就是这种机制称为对象序列化的原因。 下面是其算法(Alg):

  • A1)对你遇到的每一个对象引用都关联一个序列号;

  • A2)对于每个对象, 当第一个遇到时,保存其对象数据到流中;
  • A3)如果某个对象之前已经被保存过, 那么只写出“与之前保存过的序列号为x的对象相同”, 在读回对象时,过程是相反的;
  • A4) 对于流中的对象,在第一次遇到其序列号时, 构建它, 并使用流中数据来初始化它, 然后记录这个顺序号和新对象之间的关系;
  • A5) 当遇到 “与之前保存过的序列号为x的对象相同”标记时, 获取与这个顺序号相关联的对象引用;

  • Attention)序列化的另一种重要应用是: 通过网络将对象集合传送到另一台计算机上。 正如文件中保存原生的内存地址毫无意义一样, 这些地址对于在不同处理器之间的通信也毫无意义的。因为序列化用序列号代替了内存地址, 所以它允许将对象集合从一台机器传送到另一台机器;(干货——序列化应用于代替内存地址来表示对象集合 便于对象集合在网络间的传输)

这里写图片描述


【2】理解对象序列化的文件格式

2.1)当存储一个对象时,这个对象所属的类也必须存储。这个类的描述(Descriptions)包含:

  • D1)类名;
  • D2)序列化的版本唯一的ID, 它是数据域类型和方法签名的指纹;
  • D3)描述序列化方法的标志集;
  • D4)对数据域的描述;
    (干货——序列化版本ID 就是 指纹, 而指纹是包含于对象描述内容中的)

2.2)指纹: 是通过对类、超类、接口、域类型和方法签名按照规范方式排序的, 然后将安全散列算法(SHA) 应用于这些数据而获得的;(干货——指纹定义)

  • 2.2.1)指纹比对: 在读入一个对象时, 会拿其指纹与它所属的类的当前指纹进行比对, 如果不匹配, 那么就说明这个类的定义在该对象被写出之后发生过变化, 因此会产生一个异常;
    (干货——指纹比对)
  • 2.2.2)类标识符是如何存储的? (干货——类标识符是如何存储的,了解而已)
72
2 字节的类名长度
类名
8字节长的指纹
1字节长的标志
2字节长的数据域描述符的数量
数据域描述符
78(结束标记)
超类类型(如果没有就是70)
标志字节是由在 java.io.ObjectStreamConstants 中定义的3位掩码构成的;
  • 2.2.3)每个数据域描述符的格式如下: (干货——每个数据域描述符的格式)
1 字节长的类型编码
2 字节长的域名长度
域名
类名(如果域是对象)
其中类型编码是下列取值之一:
B byte
C char
D double
F float
I   int
J    long
L    对象
S    short
Z    boolean
[    数组

Attention)你应该记住: (干货——需要记住的对象流的key point)

  • A1)对象流输出中包含所有对象的类型和数据域;
  • A2)每个对象都被赋予一个序列号;
  • A3) 相同对象的重复出现将被存储为对这个对象的序列号的引用;

【3】修改默认的序列化机制

3.1)problem+solution (干货——某些数据域及其所属类是不可以序列化的)

  • 3.1.1)problem:某些数据域是不可以序列化的, 如, 只对本地方法有意义的存储文件句柄或窗口句柄的整数值; 这种域的值如果不恰当,还会引起本地方法崩溃;
  • 3.1.2)solution: java 将他们标记为 transient;如果这些域属于不可序列化的类, 那么类也需要标记为 transient; (干货——transient关键字的意义, transient==瞬变现象,过往旅客,候鸟;短暂的,路过的)

3.2)序列化机制为单个类提供了一种方式,去向默认的读写行为添加验证或任何其他想要的行为, 可序列化的类可以定义具有下列签名的方法:readObject 和 writeObject;

  • 3.2.1)之后数据域就再也不会被自动序列化了, 取而代之的是调用这些方法;
  • 3.2.2)除了让序列化及直接来保存和恢复对象数据, 类还可以定义它自己的机制。为了做到这一点,必须要实现 Externalizeble 接口, 这需要它定义两个方法:readExternal 和 writeExternal 方法;

Warning)

  • W1) readObject 和 writeObject是私有的, 并且只能被序列化机制调用;
  • W2)与此不同的是, readExternal 和 writeExternal 方法是公共的, 特别地, readExternal 还潜在地允许修改现有对象的状态;

【4】序列化单例和类型安全的枚举

4.1) 当类型安全的枚举实现 Serializable 接口时, 你必须牢记存在一种重要变化, 此时,默认的序列化机制是不适用的。 假设我们写出一个 Orientation 类型的值, 并将其再次读回:

Orientation original = Orientation.HORIZONTAL;
ObjectOutputStream oos = ..;
oos.write(original);
oos.close();
ObjectInputStream ois = ...;
Orientation saved = (Orientation) ois.read();

4.2)problem: 下面的测试 if(saved === Orientation.HORIZONTAL) 将失败;

这里写图片描述

4.3)solution:为解决这个问题,必须定义一种称为 readResolve 的特殊序列化方法。 如果定义了 readResolve 方法, 在对象被序列化后就会调用它。 它必须返回这个对象, 而该对象之后会成为 readObject 的返回值;

这里写图片描述

Attention)向遗留代码中所有类型安全的枚举以及向所有支持单例设计模式的类中添加readResolve 方法; (干货——添加readResolve方法的条件)


【5】版本管理 (干货——存储在磁盘上version1的对象与内存中version2的对象的输入输出问题)

5.1)如果使用序列化来保存对象,就要考虑在程序演化时会有什么问题?
5.2)无论类的定义产生什么样的变化,它的SHA指纹也会跟着变化, 而我们都知道对象流将拒绝读入具有不同指纹的对象。但是类可以表明它对其早期版本保持兼容, 要想这样做, 就必须先获得这个类的早期版本的指纹。

  • 5.2.1)我们可以使用JDK中的单机程序 serialver 来获得这个数字, 如:serialver Employee 会打印出

这里写图片描述

  • 5.2.2)如果一个类具有名为 serialVersionUID 的静态数据成员, 它就不需要再人工地计算其指纹了,而只需要直接使用这个值;
  • 5.2.3)一旦该静态成员( serialVersionUID) 被设置在某个类的内部, 那么序列化系统就可以读入这个类的对象的不同版本;

5.3)如果这个类只有方法产生了变化,那么在读入新对象数据时是不会有任何问题的。但是, 如果数据域发生了变化, 那么就有可能有问题了;
5.4)对象流会将这个类当期版本的数据域和流中版本的数据域进行比较;

  • 5.4.1)如果这两部分数据域之间名字匹配而类型不匹配, 那么对象流不会尝试将一种类型转换为另一种类型, 因为这两个对象不兼容;
  • 5.4.2)如果流中的对象具有在当前版本中所没有的数据域, 那么对象流会忽略这些额外的数据;
  • 5.4.3)如果当期版本具有在流化对象中所没有的数据域,那么这些新添加的域将被设置为它们的默认值(对象设置为null, 数组设置为0, 而boolean 设置为false)

5.5)看个荔枝:

  • 5.5.1)假设我们已经用雇员类的1.0version 在磁盘上保存了大量的雇员记录, 现在我们在 Employee 上添加了 department的数据域,从而将其演化到 version2.0;

这里写图片描述

  • 5.5.2)将version2的磁盘对象读入到version1的程序中(内存中),直接将department 数据域忽略掉;因为version1 中的类描述内容没有关于department的描述;

这里写图片描述

  • 5.5.3)以上的忽略和置空真的安全吗? 视情况而定;

  • 5.5.3.1)丢掉数据域看起来是无害的, 因为接收者仍然可以拥有它知道如何处理的所有数据, 但是将数据域设置为null, 却可能并不那么安全了;

  • 5.5.3.2) 这个问题取决于类的设计者是否能够在 readObject方法中实现额外的代码区订正版本不兼容问题, 或者是否能够确保所有的方法在处理null 数据时都足够健壮;

【6】为克隆使用序列化

6.1)序列化是一种很有用 的用法: 提供了一种克隆对象的简便途径, 只要对应的类是可序列化的即可;
6.2)做法很简单:直接将对象序列化到输入流中, 然后将其读回。

  • 6.2.1)这样产生的新对象是对现有对象 的一个深拷贝。
  • 6.2.2)在这个过程中, 我们不必将对象写出到文件中, 因为可以用 ByteArrayOutputStream 将数据保存到字节数组中;
    6.3)看个荔枝: 要想得到 clone 方法, 只需要扩展 SerialCloneable 类就可以了; (干货——为克隆使用序列化,需要实现SerialCloneable )

这里写图片描述

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

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

相关文章

maven 版本号插件_测试Maven版本插件自动递增版本号

maven 版本号插件抽象 Maven版本插件是一个非常强大的工具,我在很大程度上依赖于它来协调软件版本。 通常,软件发行版本号遵循简单的1.0.0.0-SNAPSHOT格式。 但是最近我需要在版本号中添加一个限定符,例如1.0-beta-SNAPSHOT或1.0.0-fix-bug-d…

java中Date

在类 Date 所有可以接受或返回年、月、日期、小时、分钟和秒值的方法中,将使用下面的表示形式:年份 y 由整数 y - 1900 表示。月份由从 0 至 11 的整数表示;0 是一月、1 是二月等等;因此 11 是十二月。日期(一月中的某…

如何配置android的adb环境变量,如何配置adb环境变量?win7配置adb环境变量的方法...

本文教程小编分享就是win7系统配置adb环境变量的方法, windows7系统 电脑安装adb工具时,提示“adb不是内部或外部命令”这时候我们需要重新配置adb环境变量,那么win7系统怎么配置adb环境变量?网上介绍配置adb环境变量的方法比较少…

jep290涉及jdk版本_JDK 14 / JEP 305模式匹配“ Smart Casts”实例

jep290涉及jdk版本我通常将Java代码中instanceof运算符的存在视为“ 红色标志 ”,这意味着在某些情况下使用instanceof不一定是错误的,但是使用它有时表示可以通过更干净的方式解决设计问题,如所述本文结尾处引用的一些资源中的内容&#xff…

java中Date与DateFormat的格式输出

一、DateFormat java.text.DateFormat 使用 getDateInstance 来获取该国家/地区的标准日期格式。另外还提供了一些其他静态工厂方法。使用 getTimeInstance 可获取该国家/地区的时间格式。使用 getDateTimeInstance 可获取日期和时间格式。可以将不同选项传入这些工厂方法&…

二叉堆的操作总结(insert+deleteMin+increaseKey+decreaseKey+percolateDown+percolateUp)

【0】README 本文idea 均为原创, for source code, please visit https://github.com/pacosonTang/dataStructure-algorithmAnalysis/tree/master/chapter6/p140_binaryheap_conclusion 【1】insert操作 // Attention, the index of the heap starts from 1 void …

aws生态系统集成商_通过通用数据访问扩展AWS生态系统

aws生态系统集成商Amazon Web Services(AWS)可帮助组织托管和管理其数据流程,例如构建数据可视化和执行ETL任务。 在CData,我们可以轻松地将AWS Services与异构业务应用程序和分布式数据存储连接起来,以最终帮助企业对…

HTML.parser和正则解析,正则表达式+HTMLParser使用详.ppt

getText:/body getText: getText:/htmlgetText:除了前面3.2中输出的几个Tag,其余的Tag都在这里了。 3.4 XorFilter 把前面的AndFilter换成NotFilter测试代码: NodeFilter filterID new HasAttributeFilter( "id" );NodeFilter filterChild n…

FileDescriptor的作用

一、FileDescriptor 文件描述符类的实例用作与基础机器有关的某种结构的不透明句柄,该结构表示开放文件、开放套接字或者字节的另一个源或接收者。文件描述符的主要实际用途是创建一个包含该结构的 FileInputStream 或 FileOutputStream。 二、静态标准文件描述…

java流与文件——内存映射文件

【0】README 0.1) 本文描述转自 core java volume 2, 旨在理解 java流与文件——内存映射文件 的相关知识; 0.2)内存映射文件的目的是: 提高访问速度, 缓冲区Buffer; 0.3) 本文干…

java 函数式编程 示例_功能Java示例 第8部分–更多纯函数

java 函数式编程 示例这是第8部分,该系列的最后一部分称为“示例功能Java”。 我在本系列的每个部分中开发的示例是某种“提要处理程序”,用于处理文档。 在上一期文章中,我们已经使用Vavr库看到了一些模式匹配,并且还将故障也视…

html加css作品,我的影视作品,如何在博客园发布带有CSS样式的HTML

总结一下,我做过的视频:(顺便试试在博客园如何发HTML网页)发布网页总结:HTML可以正常发送,但最好将你要发布的html文件放在一个带有ID的div中,这样方便后期添加样式时通过ID添加,不会影响到网页中的其他布局…

FileInputStream

java.ioClass FileInputStream java.lang.Object java.io.InputStream java.io.FileInputStream 所有已实现的接口: Closeable public class FileInputStream extends InputStream FileInputStream 从文件系统中的某个文件中获得输入字节。哪些文件可用取决于主机环境。 Fil…

android console命令,我们可以通过两种方式进入Android的console: 1. 直接执行命令emulator -shell; 2....

Don’t use /usr 这样子编译出来的busybox才不会安装到你主机的/usr目录下。一定要选上。4 执行make编译源码. 如果编译过程中产生无法找到头文件的错误, 可指定交叉编译器用到的头文件所在路径. 如 $make -I./home/jo/tool/arm-2008q3/arm-none-linux-gnueabi/lib…

selenium 示例_Selenium测试中所需的功能和示例

selenium 示例所需功能是用于声明一组基本要求(例如浏览器,操作系统,浏览器版本等的组合)的类,以对Web应用程序执行自动跨浏览器测试。 当我们尝试通过Selenium自动化测试来自动化测试脚本时,我们需要考虑…

java流与文件——正则表达式

【0】README 0.1) 本文描述转自 core java volume 2, 旨在理解 java流与文件——正则表达式 的相关知识; 0.2) there are my original source code below, and for them , please visit https://github.com/pacosonTang/core-ja…

pandoc讲html转换为pdf,mac下利用Pandoc、LaTeX 转换markdown成html,pdf

环境所使用的插件有pandoc,tlmgr,fc-listcssbrew install pandoc由于mac ox 系统生成pdf 须要找到对应的编码集 全部还须要去下载LaTeX,进行安装html5使用tlmgr工具安装全部要的包先进行更新,若是提示所须要的字体没有找到安装gitsudo tlmgr update --se…

java程序连接kafka_Java的Kafka:构建安全,可扩展的消息传递应用程序

java程序连接kafka使用Okta的身份管理平台轻松部署您的应用程序 使用Okta的API在几分钟之内即可对任何应用程序中的用户进行身份验证,管理和保护。 今天尝试Okta。 当今的用户希望可以通过其计算机,手机,平板电脑或任何其他设备访问您的应用…

信息系html5论文,基于HTML5的智力游戏设计电子信息工程本科学生毕业论文.doc

基于HTML5的智力游戏设计电子信息工程本科学生毕业论文本科学生毕业论文(设计)题目(中文):基于HTML5的智力游戏设计(英文):Design of Intelligent Game Based on HTML5姓 名 xxx学 号 xxx院 (系) 电子与信息工程学院专业、年级 电子信息工程指导教师 xxx…

优先队列——左式堆

【0】README 0.1) 本文文字描述部分转自 数据结构与算法分析, 旨在理解 优先队列——左式堆 的基础知识; 0.2) 本文核心思路均为原创, 源代码部分借鉴 数据结构与算法分析 ; 0.3) for origin…