You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java泛型类的对象序列化与反序列化原理探究

Java泛型类型擦除后,序列化与反序列化如何保留类型信息?

我知道Java编译器会对泛型类执行类型擦除,将类型参数的类型信息替换为Object或其上限。但有个疑问:序列化及后续反序列化时,原始类型是怎么恢复的?

比如定义如下泛型类:

public class TestGeneric<T extends Serializable> implements Serializable {
    T value;
    public TestGeneric(T value) {
        this.value = value;
    }

    public T getValue() {
        return this.value;
    }
}

按照类型擦除的规则,我认为JVM运行时会把它视为:

public class TestGeneric implements Serializable {
    Serializable value;
    public TestGeneric(Serializable value) {
        this.value = value;
    }

    public Serializable getValue() {
        return this.value;
    }
}

但执行以下代码时,第二次运行(反序列化阶段)会抛出类型转换异常:

public static void main(String argv[]) {
        File newFile = new File("./test.txt");
        try {
            if (!newFile.exists()) {
                TestGeneric<Double> testGeneric = new TestGeneric<>(0.2d);

                try (FileOutputStream fos = new FileOutputStream(newFile); ObjectOutputStream oos = new ObjectOutputStream(fos)) {
                    oos.writeObject(testGeneric);
                }
            } else {
                try (FileInputStream fis = new FileInputStream(newFile); ObjectInputStream ois = new ObjectInputStream(fis)) {
                    try {
                        TestGeneric<Boolean> genericRead = (TestGeneric<Boolean>) ois.readObject();
                        Boolean isNotABoolean = genericRead.getValue();
                        System.out.println(isNotABoolean);
                    } catch (ClassNotFoundException e) {
                        System.err.println("Class not found!");
                    }
                }
            }
        } catch(IOException e) {
            System.err.println("IO Exception occurred, reason: " + e.getMessage());
        }
}

报错信息:

Exception in thread "main" java.lang.ClassCastException: class java.lang.Double cannot be cast to class java.lang.Boolean (java.lang.Double and java.lang.Boolean are in module java.base of loader 'bootstrap')

这看起来就算经过类型擦除,JVM运行时还是知道字段的确切类型,这是怎么实现的?


核心原因:类型擦除针对泛型类定义,而非对象实例的运行时类型

  1. 类型擦除的作用范围
    类型擦除是Java泛型的编译期特性,它只修改泛型类的字节码定义——把类里的泛型参数T替换为它的上限Serializable,但这并不影响对象实例在运行时的实际类型信息。

比如你创建TestGeneric<Double>实例时,它的value字段实际存储的是Double对象,这个对象的运行时类型是明确的,不会因为类定义被擦除而改变。

  1. 序列化保存的是对象的运行时状态
    ObjectOutputStream在序列化对象时,会把对象的完整运行时类型信息(包括每个字段的实际类型)写入到序列化流中,而不是擦除后的类定义信息。

对于上面的例子,序列化TestGeneric<Double>实例时,流里会记录value字段的实际类型是Double,而不是Serializable。

  1. 反序列化的类型重建与隐式转换
    反序列化时,ObjectInputStream会根据流里的类型信息重建对象,所以value字段依然是Double类型。

代码中出现ClassCastException的原因是:

  • 你做了一个未经检查的强制转换:(TestGeneric<Boolean>) ois.readObject()——因为类型擦除,编译器无法在编译时验证这个转换的合法性,只会给出警告。
  • 当你调用genericRead.getValue()时,编译器会插入一个隐式类型转换:把擦除后的返回值Serializable强制转为Boolean。但此时value实际是Double对象,所以运行时抛出类型转换异常。

简单来说:泛型类的定义被擦除,但对象实例的实际类型信息在运行时是存在的,序列化会完整保存这些信息,反序列化时会根据这些信息重建真实类型的对象。

内容的提问来源于stack exchange,提问作者Sebi2020

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 14:24:35