你是不是想问:为什么有的类实现Serializable接口?还有什么是序列化和反序列化?
带着这个问题往下看,看完就明白了!

序列化与反序列化

  • 序列化是指把对象转换为字节序列的过程,我们称之为对象的序列化,就是把内存中的这些对象变成一连串的字节(bytes)描述的过程,也就是把一个对象,保存到一个持久的文件中,而ObjectOutputStream 类就是用来做这个事的。
  • 反序列化则相反,就是把持久化的字节文件数据恢复为对象的过程,ObjectInputStream类就是做这个事情的 。那么什么情况下需要序列化呢?大概有这样两类比较常见的场景:1)、需要把内存中的对象状态数据保存到一个文件或者数据库中的时候,这个场景是比较常见的,例如我们利用mybatis框架编写持久层insert对象数据到数据库中时;2)、网络通信时需要用套接字在网络中传送对象时,如我们使用RPC协议进行网络通信时;

Serializable接口概述

Serializable是java.io包中定义的、用于实现Java类的序列化操作而提供的一个语义级别的接口。Serializable序列化接口没有任何方法或者字段,只是用于标识可序列化的语义。实现了Serializable接口的类可以被ObjectOutputStream转换为字节流,同时也可以通过ObjectInputStream再将其解析为对象。例如,我们可以将序列化对象写入文件后,再次从文件中读取它并反序列化成对象,也就是说,可以使用表示对象及其数据的类型信息和字节在内存中重新创建对象。

而这一点对于面向对象的编程语言来说是非常重要的,因为无论什么编程语言,其底层涉及IO操作的部分还是由操作系统其帮其完成的,而底层IO操作都是以字节流的方式进行的,所以写操作都涉及将编程语言数据类型转换为字节流,而读操作则又涉及将字节流转化为编程语言类型的特定数据类型。而Java作为一门面向对象的编程语言,对象作为其主要数据的类型载体,为了完成对象数据的读写操作,也就需要一种方式来让JVM知道在进行IO操作时如何将对象数据转换为字节流,以及如何将字节流数据转换为特定的对象,而Serializable接口就承担了这样一个角色。

看下面代码实例:

package com.hqq.serializable;import java.io.Serializable;public class Student implements Serializable {private static final long serialVersionUID = 1L;private String stuID;private String name;public Student(String stuID, String name) {this.stuID = stuID;this.name = name;}@Overridepublic String toString() {return "[" + stuID + "," + name + "]";}
}
package com.hqq.serializable;import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;public class Main {public static void main(String[] args) {String filePath = "C:\\Users\\GGboy\\Desktop\\file.txt";//序列化
//      Main.writeObject(filePath);//反序列化Main.readObject(filePath);}/*** 将对象持久化存储在文件中,也就是序列化对象*/public static void writeObject(String filePath) {Student stu = new Student("1611640303", "二狗子");try {ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(new File(filePath)));objectOutputStream.writeObject(stu);/** 注意:这种关闭流的操作是不对的,close一定要在finally里面关闭, * 那时候只要把objectInputStream的声明放到try语句外面就OK* 下面的close使用才是正确的*/objectOutputStream.close();} catch (Exception e) {e.printStackTrace();}}/*** 根据存储在文件中的对象的信息讲对象读取出来,也就是对象的反序列化*/public static void readObject(String filePath) {ObjectInputStream objectInputStream = null;try {objectInputStream = new ObjectInputStream(new FileInputStream(new File(filePath)));try {Object object = objectInputStream.readObject();Student stu = (Student) object;System.out.println(stu.toString());} catch (ClassNotFoundException e) {e.printStackTrace();} finally {if (objectInputStream != null) {objectInputStream.close();}}} catch (IOException e) {e.printStackTrace();}}
}

序列化结果:

  • 这时候有的人输出的文件中文可能是乱码,出现结果是因为你的IDE编译器的编码方式和你的电脑查看文件时的工具的编码方式不一样,我一般情况下会把所有的编码方式设置成utf-8,对编码方式不了解的可以看这个https://blog.csdn.net/qq_42570601/article/details/103381092,如果对IDE修改编码方式的操作不会的话可以百度,夜很深了,想早点睡觉,主要有两种,对包和类修改,对整个编译器修改,修改完之后把原来出现乱码的代码删除,在原样重新输入一遍就OK。

反序列化结果:

如果我们序列化与反序列化的时候不实现Serializable接口会怎样?看下面结果:

而且在反序列化的时候,如果private static final long serialVersionUID = 1L;这段代码中的serialVersionUID 的值和序列化时候的serialVersionUID 的值不一样,也会报错:

关于serialVersionUID

对于JVM来说,要进行持久化的类必须要有一个标记,只有持有这个标记JVM才允许类创建的对象可以通过其IO系统转换为字节数据,从而实现持久化,而这个标记就是Serializable接口。而在反序列化的过程中则需要使用serialVersionUID来确定由那个类来加载这个对象,所以我们在实现Serializable接口的时候,一般还会要去尽量显示地定义serialVersionUID,如:

private static final long serialVersionUID = 1L;

还有一个上面已经说过了,在反序列化的过程中,如果接收方为对象加载了一个类,如果该对象的serialVersionUID与对应持久化时的类不同,那么反序列化的过程中将会导致InvalidClassException异常。

如果我们在序列化中没有显示地声明serialVersionUID,则序列化运行时将会根据该类的各个方面计算该类默认的serialVersionUID值。但是,Java官方强烈建议所有要序列化的类都显示地声明serialVersionUID字段,因为如果高度依赖于JVM默认生成serialVersionUID,可能会导致其与编译器的实现细节耦合,这样可能会导致在反序列化的过程中发生意外的InvalidClassException异常。因此,为了保证跨不同Java编译器实现的serialVersionUID值的一致,实现Serializable接口的必须显示地声明serialVersionUID字段。

此外serialVersionUID字段地声明要尽可能使用private关键字修饰,这是因为该字段的声明只适用于声明的类,该字段作为成员变量被子类继承是没有用处的!有个特殊的地方需要注意的是,数组类是不能显示地声明serialVersionUID的,因为它们始终具有默认计算的值,不过数组类反序列化过程中也是放弃了匹配serialVersionUID值的要求。

参考文献:
https://www.w3cschool.cn/java/java-serialization.html
https://developer.51cto.com/art/201905/596334.htm

Java中的Serializable接口之“序列化”与“反序列化”相关推荐

  1. java培训教程分享:Java中怎样将数据对象序列化和反序列化?

    本期为大家介绍的java培训教程是关于"Java中怎样将数据对象序列化和反序列化?"的内容,相信大家都知道,程序在运行过程中,可能需要将一些数据永久地保存到磁盘上,而数据在Java ...

  2. 学习Java中实现serializable接口

    什么是Serializable接口? 一个对象序列化的接口,一个类只有实现了Serializable接口,它的对象才是可序列化的.Serializable实际上是一个空接口,没有什么具体内容,它的目的 ...

  3. java中的IO流之序列化与反序列化(对象数据和文件的读写交互)

    前言必读 读者手册(必读)_云边的快乐猫的博客-CSDN博客 一.知识点介绍 1.什么是序列化与反序列化? 对象与磁盘的交互读写. 2.为什么要学? 2.1在之前的IO流中,都是内存与磁盘进行交互的读 ...

  4. 深圳Java培训:Java中的对象流和序列化

    深圳Java培训:Java中的对象流和序列化 最近,在讲流的使用,其中对象流的作用其实就是将自定义类的对象与流之间相互转换的流. 看起来还是挺简单的,那么看下面的例子: public class St ...

  5. Android中的Serializable和Parcelable序列化

    Serializable和Parcelable接口都可以完成对象的序列化过程,在Android中当我们需要通过Intent和Binder传输数据时,我们要传输的对象就需要使用Serializable和 ...

  6. Java中的某些接口为什么没有任何方法?

    java中,有些接口内部没有声明任何方法,也就是说,实现这些接口的类不需要重写任何方法. 这些没有任何方法声明的接口又被叫做标识接口.标识接口对实现它的类没有任何语义上的要求,仅仅充当标识的作用,用来 ...

  7. Java中的Type接口和Class类区别和联系

    Java中的Type接口和Class类有什么区别 Type是Class的父接口. Type 是 Java 编程语言中所有类型的公共高级接口.它们包括原始类型.参数化类型.数组类型.类型变量和基本类型. ...

  8. Java中抽象类和接口在概念、语法和应用上的区别和关系

    2019独角兽企业重金招聘Python工程师标准>>> 春招开始了,盆友们都忙着准备笔试.准备面试,复习学过的知识点,当然我也不例外,在这里祝每一个"有心人"心想 ...

  9. 关于Java中抽象类和接口的一点思索

    Java中抽象类和接口的一点思索 成员方法上的区别: 1. java中的抽象类可以提供成员方法的实现细节,抽象方法只能是被public和protected来修饰 配合abstract关键字,子类需要实 ...

最新文章

  1. 小鹏,该兑现PPT了
  2. Linux 系统的目录结构_【all】
  3. node读写本地文件
  4. 常见拒绝服务型攻击原理及行为特征
  5. 【网络安全】手把手给大家演练红队渗透项目
  6. BZOJ-1003-物流运输trans-ZJOI2006-SPFA+DP
  7. 【C++】Visual studio样式定制
  8. oracle 表 上限,Oracle分区表(Partition Table)的数量限制
  9. 【心路】谈谈最近的一些想法吧
  10. CLOUD TOOLKIT打包SPRINGCLOUD项目,多模块
  11. caffe-SSD源码解析——生成数据列表及数据集
  12. 【读书笔记】segment routing mpls数据平面-2
  13. msys2编译器的配置
  14. 群体智能优化算法之烟花算法(Fireworks Algorithm,FWA)
  15. 佐治亚理工计算机科学录取,佐治亚理工学院计算机科学排名第4(2018年TFE美国排名)...
  16. 如何用Python给自己做一个年终总结
  17. 计算hashCode通用计算公式
  18. 直接把结果输出到打印机
  19. 图像处理中,在图片上写字,包括中文与英文!
  20. CH579 Cortex-M0 内核低功耗蓝牙 MCU 集成 ARM 内核 32 位微控制器

热门文章

  1. 软件测试是干什么的?
  2. Linux压缩和解压命令:tar,gzip,zip(unzip),rar
  3. 第三代智能房,未来已来!与京东,一起京彩未来,与众咖论道科技地产!
  4. 大疆无人机 从Mavic Pro 兼容 Mavic 2 zoom (解决方案)
  5. 23.FastAPI后台任务
  6. 浅谈my batis 下的Bind
  7. cube开源一站式云原生机器学习平台-架构(一)
  8. 深度学习中激活函数的作用
  9. nyoj 665 光棍的yy
  10. python怎么设置字符间距_使用等字符间距python打印二维列表