Недавно я обнаружил, что некоторые классы, скомпилированные Java (Java 8) и ajc (v.1.9.2), несовместимы с сериализация. Под совместимостью с сериализацией я подразумеваю, что рассчитанные значения 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:
Поля, генерируемые ajc:

Мои вопросы:
private static влияет на генерацию serialVersionUID?5. См. 4. 6. Да, существует целая Спецификация сериализации объектов. 7. Слишком широкий. Вы всегда можете попробовать, как я предложил, вместо того, чтобы просто добавлять больше бессмысленных вопросов. Один вопрос на пост — это обычный максимум.
>6. Да, существует целая Спецификация сериализации объектов. Почему в данном случае это не работает? Если есть спецификация - значит какой-то компилятор ее игнорирует. Который из? Я хочу знать более глубокие подробности. Не только ответы Да/Нет.
Не думаю, что уточняющие вопросы бессмысленны. Они помогают объяснить контекст - то, что я не хочу знать.
Зачем усложнять простое? Укажите свой собственный serialVersionUID! Вы действительно хотите полагаться на внутренности компилятора? И, конечно же, ajc создает другой код, потому что (а) он вплетает аспекты в ваши классы и (б) является ответвлением от ejc (компилятор Eclipse Java), а не от javac. Кстати, этот вопрос, возможно, страдает от проблема XY.
@kriegaex Я хочу знать, указано ли поведение компилятора? да - поэтому у нас есть JLS. Указано поведение сериализации - да. Вот почему у нас есть спецификация сериализации. Это не просто «внутренности компилятора». Это спецификация языка/платформы. Я знаю, что добавление serialVersionUID поможет с InvalidClassException. На самом деле для меня это не "проблема". >Зачем усложнять простое? Потому что, знаете ли, мы инженеры и хотим знать, как все устроено :)
@turbanoff Этот вопрос даже не имеет смысла, и вы можете найти это docs.oracle.com/javase/tutorial/jndi/objects/serial.html. Вы путаете внутреннее использование IDE для компиляции. Пожалуйста, поймите интерпретацию класса, прежде чем спрашивать о чем-то, что не имеет ничего общего с плохой практикой кодирования, которая вызывает подобные ошибки.
@April_Nara, не могли бы вы быть более конкретными? Я понимаю, что не определять serialversionUIS — плохая практика, потому что любое изменение в классе будет несовместимым. Я не меняю класс в своем вопросе. Компиляция и сериализация стандартизированы. Так что, на мой взгляд, все должно работать хорошо.
См. §4.6 Спецификация сериализации объектов Java; он явно предупреждает о том, что вычисление по умолчанию serialVersionUID очень чувствительно к реализациям компилятора. Это также объясняет, как вычисляется значение по умолчанию. Обратите внимание, что документация Serializable также содержит такое же предупреждение.




1. Разрешено ли компилятору Java по спецификации генерировать поля, которые не определены в классе?
Да, хотя они должны быть помечены как синтетический. Синтетика включает средства доступа для внутренних классов (хотя эта реализация недавно изменилась), методы лямбда-выражений и, я думаю, конструкторы по умолчанию.
2. Почему ajc генерирует дополнительное поле? Какая-то оптимизация производительности?
Похоже на плохую оптимизацию производительности. Возможно, это как-то связано с эффективным добавлением аспектов.
3. Есть ли способ заставить ajc не генерировать дополнительное поле?
Без понятия. Вы должны ожидать, что синтетика будет сгенерирована в любом случае.
4. По какой причине private static влияет на генерацию serialVersionUID?
Сериализация Java была создана в «интернет-время». Для совместимости нам оставили то, что делала первая версия. Мораль: если вы создаете что-то в Интернете, выбросьте это.
5. Знают ли разработчики аспекта об этом поведении? Если да, то почему они все равно выбирают поле генерации?
Я бы на это надеялся. Ожидается, что компиляторы создадут синтетику.
6. Существуют ли какие-либо гарантии того, что класс Java будет сериализован JLS?
Существует спецификация сериализации Java (хотя я не ожидаю, что многие люди ее прочитают).
7. Как код, сгенерированный Javac, работает без этого поля?
Относительно легко увидеть, как можно написать строку включения без массива. Точные детали уродливой оптимизированной версии будут беспорядочными. Вы можете увидеть, что на самом деле происходит с javap -private -c.
Заключение
Рекомендуется добавить serialVersionUID, если ожидается, что данные будут использоваться между разными версиями класса. OTOH, также рекомендуется не использовать сериализацию Java.
1. Да. 2. Похоже на таблицу переключения. 3. Нет. 4. Непонятно, что делает. Вам нужно указать
serialVersionUIDсамостоятельно, чтобы решить эту проблему.