1. 概述

Java 14 引入了一个新的注解 @Serial,用于协助开发者在编译期检查与序列化相关的成员是否正确实现。

✅ 类似于我们熟悉的 @Override@Serial 本身不改变运行时行为,但它配合 -Xlint:serial 编译选项,能在编译阶段捕获常见的序列化错误,比如方法签名错误、非法使用等。

⚠️ 需要注意的是:虽然 @Serial 注解从 JDK 14 build 25 开始就已经可用,但对应的编译期检查(lint check)功能至今仍未完全发布(见 JDK-8202056)。这意味着目前大多数 JDK 发行版中,即使使用该注解也不会触发警告或错误 —— 它更多是为未来做准备。

不过,提前了解其设计意图和用法,有助于我们在后续版本中快速上手,避免踩坑。


2. 使用方式

@Serial 可应用于实现 Serializable 接口的类中,以下 7 个特定的序列化相关成员

  • serialVersionUID
  • serialPersistentFields
  • writeObject()
  • readObject()
  • readObjectNoData()
  • writeReplace()
  • readResolve()

✅ 正确示例

public class MySerialClass implements Serializable {

    @Serial
    private static final ObjectStreamField[] serialPersistentFields = null;
    
    @Serial
    private static final long serialVersionUID = 1;
    
    @Serial
    private void writeObject(ObjectOutputStream stream) throws IOException {
        // 自定义序列化逻辑
    }
    
    @Serial
    private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
        // 自定义反序列化逻辑
    }

    @Serial
    private void readObjectNoData() throws ObjectStreamException {
        // 处理流数据不匹配的情况
    }

    @Serial
    private Object writeReplace() throws ObjectStreamException {
        // 序列化时替换对象
        return null;
    }

    @Serial
    private Object readResolve() throws ObjectStreamException {
        // 反序列化后替换对象(常用于单例)
        return null;
    }
}

🔧 编译时启用检查

要让 @Serial 发挥作用,必须使用如下命令编译:

javac -Xlint:serial MySerialClass.java

此时,编译器会进行以下检查:

  • 方法签名是否正确(参数类型、返回值、异常声明)
  • 字段类型是否符合规范
  • 注解是否被误用于不支持的场景

如果不符合,将给出警告或编译错误。


❌ 编译器将报错的典型场景

1. 类未实现 Serializable

public class MyNotSerialClass {
    @Serial 
    private static final long serialVersionUID = 1; // ❌ 编译错误
}

@Serial 只适用于 SerializableExternalizable 子类。普通类中使用会直接报错。

2. 在 enum 中使用序列化方法

public enum MyEnum { 
    INSTANCE;
    
    @Serial 
    private void readObjectNoData() throws ObjectStreamException {} // ❌ 编译错误 
}

枚举类型的序列化由 JVM 特殊处理,所有自定义序列化方法都会被忽略,因此在此类中使用 @Serial 属于无效操作。

3. Externalizable 类中使用标准序列化方法

public class MyExternalizableClass implements Externalizable {
    @Serial 
    private void writeObject(ObjectOutputStream stream) throws IOException {} // ❌ 编译错误 
}

实现 Externalizable 的类应自行实现 writeExternal()readExternal(),JVM 不会调用 writeObject() 等方法,因此这些方法在此上下文中无效。


3. 总结

@Serial 是 Java 朝着“更安全、更可维护”方向迈出的小步但重要一步。它的核心价值在于:

✅ 提供编译期校验能力,预防因拼写错误或签名不一致导致的序列化失败
✅ 明确标注意图,提升代码可读性,类似 @Override 的语义化作用
❌ 当前阶段受限于 lint 功能未完全启用,实际检查效果有限

📌 建议:
尽管当前工具链支持尚不完整,但对于长期维护的项目,可以开始有意识地使用 @Serial 注解,并配合文档或静态分析工具模拟检查逻辑,为未来 JDK 升级做好准备。

所有示例代码均可在 GitHub 获取:
👉 https://github.com/eugenp/tutorials/tree/master/core-java-modules/core-java-14


原始标题:Guide to the @Serial Annotation in Java 14