Вопрос

В Java, если класс реализует Serializable, но является абстрактным, должен ли он иметь длинный объявленный серийныйVersionUID или подклассы требуют только этого?

В этом случае действительно предполагается, что все подклассы будут иметь дело с сериализацией, поскольку цель типа — использовать в вызовах RMI.

Это было полезно?

Решение

SerialVersionUID предоставляется для определения совместимости между десериализованным объектом и текущей версией класса. По сути, в первой версии класса или, в данном случае, в абстрактном базовом классе в этом нет необходимости.У вас никогда не будет экземпляра этого абстрактного класса для сериализации/десериализации, поэтому для него не требуется серийныйVersionUID.

(Конечно, компилятор генерирует предупреждение, от которого вы хотите избавиться, верно?)

Оказывается, комментарий Джеймса верен.SerialVersionUID абстрактного базового класса. делает распространяться на подклассы.В свете этого вы делать вам нужен SerialVersionUID в вашем базовом классе.

Код для проверки:

import java.io.Serializable;

public abstract class Base implements Serializable {

    private int x = 0;
    private int y = 0;

    private static final long serialVersionUID = 1L;

    public String toString()
    {
        return "Base X: " + x + ", Base Y: " + y;
    }
}



import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class Sub extends Base {

    private int z = 0;

    private static final long serialVersionUID = 1000L;

    public String toString()
    {
        return super.toString() + ", Sub Z: " + z;
    }

    public static void main(String[] args)
    {
        Sub s1 = new Sub();
        System.out.println( s1.toString() );

        // Serialize the object and save it to a file
        try {
            FileOutputStream fout = new FileOutputStream("object.dat");
            ObjectOutputStream oos = new ObjectOutputStream(fout);
            oos.writeObject( s1 );
            oos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        Sub s2 = null;
        // Load the file and deserialize the object
        try {
            FileInputStream fin = new FileInputStream("object.dat");
            ObjectInputStream ois = new ObjectInputStream(fin);
            s2 = (Sub) ois.readObject();
            ois.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println( s2.toString() );
    }
}

Запустите main в Sub один раз, чтобы он создал и сохранил объект.Затем измените SerialVersionUID в базовом классе, закомментируйте строки main, которые сохраняют объект (чтобы он не сохранял его снова, вы просто хотите загрузить старый) и запустите его снова.Это приведет к исключению

java.io.InvalidClassException: Base; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2

Другие советы

Да, в общем, по той же причине, по которой любому другому классу нужен серийный идентификатор — чтобы избежать его генерации.По сути, любой класс (не интерфейс), реализующий сериализуемость, должен определять идентификатор последовательной версии, иначе вы рискуете совершить ошибку десериализации, если одна и та же компиляция .class отсутствует в серверной и клиентской JVM.

Если вы хотите сделать что-то необычное, есть и другие варианты.Я не уверен, что вы подразумеваете под «это намерение подклассов...».Собираетесь ли вы писать собственные методы сериализации (например.writeObject, readObject)?Если да, то есть и другие варианты работы с суперклассом.

видеть:http://java.sun.com/javase/6/docs/api/java/io/Serializable.html

HTH Том

Собственно, указываю на ссылку Тома, если она отсутствует. serialVersionID фактически рассчитывается по времени выполнения сериализации, т.е.не во время компиляции

Если сериализуемый класс не объявляет явным образом serialVersionUID, то время выполнения сериализации вычисляет default serialVersionUID значение для этого класса на основе различных аспектов класса...

Это еще больше усложняет работу с разными версиями JRE.

Концептуально сериализованные данные выглядят так:

subClassData(className + version + fieldNames + fieldValues)
parentClassData(className + version + fieldNames + fieldValues)
... (up to the first parent, that implements Serializable)

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

Отвечать: да, вам нужно предоставить serialVersionUID в базовом абстрактном классе.Даже если в нем нет полей (className + version сохраняются, даже если полей нет).

Также обратите внимание на следующее:

  1. Если у класса нет поля, найденного в сериализованных данных (удаленное поле), оно игнорируется.
  2. Если у класса есть поле, которого нет в сериализованных данных (новое поле), ему присваивается значение 0/false/null (а не значение по умолчанию, как можно было бы ожидать).
  3. если поле меняет тип данных, десериализованное значение должно быть присвоено новому типу.Например.если бы у тебя был Object поле с String значение, изменив тип поля на String добьется успеха, но изменится на Integer не будет.Однако изменение поля с int к long не будет работать, даже если вы можете назначить int значение для long переменная.
  4. Если подкласс больше не расширяет родительский класс, который он расширяет в сериализованных данных, он игнорируется (как в случае 1).
  5. Если подкласс теперь расширяет класс, которого нет в сериализованных данных, поля родительского класса восстанавливаются со значением 0/false/null (как в случае 2).

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

Примечание:если базовый класс не реализует Serializable, а реализует только подкласс, то поля базового класса будут вести себя как transient.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top