中午一个网友来信说自己和面试官干起来了,看完他的描述真是苦笑不得,这年头是怎么了,最近互联网CS消息满天飞,怎么连面试官都SB起来了呢?
大概是这样的:这位网友面试时被问及了Serializable接口的底层实现原理,因为这是一个标识性的空接口,大部分同学在学习时都秉持着会用就行(说实话,Build哥在这之前也没怎么细研究过,都是拿来就用),几乎不太去关注底层的东西,这位网友亦是如此,在这种情况下,自然回答的心虚,这下可被面试官抓住了把柄,一顿带有人身攻击的狂输出,让面试现场变成了撕B现场,具体可看聊天截图😂😂😂
基于这位网友的面试经历,Build哥又赶紧去重新学了一下Serializable关键字,以及它背后的实现,别到时候咱也被暴怼,下面咱们一起来重温一下。
一、序列化与反序列化
首先,我们先来了解一下两个概念 序列化
与 反序列化
。
- 序列化: 将Java对象转换为一个字节序列(包含对象的数据、对象的类型和对象中存储的属性等信息)的过程,以便于在网络上传输或者存储在文件中。
- 反序列化: 是序列化的逆过程,将字节序列转为Java对象的过程。
1.1 序列化与反序列化的应用场景
- 对象在进行网络传输(比如远程方法调用 RPC 的时候)之前需要先被序列化,接收到序列化的对象之后需要再进行反序列化;
- 将对象存储到文件(如系统中excle的上传与下载)之前需要进行序列化,将对象从文件中读取出来需要进行反序列化;
- 将对象存储到数据库(如 Redis)之前需要用到序列化,将对象从缓存数据库中读取出来需要反序列化;
- 将对象存储到内存之前需要进行序列化,从内存中读取出来之后需要进行反序列化。
序列化与发序列化的流转过程可参考下图:
有个问题,如果在我的对象中,有些变量并不想被序列化应该怎么办呢?
答:不想被序列化的变量我们可以使用transient
或static
关键字修饰;transient 关键字的作用是阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被 transient 修饰的变量值不会被持久化和恢复;而static关键字修饰的变量并不属于对象本身,所以也同样不会被序列化!具体原因,我们在后面会解释,继续往下看。
二、Java中的序列流
为了探讨Java对象序列化与反序列化的过程,以及Serializable关键字在整个过程中的作用,我们先来提一个 序列流
的概念,刚好我们最近也在写关于Java IO的相关博客。
Java 的序列流(ObjectInputStream 和 ObjectOutputStream)是一种可以将 Java 对象序列化和反序列化的流。这个属于基本的字节输入流与输出流的演变,之前的博文中已经介绍了它们的用法,在这里就不再展开了。
- ObjectOutputStream:将序列化后的字节序列写入到文件、网络等输出流中。
- ObjectInputStream:可以读取 ObjectOutputStream 写入的字节流,并将其反序列化为相应的对象(包含对象的数据、对象的类型和对象中存储的属性等信息)。
三、序列化实战
OK,有了上面两个理论知识作为铺垫,我们接下来就可以进行序列化的实战了,首先,我们要先创建一个包含简单属性的类,这里我们创建了一个Person类,里面有name和age两个属性字段。然后,我们通过ObjectOutputStream流将对象写出到文件(序列化),然后再通过ObjectInputStream读取文件中的数据,输出为一个person对象(反序列化)。
话不多说,直接上代码:
public class Test {
public static void main(String[] args) throws IOException {
//初始化对象信息
Person person =