Несовместимость сериализации между классами, сгенерированными ajc и javac - PullRequest
0 голосов
/ 29 апреля 2019

Недавно я обнаружил, что некоторые классы, скомпилированные Java (Java 8) и ajc (v.1.9.2), несовместимы с serialization .Под совместимостью сериализации я подразумеваю, что рассчитанные значения по умолчанию serialVersionUID не совпадают.

Пример:

public class Markup implements Serializable {
    private final MyUnit unit;
    public Markup(MyUnit unit) { this.unit = unit; }

    public enum MyUnit { DOUBLE, STRING }

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Path path = Paths.get("markup.bin");
        if (args.length == 0) {
            try (OutputStream fileOutput = Files.newOutputStream(path);
                 ObjectOutputStream objectOutput = new ObjectOutputStream(fileOutput))
            {
                objectOutput.writeObject(new Markup(MyUnit.STRING));
            }
        } else {
            try (InputStream fileInput = Files.newInputStream(path);
                 ObjectInputStream objectInput = new ObjectInputStream(fileInput))
            {
                System.out.println(objectInput.readObject());
            }
        }
    }

    static String switchType(MyUnit unit) {
        switch (unit) {
            case STRING: return "%";
            case DOUBLE: return "p";
            default: return "Undefined";
        }
    }
}

Когда я компилирую этот класс с помощью ajc и запускаю, а затем компилирую этот класс с помощью javac и запускаюЯ получаю исключение о несовместимости формата сериализации:

Exception in thread "main" java.io.InvalidClassException: Markup; local class incompatible: stream classdesc serialVersionUID = -1905477862550005139, local class serialVersionUID = 793529206923536473

Я также узнал, что это из-за генератора кода коммутатора ajc.Создает дополнительное поле в классе private static int[] $SWITCH_TABLE$Markup$MyUnit

Поля, сгенерированные javac: enter image description here Поля, сгенерированные ajc: enter image description here

Myвопросы:

  1. Разрешено ли спецификацией для компилятора java генерировать поля, которые не определены в классе?
  2. Почему ajc генерирует дополнительное поле?Какая-то оптимизация производительности?
  3. Есть ли способ заставить ajc не генерировать дополнительное поле?
  4. В чем причина private static влияет на генерацию serialVersionUID?
  5. разработчики аспекта осведомлены об этом поведении?Если так, почему они все равно выбирают поле генерации?
  6. Существуют ли какие-либо гарантии того, как класс Java будет сериализован JLS?
  7. Как сгенерированный Javac-код работает без этого поля?

1 Ответ

3 голосов
/ 06 мая 2019

1. Разрешено ли спецификацией для компилятора Java генерировать поля, которые не определены в классе?

Да, хотя они должны быть помечены как синтетические . Синтетика включает средства доступа для внутренних классов (хотя эта реализация недавно изменилась), методы лямбда-выражений и, как мне кажется, конструкторы по умолчанию.

2. Почему ajc генерирует дополнительное поле? Какая-то оптимизация производительности?

Похоже, плохая оптимизация производительности. Возможно, это связано с эффективным добавлением аспектов.

3. Есть ли способ заставить ajc не генерировать дополнительное поле?

Понятия не имею. Вы должны ожидать, что синтетика будет генерироваться независимо.

4. В чем причина частной статики, влияющей на генерацию serialVersionUID?

Сериализация Java была создана в «Интернет время». Для совместимости мы оставляем все, что делала первая версия. Мораль: если ты что-то создаешь в Интернете, выбрось.

5. Знают ли разработчики aspectj об этом поведении? Если так, почему они все равно выбирают поле генерации?

Я бы на это надеялся. Ожидается, что компиляторы родят синтетику.

6. Есть ли гарантии того, что класс Java будет сериализован JLS?

Существует спецификация Java Serialization (хотя я не ожидаю, что многие ее прочитают).

7. Как сгенерированный Javac-код работает без этого поля?

Относительно легко увидеть, как можно написать строку включения без массива. Точные детали уродливой оптимизированной версии будут беспорядочными. Вы можете увидеть, что на самом деле происходит с javap -private -c.

Заключение

Рекомендуется добавить serialVersionUID, если предполагается, что данные будут использоваться между различными версиями класса. OTOH, также рекомендуется не использовать сериализацию Java.

...