Eclipse выдает предупреждения при отсутствии serialVersionUID.
The serializable class Foo does not declare a static final serialVersionUID field of type long
Что такое serialVersionUID и почему это важно? Покажите пример, в котором отсутствие serialVersionUID вызовет проблему.




Документы для java.io.Serializable, вероятно, дают такое же хорошее объяснение, как и вы:
The serialization runtime associates with each serializable class a version number, called a
serialVersionUID, which is used during deserialization to verify that the sender and receiver of a serialized object have loaded classes for that object that are compatible with respect to serialization. If the receiver has loaded a class for the object that has a differentserialVersionUIDthan that of the corresponding sender's class, then deserialization will result in anInvalidClassException. A serializable class can declare its ownserialVersionUIDexplicitly by declaring a field namedserialVersionUIDthat must be static, final, and of typelong:
ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
If a serializable class does not explicitly declare a
serialVersionUID, then the serialization runtime will calculate a defaultserialVersionUIDvalue for that class based on various aspects of the class, as described in the Java(TM) Object Serialization Specification. However, it is strongly recommended that all serializable classes explicitly declareserialVersionUIDvalues, since the defaultserialVersionUIDcomputation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpectedInvalidClassExceptionsduring deserialization. Therefore, to guarantee a consistentserialVersionUIDvalue across different java compiler implementations, a serializable class must declare an explicitserialVersionUIDvalue. It is also strongly advised that explicitserialVersionUIDdeclarations use the private modifier where possible, since such declarations apply only to the immediately declaring class —serialVersionUIDfields are not useful as inherited members.
Итак, по сути, вы говорите, что если пользователь не понимает всего вышеупомянутого материала, он может не беспокоиться о сериализации? Я полагаю, вы ответили на вопрос "как?" вместо того, чтобы объяснять «почему?». Я, например, не понимаю, почему я вообще беспокоюсь о SerializableVersionUID.
Причина во втором абзаце: если вы явно не укажете serialVersionUID, значение будет сгенерировано автоматически, но это непостоянно, потому что оно зависит от реализации компилятора.
И почему Eclipse говорит, что мне нужен "private static final long serialVersionUID = 1L;" когда я расширяю класс Exception?
@JohnMerlino: Ну, я бы не ожидал, что он скажет, что вы необходимость, но это может быть предлагая, чтобы помочь вам правильно сериализовать исключения. Если вы не собираетесь сериализовать их, вам действительно не нужна константа.
@JohnMerlino, чтобы ответить на ваш вопрос, почему: Exception реализует Сериализуемый, а eclipse предупреждает, что вы не установили serialVersionUID, что было бы хорошей идеей (если вы не хотите сериализовать класс), чтобы избежать проблем, которые сообщение JonSkeet очертания.
@JonSkeet Значит ли это, что помимо идентичного определения класса я должен использовать точно такой же номер версии? Как я мог это узнать, если у меня нет возможности взглянуть на исходный код сериализованного класса?
@smwikipedia: Для десериализации данных? да. Однако было бы странно десериализовать данные для класса, для которого у вас не было источника. Двоичная сериализация не должна (IMO) использоваться в качестве общедоступного протокола общего назначения.
@Ziggy If ".. поскольку вычисление serialVersionUID по умолчанию очень чувствительно к деталям класса, которые могут различаться в зависимости от реализаций компилятора и, таким образом, могут привести к неожиданным InvalidClassExceptions во время десериализации." не отвечает Почему тогда я боюсь, придется попросить вас определить почему: D
@JonSkeet В своем ответе вы упомянули, что «поскольку такие объявления применяются только к немедленно объявленному классу - поля serialVersionUID не используются в качестве унаследованных членов». не могли бы вы объяснить это? Поскольку, насколько я знаю, если родительский класс сериализуется, дочерний класс также сериализуется по умолчанию, поэтому нам нужно также объявить SerialVersionUID для дочернего элемента?
@proudandhonour: Да, вы должны это сделать.
@JonSkeet Если я сделаю его защищенным, то что? Не будет ли это унаследовано в дочернем классе?
@proudandhonour: Я точно не знаю, что происходит в этих случаях, но почему бы просто не объявить это отдельно в каждом классе, как следует из документации?
Всего два цента, это проблема Spark и других API-интерфейсов распараллеливания, которые зависят от сериализации классов Java в кластере.
@JonSkeet, кто генерирует UID по умолчанию? JVM или компилятор / JDK?
@UnKnown: среда выполнения сериализации - это время выполнения.
Итак, если я правильно понимаю, указывать uid вручную небезопасно, чем позволять среде выполнения генерировать его. Потому что рано или поздно кто-то его забудет обновить ....
@ user1050755: В принципе, у каждого есть свои плюсы и минусы.
Я не вижу никаких минусов против того, чтобы среда выполнения могла использовать "подпись" класса. Кажется, это идеальное решение для меня.
@ user1050755: Итак, вы счастливы, что изменение вашего компилятора, даже если оно не изменит никаких деталей самих полей, может сделать любые сохраненные данные нечитаемыми? Или добавление общедоступного статического метода, который действительно не должен изменять детали сериализации? Раньше я был укушен подобными вещами, поэтому это явный "недостаток". Возможно, если бы алгоритм был разработан лучше, все было бы хорошо, но с тем, как он спроектирован является, это плохие новости.
Совет: если вы забыли добавить serialVersionUIDfield и хотите изменить класс, не затрагивая его, вы можете проверить его значение на немодифицированном и установить его для этого класса после того, как вы его изменили, используя этот вызов ( найдено по адресу: stackoverflow.com/a/26822688/878126): long serialVersionUID = ObjectStreamClass.lookup(YourClass.class).getSerialVersionUID();
Большая часть этого вращается вокруг сериализации и того, почему ее, вероятно, НЕ следует использовать в качестве беспроводного протокола или постоянно сохранять и извлекать через десериализацию. Он эффективно создает реализацию «равных» на уровне класса и без каких-либо разумных методов для JVM и бесчисленных приложений / классов, созданных любым количеством разработчиков, чтобы сделать это согласованным / уникальным для данного класса ... немедленно создает нюансированную ошибку для младших разработчиков, если они сохранят класс как сериализованные данные, а затем позволят компилятору попытаться отсортировать версию класса во время десериализации.
Итак, что произойдет, если я обновлю свой класс, но не обновлю serialVersionUIDfield?
@bpoiss: Затем он попытается десериализовать старые данные с помощью нового класса и либо вызовет ошибки, либо фактически повредит данные, в зависимости от точного изменения.
Если вам никогда не нужно будет сериализовать ваши объекты в байтовый массив и отправлять / хранить их, то вам не нужно об этом беспокоиться. Если да, то вы должны учитывать свой serialVersionUID, поскольку десериализатор объекта сопоставит его с версией объекта, имеющегося в загрузчике классов. Подробнее об этом читайте в Спецификации языка Java.
Если вы не собираетесь сериализовать объекты, почему они сериализуемы?
@erickson - родительский класс может быть сериализуемым, например, ArrayList, но вы хотите, чтобы ваш собственный объект (скажем, модифицированный список массивов) использовал его в качестве основы, но вы никогда не собираетесь сериализовать созданную вами коллекцию.
Это нигде не упоминается в Спецификации языка Java. Это упоминается в Спецификации управления версиями объектов.
Вот ссылка на Java 8 Спецификация управления версиями объектов.
Я не могу упустить возможность подключить книгу Джоша Блоха Эффективная Java (2nd Edition). Глава 11 - незаменимый ресурс по сериализации Java.
Согласно Джошу, автоматически сгенерированный UID создается на основе имени класса, реализованных интерфейсов и всех открытых и защищенных членов. Любое изменение любого из них изменит serialVersionUID. Таким образом, вам не нужно связываться с ними, только если вы уверены, что не более одной версии класса когда-либо будут сериализованы (либо между процессами, либо извлечены из хранилища позже).
Если вы проигнорируете их сейчас и обнаружите позже, что вам нужно каким-то образом изменить класс, но сохранить совместимость со старой версией класса, вы можете использовать инструмент JDK серийный номер для генерации serialVersionUID в классе Старый и явно установить что на новом классе. (В зависимости от ваших изменений вам может потребоваться реализовать настраиваемую сериализацию, добавив методы writeObject и readObject - см. Serializable javadoc или вышеупомянутую главу 11.)
Итак, можно было бы побеспокоиться о SerializableVersionUID, если бы вы беспокоились о совместимости со старыми версиями класса?
Да, в случае, если более новая версия изменит какой-либо публичный член на защищенный, SerializableVersionUID по умолчанию будет другим и вызовет InvalidClassExceptions.
Имя класса, реализованные интерфейсы, все общедоступные и защищенные методы, ВСЕ переменные экземпляра.
Стоит отметить, что Джошуа Блох советует для класса каждый Serializable указывать uid серийной версии. Цитата из главы 11: Независимо от того, какую сериализованную форму вы выберете, объявляйте явный UID последовательной версии в каждом сериализуемом классе, который вы пишете. Это исключает UID серийной версии как потенциальный источник несовместимости (элемент 74). Также есть небольшой выигрыш в производительности. Если не указан UID серийной версии, для его генерации во время выполнения требуются дорогостоящие вычисления.
Кажется актуальным. Ссылка на пару подходов к созданию UID серийной версии с использованием IDE: mkyong.com/java/how-to-generate-serialversionuid
Вы можете указать Eclipse игнорировать эти предупреждения serialVersionUID:
Window > Preferences > Java > Compiler > Errors / Warnings > Potential Programming Problems
Если вы не знали, есть много других предупреждений, которые вы можете включить в этом разделе (или даже сообщить о некоторых как об ошибках), многие из них очень полезны:
и многое другое.
проголосовать за, но только потому, что исходный постер, похоже, ничего не сериализирует. Если бы на плакате было написано: «Я сериализую эту штуку и ...», то вместо этого вы получите голос «против»: P
Верно - я предполагал, что спрашивающий просто не хотел, чтобы его предупреждали
@Gardner -> согласен! Но спрашивающий также хочет знать, почему он может не хотеть, чтобы его предупреждали.
Спрашивающего явно волнует, почему должен быть UID. Так что простое указание ему игнорировать предупреждение должно быть отвергнуто.
Если вы сериализуете только потому, что вам нужно сериализовать ради реализации (кого волнует, сериализуете ли вы, например, для HTTPSession ... если он хранится или нет, вам, вероятно, все равно, что de-serializing - объект формы), тогда вы можете игнорировать это.
Если вы действительно используете сериализацию, это имеет значение только в том случае, если вы планируете хранить и извлекать объекты напрямую с помощью сериализации. serialVersionUID представляет версию вашего класса, и вам следует увеличить ее, если текущая версия вашего класса не имеет обратной совместимости с предыдущей версией.
В большинстве случаев вы, вероятно, не будете использовать сериализацию напрямую. В этом случае сгенерируйте SerialVersionUID по умолчанию, щелкнув опцию быстрого исправления, и не беспокойтесь об этом.
Я бы сказал, что если вы не используете сериализацию для постоянного хранения, вам следует использовать @SuppressWarnings, а не добавлять значение. Он меньше загромождает класс и сохраняет способность механизма serialVersionUID защищать вас от несовместимых изменений.
Я не вижу, как добавление одной строки (аннотация @SuppressWarnings) в противоположность другой строке (сериализуемый идентификатор) «меньше загромождает класс». И если вы не используете сериализацию для постоянного хранения, почему бы вам просто не использовать «1»? В любом случае вам не будет важен автоматически сгенерированный идентификатор.
@ MetroidFan2002: Я думаю, что точка зрения @TomAnderson о том, что serialVersionUID защищает вас от несовместимых изменений, верна. Использование @SuppressWarnings лучше документирует намерение, если вы не хотите использовать класс для постоянного хранения.
«Вы должны увеличить его, если текущая версия вашего класса не имеет обратной совместимости с предыдущей версией:» Сначала вы должны изучить обширную поддержку управления версиями объектов в Serialization, (a) чтобы убедиться, что класс действительно теперь несовместим с сериализацией, чего по спецификации достичь довольно сложно; (b) попробовать такую схему, как пользовательские методы чтения / записиObject (), методы readResolve / writeReplace (), объявления serializableFields и т. д., чтобы убедиться, что поток остается совместимым. Смена serialVersionUID - крайняя мера, совет отчаяния.
@EJP приращение serialVersionUID появляется, когда первоначальный автор класса явно представил его. Я бы сказал, что сгенерированный jvm серийный идентификатор должен быть в порядке. это лучший отвечать, который я видел при сериализации.
@overexchange Что означает "явно введено"?
Я имею в виду явно заявлено
@overexchange Вы имеете в виду «явно объявил» какие?
@EJP Явно объявлен serialVersionUID.
Если вы не используете сериализацию (примерно в 99,9999% случаев), просто отключите это глупое предупреждение по умолчанию. Никаких бессмысленных бессмысленных случайных чисел в вашем коде и никаких ненужных предупреждений SuppressWarning. Боже, каждый раз, когда я вижу базы кода, которые «обходят» это предупреждение, я действительно удивляюсь, почему они просто не отключают это предупреждение (и, возможно, активируют несколько более полезных), вместо этого зомби-менталитета, чтобы просто «исправить» предупреждение.
@TomAnderson По умолчанию в IDEA предупреждения не отображаются.
@overexchange Я полностью согласен с ответом @JBNizet, но там ничего не говорится об увеличении serialVersionUID. Вы должны либо оставить его в покое, либо изменить его на новое вычисленное значение, как сообщает инструмент serialver, предварительно тщательно изучив альтернативы, о которых я упоминал, и еще до этого определенно определив, что это действительно нужно изменить, чего обычно не происходит '' т. Намеренно вводить несовместимые средства, особенно если они бесплатны, не дает никаких результатов.
Было бы неплохо, если бы CheckStyle мог проверить, что serialVersionUID в классе, который реализует Serializable, имеет хорошее значение, т.е. что он соответствует тому, что генерирует генератор идентификатора последовательной версии. Если у вас есть проект с большим количеством сериализуемых DTO, например, не забудьте удалить существующий serialVersionUID и восстановить его, и в настоящее время единственный способ (который я знаю) проверить, это восстановить для каждого класса и сравнить с Старый. Это очень больно.
Если вы всегда устанавливаете для serialVersionUID то же значение, которое генерирует генератор, он вам вообще не нужен. В конце концов, смысл его существования - оставаться неизменным после изменений, когда класс все еще совместим.
Причина в том, что разные компиляторы дают одно и то же значение для одного и того же класса. Как объясняется в документации javadocs (на которую также дан ответ выше), сгенерированная версия является хрупкой и может изменяться, даже если класс десериализуем должным образом. Пока вы запускаете этот тест каждый раз на одном и том же компиляторе, он должен быть безопасным. Бог поможет вам, если вы обновите jdk и появится новое правило, даже если ваш код не изменился.
Не обязательно совпадать с тем, что будет производить serialver. -1
В общем, это совсем не обязательно. В случае @ AndrewBacker потребуется два разных компилятора для одного и того же файла .java, при этом обе версии файлов .class будут взаимодействовать друг с другом - в большинстве случаев вы просто создаете класс и распространяете его. Если это так, то подойдет и отсутствие SUID.
Люди, которые действительно используют сериализацию для целей хранения / поиска, обычно устанавливают serialVersionUID на 1. Если более новая версия класса несовместима, но по-прежнему требует возможности обрабатывать старые данные, вы увеличиваете номер версии и добавляете специальный код. для работы со старыми форматами. Я плачу каждый раз, когда вижу serialVersionUID, размер которого превышает 1 цифру, либо потому, что это было случайное число (бесполезно), либо потому, что класс, по-видимому, должен иметь дело с более чем 10 различными версиями.
Если вы получили это предупреждение в классе, о котором вы никогда не задумывались, и что вы не объявляли себя implements Serializable, это часто происходит из-за того, что вы унаследовали от суперкласса, который реализует Serializable. Часто тогда было бы лучше делегировать такому объекту вместо использования наследования.
Итак, вместо
public class MyExample extends ArrayList<String> {
public MyExample() {
super();
}
...
}
делать
public class MyExample {
private List<String> myList;
public MyExample() {
this.myList = new ArrayList<String>();
}
...
}
и в соответствующих методах назовите myList.foo() вместо this.foo() (или super.foo()). (Это подходит не во всех случаях, но все же довольно часто.)
Я часто вижу людей, расширяющих JFrame или тому подобное, когда им действительно нужно только делегировать это. (Это также помогает для автозаполнения в среде IDE, поскольку JFrame имеет сотни методов, которые вам не нужны, если вы хотите вызвать свои собственные в своем классе.)
Один случай, когда предупреждение (или serialVersionUID) неизбежно, - это когда вы расширяетесь из AbstractAction, обычно в анонимном классе, только добавляя метод actionPerformed. Я думаю, что в этом случае не должно быть предупреждения (поскольку обычно вы не можете надежно сериализовать и десериализовать такие анонимные классы в любом случае в разных версиях вашего класса), но я не уверен, как компилятор мог это распознать.
Думаю, вы правы в том, что композиция важнее инертности, особенно когда вы обсуждаете такие классы, как ArrayList. Однако многие фреймворки требуют, чтобы люди расширялись от абстрактных суперклассов, которые можно сериализовать (например, класс ActionForm Struts 1.2 или Saxon ExtensionFunctionDefinition), и в этом случае это решение невозможно. Я думаю, что вы правы, было бы неплохо, если бы предупреждение игнорировалось в определенных случаях (например, если бы вы расширяли абстрактный сериализуемый класс)
Конечно, если вы добавляете класс в качестве члена, а не наследуете от него, вам придется написать метод-оболочку для КАЖДОГО метода класса-члена, который вы хотите использовать, что сделает его невозможным в большом количестве ситуаций. ... если в java нет функции, подобной Perl __AUTOLOAD, о которой я не знаю.
@M_M: Когда вы делегируете множество методов своему обернутому объекту, конечно, было бы нецелесообразно использовать делегирование. Но я полагаю, что этот случай является признаком ошибки дизайна - пользователям вашего класса (например, «MainGui») не нужно вызывать множество методов обернутого объекта (например, JFrame).
Что мне не нравится в делегировании, так это необходимость содержать ссылку на делегата. И каждая ссылка означает больше памяти. Поправьте меня если я ошибаюсь. Если мне нужен CustomizedArrayList из 100 объектов, это не имеет большого значения, но если мне нужны сотни CustomizdeArrayList из нескольких объектов, использование памяти значительно возрастет.
Throwable можно сериализовать, и только Throwable можно выбросить, поэтому невозможно определить исключение, которое не сериализуемо. Делегирование невозможно.
@Phil Я думаю, что это сделано намеренно - даже если вы не хотите сериализовать свой класс исключения, кто-то другой может захотеть сериализовать созданное вами исключение. (Конечно, можно утверждать, что большинство классов исключений должно работать с автоматически сгенерированным serialVersionUID и не нуждаться в фиксированном.)
@WVrock Что ж, ничего бы не изменилось, если бы у вас было множество экземпляров ArrayList. Экземпляр вашего класса-оболочки имеет размер порядка байтов + размер arrayylist, тогда как посредством наследования вы инициализируете такое же количество, но в соответствии с определением 1 класса. Ссылки легкие, лежащие в основе объекты тяжелые.
Не беспокойтесь, расчет по умолчанию действительно хорош и достаточен для 99,9999% случаев. И если вы столкнетесь с проблемами, вы можете, как уже было сказано, ввести UID по мере необходимости (что маловероятно)
Мусор. Достаточно в том случае, если класс не изменился. У вас нет никаких доказательств, подтверждающих «99,9999%».
Проблема не в том, что он «плохой», но нет гарантии, что он будет соответствовать разным версиям.
serialVersionUID облегчает управление версиями сериализованных данных. Его значение сохраняется вместе с данными при сериализации. При десериализации проверяется та же версия, чтобы увидеть, насколько сериализованные данные соответствуют текущему коду.
Если вы хотите изменить версию своих данных, вы обычно начинаете с serialVersionUID, равным 0, и увеличиваете его с каждым структурным изменением вашего класса, которое изменяет сериализованные данные (добавляя или удаляя непереходные поля).
Встроенный механизм десериализации (in.defaultReadObject()) откажется выполнять десериализацию из старых версий данных. Но при желании вы можете определить свою собственную функцию readObject (), которая может считывать старые данные. Этот специальный код может затем проверить serialVersionUID, чтобы узнать, в какой версии находятся данные, и решить, как их десериализовать. Этот метод управления версиями полезен, если вы храните сериализованные данные, которые переживают несколько версий вашего кода.
Но хранение сериализованных данных в течение такого длительного промежутка времени не очень распространено. Гораздо более распространено использование механизма сериализации для временной записи данных, например, в кеш или отправки их по сети в другую программу с той же версией соответствующих частей кодовой базы.
В этом случае вы не заинтересованы в поддержании обратной совместимости. Вы только заботитесь о том, чтобы убедиться, что кодовые базы, которые взаимодействуют, действительно имеют одинаковые версии соответствующих классов. Чтобы облегчить такую проверку, вы должны поддерживать serialVersionUID, как и раньше, и не забывать обновлять его при внесении изменений в ваши классы.
Если вы все же забудете обновить поле, вы можете получить две разные версии класса с разной структурой, но с одним и тем же serialVersionUID. Если это произойдет, механизм по умолчанию (in.defaultReadObject()) не обнаружит никакой разницы и попытается десериализовать несовместимые данные. Теперь вы можете получить загадочную ошибку времени выполнения или тихий сбой (пустые поля). Ошибки такого типа трудно найти.
Таким образом, чтобы помочь этому варианту использования, платформа Java предлагает вам выбор не настраивать serialVersionUID вручную. Вместо этого во время компиляции будет сгенерирован хэш структуры класса, который будет использоваться в качестве идентификатора. Этот механизм гарантирует, что у вас никогда не будет разных структур классов с одним и тем же идентификатором, и поэтому вы не получите этих трудно отслеживаемых сбоев сериализации во время выполнения, упомянутых выше.
Но у стратегии автоматического создания идентификаторов есть и обратная сторона. А именно, что сгенерированные идентификаторы для одного и того же класса могут различаться между компиляторами (как упоминалось выше Джоном Скитом). Поэтому, если вы передаете сериализованные данные между кодом, скомпилированным с помощью разных компиляторов, в любом случае рекомендуется поддерживать идентификаторы вручную.
И если у вас есть обратная совместимость с вашими данными, как в первом упомянутом случае использования, вы также, вероятно, захотите сохранить идентификатор самостоятельно. Это для того, чтобы получить удобочитаемые идентификаторы и лучше контролировать, когда и как они меняются.
Добавление или удаление непереходных полей не делает класс несовместимым с сериализацией. Следовательно, нет никаких причин «навязывать» такие изменения.
@EJP: А? Добавление данных определенно меняет данные сериализации в моем мире.
@AlexanderTorstling Прочтите, что я написал. Я не сказал, что это не «меняет данные сериализации». Я сказал, что это «не делает сериализацию классов несовместимой». Это не одно и то же. Вам необходимо прочитать главу «Управление версиями» Спецификации сериализации объектов.
@EJP: Я понимаю, что добавление непереходного поля не обязательно означает, что вы делаете класс несовместимым с сериализацией, но это структурное изменение, которое изменяет сериализованные данные, и вы обычно хотите поднять версию при этом, если вы не обрабатывать обратную совместимость, что я также объяснил позже в этом посте. Что именно вы хотите сказать?
Моя точка зрения остается именно такой, как я сказал. Добавление или удаление непереходных полей не делает класс несовместимым с сериализацией. Поэтому вам не нужно увеличивать serialVersionUID каждый раз, когда вы это делаете.
Данные поля представляют некоторую информацию, хранящуюся в классе.
Класс реализует интерфейс Serializable,
поэтому eclipse автоматически предложил объявить поле serialVersionUID. Начнем с установленного здесь значения 1.
Если вы не хотите, чтобы это предупреждение появилось, используйте это:
@SuppressWarnings("serial")
В исходном вопросе был задан вопрос «почему это важно» и «пример», в котором этот Serial Version ID был бы полезен. Я нашел одну.
Допустим, вы создаете класс Car, инстанцируете его и записываете в поток объектов. Сплющенный объект автомобиля некоторое время находится в файловой системе. Между тем, если класс Car изменяется путем добавления нового поля. Позже, когда вы попытаетесь прочитать (т.е. десериализовать) сплющенный объект Car, вы получите java.io.InvalidClassException, потому что всем сериализуемым классам автоматически присваивается уникальный идентификатор. Это исключение возникает, когда идентификатор класса не равен идентификатору сглаженного объекта. Если вы действительно думаете об этом, исключение выдается из-за добавления нового поля. Вы можете избежать возникновения этого исключения, контролируя управление версиями самостоятельно, объявив явный serialVersionUID. Также явное объявление вашего serialVersionUID дает небольшой выигрыш в производительности (поскольку его не нужно вычислять). Итак, рекомендуется добавить свой собственный serialVersionUID в классы Serializable, как только вы их создадите, как показано ниже:
public class Car {
static final long serialVersionUID = 1L; //assign a long value
}
И каждому UID следует присвоить случайное длинное число, а не 1L.
@abbas 'Надо' так почему? Пожалуйста, объясните, в чем разница.
Как следует из названия, он представляет версию класса, которая использовалась для сериализации объекта. Если вы каждый раз присваиваете ему один и тот же номер, вы не сможете найти правильный класс для его десериализации. Поэтому всякий раз, когда вы вносите изменения в класс, лучше изменить и версию.
@abbas, это намерение не противоречит использованию увеличивающихся натуральных чисел из 1 и так далее.
@BillK, я думал, что проверка сериализации привязана к паре classname и serialVersionUID. Так что разные схемы нумерации разных классов и библиотек никак не могут мешать. Или вы имели в виду библиотеки для генерации кода?
@abbas serialVersionUID не имеет ничего общего с «поиском правильной версии класса».
То, что я всегда находил безумным, заключалось в том, что алгоритм получения serialVersionUID, когда ни один явно не объявлен, основан на пакете, имени, атрибутах, но ТАКЖЕ методы ... методы не сериализуются, и добавление / удаление методов не влияет на сериализованную форму объект, так зачем создавать другой serialVersionUID при добавлении / удалении / изменении метода »?
Что касается примера, в котором отсутствующий serialVersionUID может вызвать проблему:
Я работаю над этим приложением Java EE, которое состоит из веб-модуля, использующего модуль EJB. Веб-модуль вызывает модуль EJB удаленно и передает POJO, который реализует Serializable в качестве аргумента.
Этот класс POJO's был упакован внутри jar-файла EJB и внутри собственного jar-файла в WEB-INF / lib веб-модуля. На самом деле это один и тот же класс, но когда я упаковываю модуль EJB, я распаковываю эту банку POJO, чтобы упаковать ее вместе с модулем EJB.
Вызов EJB завершился ошибкой из-за исключения ниже, потому что я не объявил его serialVersionUID:
Caused by: java.io.IOException: Mismatched serialization UIDs : Source
(Rep.
IDRMI:com.hordine.pedra.softbudget.domain.Budget:5CF7CE11E6810A36:04A3FEBED5DA4588)
= 04A3FEBED5DA4588 whereas Target (Rep. ID RMI:com.hordine.pedra.softbudget.domain.Budget:7AF5ED7A7CFDFF31:6227F23FA74A9A52)
= 6227F23FA74A9A52
Чтобы понять значение поля serialVersionUID, нужно понимать, как работает сериализация / десериализация.
Когда сериализуется объект класса Serializable, среда выполнения Java связывает серийный номер версии (называемый serialVersionUID) с этим сериализованным объектом. Во время десериализации этого сериализованного объекта среда выполнения Java сопоставляет serialVersionUID сериализованного объекта с serialVersionUID класса. Если оба равны, то только продолжается дальнейший процесс десериализации, в противном случае возникает исключение InvalidClassException.
Таким образом, мы заключаем, что для успешного выполнения процесса сериализации / десериализации serialVersionUID сериализованного объекта должен быть эквивалентен serialVersionUID класса. В случае, если программист явно указывает значение serialVersionUID в программе, то одно и то же значение будет связано с сериализованным объектом и классом, независимо от платформы сериализации и десериализации (например, сериализация может выполняться на платформе, такой как Windows, с использованием sun или MS JVM и десериализация могут быть на другой платформе Linux с использованием Zing JVM).
Но в случае, если serialVersionUID не указан программистом, тогда при выполнении Serialization \ DeSerialization любого объекта среда выполнения Java использует свой собственный алгоритм для его вычисления. Этот алгоритм вычисления serialVersionUID варьируется от одной JRE к другой. Также возможно, что среда, в которой сериализуется объект, использует одну JRE (например, SUN JVM), а среда, в которой происходит десериализация, использует Linux Jvm (zing). В таких случаях serialVersionUID, связанный с сериализованным объектом, будет отличаться от serialVersionUID класса, вычисленного в среде десериализации. В свою очередь десериализация не будет успешной. Поэтому, чтобы избежать таких ситуаций / проблем, программист всегда должен указывать serialVersionUID класса Serializable.
Алгоритм не меняется, но он немного занижен.
... алгоритм не меняется, но он немного недооценен ... это означает, что любой jvm может отличаться ..... @ user207421
What is a serialVersionUID and why should I use it?
SerialVersionUID - это уникальный идентификатор для каждого класса, JVM использует его для сравнения версий класса, гарантируя, что тот же класс, который использовался во время сериализации, загружается во время десериализации.
Указание одного дает больше контроля, хотя JVM создает его, если вы не укажете. Сгенерированное значение может отличаться между разными компиляторами. Более того, иногда вы просто хотите по какой-то причине запретить десериализацию старых сериализованных объектов [backward incompatibility], и в этом случае вам просто нужно изменить serialVersionUID.
javadocs для Serializable говорят:
the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected
InvalidClassExceptions during deserialization.
Следовательно, вы должны объявить serialVersionUID, потому что это дает нам больше контроля..
У эта статья есть несколько хороших моментов по этой теме.
@Vinothbabu, но serialVersionUID является статическим, поэтому статические переменные не могут быть сериализованы. тогда почему jvm проверит версию, не зная, какая версия десериализуемого объекта
В этом ответе не упоминается одна вещь: вы можете вызвать непредвиденные последствия, слепо включив serialVersionUID, не зная почему. Комментарий Тома Андерсона к ответу MetroidFan2002 касается этого: «Я бы сказал, что если вы не используете сериализацию для постоянного хранения, вам следует использовать @SuppressWarnings, а не добавлять значение. Это меньше загромождает класс и сохраняет способность Механизм serialVersionUID для защиты от несовместимых изменений. "
serialVersionUID - это нет, «уникальный идентификатор для каждого класса». Это полное имя класса. Это индикатор версия.
Обычно я использую serialVersionUID в одном контексте: когда я знаю, что он выйдет из контекста виртуальной машины Java.
Я бы знал это, когда я использовал ObjectInputStream и ObjectOutputStream для своего приложения, или если бы я знал, что библиотека / фреймворк, которые я использую, будет использовать его. SerialVersionID гарантирует, что разные виртуальные машины Java разных версий или поставщиков будут правильно взаимодействовать, или, если он хранится и извлекается за пределами виртуальной машины, например HttpSession, данные сеанса могут сохраняться даже во время перезапуска и обновления сервера приложений.
Во всех остальных случаях я использую
@SuppressWarnings("serial")
поскольку в большинстве случаев достаточно serialVersionUID по умолчанию. Сюда входят Exception, HttpServlet.
Он не включает HttpServlet в контейнеры, где они могут быть заменены, или Exception в RMI, например.
SerialVersionUID используется для контроля версий объекта. вы также можете указать serialVersionUID в своем файле класса. Следствием отсутствия указания serialVersionUID является то, что при добавлении или изменении любого поля в классе уже сериализованный класс не сможет восстановить, поскольку serialVersionUID, сгенерированный для нового класса и для старого сериализованного объекта, будет отличаться. Процесс сериализации Java полагается на правильный serialVersionUID для восстановления состояния сериализованного объекта и выдает исключение java.io.InvalidClassException в случае несоответствия serialVersionUID
Подробнее: http://javarevisited.blogspot.com/2011/04/top-10-java-serialization-interview.html#ixzz3VQxnpOPZ
Если вы хотите изменить огромное количество классов, для которых в первую очередь не задан serialVersionUID, при этом сохраняя совместимость со старыми классами, инструменты, такие как IntelliJ Idea, Eclipse, не работают, поскольку они генерируют случайные числа и не работают с кучей файлов. за один присест. Я придумываю следующий сценарий bash (извините за пользователей Windows, подумайте о покупке Mac или преобразовании в Linux), чтобы с легкостью исправить проблему с serialVersionUID:
base_dir=$(pwd)
src_dir=$base_dir/src/main/java
ic_api_cp=$base_dir/target/classes
while read f
do
clazz=${f////.}
clazz=${clazz/%.java/}
seruidstr=$(serialver -classpath $ic_api_cp $clazz | cut -d ':' -f 2 | sed -e 's/^\s\+//')
perl -ni.bak -e "print $_; printf qq{%s\n}, q{ private $seruidstr} if /public class/" $src_dir/$f
done
вы сохраняете этот скрипт, скажите add_serialVersionUID.sh вам ~ / bin. Затем вы запускаете его в корневом каталоге вашего проекта Maven или Gradle, например:
add_serialVersionUID.sh < myJavaToAmend.lst
Этот .lst включает список java-файлов для добавления serialVersionUID в следующем формате:
com/abc/ic/api/model/domain/item/BizOrderTransDO.java
com/abc/ic/api/model/domain/item/CardPassFeature.java
com/abc/ic/api/model/domain/item/CategoryFeature.java
com/abc/ic/api/model/domain/item/GoodsFeature.java
com/abc/ic/api/model/domain/item/ItemFeature.java
com/abc/ic/api/model/domain/item/ItemPicUrls.java
com/abc/ic/api/model/domain/item/ItemSkuDO.java
com/abc/ic/api/model/domain/serve/ServeCategoryFeature.java
com/abc/ic/api/model/domain/serve/ServeFeature.java
com/abc/ic/api/model/param/depot/DepotItemDTO.java
com/abc/ic/api/model/param/depot/DepotItemQueryDTO.java
com/abc/ic/api/model/param/depot/InDepotDTO.java
com/abc/ic/api/model/param/depot/OutDepotDTO.java
Этот сценарий использует скрытый инструмент serialVer JDK. Поэтому убедитесь, что ваш $ JAVA_HOME / bin находится в PATH.
Это дает мне идею: всегда регенерируйте uid серийной версии с помощью подобного инструмента, а не вручную, перед выпуском - таким образом вы не забудете внести изменения в класс, чей uid серийной версии должен был измениться из-за фактического несовместимое изменение. Отследить это вручную очень сложно.
Этот вопрос очень хорошо задокументирован в книге Джошуа Блоха «Эффективная Java». Очень хорошая книга, которую нужно прочитать. Ниже я назову некоторые из причин:
Среда выполнения сериализации имеет номер, называемый серийной версией, для каждого сериализуемого класса. Этот номер называется serialVersionUID. Теперь за этим числом стоит некоторая математика, и она определяется на основе полей / методов, определенных в классе. Для одного и того же класса каждый раз создается одна и та же версия. Этот номер используется во время десериализации, чтобы убедиться, что отправитель и получатель сериализованного объекта загрузили классы для этого объекта, которые совместимы с сериализацией. Если получатель загрузил класс для объекта, который имеет другой serialVersionUID, чем у соответствующего класса отправителя, то десериализация приведет к InvalidClassException.
Если класс является сериализуемым, вы также можете явно объявить свой собственный serialVersionUID, объявив поле с именем «serialVersionUID», которое должно быть статическим, окончательным и иметь тип long. Большинство IDE, таких как Eclipse, помогают сгенерировать такую длинную строку.
Каждый раз, когда объект сериализуется, на нем ставится отметка с номером версии для класса объекта. Этот идентификатор называется serialVersionUID и вычисляется на основе информации о структуре класса. Предположим, вы создали класс Employee с идентификатором версии # 333 (присвоенным JVM). Теперь, когда вы сериализуете объект этого класса (предположим, объект Employee), JVM назначит ему UID как # 333.
Рассмотрим ситуацию - в будущем вам нужно будет отредактировать или изменить свой класс, и в этом случае, когда вы его измените, JVM назначит ему новый UID (предположим, # 444). Теперь, когда вы пытаетесь десериализовать объект сотрудника, JVM будет сравнивать идентификатор версии сериализованного объекта (объект Employee) (№ 333) с идентификатором класса, то есть № 444 (поскольку он был изменен). При сравнении JVM обнаружит, что UID обеих версий различаются, и, следовательно, десериализация не удастся. Следовательно, если serialVersionID для каждого класса определяется самим программистом. Это будет то же самое, даже если класс будет развиваться в будущем, и, следовательно, JVM всегда обнаружит, что этот класс совместим с сериализованным объектом, даже если класс изменился. Для получения дополнительной информации вы можете обратиться к главе 14 HEAD FIRST JAVA.
Каждый раз, когда класс объекта сериализуется, передается serialVersionUID. Он не отправляется с каждым объектом.
Зачем использовать SerialVersionUID внутри класса Serializable в Java?
Во время serialization среда выполнения Java создает номер версии для класса, чтобы он мог де-сериализовать его позже. Этот номер версии в Java известен как SerialVersionUID.
SerialVersionUID используется для версии сериализованных данных. Вы можете десериализовать класс, только если он SerialVersionUID совпадает с сериализованным экземпляром. Когда мы не объявляем SerialVersionUID в нашем классе, среда выполнения Java генерирует его для нас, но это не рекомендуется. Рекомендуется объявить SerialVersionUID как переменную private static final long, чтобы избежать механизма по умолчанию.
Когда вы объявляете класс как Serializable, реализуя интерфейс маркера java.io.Serializable, среда выполнения Java сохраняет экземпляр этого класса на диск, используя механизм сериализации по умолчанию, при условии, что вы не настроили процесс с помощью интерфейса Externalizable.
см. также Зачем использовать SerialVersionUID внутри класса Serializable в Java
Сначала мне нужно объяснить, что такое сериализация.
Сериализация позволяет преобразовать объект в поток для отправки этого объекта по сети ИЛИ сохранить в файл ИЛИ сохранить в БД для использования в письмах.
Есть некоторые правила сериализации.
Объект является сериализуемым, только если его класс или его суперкласс реализует интерфейс Serializable.
Объект является сериализуемым (сам реализует интерфейс Serializable), даже если его суперкласс - нет. Однако первый суперкласс в иерархии сериализуемого класса, который не реализует интерфейс Serializable, ДОЛЖЕН иметь конструктор без аргументов. Если это нарушено, readObject () создаст исключение java.io.InvalidClassException во время выполнения.
Все примитивные типы сериализуемы.
Поля переходного процесса (с модификатором переходного процесса) НЕ сериализуются (т. Е. Не сохраняются и не восстанавливаются). Класс, реализующий Serializable, должен отмечать переходные поля классов, которые не поддерживают сериализацию (например, файловый поток).
Статические поля (со статическим модификатором) не сериализуются.
Когда Object сериализуется, среда выполнения Java связывает серийный номер версии, также известный как serialVersionID.
Где нам нужен serialVersionID:
Во время десериализации, чтобы убедиться, что отправитель и получатель совместимы в отношении сериализации. Если получатель загрузил класс другим serialVersionID, десериализация закончится на InvalidClassCastException.
.
Сериализуемый класс может явно объявить свой собственный serialVersionUID, объявив поле с именем serialVersionUID, которое должно быть статическим, окончательным и иметь тип long.
Попробуем это на примере.
import java.io.Serializable;
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private String empname;
private byte empage;
public String getEmpName() {
return name;
}
public void setEmpName(String empname) {
this.empname = empname;
}
public byte getEmpAge() {
return empage;
}
public void setEmpAge(byte empage) {
this.empage = empage;
}
public String whoIsThis() {
return getEmpName() + " is " + getEmpAge() + "years old";
}
}
Создать сериализованный объект
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class Writer {
public static void main(String[] args) throws IOException {
Employee employee = new Employee();
employee.setEmpName("Jagdish");
employee.setEmpAge((byte) 30);
FileOutputStream fout = new
FileOutputStream("/users/Jagdish.vala/employee.obj");
ObjectOutputStream oos = new ObjectOutputStream(fout);
oos.writeObject(employee);
oos.close();
System.out.println("Process complete");
}
}
Десериализовать объект
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class Reader {
public static void main(String[] args) throws ClassNotFoundException, IOException {
Employee employee = new Employee();
FileInputStream fin = new FileInputStream("/users/Jagdish.vala/employee.obj");
ObjectInputStream ois = new ObjectInputStream(fin);
employee = (Employee) ois.readObject();
ois.close();
System.out.println(employee.whoIsThis());
}
}
ПРИМЕЧАНИЕ. Теперь измените serialVersionUID класса Employee и сохраните:
private static final long serialVersionUID = 4L;
И выполните класс Reader. Не выполнять класс Writer, и вы получите исключение.
Exception in thread "main" java.io.InvalidClassException:
com.jagdish.vala.java.serialVersion.Employee; local class incompatible:
stream classdesc serialVersionUID = 1, local class serialVersionUID = 4
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1623)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
at com.krishantha.sample.java.serialVersion.Reader.main(Reader.java:14)
Поправьте меня, если я ошибаюсь - локальный класс - это тот, который вы в настоящее время используете / используете в пути к классам, а поток - это тот, который используется другой стороной (например, сервер, который возвращает вам ответ и сериализует отклик). Вы можете столкнуться с этой ситуацией, когда общаетесь с сервером, который обновил свои сторонние библиотеки, но вы (клиент) этого не сделали.
Простое объяснение:
Вы сериализуете данные?
Сериализация - это в основном запись данных класса в файл / поток / и т. д. Десериализация - это чтение этих данных обратно в класс.
Собираетесь ли вы в производство?
Если вы просто тестируете что-то с неважными / поддельными данными, не беспокойтесь об этом (если вы не тестируете сериализацию напрямую).
Это первая версия?
Если да, установите serialVersionUID=1L.
Это вторая, третья и т.д. прод версия?
Теперь вам нужно побеспокоиться о serialVersionUID и изучить его более подробно.
В принципе, если вы неправильно обновляете версию при обновлении класса, который вам нужно писать / читать, вы получите сообщение об ошибке при попытке прочитать старые данные.
Короче говоря, это поле используется для проверки возможности десериализации сериализованных данных. Сериализация и десериализация часто выполняются разными копиями программы - например, сервер преобразует объект в строку, а клиент преобразует полученную строку в объект. Это поле говорит о том, что оба оперируют одним и тем же представлением о том, что это за объект. Это поле помогает, когда:
у вас есть много разных копий вашей программы в разных местах (например, 1 сервер и 100 клиентов). Если вы измените свой объект, измените номер версии и забудете обновить одного из этих клиентов, он будет знать, что он не может десериализоваться.
вы сохранили свои данные в каком-то файле, а позже вы пытаетесь открыть его с помощью обновленной версии вашей программы с измененным объектом - вы будете знать, что этот файл несовместим, если вы сохраните правильную версию
Когда это важно?
Самое очевидное - если вы добавите несколько полей в свой объект, более старые версии не смогут их использовать, потому что они не имеют этих полей в своей структуре объекта.
Менее очевидно - когда вы десериализуете объект, поля, которых нет в строке, будут сохранены как NULL. Если вы удалили поле из своего объекта, более старые версии сохранят это поле как всегда-NULL, что может привести к неправильному поведению, если более старые версии полагаются на данные в этом поле (в любом случае вы создали его для чего-то, а не только для развлечения :-))
Наименее очевидный - иногда вы меняете идею, которую вкладываете в какое-то значение поля. Например, когда вам 12 лет, вы подразумеваете «велосипед» под «байком», но когда вам 18, вы имеете в виду «мотоцикл» - если ваши друзья пригласят вас «прокатиться на велосипеде по городу», и вы будете единственным, кто приехал на велосипеде, вы поймете, насколько важно сохранять одинаковый смысл в полях :-)
Во-первых, чтобы ответить на ваш вопрос, когда мы не объявляем SerialVersionUID в нашем классе, среда выполнения Java генерирует его для нас, но этот процесс чувствителен ко многим метаданным класса, включая количество полей, тип полей, модификатор доступа к полям, реализованный интерфейс по классу и т. д. Поэтому рекомендуется объявить это самостоятельно, и Eclipse предупреждает вас об этом же.
Сериализация: Мы часто работаем с важными объектами, состояние которых (данные в переменных объекта) настолько важно, что мы не можем рисковать потерять его из-за сбоев питания / системы (или) сетевых сбоев в случае отправки состояния объекта на другую машину. Решение этой проблемы называется «Постоянство», что просто означает сохранение (удержание / сохранение) данных. Сериализация - это один из многих других способов достижения постоянства (путем сохранения данных на диск / в память). При сохранении состояния объекта важно создать идентификатор для объекта, чтобы иметь возможность правильно его прочитать (десериализация). Этот уникальный идентификатор - это идентификатор SerialVersionUID.
Что такое SerialVersionUID? Ответ: - Допустим, есть два человека, один из штаб-квартиры, а другой из ODC, оба будут выполнять сериализацию и десериализацию соответственно. В этом случае для подтверждения того, что получатель, находящийся в ODC, является аутентифицированным лицом, JVM создает уникальный идентификатор, который известен как SerialVersionUID.
Вот хорошее объяснение, основанное на сценарии,
Почему именно SerialVersionUID?
Сериализация: во время сериализации JVM на стороне отправителя каждого объекта сохранит уникальный идентификатор. JVM отвечает за создание этого уникального идентификатора на основе соответствующего файла .class, который присутствует в системе отправителя.
Десериализация: во время десериализации JVM на стороне получателя будет сравнивать уникальный идентификатор, связанный с объектом, с уникальным идентификатором локального класса, то есть JVM также создаст уникальный идентификатор на основе соответствующего файла .class, который присутствует в системе получателя. Если оба уникальных идентификатора совпадают, будет выполнена только десериализация. В противном случае мы получим исключение времени выполнения с сообщением InvalidClassException. Этот уникальный идентификатор - не что иное, как SerialVersionUID.
serialVersionUID - это 64-битное число, используемое для уникальной идентификации класса в процессе десериализации. Когда вы сериализуете объект, serialVersionUID класса также записывается в файл. Всякий раз, когда вы десериализуете этот объект, среда выполнения java извлекает это значение serialVersionUID из сериализованных данных и сравнивает то же значение, связанное с классом. Если оба не совпадают, будет выбрано исключение java.io.InvalidClassException.
Если сериализуемый класс явно не объявляет serialVersionUID, то среда выполнения сериализации вычислит значение serialVersionUID для этого класса на основе различных аспектов класса, таких как поля, методы и т. д. ,, Вы можете сослаться на этот связь для демонстрационного приложения.
Найдите полезный опыт использования serialversionUID; dzone.com/articles/what-is-serialversionuid