Почему я вижу, что значения полей констант Enum сериализуются / десериализуются?По какому сценарию и что именно не сериализуется в перечислениях? - PullRequest
0 голосов
/ 31 декабря 2018

Спецификация сериализации (SE8) подразумевает, что значения полей перечисляемых констант не сериализуются (как я прочитал):

1.12 Сериализация констант перечислений Константы перечисления сериализуются иначе, чем , чем обычные сериализуемые или экстернализуемые объекты.Сериализованная форма константы перечисления состоит исключительно из ее имени; значения полей константы отсутствуют в форме .

Но я вижу, что они сериализуются / десериализуются.

В приведенном ниже поле public int x = 1234; константы перечисления INSTANCE сериализуются / десериализуются в двух сценариях:

  1. Устанавливается только в конструкторе (после public int x = 1234;, setX() метод никогда не вызывался), затем x = 1234 успешно десериализуется.
  2. После конструктораЯ называю setX(7), но это заданное значение (x=7) затем успешно десериализуется.

Итак что именно и при каком сценарии не сериализуется / десериализуется в enum? А что означает термин "форма" (из приведенной выше цитаты)?

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)

    }

}

PS Этот ответ также подразумевает для меня, что полявнутри константы enum не сериализуются:

На мой взгляд, не имеет смысла отмечать значения полей Enum как переходные или делать их реализующими Serializable, поскольку они никогда не будут сериализованы , независимо от того, помечены они как временные или какMent Serializable.

Но все (непереходные) поля (по крайней мере, примитивные) либо в пределах константы перечисления (например, Color { RED { fields }; }) , либо в перечислении, но "снаружи "каждая отдельная константа (Color { RED { fields }; fields_for_all_constants}) действительно сериализуется , как я вижу.

1 Ответ

0 голосов
/ 31 декабря 2018

Ваш тест некорректен, так как предполагает десериализацию константы перечисления более чем в одном экземпляре константы перечисления.Из §8.9 JLS ( выделение мой):

Тип перечисления не имеет экземпляров, отличных от тех, которые определены его константами перечисления.Попытка явно создать экземпляр типа enum является ошибкой во время компиляции (§15.9.1).

В дополнение к ошибке времени компиляции, еще три механизма гарантируют, что экземпляры типа enum не существуют за пределамите, которые определены его константами перечисления:

  • Последний метод клонирования в Enum гарантирует, что константы перечисления никогда не могут быть клонированы.

  • Рефлексивная реализация enumтипы запрещены.

  • Специальная обработка механизмом сериализации гарантирует, что повторяющиеся экземпляры никогда не будут созданы в результате десериализации.

Это означает, что установка x на MyEnum1.INSTANCE изменяет значение глобально, так же, как и изменение состояния "обычного" синглтона.Когда вы десериализуете константу, вы получаете тот же экземпляр, который уже существует , что означает, что он имеет текущее значение 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 save для сериализации константы enum в файл.Затем выполните java Main load, который будет десериализовать константу перечисления, и выведите value.Несмотря на сериализацию константы enum, в то время как value был 10, десериализованный экземпляр будет иметь value быть 5 (начальное значение).Это строго указывает на то, что поля не сериализуются вместе с константой перечисления.


Поскольку я вижу, что мой пример кода почти такой же, как ваш, я не вижу, какая разница делает ваш фрагмент и мойведут себя по-разному ...

Ваш код создает одиночный экземпляр JVM, который устанавливает значение, сериализует перечисление, а затем десериализует перечисление.Поскольку все это происходит в одной и той же JVM, десериализованное перечисление будет тем же экземпляром, который уже существовал и изменил x.

Мой код имеет два режима: "сохранить" и "загрузить".

  • «save» создает экземпляр JVM, устанавливает value константы enum, затем сериализует перечисление в файл
  • «load» создает экземпляр JVM и десериализует константу enum изфайл.Он не изменяет value

Два режима не могут произойти во время одного вызова программы, что означает, что для каждого режима используется другой экземпляр JVM.Поскольку это происходит в двух разных экземплярах JVM, десериализованное перечисление - это не тот экземпляр , который был сериализован.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...