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

如何通过超类构造函数创建子类实例?Java序列化实现解析

Java序列化通过超类构造创建子类实例的原理拆解

这个问题问到了Java序列化里一个很容易被忽略的细节——为什么反序列化时能通过超类构造函数生成子类实例,咱们先从完整的测试代码入手,再一步步拆解背后的原理。

完整测试代码

先把你提供的代码补全,能直接运行看到现象:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class Test {
    public static class A {
        public final int a;
        public A() {
            this.a = 0;
            System.out.println("父类A的无参构造被调用");
        }
    }

    public static class B extends A implements Serializable {
        public final int b;
        public B() {
            this.b = 1;
            System.out.println("子类B的无参构造被调用");
        }
    }

    public static void main(String[] args) throws Exception {
        // 序列化过程
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(new B());
        oos.close();

        // 反序列化过程
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        B b = (B) ois.readObject();
        ois.close();

        System.out.println("反序列化得到的实例:a=" + b.a + ", b=" + b.b);
    }
}

运行这段代码,你会看到输出:

父类A的无参构造被调用
子类B的无参构造被调用
父类A的无参构造被调用
反序列化得到的实例:a=0, b=1

注意看:反序列化时只调用了父类A的构造,子类B的构造完全没被触发,但我们却得到了一个完整的B实例——这就是核心疑问点。

背后的核心原理

Java反序列化的对象创建逻辑和平时new对象完全不同,关键在于这两点:

1. 子类实例:绕过构造函数直接创建

反序列化时,ObjectInputStream会使用底层的非构造方法实例化机制(比如sun.misc.Unsafe.allocateInstance)直接在堆上分配子类对象的内存,完全不调用子类的任何构造函数。这是它能在不执行子类构造的情况下生成实例的核心原因。

2. 未序列化超类:必须用构造初始化

对于没有实现Serializable接口的超类(比如例子里的A),它的成员变量并没有被序列化到字节流中(只有实现Serializable的类的成员才会被序列化)。为了保证超类部分的状态是合法的,JVM会调用这个超类的无参构造函数来初始化它的成员——这就是为什么你会看到反序列化时父类构造被调用的原因。

如果超类没有提供无参构造,反序列化会直接抛出java.io.InvalidClassException,因为JVM没办法初始化超类的状态。

额外的细节补充

  • 如果把父类A也实现Serializable,那么反序列化时连父类的构造也不会被调用,所有成员变量的状态都会直接从字节流中恢复;
  • 对于final成员变量:如果是在未序列化超类的构造里赋值(比如A的a),反序列化时会通过构造重新赋值;而子类的final变量(比如B的b)因为被序列化了,会直接从字节流读取值,不会受构造函数影响。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 03:59:02