絮叨
Java序列化其实这个用的多不多,我觉得看公司的技术栈吧,如果用的是cloud那套,估计接触的会少点,但是也不是说没有,如果是dubbo那套的话,就多点,上次我们说Netty的编码解码的时候说到了谷歌的protoBuf,今天的话我们就来好好看看Java序列化,详细的聊聊
一、认识序列化
从网络通信认识序列化
从上面这张图我们可以看到,两个进程进行通信时候,想要发送数据,要先要把数据发送到TCP缓冲区,然后形成报文再发送出去,同样的道理,接收端也是一样。我们可以相互发送各种类型的数据,包括文本、图片、音频、视频等, 而这些数据都会以二进制序列的形式在网络上传送。同样的,当两个Java进程进行通信时,也可以使用序列化技术实现对象之间的传递。一句话:Java序列化是指把Java对象转换为字节序列的过程,而Java反序列化是指把字节序列恢复为Java对象的过程
序列化的使用场景
这个使用场景应该算是最重要的一环了,因为我们学习序列化就是为了使用他,现在把他们归纳一下:
- 永久性保存对象,保存对的字节序列到本地文件或者数据库中;
- 通过序列化以字节流的形式对象在网络中进行传递和接收;
- 通过序列化在进程间传递对象
序列化有什么好处呢?
- 实现了数据的持久化,通过序列化可以把数据永久地保存到硬盘上
- 利用序列化实现远程通信,即在网络上传送对象的字节序列。
java序列化机制
java序列化主要有两个接口,这两个接口的实现方式,我都会给出,但是重点在于serialize接口的实现方式。在这一部分中,先给出序列化基本的代码实现,在下一部分当中再来看序列化有哪些需要注意的问题。OK,现在开始代码实现java的序列化机制。
使用Serializable接口实现序列化(重点,要牢记,第三部分会多次使用)
首先我们定义一个对象类User
接下来,在Test类中去实现序列化和反序列化。
当我们运行序列化方法时候,就可以看到,我们把数据存在了G://Test/template。
同时当我们运行反序列化方法的时候,就可以看到,反序列化成功,结果就不贴出来了,比较简单。
使用Externalizable接口实现序列化
然后就是Test1类,和上面的Test一样,就不再贴出来了。在这里主要看Externalizable和Serializable接口的区别。下面对其进行归纳一下。
- Externalizable继承自Serializable接口
- 需要我们重写writeExternal()与readExternal()方法
- 实现Externalizable接口的类必须要提供一个public的无参的构造器。
到这,java序列化机制的基本使用就讲完了,从上面可以看出,使用起来还是非常简单的。不过,仅仅会基本的使用还不行,想要面试的时候更加的装13,还需要进一步深化。因此下面一部分主要就是对序列化机制的深入分析。还需要说一点,对于Serializable接口实现的序列化方式一定要牢记,因为下面要多次使用
深入分析java序列化机制
serialVersionUID的作用
一句话:其目的是序列化对象版本控制,有关各版本反序列化时是否兼容。如果在新版本中这个值修改了,新版本就不兼容旧版本,反序列化时会抛出InvalidClassException异常。如果修改较小,比如仅仅是增加了一个属性,我们希望向下兼容,老版本的数据都能保留,那就不用修改;如果我们删除了一个属性,或者更改了类的继承关系,必然不兼容旧数据,这时就应该手动更新版本号,即SerialVersionUid。
serialVersionUID有两种显示的生成方式:
- 一是默认的1L,比如:private static final long serialVersionUID = 1L;
- 二是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段,
首先我们在User里面设置一下serialVersionUID=123456L。
接着我们更改serialVersionUID = 123456789L。然后再反序列化,就可以看到如下的错误了。同时也验证了序列化和反序列化需要版本一致的问题。
静态变量的序列化
首先需要说一下,静态变量不会被序列化。因为静态变量在全局区,本来流里面就没有写入静态变量,我打印静态变量当然会去全局区查找,当我write read 同时使用时,内存中的静态变量变了。
下面这个例子,主要是在对静态变量序列化之后,然后更改静态变量age的值,再重新反序列化输出一下。结果会出现两种情况:
-
(1)反序列输出的静态变量值没有变化:说明静态变量被序列化了。
-
(2)反序列输出的静态变量值变化了:说明静态变量没有被序列化了。
然后看一下输出结果
Transient 关键字作用
Transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。
这个很好理解也很简单。下面代码来看一下他的作用
-
第一步:把User里面的年龄属性改成Transient 修饰。private transient int age;
-
第二步:Test不变,进行序列化和反序列化。
-
第三步:看结果。反序列化之后的输出age应该为0;