Если абстрактный класс имеет serialVersionUID



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



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

787   4  

4 ответов:

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

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

оказывается, комментарий Джеймса верен. В serialVersionUID абстрактного базового класса тут распространяется на подклассы. В свете этого, вы do нужен 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 In Sub один раз, чтобы получить его для создания и сохранения объекта. Затем измените serialVersionUID в базовом классе, закомментируйте строки в main, которые сохраняют объект (так это не сохраняет его снова, вы просто хотите загрузить старый), и запустить его снова. Это приведет к исключению

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

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

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

посмотреть: http://java.sun.com/javase/6/docs/api/java/io/Serializable.html

HTH Tom

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

если сериализуемый класс явно не объявляет serialVersionUID, затем среда выполнения сериализации вычислит значение 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.

Comments

    Ничего не найдено.