Java 序列化和反序列化为什么要实现 Serializable 接口?

52 篇文章 1 订阅
5 篇文章 0 订阅

最近在做服务化, 需要把所有 model 包里的类都实现 Serializable 接口, 同时还要显示指定 serialVersionUID 的值. 听到这个需求, 我脑海里就突然出现了好几个问题, 比如说:

  • 序列化和反序列化是什么?
  • 实现序列化和反序列化为什么要实现 Serializable 接口?
  • 实现 Serializable 接口就算了, 为什么还要显示指定 serialVersionUID 的值?
  • 我要为 serialVersionUID 指定个什么值?

其实我上面思考的过程大家也可以借鉴,不要一上来就要去编码,否则会使自己特别的局限。

下面我们来一一解答这几个问题.

1.序列化和反序列化

  • 序列化:把对象转换为字节序列的过程称为对象的序列化.
  • 反序列化:把字节序列恢复为对象的过程称为对象的反序列化.

上面说的太过于书面化,我给大家用通俗的语言描述一下:

我们在前后端数据交互时,会将我们请求的数据放在body(请求体中),而这些内容只能是二进制 01 串,毕竟计算机只认识这玩意,但结构体呢,我们得想个办法将它也转为二进制 01 串,这样的方案现在也有很多现成的,比如JSONProtocol Buffers (Protobuf)

这个将结构体转为二进制数组的过程就叫序列化,反过来将二进制数组复原成结构体的过程叫反序列化。

2.什么时候需要用到序列化和反序列化呢?

当我们只在本地 JVM 里运行下 Java 实例, 这个时候是不需要什么序列化和反序列化的, 但当我们需要将内存中的对象持久化到磁盘,数据库中时, 当我们需要与浏览器进行交互时, 当我们需要实现 RPC 时, 这个时候就需要序列化和反序列化了。

前两个需要用到序列化和反序列化的场景, 是不是让我们有一个很大的疑问? 我们在与浏览器交互时, 还有将内存中的对象持久化到数据库中时, 好像都没有去进行序列化和反序列化, 因为我们都没有实现 Serializable 接口, 但一直正常运行.

下面先给出结论:

只要我们对内存中的对象进行持久化网络传输, 这个时候都需要序列化和反序列化.

理由:

服务器与浏览器交互时真的没有用到 Serializable 接口吗? JSON 格式实际上就是将一个对象转化为字符串, 所以服务器与浏览器交互时的数据格式其实是字符串, 我们来看来 String 类型的源码:
在这里插入图片描述

String 类型实现了 Serializable 接口, 并显示指定 serialVersionUID 的值.

然后我们再来看对象持久化到数据库中时的情况, Mybatis 数据库映射文件里的 insert 代码:

<insert id="insertUser" parameterType="org.tyshawn.bean.User">
    INSERT INTO t\_user(name, age) VALUES (#{name}, #{age})
</insert>

实际上我们并不是将整个对象持久化到数据库中, 而是将对象中的属性持久化到数据库中, 而这些属性都是实现了 Serializable 接口的基本属性。

3.实现序列化和反序列化为什么要实现 Serializable 接口?

在 Java 中实现了 Serializable 接口后, JVM 会在底层帮我们实现序列化和反序列化, 如果我们不实现 Serializable 接口, 那自己去写一套序列化和反序列化代码也行。

实现 Serializable 接口就算了, 为什么还要显示指定 serialVersionUID 的值?

如果不显示指定serialVersionUID, JVM在序列化时会根据属性自动生成一个 serialVersionUID, 然后与属性一起序列化, 再进行持久化或网络传输。

反序列化时, JVM 会再根据属性自动生成一个新版 serialVersionUID, 然后将这个新版 serialVersionUID 与序列化时生成的旧版 serialVersionUID 进行比较, 如果相同则反序列化成功, 否则报错。

大家看,这个过程是不是像极了cas控制原子性操作的过程。

如果显示指定了 serialVersionUID, JVM 在序列化和反序列化时仍然都会生成一个 serialVersionUID, 但值为我们显示指定的值, 这样在反序列化时新旧版本的 serialVersionUID 就一致了。

在实际开发中, 不显示指定 serialVersionUID 的情况会导致什么问题?

如果我们的类写完后不再修改, 那当然不会有问题, 但这在实际开发中是不可能的, 我们的类会不断迭代, 一旦类被修改了, 那旧对象反序列化就会报错。 所以在实际开发中, 我们都会显示指定一个 serialVersionUID, 值是多少无所谓, 只要不变就行。

4.Java 序列化的其他特性

先说结论:

  • transient 关键字修饰的属性不会被序列化,
  • static 属性也不会被序列化。

那么问题来啦:static 属性为什么不会被序列化?

因为序列化是针对对象而言的, 而 static 属性优先于对象存在, 随着类的加载而加载, 所以不会被序列化.

看到这个结论, 是不是有人会问, serialVersionUID 也被 static 修饰, 为什么 serialVersionUID 会被序列化?

其实 serialVersionUID 属性并没有被序列化, JVM 在序列化对象时会自动生成一个 serialVersionUID, 然后将我们显示指定的 serialVersionUID 属性值赋给自动生成的 serialVersionUID.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZNineSun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值