34

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

16

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

Сообщение предупреждения от Eclipse:

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

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

3 ответ(ов)

5

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

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

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

1

Вы можете настроить Eclipse так, чтобы он игнорировал предупреждения о serialVersionUID:

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

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

  • Потенциальные проблемы программирования: Возможное случайное присваивание булевого значения
  • Потенциальные проблемы программирования: Доступ к нулевому указателю
  • Ненужный код: Локальная переменная никогда не читается
  • Ненужный код: Избыточная проверка на null
  • Ненужный код: Ненужное приведение типа или 'instanceof'

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

0

Прежде чем ответить на вопрос, важно объяснить, что такое сериализация.

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

Существует несколько правил для сериализации:

  • Объект сериализуем только в том случае, если его класс или его суперкласс реализует интерфейс 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, можно избежать проблем с несовместимостью классов при сериализации и десериализации объектов.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь