Java中的 SerialVersionUID
运行时的序列化与每个可序列化的类关联一个称为 serialVersionUID 的版本号,在反序列化期间使用该版本号来验证序列化对象的发送方和接收方是否已为该对象加载了与序列化兼容的类。极客,现在您一定想知道为什么我们使用 SerialVersionUID?
这是因为 SerialVersionUID 用于确保在反序列化期间加载相同的类(在序列化过程中使用)。考虑运行下面给出的插图,以更公平地理解序列化和反序列化。
插图:
- 假设一个人在英国,另一个人在印度,分别要进行序列化和反序列化。在这种情况下,为了验证在印度的接收者是经过身份验证的人,JVM 创建了一个唯一的 ID,称为SerialVersionUID 。
- 在大多数情况下,序列化和反序列化这两个活动都是由同一系统和同一位置的一个人完成的。但是在序列化中,发送者和接收者不是同一个人,即人可能不同,机器或系统可能不同,位置必须不同,然后SerialVersionUID就出现了。在序列化中,发送者和接收者都应该只在开始时有.class文件,即要进行序列化的人和准备反序列化的人应该只在开始时包含相同的.class文件。
序列化时序列化,每个对象发送方 JVM 都会保存一个Unique Identifier 。 JVM 负责根据发送方系统中存在的相应 .class 文件生成该唯一 ID。
反序列化在反序列化时,接收方 JVM 会将与对象关联的唯一 ID 与本地类唯一 ID 进行比较,即 JVM 还将根据接收方系统中存在的相应 .class 文件创建唯一 ID。如果两个唯一 ID 匹配,则仅执行反序列化。否则,我们将得到运行时异常,即 InvalidClassException。这个唯一标识符不过是SerialVersionUID 。
根据 JVM 生成的默认 SerialVersionUID 也存在某些问题关联,如下所示:
- 发送者和接收者都应该在平台和版本方面使用相同的 JVM。否则,由于 SerialVersionUID 不同,接收方无法反序列化。
- 发送者和接收者都应该使用相同的“.class”文件版本。序列化后,如果接收方的“.class”文件有任何变化,则接收方无法反序列化。
- 为了在内部生成 SerialVersionUID,JVM 可能会使用可能会产生性能问题的复杂算法。
执行:
我们可以通过配置自己的 SerialVersionUID 来解决上述问题。我们可以配置我们自己的 SerialVersionUID,我们需要 3 个类,如下所示:
- 包含两个要序列化的变量的随机类,让它成为“极客”
- 将序列化对象的发送方类
- 将要反序列化的接收方的类
句法:
private static final long SerialVersionUID=10l;
示例 1:
JAVA
// Java program to illustrate Implementation of
// User-defined SerialVersionUID
// Main class
// // Geeks which contains two variable which
// are going to Serialize to
// Illustrate Implementation of Serializable Interface
class Geeks implements Serializable {
// User-defined SerialVersionUID
// Custom initialization
private static final long SerialVersionUID = 10l;
int i = 10;
int j = 20;
}
JAVA
// Java program to illustrate
// implementation of User-defined
// SerialVersionUID
// Importing required classes
import java.io.*;
// Sender side class which is going to Serialize object
class Sender {
// Main driver method
public static void main(String[] args)
{
// Creating object of class Random class which
// contains two variables which are going to
// Serialize In simpler words , object of class
// 'Geeks'
Geeks g = new Geeks();
// Here xyz.txt is the file name where the object is
// going to serialize
FileOutputStream fos
= new FileOutputStream("xyz.txt");
ObjectOutputStream oos
= new ObjectOutputStream(fos);
oos.writeObject(g);
}
}
JAVA
// Java program to illustrate Implementation of
// User-defined SerialVersionUID
// Importing I/O classes
import java.io.*;
// Receiver side class which is going to deserialize
class Receiver {
// main driver method
public static void main(String[] args)
{
// Here xyz.txt is the file name where the object is
// going to Deserialized
FileInputStream fis
= new FileInputStream("xyz.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
// Creating object of class 'Geeks'
Geeks g1 = (Geeks)ois.readObject();
// Print and display the
// deserialized object value
System.out.println("Deserialized Object Value:"
+ g1.i + "..." + g1.j);
}
}
示例 2:将序列化对象的发送方类
Java
// Java program to illustrate
// implementation of User-defined
// SerialVersionUID
// Importing required classes
import java.io.*;
// Sender side class which is going to Serialize object
class Sender {
// Main driver method
public static void main(String[] args)
{
// Creating object of class Random class which
// contains two variables which are going to
// Serialize In simpler words , object of class
// 'Geeks'
Geeks g = new Geeks();
// Here xyz.txt is the file name where the object is
// going to serialize
FileOutputStream fos
= new FileOutputStream("xyz.txt");
ObjectOutputStream oos
= new ObjectOutputStream(fos);
oos.writeObject(g);
}
}
示例 3:将要反序列化的接收方类
Java
// Java program to illustrate Implementation of
// User-defined SerialVersionUID
// Importing I/O classes
import java.io.*;
// Receiver side class which is going to deserialize
class Receiver {
// main driver method
public static void main(String[] args)
{
// Here xyz.txt is the file name where the object is
// going to Deserialized
FileInputStream fis
= new FileInputStream("xyz.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
// Creating object of class 'Geeks'
Geeks g1 = (Geeks)ois.readObject();
// Print and display the
// deserialized object value
System.out.println("Deserialized Object Value:"
+ g1.i + "..." + g1.j);
}
}
输出:
We can see the file which is xyz.txt where object is Serialize and also the output when we deserialize the Object.
Note: In the above program, if we perform any change to the Geeks .class file at the receiver end. We don’t get any problem at the time of deserialization. In this case, sender and receiver are not required to maintain the same JVM versions.