1、什么是序列化?什么是反序列化?
如果我们需要持久化 Java 对象比如将 Java 对象保存在文件中,或者在网络传输 Java 对象,这些场景都需要用到序列化。 (只要我们对内存中的对象进行持久化或网络传输,这个时候都需要序列化和反序列化.)
简单来说:
- 序列化: 将数据结构或对象转换成二进制字节流(字节序列)的过程
- 反序列化:将在序列化过程中所生成的二进制字节流(字节序列)转换成数据结构或者对象的过程
对于 Java 这种面向对象编程语言来说,我们序列化的都是对象(Object)也就是实例化后的类(Class),但是在 C++这种半面向对象的语言中,struct(结构体)定义的是数据结构类型,而 class 对应的是对象类型。
下面是序列化和反序列化常见应用场景:
- 对象在进行网络传输(比如远程方法调用 RPC 的时候)之前需要先被序列化,接收到序列化的对象之后需要再进行反序列化;
- 将对象存储到文件之前需要进行序列化,将对象从文件中读取出来需要进行反序列化;
- 将对象存储到数据库(如 Redis)之前需要用到序列化,将对象从缓存数据库中读取出来需要反序列化;
- 将对象存储到内存之前需要进行序列化,从内存中读取出来之后需要进行反序列化。
综上:序列化的主要目的是通过网络传输对象或者说是将对象存储到文件系统、数据库、内存中。
Serializable接口是启用其序列化功能的接口。实现java.io.Serializable 接口的类是可序列化的。没有实现此接口的类将不能使它们的任意状态被序列化或逆序列化。
2、如何实现序列化和反序列化?
Java的序列化能够将一个对象转化为字节流以便于在网络上传输或者将对象保存到磁盘上。反序列化则是将字节流还原为原始的Java对象以便于进行读取和处理,以下是Java中实现序列化和反序列化的方式:
(1)实现Serializable接口
Java中,实现了 Serializable 接口的对象可以被序列化和持久化。一个类仅仅需要实现 Serializable 接口,并且该接口没有抽象方法,这样该类就自动有序列化和反序列化的能力。例如:
public class User implements Serializable {
private static final long serialVersionUID = 1L; // 序列化版本号,用于标识对象的版本
private String name;
private int age;
private String profession;
// ... 省略其他属性及构造函数及get/set方法
}
(2)使用ObjectOutputStream和ObjectInputStream类
Java中,可以通过Java序列化机制将对象写入到一个输出流中,如 FileOutputStream,以便于在各种应用之间交换数据或者在本地进行持久化。ObjectOutputStream 是用于将对象串化为字节流的类,而ObjectInputStream 则可以用于反串行化出类(也就是将字节流反序列化为 Java 对象)。例如:
// 序列化对象到文件
User user = new User("Tom", 28, "Engineer");
FileOutputStream fileOut = new FileOutputStream("userdata.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(user);
out.close();
fileOut.close();
// 反序列化对象
FileInputStream fileIn = new FileInputStream("userdata.ser");
ObjectInputStream in = new ObjectInputStream(fileIn);
User readUser = (User) in.readObject();
in.close();
fileIn.close();
System.out.println(readUser.getName()); // 输出:Tom
在序列化过程中,需要注意版本的一致性,使用 serialVersionUID 来控制版本的一致性,当版本不一致的时,反序列化会抛出 InvalidClassException 异常。
(3)使用JSON等方式
除了使用Java特有的序列化机制实现对象的序列化和反序列化之外,常常也可以使用通用的数据格式如JSON等实现对象的序列化和反序列化。例如:
// 将Java对象转为JSON字符串
User user = new User("Tom", 28, "Engineer");
String json = new Gson().toJson(user);
// 将JSON字符串反序列化为Java对象
User readUser = new Gson().fromJson(json, User.class);
System.out.println(readUser.getName()); // 输出:Tom
在使用通用的数据格式实现对象的序列化和反序列化时需要注意,JSON等数据格式一般不能实现对象的完全持久化,一些Java对象的属性,如方法,静态变量等都不能通过序列化机制进行持久化。
序列化和反序列化是Java中重要的概念,经常被问及面试题。以下是一些可能的高频面试题:
- 什么是序列化和反序列化?
- Java中的哪些类和接口用于支持序列化和反序列化?
- 为什么要进行对象的序列化?
- 如何实现自定义对象的序列化?
- 序列化的常见问题有哪些?
- 如何避免对象序列化中的安全问题?
- 在分布式系统中,如何将对象进行序列化和反序列化?
- 如何解决序列化版本不兼容的问题?
- 在Java中,如何实现跨JVM的对象传输?
- 序列化与JSON、XML、Protobuf等方式的比较有哪些?