serial - сериализация java




Что такое serialVersionUID и почему я должен его использовать? (14)

Что такое serialVersionUID и почему я должен его использовать?

SerialVersionUID является уникальным идентификатором для каждого класса, JVM использует его для сравнения версий класса, обеспечивающих загрузку одного и того же класса во время сериализации во время десериализации.

Указание одного дает больше контроля, хотя JVM создает его, если вы не укажете. Созданное значение может различаться между разными компиляторами. Кроме того, иногда вы просто хотите по какой-то причине запретить десериализацию старых сериализованных объектов [ backward incompatibility ], и в этом случае вам просто нужно изменить serialVersionUID.

Javadocs для Serializable говорят :

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

Поэтому вы должны объявить serialVersionUID, потому что это дает нам больше контроля .

В этой статье есть несколько хороших точек на эту тему.

Eclipse выдает предупреждения, когда отсутствует serialVersionUID .

Сериализуемый класс Foo не объявляет статическое окончательное поле serialVersionUID типа long

Что такое serialVersionUID и почему это важно? Пожалуйста, покажите пример, в котором отсутствие serialVersionUID вызовет проблему.


Вы можете сказать Eclipse игнорировать эти предупреждения serialVersionUID:

Окно> Предпочтения> Java> Компилятор> Ошибки / предупреждения> Потенциальные проблемы программирования

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

  • Возможные проблемы программирования: Возможное случайное логическое назначение
  • Потенциальные проблемы программирования: нулевой указатель доступа
  • Ненужный код: Локальная переменная никогда не читается
  • Необязательный код: избыточная нулевая проверка
  • Необязательный код: ненужный листинг или «экземпляр»

и многое другое.


Документы для java.io.Serializable , вероятно, являются хорошим объяснением, которое вы получите:

Среда сериализации связывает каждый сериализуемый класс с номером версии, называемым serialVersionUID, который используется во время десериализации для проверки того, что отправитель и получатель сериализованного объекта загружают классы для этого объекта, которые совместимы с сериализацией. Если получатель загрузил класс для объекта с другим идентификатором serialVersionUID, чем класс соответствующего класса отправителя, то десериализация приведет к InvalidClassException . Сериализуемый класс может объявить свой собственный serialVersionUID явно, объявив поле с именем « serialVersionUID », которое должно быть статическим, окончательным и long :

ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;

Если сериализуемый класс явно не объявляет serialVersionUID, то среда выполнения сериализации будет вычислять значение serialVersionUID по умолчанию для этого класса на основе различных аспектов класса, как описано в Спецификации сериализации объектов Java (TM). Тем не менее, настоятельно рекомендуется, чтобы все сериализуемые классы явно объявляли значения serialVersionUID, поскольку вычисление по умолчанию serialVersionUID очень чувствительно к деталям класса, которые могут различаться в зависимости от реализаций компилятора и, таким образом, могут приводить к неожиданным InvalidClassExceptions во время десериализации. Поэтому, чтобы гарантировать последовательное значение serialVersionUID в разных реализациях java-компилятора, сериализуемый класс должен объявить явное значение serialVersionUID. Также настоятельно рекомендуется, чтобы явные объявления serialVersionUID использовали, по возможности, частный модификатор, поскольку такие объявления применяются только к полям, объявляющим сразу же: classVersionUID не полезны как унаследованные.


Если вам никогда не понадобится сериализовать ваши объекты в массив байтов и отправить / сохранить их, то вам не нужно беспокоиться об этом. Если да, то вы должны учитывать свой serialVersionUID, поскольку десериализатор объекта будет соответствовать его версии объекта, который имеет его загрузчик классов. Подробнее об этом читайте в спецификации языка Java.


Если вы сериализуете только потому, что вам нужно сериализовать для реализации (кто волнуется, если вы сериализуете для HTTPSession, например ... если он сохранен или нет, вам, вероятно, не нужно де-сериализовать объект формы) , то вы можете игнорировать это.

Если вы фактически используете сериализацию, это имеет значение только в том случае, если вы планируете хранить и извлекать объекты, используя сериализацию напрямую. SerialVersionUID представляет вашу версию класса, и вы должны увеличивать его, если текущая версия вашего класса не обратно совместима с предыдущей версией.

В большинстве случаев вы, вероятно, не будете использовать сериализацию напрямую. Если это так, создайте стандартный сериализуемый uid по умолчанию, щелкнув опцию быстрого исправления и не беспокойтесь об этом.


Не беспокойтесь, расчет по умолчанию действительно хорош и достаточен для 99,9999% случаев. И если у вас возникнут проблемы, вы можете - как уже было сказано, ввести UID в качестве необходимости (что маловероятно)


Оригинальный вопрос задал вопрос «почему это важно» и «пример», где этот Serial Version ID будет полезен. Ну, я нашел его.

Скажем, вы создаете класс Car , создаете его экземпляр и записываете в поток объектов. Сплющенный автомобильный объект находится в файловой системе в течение некоторого времени. Между тем, если класс Car модифицирован добавлением нового поля. Позже, когда вы пытаетесь прочитать (то есть deserialize) сплющенный объект Car , вы получите java.io.InvalidClassException - потому что всем сериализуемым классам автоматически присваивается уникальный идентификатор. Это исключение выдается, когда идентификатор класса не равен идентификатору сплющенного объекта. Если вы действительно думаете об этом, исключение вызывается из-за добавления нового поля. Вы можете избежать исключения этого исключения, контролируя само управление версиями, объявив явный serialVersionUID. Существует также небольшое преимущество в производительности при явном объявлении вашего serialVersionUID (потому что его не нужно вычислять). Таким образом, лучше всего добавить свой собственный serialVersionUID в свои классы Serializable, как только вы их создадите, как показано ниже:

public class Car {
static final long serialVersionUID = 1L; //assign a long value
}

Сначала мне нужно объяснить сериализацию.
Сериализация позволяет преобразовать объект в поток, для отправки этого объекта по сети ИЛИ Сохранить в файл ИЛИ сохранить в БД для использования букв.

Существуют некоторые правила для сериализации .

  • Объект сериализуется только в том случае, если его класс или его суперкласс реализует интерфейс Serializable

  • Объект сериализуем (сам реализует интерфейс Serializable), даже если его суперкласс не является. Однако первый суперкласс в иерархии сериализуемого класса, который не реализует интерфейс Serializable, ДОЛЖЕН иметь конструктор no-arg. Если это нарушено, readObject () создаст исключение java.io.InvalidClassException во время выполнения

  • Все примитивные типы являются сериализуемыми.

  • Временные поля (с переходным модификатором) НЕ сериализуются (т.е. не сохраняются или не восстанавливаются). Класс, который реализует Serializable, должен отмечать переходные поля классов, которые не поддерживают сериализацию (например, поток файлов).

  • Статические поля (со статическим модификатором) не сериализованы.

Когда объект сериализован JAVA Runtime Сопоставляет серийный номер версии, который называется serialVersionID.

Где нам нужен serialVersionID: во время десериализации для проверки совместимости отправителя и получателя в отношении сериализации. Если приемник загрузил класс с помощью другого serialVersionID, то десериализация закончится InvalidClassCastException .
Сериализуемый класс может объявить свой собственный serialVersionUID явно, объявив поле с именем «serialVersionUID», которое должно быть статическим, окончательным и длинным:.

Попробуем это на примере.

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() {
    StringBuffer employee = new StringBuffer();
    employee.append(getEmpName()).append(" is ).append(getEmpAge()).append("
years old  "));
    return employee.toString();
}
}

Создать объект Serialize

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");
}
}

Deserializ объект

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, нужно понимать, как работает сериализация / десериализация.

Когда объект класса Serializable сериализуется, Java Runtime связывает серийный номер версии (называемый serialVersionUID) с этим сериализованным объектом. В то время, когда вы десериализируете этот сериализованный объект, Java Runtime соответствует serialVersionUID сериализованного объекта с serialVersionUID этого класса. Если оба они равны, то только он переходит к дальнейшему процессу десериализации иначе бросает InvalidClassException.

Поэтому мы заключаем, что для успешного завершения процесса сериализации / десериализации сериализованный объект serialVersionUID должен быть эквивалентен serialVersionUID этого класса. В случае, если программист явно указывает значение serialVersionUID в программе, то такое же значение будет связано с сериализованным объектом и классом, независимо от платформы сериализации и десериализации (например, сериализация может выполняться на платформе, например, с помощью окна или MS JVM и Deserialization могут быть на другой платформе Linux с использованием Zing JVM).

Но в случае, если serialVersionUID не указан программистом, то при выполнении Serialization \ DeSerialization любого объекта среда выполнения Java использует свой собственный алгоритм для ее вычисления. Этот алгоритм вычисления serialVersionUID варьируется от одной JRE к другой. Также возможно, что среда, в которой объект сериализуется, использует одну JRE (например: SUN JVM) и среду, в которой происходит десериализация, используется Linux Jvm (zing). В таких случаях serialVersionUID, связанный с сериализованным объектом, будет отличаться от serialVersionUID класса, рассчитанного в среде десериализации. В свою очередь десериализация не будет успешной. Поэтому, чтобы избежать таких ситуаций / проблем, программист должен всегда указывать serialVersionUID класса Serializable.


Я не могу упустить эту возможность, чтобы подключить книгу Джоша Блоха « Эффективная Java» (2-е издание). Глава 11 является незаменимым ресурсом для сериализации Java.

Per Josh автоматически генерируемый UID генерируется на основе имени класса, реализованных интерфейсов и всех открытых и защищенных членов. Изменение любого из них каким-либо образом изменит serialVersionUID . Поэтому вам не нужно возиться с ними, только если вы уверены, что не более чем одна версия класса будет когда-либо сериализована (либо через процессы, либо позже из хранилища).

Если вы проигнорируете их пока, и найдите позже, что вам нужно каким-то образом изменить класс, но сохраните совместимость с старой версией класса, вы можете использовать последовательный инструмент JDK для генерации serialVersionUID в старом классе и явно установить что на новый класс. (В зависимости от ваших изменений вам может потребоваться также реализовать пользовательскую сериализацию, добавив writeObject и readObject - см. Serializable javadoc или вышеупомянутую главу 11.)


Зачем использовать SerialVersionUIDвстроенный Serializableкласс в Java?

Во serializationвремя выполнения Java время исполнения создает номер версии для класса, так что он может де-сериализовать его позже. Этот номер версии известен как SerialVersionUIDJava.

SerialVersionUIDиспользуется для версии сериализованных данных. Вы можете только де-сериализовать класс, если он SerialVersionUIDсовпадает с сериализованным экземпляром. Когда мы не объявляем SerialVersionUIDв нашем классе, Java runtime генерирует его для нас, но не рекомендуется. Рекомендуется , чтобы объявить , SerialVersionUIDкак private static final longпеременные , чтобы избежать механизма по умолчанию.

Когда вы объявляете класс Serializable, реализуя интерфейс маркера java.io.Serializable, Java runtime сохраняет экземпляр этого класса на диск, используя механизм Serialization по умолчанию, если вы не настроили процесс с помощью Externalizableинтерфейса.

см. также Зачем использовать SerialVersionUID внутри класса Serializable в Java

Код: javassist.SerialVersionUID


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

Этот скрипт использует инструмент JDK serialVer под капотом. Поэтому убедитесь, что ваш JAVA_HOME / bin находится в PATH.


Каждый раз, когда объект сериализуется, объект имеет штамп с номером идентификатора версии для класса объекта. Этот идентификатор называется serialVersionUID и вычисляется на основе информации о структуре класса. Предположим, что вы создали класс Employee, и у него есть версия id 333 (назначенная JVM). Теперь, когда вы будете сериализовывать объект этого класса (предположим объект Employee), JVM присваивает ему UID как # 333.

Рассмотрим ситуацию - в будущем вам нужно отредактировать или изменить свой класс, и в этом случае, когда вы измените его, JVM назначит ему новый UID (предположим # 444). Теперь, когда вы пытаетесь десериализовать объект employee, JVM будет сравнивать идентификатор версии объекта (Employee) объекта сериализованного объекта (# 333) с классом класса, то есть # 444 (поскольку он был изменен). При сравнении JVM найдет и версию UID разных, и, следовательно, Deserialization не удастся. Следовательно, если serialVersionID для каждого класса определяется самим программистом. Он будет таким же, даже если класс будет развиваться в будущем, и поэтому JVM всегда найдет, что класс совместим с сериализованным объектом, даже если класс изменен. Для получения дополнительной информации вы можете обратиться к главе 14 HEAD FIRST JAVA.





serialversionuid