java 序列化规范_Java序列化格式详解
RPC的世界,由于涉及到进程间网络远程通信,不可避免的需要将信息序列化后在网络间传送,序列化有两大流派: 文本和二进制.
文本序列化
序列化的实现有很多方式,在异构系统中最常用的就是定义成人类可读的文本形式,其在开发时debug比较方便.
常见的有:
如通过http协议传送并用soap协议(实际形式为xml)封装的webservice方式.
http传送,封装形式为json.
二进制序列化
二进制序列化会比较复杂,由于字节流只是一组101010,需要一个比较详细的协议来定义被序列化后的二进制流的每个字节的含义是什么.一般使用其原因是其在网络传送效率较高,但牺牲了可读性.
常见的有:
Protocol Buffers
Thrift
Java序列化
重点介绍下java序列化. JDK1.1起,sun就有Java Object Serialization Specification定义java的序列化方式,根据文档,可以根据字节流读出序列化后的含义. 地址在这里.
借用一张java rmi的图解释一下其工作方式:
客户端与服务端都需要有实现了rmi接口的实现类,该接口在实际远程通信的时候作为一个桩类来处理网络通信的各种细节.而其传递的信息,就是java对象序列化后的二进制流.
读懂java序列化流
我们给一个简单的例子,定义一个简单实现了serializable接口的类:
class Pet implements Serializable{
private static final long serialVersionUID = 2L;
int paws;
}
再写一段将Pet序列化到文件中的程序:
public class Main {
public static void main(String[] args) {
System.out.println("Hello World!");
Pet dto = new Pet();
File file = new File("/users/kunrong/p3.txt");
try {
ObjectOutputStream oout =
new ObjectOutputStream(new FileOutputStream(file));
oout.writeObject(dto);
} catch (IOException e) {
e.printStackTrace();
}
}
}
什么也没干,直接将其内容输出到p3.txt文件中.运行后,由于序列化后写出的是二进制流,所以用能打开二进制文件的编辑器打开,在mac下我用的是HexMiner,免费.
这些是什么意思呢?先摘取部分协议文档定义的常量内容:
final static short STREAM_MAGIC = (short)0xaced;
final static short STREAM_VERSION = 5;
final static byte TC_CLASSDESC = (byte)0x72;
final static byte TC_OBJECT = (byte)0x73;
下面对照图中每个16进制
0xACED:根据协议文档的约定,由于所有二进制流(文件)都需要在流的头部约定魔数(magic number=STREAM_MAGIC),既java object 序列化后的流的魔数约定为ACED;
0x0005:然后是流协议版本是short类型(2字节)并且值为5,则十六进制值为0005;
0x7372:java byte型长度为1字节,所以0x73 0x72直接对应到字节流上,0x73(TC_OBJECT)代表这是一个对象,0x72(TC_CLASSDESC)代表这个之后开始是对类的描述.
0x0003:类名的长度,这个类名是Pet,是三个字符,所以长度是3,对应16进制中就是0x0003.
0x506574:这三个字节转为ASCII码就是类名Pet.
0x00 00 00 00 00 00 00 02: 由于序列化中标识类版本是这样定义的
private static final long serialVersionUID = 2L;
是long型,long在java中的定义是8字节,所以这里2L对应的二进制值就是这个.
0x02: 关于classDescFlags的定义在协议文档是这样的:
The flag byte classDescFlags may include values of
final static byte SC_WRITE_METHOD = 0x01; //if SC_SERIALIZABLE
final static byte SC_BLOCK_DATA = 0x08; //if SC_EXTERNALIZABLE
final static byte SC_SERIALIZABLE = 0x02;
final static byte SC_EXTERNALIZABLE = 0x04;
final static byte SC_ENUM = 0x10;
所以0x02代表了可序列化.
0x0001: 这一位代表了类中域的个数,在我们的Pet类里,只有一个域就是int paws,所以为1.
0x49: 这个二进制对应的ASCII值是I,这在规范里有定义,我们看下规范定义的是什么:
(B' for byte,C' for char, D' for double,F' for float, I' for int, >J' for long, L' for non-array object types,S' for short, `Z' for
boolean, and [ for arrays)
所以I的意思就是域的类型int型,跟我们在Pet类中的定义一样.
0x00 04 : 既然前面已经指明了域的类型和个数,这里就是域名的长度,就是4个字节,代表这里之后的4个字节代表的是域名.
0x70617773: 这里就是域名的ASCII字符,转换成ASCII就是paws,我们在Pet类声明的变量名.
0x78 70: 看下协议中的这两个值的含义:
final static byte TC_ENDBLOCKDATA = (byte)0x78;
final static byte TC_NULL = (byte)0x70;
所以0x78代表该块定义的序列化流结束了,0x70是NULL,没有超类了.
0x00000000
至此,对于需要传送的类的整个定义已经完成了,那么最后面四个字节的0x00000000是什么意思呢?很简单,就是之前定义的我们的int型域变量paws里存储的实际值了,由于我们声明了但没有给值,所以这里就是4个字节的00000000.
总结
看了以上对序列化后的二进制流的逐字翻译,我们可以看到为什么java的序列化需要服务器和客户端都需要同一个类的jar包.
因为序列化的流只定义了所传输类的一些定义和域的值,其写入和读取是分别由客户端程序和服务端程序组装完成的,如果双方没有一个共同的基础(同一个类),是无法完成的.
而且由于在网络上传输的两端的进程可能跑的是不同jdk版本的虚拟机,这个协议还要保证序列化仍然能成功,这个协议文档中明确写明了版本设计在序列化中的目标:
The goals are to:
Support bidirectional communication between different versions of a class >operating in different virtual machines by:
Defining a mechanism that allows Java classes to read streams written by >older versions of the same class.
Defining a mechanism that allows Java classes to write streams intended to >be read by older versions of the same class.
Provide default serialization for persistence and for RMI.
Perform well and produce compact streams in simple cases, so that RMI can >use serialization.
Be able to identify and load classes that match the exact class used to >write the stream.
Keep the overhead low for nonversioned classes.
Use a stream format that allows the traversal of the stream without having >to invoke methods specific to the objects saved in the stream.
比较重要的就是:
java类可以读取其自身的老版本流信息.
java类可以输出一个其老版本的同样的类能读出的流.
文章来自微信平台「麦芽面包」,微信号「darkjune_think」。转载请注明。
java 序列化规范_Java序列化格式详解相关推荐
- php serialize取值,PHP 序列化(serialize)格式详解
PHP 序列化(serialize)格式详解(转) 1.前言 PHP (从 PHP 3.05 开始)为保存对象提供了一组序列化和反序列化的函数:serialize.unserialize.不过在 PH ...
- php serialize mysql_php 序列化(serialize)格式详解
1.前言 PHP (从 PHP 3.05 开始)为保存对象提供了一组序列化和反序列化的函数:serialize.unserialize.不过在 PHP 手册中对这两个函数的说明仅限于如何使用,而对序列 ...
- PHP 序列化(serialize)格式详解
1.前言 PHP (从 PHP 3.05 开始)为保存对象提供了一组序列化和反序列化的函数:serialize.unserialize.不过在 PHP 手册中对这两个函数的说明仅限于如何使用,而对 ...
- php serialize参数,PHP 序列化(serialize)格式详解
PHP 序列化(serialize)格式详解 1.前言 PHP (从 PHP 3.05 开始)为保存对象提供了一组序列化和反序列化的函数:serialize.unserialize.不过在 PHP 手 ...
- java linkedlist 节点_JAVA学习-LinkedList详解
1.定义 实现List接口与Deque接口双向链表,实现了列表的所有操作,并且允许包括null值的所有元素,对于LinkedList定义我产生了如下疑问: 1.Deque接口是什么,定义了一个怎样的规 ...
- java super实例_java Super 用法详解及实例代码
java Super 用法详解及实例代码 发布于 2021-1-8| 复制链接 摘记: java Super 用法详解 1)有人写了个很好的初始化属性的构造函数,而你仅仅想要在其中添加另一些自己新建 ...
- java enum 变量_java枚举使用详解
package com.ljq.test; /** * 枚举用法详解 * * @author jiqinlin * */ public class TestEnum { /** * 普通枚举 * * ...
- java异常体系_JAVA异常体系结构详解
一.什么是异常 异常:程序在运行过程中发生由于硬件设备问题.软件设计错误等导致的程序异常事件.(在Java等面向对象的编程语言中)异常本身是一个对象,产生异常就是产生了一个异常对象. --百 ...
- java list用法_Java List 用法详解及实例分析
Java List 用法详解及实例分析 Java中可变数组的原理就是不断的创建新的数组,将原数组加到新的数组中,下文对Java List用法做了详解. List:元素是有序的(怎么存的就怎么取出来,顺 ...
- java 内存指针_java内存模型详解
借用一句话:Java与C++之间有一堵内存动态分配和垃圾收集技术围成的高墙,墙外面的人想进来,墙里面的人却想出去. 一.我们为什么要了解JAVA内存 因为虚拟机帮我们JAVA程序员管理着内存,我们在n ...
最新文章
- python大牛分享一些对python的看法
- weex web获取html高度,weex 中的 web 标签如何控制高度?
- B 站面试官:“啥是重定向?”
- .Net Core 之 Ubuntu 14.04 部署过程
- Java BufferedReader skip()方法与示例
- 提高redis cluster集群的安全性,增加密码验证
- 第七十五期:网络 | 5G那些运营商不愿说的秘密
- EditPlus软件自动补全文档htmlbar.acp设置 及 模板文件格式
- 今天的从上往下望去的企业即时通讯
- RocketMQ(五)——工作原理
- SAP License:复制或删除全数字的公司代码的TK455错误
- POJ 2195 Going Home(最小费用最大流)题解
- The JRE you are running Eclipse with appears to not be a JDK .Spring Boot Live hovers will not work
- 【OpenCV学习笔记】【函数学习】十三(剔除检测到的不符合要求的对象)
- 软件开发 外包_软件开发外包:选择它的理由
- WPF基础(八)bitmapImage.EndInit()引发异常 未找到适用于完成此操作的图像处理组件:可能是收发图片格式不一致导致的。
- 30 道 MySQL 基础知识
- 微信小程序 数据库获取字符串 在view中显示换行
- 在gitee码云上搭建一个网站
- Android 网易云信直播