当前位置: 首页 > 知识库问答 >
问题:

为什么我看到枚举常量的字段值会被序列化/反序列化?在哪种情况下,枚举中没有序列化哪些内容?

田翰林
2023-03-14

序列化规范(SE8)意味着枚举常量的字段值没有序列化(据我所知):

1.12枚举常量的序列化枚举常量的序列化方式不同于普通的可序列化或可外部化对象。枚举常量的序列化形式仅由其名称组成;表单中不存在常量的字段值。

但是我看到它们确实被序列化/反序列化了。

在字段public int x=1234下面的代码中;枚举常量INSTANCE在两种情况下被序列化/反序列化:

  1. 仅在构造函数中设置(在从未调用过的公共int x=1234;之后,setX()方法),然后成功地反序列化

那么到底是什么以及在什么情况下没有在枚举中序列化/反序列化?术语“形式”是什么意思(来自上面的引用)?

enum MyEnum1 {

INSTANCE {
    public int x = 1234;

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }
};

public abstract int getX();

public abstract void setX(int x);

}

public class AAA {

    public static void main(String[] args) {


        MyEnum1 obj = MyEnum1.INSTANCE; 
        obj.setX(7);
        System.out.println(obj.getX());
        String fileName = "d:\\del.me";
        int bufSz = 8 * 1024;

        try {
            ObjectOutputStream oos = new ObjectOutputStream(
                    new BufferedOutputStream(new FileOutputStream(fileName), bufSz));
            oos.writeObject(obj);
            oos.flush();
        } catch (IOException e) {   }


        MyEnum1 obj1 = null;
        MyEnum1 obj2 = null;
        try {
            ObjectInputStream ois = new ObjectInputStream(
                    new BufferedInputStream(new FileInputStream(fileName), bufSz));

            ObjectInputStream ois1 = new ObjectInputStream(
                    new BufferedInputStream(new FileInputStream(fileName), bufSz));

            obj1 = (MyEnum1) ois.readObject();
            obj2 = (MyEnum1) ois1.readObject();
        } catch (IOException | ClassNotFoundException e) {  }

        // x is restored, either the one set by setX()
        // or the one just set in inline constructor (initializer) without calling setX() 
        System.out.println(obj1.getX());  // 1234 without setX(7), 7 with setX(7)
        System.out.println(obj2.getX());  // 1234 without setX(7), 7 with setX(7)

    }

}

附言。这个答案也暗示我枚举常量中的字段不会序列化:

在我看来,将枚举的字段值标记为transient或使其实现可序列化是没有意义的,因为它们永远不会被序列化,无论它们是标记为transient还是实现可序列化。

但在我看来,枚举常量内(如Color{RED{fields};})或枚举内但“在”每个单独常量之外(如Color{RED{fields};fields\u for\u all\u constants})的所有(非瞬态)字段(至少是原始字段)都会被序列化。

共有1个答案

皇甫浩壤
2023-03-14

您的测试有缺陷,因为它假设反序列化枚举常量会导致枚举常量的多个实例。来自JLS的§8.9(强调我的):

枚举类型除了由其枚举常量定义的实例外,没有其他实例。试图显式实例化枚举类型是编译时错误(§15.9.1)。

除了编译时错误之外,还有三种机制确保枚举类型的实例不存在超出其枚举常量定义的实例:

>

禁止枚举类型的反射实例化。

序列化机制的特殊处理可确保不会因反序列化而创建重复实例。

这意味着在MyEnum1上设置x。实例全局更改值,与更改“常规”单例的状态相同。当您反序列化常量时,您会得到已经存在的相同实例,这意味着它的当前值为x。

更好的测试是在程序的一次运行中序列化常量,然后在后续运行中反序列化。请尝试以下示例:

import java.io.*;
import java.nio.file.*;

public class Main {

    public static void main(String[] args) throws Exception {
        var file = Path.of(System.getProperty("user.dir")).resolve("myenum.bin");
        switch (args[0].toLowerCase()) {
            case "save":
                var instance = MyEnum.INSTANCE;
                instance.setValue(10);
                try (var oos = new ObjectOutputStream(Files.newOutputStream(file))) {
                    oos.writeObject(instance);
                }
                break;
            case "load":
                try (var ois = new ObjectInputStream(Files.newInputStream(file))) {
                    System.out.println(((MyEnum) ois.readObject()).getValue());
                }
                break;
            default:
                throw new IllegalArgumentException("expected 'save' or 'load', actual = " + args[0]);
        }
    }

    public enum MyEnum {
        INSTANCE {
            private int value = 5;
            @Override public void setValue(int value) { this.value = value; }
            @Override public int getValue() { return value; }
        };
        public abstract void setValue(int value);
        public abstract int getValue();
    }
}

首先运行java Main sav将枚举常量序列化为文件。然后执行java Main load,这将反序列化枚举常量并打印。尽管在10时序列化了枚举常量,但反序列化的实例将具有5(初始值)。这强烈表明字段没有与枚举常量一起序列化。

正如我所看到的,我的示例代码几乎与您的完全相同,我不知道是什么不同导致了您的代码片段和我的代码片段的行为不同。。。

您的代码创建一个设置值的JVM实例,序列化枚举,然后反序列化枚举。由于这一切都发生在同一个JVM中,因此反序列化枚举将是已经存在并更改了x的同一个实例。

我的代码有两种模式:“保存”和“加载”。

  • “保存”创建一个JVM实例,设置枚举常量的,然后将枚举序列化为文件
  • "load"创建一个JVM实例并从文件中反序列化枚举常量。它不修改

这两种模式不能在一次调用程序的过程中发生,这意味着每种模式使用不同的JVM实例。因为这发生在两个不同的JVM实例上,所以反序列化的枚举与序列化的实例不同。

 类似资料:
  • 问题内容: 我如何使用gson 2.2.4序列化和反序列化一个简单的枚举? 问题答案: 根据 GsonAPI文档 ,Gson提供了的默认序列化/反序列化,因此基本上,应使用标准和方法(与其他类型一样)对序列化和反序列化。

  • 问题内容: 我正在使用JAVA 1.6和Jackson 1.9.9我有一个枚举 我添加了一个@JsonValue,这似乎可以将对象序列化为: 但是当我尝试反序列化时,我得到了 我在这里想念什么? 问题答案: 如果你希望将枚举类与其JSON表示完全脱钩,则@xbakesx指出的序列化器/反序列化器解决方案是一个很好的解决方案。 另外,如果你喜欢一个独立的解决方案,则基于·和·注释的实现会更方便。 因

  • 我使用的是JAVA 1.6和Jackson 1.9.9我有一个枚举 我添加了一个@jsonValue,这似乎完成了它将对象序列化为:

  • 我需要序列化/反序列化特定枚举: 我有个例外: 我如何通过GSON序列化/反序列化它?

  • 从ObjectInputStream的Javadoc: 枚举常量的反序列化方式不同于普通的可序列化或可外部化对象。枚举常量的序列化形式仅由其名称组成;常数的字段值不会被传输。要反序列化枚举常量,ObjectInputStream从流中读取常量名称;然后通过调用静态方法Enum获得反序列化常量。valueOf(类,字符串),枚举常量的基类型和接收到的常量名称作为参数。与其他可序列化或可外部化的对象一

  • 问题内容: 我正在尝试并且未能对Jackson 2.5.4的枚举进行反序列化,并且我不太清楚我的情况。我的输入字符串是驼峰式的,我想简单地映射到标准的Enum约定。 我也尝试了吸气剂,但没有成功,这是我在其他地方看到的一种选择。他们都炸毁了: 我究竟做错了什么? 问题答案: 编辑: 从Jackson 2.6开始,您可以在枚举的每个元素上使用以指定其序列化/反序列化值(请参见此处): (此答案的其余