Java序列化机制与原理的深入分析
序列化的必要性
如何序列化一个对象
importjava.io.Serializable;
classTestSerialimplementsSerializable{
publicbyteversion=100;
publicbytecount=0;
}
然后我们写个程序将对象序列化并输出。ObjectOutputStream能把Object输出成Byte流。我们将Byte流暂时存储到temp.out文件里。
publicstaticvoidmain(Stringargs[])throwsIOException{
FileOutputStreamfos=newFileOutputStream("temp.out");
ObjectOutputStreamoos=newObjectOutputStream(fos);
TestSerialts=newTestSerial();
oos.writeObject(ts);
oos.flush();
oos.close();
}
如果要从持久的文件中读取Bytes重建对象,我们可以使用ObjectInputStream。
publicstaticvoidmain(Stringargs[])throws IOException{
FileInputStreamfis=newFileInputStream("temp.out");
ObjectInputStreamoin=newObjectInputStream(fis);
TestSerialts=(TestSerial)oin.readObject();
System.out.println("version="+ts.version);
}
执行结果为
对象的序列化格式
ACED00057372000A53657269616C5465
这一坨字节就是用来描述序列化以后的TestSerial对象的,我们注意到TestSerial类中只有两个域:
Java的序列化算法
classparentimplementsSerializable{
intparentVersion=10;
}
classcontainimplementsSerializable{
intcontainVersion=11;
}
publicclassSerialTestextendsparentimplementsSerializable{
intversion=66;
containcon=newcontain();
publicintgetVersion(){
returnversion;
}
publicstaticvoidmain(Stringargs[])throwsIOException{
FileOutputStreamfos=newFileOutputStream("temp.out");
ObjectOutputStreamoos=newObjectOutputStream(fos);
SerialTestst=newSerialTest();
oos.writeObject(st);
oos.flush();
oos.close();
}
}
这个例子是相当的直白啦。SerialTest类实现了Parent超类,内部还持有一个Container对象。
序列化后的格式如下:
我们来仔细看看这些字节都代表了啥。开头部分,见
-
ACED:STREAM_MAGIC. 声明使用了序列化协议.
-
0005:STREAM_VERSION. 序列化协议版本.
-
0x73:TC_OBJECT. 声明这是一个新的对象.
序列化算法的第一步就是输出对象相关类的描述。例子所示对象为SerialTest类实例,因此接下来输出SerialTest类的描述。见
-
0x72:TC_CLASSDESC. 声明这里开始一个新Class。
-
000A:Class名字的长度.
-
53657269616c54657374: SerialTest,Class类名.
-
0552815AAC6602F6: SerialVersionUID, 序列化ID,如果没有指定,则会由算法随机生成一个8byte的ID.
-
0x02: 标记号. 该值声明该对象支持序列化。
-
0002: 该类所包含的域个数。
接下来,算法输出其中的一个域,int version=66;见
-
0x49: 域类型.49 代表"I", 也就是Int.
-
0007: 域名字的长度.
-
76657273696F6E:version,域名字描述.
然后,算法输出下一个域,containcon=newcontain();这个有点特殊,是个对象。描述对象类型引用时需要使用JVM的标准对象签名表示法,见
-
0x4C: 域的类型.
-
0003: 域名字长度.
-
636F6E: 域名字描述,con
-
0x74:TC_STRING. 代表一个newString.用String来引用对象。
-
0009: 该String长度.
-
4C636F6E7461696E3B: Lcontain;,JVM的标准对象签名表示法.
-
0x78:TC_ENDBLOCKDATA,对象数据块结束的标志
.接下来算法就会输出超类也就是Parent类描述了,见
-
0x72:TC_CLASSDESC. 声明这个是个新类.
-
0006: 类名长度.
-
706172656E74:parent,类名描述。
-
0EDBD2BD85EE637A: SerialVersionUID, 序列化ID.
-
0x02: 标记号. 该值声明该对象支持序列化.
-
0001: 类中域的个数.
下一步,输出parent类的域描述,int parentVersion=100;同见
-
0x49: 域类型.49 代表"I", 也就是Int.
-
000D: 域名字长度.
-
706172656E7456657273696F6E: parentVersion,域名字描述。
-
0x78:TC_ENDBLOCKDATA,对象块结束的标志。
-
0x70:TC_NULL, 说明没有其他超类的标志。.
到此为止,算法已经对所有的类的描述都做了输出。下一步就是把实例对象的实际值输出了。这时候是从parentClass的域开始的,见
-
00 00 00 0A:10, parentVersion域的值.
还有SerialTest类的域:
-
00 00 00 42:66,version域的值.
再往后的bytes比较有意思,算法需要描述contain类的信息,要记住,现在还没有对contain类进行过描述,见
-
0x73:TC_OBJECT, 声明这是一个新的对象.
-
0x72:TC_CLASSDESC声明这里开始一个新Class.
-
0007: 类名的长度.
-
636F6E7461696E:contain,类名描述.
-
FCBBE60EFBCB60C7: SerialVersionUID, 序列化ID.
-
0x02:Variousflags. 标记号. 该值声明该对象支持序列化
-
0001: 类内的域个数。
.输出contain的唯一的域描述,int containVersion=11;
-
0x49: 域类型.49 代表"I", 也就是Int..
-
000E: 域名字长度.
-
636F6E7461696E56657273696F6E: containVersion, 域名字描述.
-
0x78:TC_ENDBLOCKDATA对象块结束的标志.
这时,序列化算法会检查contain是否有超类,如果有的话会接着输出。
-
0x70:TC_NULL,没有超类了。
最后,将contain类实际域值输出。
-
00 00 00 0B:11, containVersion的值.
OK,我们讨论了java序列化的机制和原理,希望能对同学们有所帮助。
相关文章
- java课程设计成绩管理系统_Java课程设计–学生成绩管理系统
- Java后台接收参数出现java.lang.Integer cannot be cast to java.lang.Double错误(已解决)[通俗易懂]
- Java写 soapclient,PHP通过SoapClient调用Java发布的WebService
- 【java并发编程】底层原理——用户态和内核态的区别
- Java栈结构_栈java
- java socket详解_Java Socket 编程原理及教程「建议收藏」
- java list 转json 字符串_Java之JSON字符串与List集合之间相互转换
- java垃圾回收机制原理_java垃圾回收的缺点
- JAVA外文参考文献_java参考文献近五年
- 【Java 虚拟机原理】垃圾回收算法 ( Java 虚拟机内存分区 | 垃圾回收机制 | 引用计数器算法 | 引用计数循环引用弊端 )
- 【Java 虚拟机原理】Java 反射原理 ( 反射作用 | 反射用法 )
- 写文件流报:java.io.FileNotFoundException…../img (拒绝访问。)的问题真正病根详解编程语言
- Java 实现二分查找/折半查找详解编程语言
- Java操作MySQL数据库:实现看似不可能的梦想(java连接mysql数据库)
- Java常见的几种内存溢出及解决方案详解编程语言
- Java里的Redis事务处理(redis事务java)
- 处理Java实现Redis过期数据管理(redisjava过期)
- 数据库简易指南:如何使用 Java 连接 MySQL 数据库(java连接mysql)
- Redis Java处理过期机制实战(redisjava过期)
- _home让Linux下Java更有效:更新Java_Home(linux更改java)
- 深入浅出 使用 Java 连接 Neo4j(java连接neo4j)
- Java锁表与Oracle数据库协调实现数据安全(java锁表oracle)
- Java编程从Oracle中读取数据(java读取oracle)
- 编程玩转Java之Oracle编程实战(java中的oracle)
- 安装Oracle JDK 从Java进入下一个级别(oracle下载java)
- Java快速排序(QuickSort)原理及实现代码