Java中序列化与反序列化(五)——static字段


大家好,我是欧阳方超,可以扫描下方二维码关注我的公众号“欧阳方超”,后续内容将在公众号首发。
在这里插入图片描述

1、概述

静态字段(static)不会被序列化。静态字段属于类本身,而不是某个实例。
在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序列化和反序列化过程中的版本一致性检查。虽然它是静态的,但它并不会被序列化到字节流中,而是用于在序列化和反序列化过程中进行版本控制,以确保类的兼容性。

我是欧阳方超,把事情做好了自然就有兴趣了,如果你喜欢我的文章,欢迎点赞、转发、评论加关注。我们下次见。
在这里插入图片描述

  • 9
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值