Java中序列化与反序列化---static字段
1、概述
在Java中,静态字段(static fields)是与类本身相关联的,而不是与类的任何特定实例相关联。这意味着静态字段在类加载时初始化,并且在整个应用程序的生命周期内保持一个唯一的副本。
2、静态字段的特性
2.1、与类相关联
静态字段是在类加载时初始化的,而不是在实例化对象时初始化的。它们属于类本身,而不是类的任何一个实例。因此,所有该类的实例共享同一个静态字段。
2.2、唯一副本
无论创建多少个类的实例,静态字段在内存中都只有一个副本。所有实例都共享这个唯一的副本。
2.3、通过类名访问
静态字段可以通过类名直接访问,而不需要创建类的实例。
2.4、生命周期
静态字段的生命周期与类的生命周期一致。在类加载时初始化,在类卸载时销毁。
3、静态字段不被序列化
序列化是将对象的状态转换为字节流的过程,方便将对象保存到文件中获通过网络传输。由于静态字段与类相关联,而不是与类的实例关联,因此在序列化对象时,静态字段的值不会被包含在序列化的字节流中。换言之,静态字段是类级别的状态,而序列化只保存实例级别的状态。下面用一个实例来演示静态字段不被序列化。
import java.io.*;
class MyClass implements Serializable {
public static int staticField = 100;
public int instanceField;
public MyClass(int instanceField) {
this.instanceField = instanceField;
}
}
public class Main {
public static void main(String[] args) {
MyClass obj = new MyClass(200);
try {
//
FileOutputStream fileOutputStream = new FileOutputStream("C:\\object.ser");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(myClass);
objectOutputStream.close();
fileOutputStream.close();
MyClass.staticField = 400;
FileInputStream fileInputStream = new FileInputStream("C:\\object.ser");
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
MyClass deserializedObj = (MyClass)objectInputStream.readObject();
objectInputStream.close();
fileInputStream.close();
// 输出反序列化对象的字段值
System.out.println("Static Field: " + MyClass.staticField); // 输出 400
System.out.println("Instance Field: " + deserializedObj.instanceField); // 输出 200
} catch (Exception e) {
e.printStackTrace();
}
}
}
示例中,首先对MyClass类进行实例化得到obj对象,并将obj对象进行序列化,然后将其保存到磁盘中;接着对静态字段staticField进行修改;然后对磁盘中保存的文件进行反序列化,以得到MyClass类的对象,打印staticField属性,发现是修改后的值。说明静态字段不会被序列化。
4、一个疑问
serialVersionUID不是被 static 变量修饰了吗?它会被“序列化”吗?
serialVersionUID确实是一个用static修饰的字段,但它并不是被序列化的对象的一部分。它的作用和其他静态字段不同,主要用于控制序列化和反序列化的过程。
4.1、作用
serialVersionUID是Java序列化机制中的一个版本控制标识符。它用于验证在序列化和反序列化过程中,发送端和接收端的类是否兼容。如果类的serialVersionUID不匹配,反序列化会失败并抛出InvalidClassException。
4.2、使用
在实现Serializable接口的类中,可以显式地声明serialVersionUID,例如:
public class MyClass implements Serializable {
private static final long serialVersionUID = 1L;
private int instanceField;
public MyClass(int instanceField) {
this.instanceField = instanceField;
}
}
如果没有显式声明serialVersionUID,Java编译器会根据类的结构自动生成一个serialVersionUID。然后,自动生成的serialVersionUID是基于类的结构(包括字段、方法等),因此如果类的结构发生变化,自动生成的serialVersionUID也会变化,导致反序列化失败。
4.2、为什么serialVersionUID是静态的
serialVersionUID被声明为static是因为它与类本身相关联,而不是与类的实例相关联。它用于在类的版本之间进行一致性检查,而不需要保存在每个序列化的对象中。
4.2、为什么serialVersionUID会被“序列化”
实际上,serialVersionUID并不会被序列化。它的作用是在序列化和反序列化过程中进行版本控制。
序列化时:Java序列化机制会将类的serialVersionUID写入序列化的字节流中。
反序列化时:Java序列化机制会读取字节流中的serialVersionUID,并将其与当前类的serialVersionUID进行比较。如果匹配,则反序列化成功;如果不匹配,则抛出InvalidClassException。
5、总结
静态字段在Java中属于类本身,而不是类的实例。在序列化过程中,静态字段的值不会被包含在序列化的字节流中,因为它们是类级别的状态,而序列化只保存实例级别的状态。这是因为静态字段在内存中只有一个副本,并且与类的生命周期一致。serialVersionUID是一个静态字段,用于控制Java序列化和反序列化过程中的版本一致性检查。虽然它是静态的,但它并不会被序列化到字节流中,而是用于在序列化和反序列化过程中进行版本控制,以确保类的兼容性。
免责声明
本文仅用于技术讨论与学习,利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,本平台和发布者不为此承担任何责任。