讲讲Java的序列化反序列化?

打印 上一主题 下一主题

主题 912|帖子 912|积分 2736


  • 序列化:把对象转换为字节序列的过程称为对象的序列化.
  • 反序列化:把字节序列恢复为对象的过程称为对象的反序列化.
什么时间会用到

当只在当地 JVM 里运行下 Java 实例,这个时间是不需要什么序列化和反序列化的,但当出现以了局景时,就需要序列化和反序列化了:

  • 当需要将内存中的对象持久化到磁盘,数据库中时
  • 当需要与浏览器进行交互时
  • 当需要实现 RPC 时
但是当我们在与浏览器交互时,另有将内存中的对象持久化到数据库中时,好像都没有去进行序列化和反序列化,因为我们都没有实现 Serializable 接口,但一直正常运行?
先给出结论:只要我们对内存中的对象进行持久化或网络传输,这个时间都需要序列化和反序列化.
理由:服务器与浏览器交互时真的没有用到 Serializable 接口吗? JSON 格式实际上就是将一个对象转化为字符串,所以服务器与浏览器交互时的数据格式其实是字符串,我们来看来 String 类型的源码:
  1. public final class String implements java.io.Serializable,Comparable<String>,CharSequence {
  2.     /\*\* The value is used for character storage. \*/
  3.     private final char value\[\];
  4.     /\*\* Cache the hash code for the string \*/
  5.     private int hash; // Default to 0
  6.     /\*\* use serialVersionUID from JDK 1.0.2 for interoperability \*/
  7.     private static final long serialVersionUID = -6849794470754667710L;
  8.     ......
  9. }
复制代码
String 类型实现了 Serializable 接口,并显示指定 serialVersionUID 的值.
然后再来看对象持久化到数据库中时的情况,Mybatis 数据库映射文件里的 insert 代码:
  1. <insert id="insertUser" parameterType="org.tyshawn.bean.User">
  2.     INSERT INTO t\_user(name,age) VALUES (#{name},#{age})
  3. </insert>
复制代码
实际上并不是将整个对象持久化到数据库中,而是将对象中的属性持久化到数据库中,而这些属性(如Date/String)都实现了 Serializable 接口。
为什么要实现 Serializable 接口?

在 Java 中实现了 Serializable 接口后, JVM 在类加载的时间就会发现我们实现了这个接口,然后在初始化实例对象的时间就会在底层实现序列化和反序列化。假如被写对象类型不是String、数组、Enum,而且没有实现Serializable接口,那么在进行序列化的时间,将抛出NotSerializableException。源码如下:
  1. // remaining cases
  2. if (obj instanceof String) {
  3.     writeString((String) obj, unshared);
  4. } else if (cl.isArray()) {
  5.     writeArray(obj, desc, unshared);
  6. } else if (obj instanceof Enum) {
  7.     writeEnum((Enum<?>) obj, desc, unshared);
  8. } else if (obj instanceof Serializable) {
  9.     writeOrdinaryObject(obj, desc, unshared);
  10. } else {
  11.     if (extendedDebugInfo) {
  12.         throw new NotSerializableException(
  13.             cl.getName() + "\n" + debugInfoStack.toString());
  14.     } else {
  15.         throw new NotSerializableException(cl.getName());
  16.     }
  17. }
复制代码
为什么要显示指定 serialVersionUID 的值?

假如不显示指定 serialVersionUID,JVM 在序列化时会根据属性自动生成一个 serialVersionUID,然后与属性一起序列化,再进行持久化或网络传输. 在反序列化时,JVM 会再根据属性自动生成一个新版 serialVersionUID,然后将这个新版 serialVersionUID 与序列化时生成的旧版 serialVersionUID 进行比较,假如相同则反序列化乐成,否则报错.
假如显示指定了 serialVersionUID,JVM 在序列化和反序列化时仍旧都会生成一个 serialVersionUID,但值为显示指定的值,这样在反序列化时新旧版本的 serialVersionUID 就一致了.
固然了,假如类写完后不再修改,那么不指定serialVersionUID,不会有问题,但这在实际开辟中是不可能的,类会不停迭代,一旦类被修改了,那旧对象反序列化就会报错。 所以在实际开辟中,都会显示指定一个 serialVersionUID。
static 属性为什么不会被序列化?

因为序列化是针对对象而言的,而 static 属性优先于对象存在,随着类的加载而加载,所以不会被序列化.
看到这个结论,是不是有人会问,serialVersionUID 也被 static 修饰,为什么 serialVersionUID 会被序列化? 其实 serialVersionUID 属性并没有被序列化,JVM 在序列化对象时会自动生成一个 serialVersionUID,然后将显示指定的 serialVersionUID 属性值赋给自动生成的 serialVersionUID。
不同序列化工具之间的权衡

不同序列化工具的权衡

  • 容量
  • 可读性
  • 操作难易
  • 性能
  • 兼容性
transient关键字

Java语言的关键字,变量修饰符,假如用transient声明一个实例变量,当对象存储时,它的值不需要维持。
也就是说被transient修饰的成员变量,在序列化的时间其值会被忽略,在被反序列化后, transient 变量的值被设为初始值, 如 int 型的是 0,对象型的是 null。
关于作者

来自一线步伐员Seven的探索与实践,持续学习迭代中~
本文已收录于我的个人博客:https://www.seven97.top
公众号:seven97,欢迎关注~

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

卖不甜枣

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表