Что такое serialVersionUID и почему его следует использовать?
Проблема: Eclipse выдает предупреждения, когда отсутствует поле serialVersionUID
.
Сообщение предупреждения от Eclipse:
Сериализуемый класс Foo не объявляет статическое финальное поле serialVersionUID типа long.
Вопрос: Что такое serialVersionUID
и почему он важен? Приведите пример, в котором отсутствие serialVersionUID
приведет к возникновению проблемы.
3 ответ(ов)
Если вы сериализуете объекты только ради реализации (например, для HTTPSession
), и вам не важно, сохраняются ли объекты или нет, то можете не обращать на это внимание.
Если же вы действительно используете сериализацию и собираетесь хранить и восстанавливать объекты напрямую, то вам следует учитывать serialVersionUID
, который представляет собой версию вашего класса. Вам стоит увеличивать этот идентификатор, если текущая версия вашего класса несовместима с предыдущей.
Чаще всего вы, вероятно, не будете использовать сериализацию напрямую. Если это так, то просто создайте SerialVersionUID
по умолчанию, выбрав вариант быстрого исправления, и не переживайте об этом.
Вы можете настроить Eclipse так, чтобы он игнорировал предупреждения о serialVersionUID
:
Окно > Настройки > Java > Компилятор > Ошибки / Предупреждения > Потенциальные проблемы программирования
Кроме того, если вы не знали, в этом разделе можно включить множество других предупреждений (или даже настроить их как ошибки), многие из которых весьма полезны:
- Потенциальные проблемы программирования: Возможное случайное присваивание булевого значения
- Потенциальные проблемы программирования: Доступ к нулевому указателю
- Ненужный код: Локальная переменная никогда не читается
- Ненужный код: Избыточная проверка на null
- Ненужный код: Ненужное приведение типа или 'instanceof'
и многие другие.
Прежде чем ответить на вопрос, важно объяснить, что такое сериализация.
Сериализация позволяет конвертировать объект в поток данных, чтобы отправить этот объект по сети, сохранить в файл или сохранить в базу данных для последующего использования.
Существует несколько правил для сериализации:
- Объект сериализуем только в том случае, если его класс или его суперкласс реализует интерфейс Serializable.
- Объект также может быть сериализуемым (если он сам реализует интерфейс Serializable), даже если его суперкласс этого не делает. Однако, первый суперкласс в иерархии сериализуемого класса, который не реализует интерфейс Serializable, ДОЛЖЕН иметь конструктор без аргументов. Если это правило нарушено, выполнение метода readObject() приведет к исключению java.io.InvalidClassException на этапе выполнения.
- Все примитивные типы сериализуемы.
- Поля с модификатором transient (временное) НЕ сериализуются (т.е. не сохраняются и не восстанавливаются). Класс, реализующий Serializable, должен помечать transient поля классов, которые не поддерживают сериализацию (например, поток файла).
- Статические поля (с модификатором static) также не сериализуются.
Когда объект сериализуется, Java Runtime связывает его с номером версии сериализации, известным как serialVersionUID
.
Где нам нужен serialVersionUID:
Во время десериализации для проверки совместимости отправителя и получателя относительно сериализации. Если получатель загрузил класс с другим serialVersionUID
, то десериализация завершится исключением 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 empname;
}
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;
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
, можно избежать проблем с несовместимостью классов при сериализации и десериализации объектов.
Инициализация ArrayList в одну строчку
Что значит "Не удалось найти или загрузить основной класс"?
Как установить Java 8 на Mac
Почему в RecyclerView отсутствует onItemClickListener()?
Что значит 'synchronized'?