理解对象流的输出格式:
这里我取以前Java课程设计的一个对象来分析,具体实体的各个属性可以不去了解,你只要记住它有这几个属性,各个属性是什么类型就OK了,now let's begin!
一、首先参考一下对应的对象类的结构:
package net.taball.entity; public class Photographs implements Serializable{ private String title; private Date date; private String location; private Long number; ………… } |
二、原始数据:
|
三、十六进制文件/对象文件
|
|
四、注释列表:
AC ED 00 05 文件头(“魔力数字”、序列化版本号) 73 新对象(序列号#4) 72 00 1D Photographs 新类、字符串长度、类名(序列号#0) 00 00 00 00 00 00 00 01 02 指纹和标志(8+1字节) 00 04 数据域的数量(4) L 00 04 date 实例域类型、长度和名字 74 00 10 Ljava.util.Date; 实例域类名---Date(序列号#1) L 00 08 location 实例域类型、长度和名字 74 00 12 Ljava.util.String; 实例域类名---String(序列号#2) L 00 06 number 实例域类型、长度和名字 74 00 10 Ljava.util.Long; 实例域类名---Long(序列号#3) L 00 05 title 实例域类型、长度和名字 71 00 7E 00 02 已存在的类<使用序列号#2> 78 结束标志 70 无超类
73 date域的值----新对象(序列号#6) 72 00 0E java.util.Date 新类、字符串长度、类名(序列号#5) 68 00 00 无实例变量 78 结束标志 70 无超类 77 08 扩充存储、字节数(8) 00 00 01 78 结束标志 74 00 03 Jnu location域的值(序列号#7) 73 number域的值---新对象(序列号#10) 72 00 0E java.lang.Long 新类、字符串长度、类名(序列号#8) 3B 8B E4 90 CC 00 01 有一个实例变量 I 00 05 value 实例域类型、长度和名字 78 结束标志 72 00 10 java.lang.Number 新类、字符串长度、类名(序列号#9) 00 00 无实例变量 78 结束标志 70 无超类 00 00 00 00 00 01 E2 40 数字编号 74 00 06 tsball title域的值(序列号#11)
73 71 00 7E 00 00 已存在的类<使用序列号#0> 73 71 00 7E 00 05 已存在的类<使用序列号#5> 77 08 扩充存储、字节数(8) 00 00 01 17 DA E0 78 74 00 03 Apj 73 71 00 7E 00 08 已存在的类<使用序列号#8> 00 00 00 00 00 01 E2 40 数字编号 74 00
……//循环部分(红色部分)
70 无超类 |
部分注释:
1、 左边的L是与十六进制
2、 其他字符串名字都要转化为十六进制,类名要加上包名,如Photographs应该写为net.taball.entity.Photographs再转变;
3、 Ljava.util.Date要改为 Ljava/util/Date再转变为十六进制数,但java.lang.String等没有多加其它字符的类型就不一样,直接转化便可。
五、总结:(自己总结,希望网友多多更正,谢谢)
问题一:用notepad打开这个文件,你可以看到一些类名,你自己的和java的,也可以看到编辑的数据,但排列并不整齐,多出了一些奇怪的字符。这些字符的含义是什么?
答:
通俗点的理解是这样的:因为数据是通过对象流的方法写如进去的,所以“长的像对象”,包含了对象的对应的类名和对象属性的类型名字等等。
但要真正的理解这些在notepad下显示类似于乱码的字符,还必须要具体对ObjectOutputStream类 和 对象数据进行具体的分析。
首先,序列化后的数据是以二进制形式来表示的, ObjectOutputStream类实现了对数据的加工,最终以十六进制的形式来表示并存储到文件中去。
当我们通过MadEdit类型的软件查看时就可以看到二进制文件中数据的原貌,从第三部分你可以发现它们是用十六进制数表示的,而且是单字节表示一个字符。
文件中的十六进制数据集合表示的是什么意思呢?其实它们主要是分为两部分,一部分是对类的描述;另一部分是对对象数据的表达。具体的规则在第四部分内容中有描述。
当我们用notepad打开这些数据的时候,我们看到的是一些乱乱的字符,这跟notepad本身有关。对象流写入的数据是二进制数(用十六进制表示),就是说存储的文件是二进制文件。
因为Notepad默认情况有ASCII、UTF-8等多种编码方式,当用Notepad打开文件时,它都会尝试用这几种编码方式去解读,而非直接把十六进制数显示给大家看,这也是为什么我们平时用Notepad打开文本文件时并非显示机器代码,而是可阅读的文字。这一解读的话就问题就出现了,因为利用ObjectOutputStream写入对象流时,并不是直接的把对象中的数据直接转化为对应的二进制数写进文件,而是有一套自己约定的规则,这规制规定了类和数据的表示方式,
当然这就需要中间穿插很多额外的辅助作用的十六进制数。但是Notepad不知道这些约定,它只会调用本身具有的编码方式去解析,并不会判断读出来的东西是否正确,是否客户需要的,所以出来的数据是“杂乱无章“的。加上可能是某些编码方式支持范围的原因,造成有些数据没有得到正确的显示。
要想看到第二部分中的数据,还必须依靠反序列化API和 对象流的处理。
问题二:(接上)为什么java读到这些内容就能创建出你所需要的对象?
答:
因为对象流存储数据和读取数据时对对象的加工采取的是同一种规制或约定。存储数据时是按某种约定来表示,在(四)中我已经做了详细的结构分析。当用ObjectInputStream去读取时,它会在各个特定意义的字符或特定的组合方式的帮组提示下,慢慢地一点点的读懂二进制文件里的内容。这应该说它认识那些约定和字符,所以知道它们要表达什么意思。就像你认识了汉字的意思和句子的构成,就能读懂一篇文章的原理。
但是Java本身对这有些验证规制,要确定是同一对象才能正确解析原来的数据,以免破坏内存。具体是通过硬盘的对象和内存的对象对比,如果验证标识一样,则通过验证,正常读取。