前言
在IO体系中,ByteArrayInputStream可以将所有能转为byte[]的对象作为流缓存在内存里。那么如果要持久化一个对象应该怎么操作呢?
序列化与反序列化是针对java对象怎么持久化的一套操作,涉及到的知识点除了IO体系中的ObjectInputStream、ObjectOutputStream,还有序列化接口Serializable、序列化版本变量serialVersionUID、序列化禁用关键字transient、一些特有的异常等等。
所以为了形成一个整体,我在这篇文章中将它涉及到的内容都归纳到了一起。
一切源于顶层设计,首先从接口Serializable说起。
Serializable
这是一个标记对象可以被序列化的标记接口,类为Serializable。它是一个标记接口,没有定义任何行为方法。从它的JDK源码说明中,提及了其他知识点:
1. ObjectInputStream、ObjectOutputStream
2. serialVersionUID
3. transient
4. ClassNotFoundException、InvalidClassException、NotSerializableException
- 在Serializable接口的JDK源码说明中大概解释了下面这个意思:
Serializable是一个序列化的标记接口,只有实现了Serializable接口的类生成的对象,才能被序列化。
ObjectInputStream、ObjectOutputStream是反序列化与序列化的流化工具。ObjectInputStream、ObjectOutputStream是一个包装流,通过指定一个节点流地址,可以通过ObjectInputStream的readObject将节点流的数据反序列化成对象 或ObjectOutputStream的writeObject序列化到节点流。
在序列化与反序列化中,必须指定 serialVersionUID变量,并且是public static final 修饰,保证序列化与反序列的版本一致。否则程序将会自动随机生成serialVersionUID变量值,这可能导致序列化与反序列由于版本不一致而失败。在序列化过程中,如果不想序列化某些字段,可以通过关键字 transient修饰成员变量而阻止序列化。
如果反序列化过程中,类不存在或者serialVersionUID版本不一致或者类没有实现Serializable,都将会抛出对应的异常。
流程
为了串联本章的知识点,下面举个例子说明序列化与反序列化的流程及涉及的知识点。
- 序列化场景
如果想要持久化一个对象,将其保存为硬盘上的一个文件,或者想将这个对象通过网络传输到其他地方,便可以使用序列化。那么序列化是什么?和持久化有什么关系?往下看。
- 序列化原理
序列化是指的将一个对象(而非类)进行流化的操作。InputStream是流化的顶级父类,而对象的序列化就是指的通过ObjectInputStream将对象转为二进制序列的过程。这里指的序列化是针对对象,所以static修饰的成员变量能被序列化吗?答案显然不能。
通过序列化将对象二进制流化以后,一是可以进行读写操作,比如存为硬盘上的一个文件作为持久化,二是可以将流化后的对象通过网络传输到其他地方。
- 序列化条件
了解序列化的原理后,序列化需要什么条件呢?
- 序列化和反序列化的类必须实现Serializable接口
- 必须显示指定一个public static final long serialVersionUID, 序列化和反序列化的serialVersionUID值必须保持一致。
- 序列化必须是对象的非transient修饰的成员变量
提问
-
父类是可序列化类,子类是否可以实现序列化?
由于子类继承父类,所以当父类可实现序列化时,子类也满足序列化要求。
-
子类是可序列化类,父类是否可以实现序列化?
子类是可序列化类,父类不一定是可序列化类,所以父类可能不满足未实现Serializable接口的要求。
-
序列化对象的成员变量是否也要满足可序列化?
序列化对象包含其成员变量的序列化,所以成员变量必须也要满足可序列化。
-
序列化的前后,成员变量不一致,是否可以反序列化?
如果反序列化时,被序列化的成员变量仍然存在,并且满足反序列化要求时,则可以反序列化。
最后
序列化这一节没仔细阅读源码,感觉挺枯燥的。并且不是学习的重点,所以简笔带过。
如有不对,感谢指正!