对象序列化和反序列化,在Java中体现为两种字节流: ObjectInputStream
、ObjectOutputStream
序列化的概念
指堆内存中的java对象数据,通过某种方式存储到磁盘文件中,或者传递给其他网络节点(网络传输)。这个过程称为序列化,通常是指将数据结构或对象转化成二进制的过程。
反序列化,顾名思义,则是将二进制字节流转化回java对象的过程。
java序列化是平台无关的。
序列化的作用
- 对象持久化
- 网络传输对象
- 进程间传递对象
如何序列化
默认序列化
- 为类实现一个Serializable接口,这是一个空接口,没有需要实现的方法。
- 可以调用ObjectOutputStream.writeObject方法来序列化了。
- 调用ObjectInputStream.readObject来反序列化
自定义序列化过程
实现Serializable接口之后,重写 writeObject(ObjectOutputStream) 、 readObject(ObjectInputStream) 方法。
序列化ID
通常可序列化的类会使用一个序列化ID:
private static final long serialVersionUID = 1L;
java的序列化机制是通过判断运行时类的serialVersionUID来验证版本一致性的,在进行反序列化时,JVM会把传进来的字节流中的serialVersionUID与本地实体类中的serialVersionUID进行比较,如果相同则认为是一致的,便可以进行反序列化,否则就会报序列化版本不一致的异常InvalidClassException
。
序列化之后的字节流格式
我们用这样一段代码做实验:
class SerializableObject implements Serializable{ }
public class Main {
public static void main(String[] args) throws IOException {
ObjectOutputStream out = new ObjectOutputStream(System.out);
Object obj = new SerializableObject();
out.writeObject(obj);
}
}
得到的结果为:
aced 0005 7372 0012 5365 7269 616c 697a
6162 6c65 4f62 6a65 6374 0b8e b091 7d68
738d 0200 0078 70
前两个字节固定: ac ed
紧接的是对象序列化格式的版本号: 00 05
接下来,73 代表接下来读取到的将是一个对象,72 代表该对象是一个对类的描述:73 72
接下来的两字节描述类名长度:00 12
,对应18个字节的全限定类名。
于是接下来的18字节就是类名的UTF编码:5365 7269 616c 697a 6162 6c65 4f62 6a65 6374
,就是类名SerializableObject。
接下来八个字节是序列化版本ID:0b8e b091 7d68 738d
接下来一个字节02
代表了序列化中标识类版本。
继续往下两个字节就是 00 00
, 代表该类中字段的个数,由于我这个类内部为空没有字段,所以这里就是0。
然后是它包含的字段序列类型描述,其顺序即它们存储的顺序.
基本类型的组成为:一字节类型描述符 两字节字段名称长度 字段名称
类型描述符:
解码字符 | 代表类型 |
---|---|
B | byte |
C | char |
D | double |
F | float |
I | int |
J | long |
L | 对象 |
S | short |
Z | boolean |
[ | 数组 |
字段序列类型描述结束后附加两个字节是固定的78 70
最后是字段信息(内容)的描述,这里都没有。
特殊情况
- 静态变量不会被序列化(static),transient则可以限定字段不参与序列化。
- 当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口。
- 当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化。
原创文章,作者:彭晨涛,如若转载,请注明出处:https://www.codetool.top/article/%e5%af%b9%e8%b1%a1%e7%9a%84%e8%be%93%e5%85%a5%e8%be%93%e5%87%ba-java%e5%ba%8f%e5%88%97%e5%8c%96%e6%9c%ba%e5%88%b6/