Czy klasa abstrakcyjna ma identyfikator serialVersionUID

W Javie, jeśli klasa implementuje Serializable, ale jest abstrakcyjna, czy powinna mieć zadeklarowany długi identyfikator serialVersionUID, czy tylko tego wymagają podklasy?

W tym przypadku intencją jest, aby wszystkie podklasy zajmowały się serializacją, ponieważ celem tego typu jest użycie w wywołaniach RMI.

Author: Yishai, 2009-05-21

4 answers

Identyfikator serialVersionUID jest dostarczany w celu określenia zgodności pomiędzy dezercjalizowanym obiektem a bieżącą wersją klasy. jako takie, nie jest to tak naprawdę konieczne w pierwszej wersji klasy, ani w tym przypadku w abstrakcyjnej klasie bazowej. Nigdy nie będziesz miał instancji tej abstrakcyjnej klasy do serializacji/deserializacji, więc nie potrzebuje ona identyfikatora serialVersionUID.

(oczywiście generuje ostrzeżenie kompilatora, którego chcesz się pozbyć, prawda?)

Okazuje się komentarz Jamesa jest poprawny. SerialVersionUID abstrakcyjnej klasy bazowej powoduje, że jest propagowana do podklas. W związku z tym, ty czy potrzebujesz identyfikatora serialVersionUID w swojej klasie bazowej.

Kod do testu:

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

Uruchom main in Sub raz, aby go utworzyć i zapisać obiekt. Następnie zmień serialVersionUID w klasie bazowej, skomentuj linie w main, które zapisują obiekt (aby nie zapisał go ponownie, po prostu chcesz załadować stary), i uruchom go jeszcze raz. Spowoduje to wyjątek

java.io.InvalidClassException: Base; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2
 41
Author: Bill the Lizard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-21 18:08:26

Tak, ogólnie rzecz biorąc, z tego samego powodu, dla którego każda inna klasa potrzebuje identyfikatora szeregowego - aby uniknąć generowania go dla niej. Zasadniczo każda klasa (Nie interfejs), która implementuje serializable powinna zdefiniować identyfikator wersji szeregowej lub ryzyko błędów de-serializacji, gdy to samo .class compile nie znajduje się w serwerze i kliencie JVMs.

Są inne opcje, jeśli próbujesz zrobić coś wymyślnego. Nie jestem pewien, co masz na myśli mówiąc " to jest intencja podklas...". Napiszesz niestandardowe metody serializacji (np. writeObject, readObject)? Jeśli tak, istnieją inne opcje do czynienia z super klasy.

Zobacz: http://java.sun.com/javase/6/docs/api/java/io/Serializable.html

HTH Tom

 6
Author: Tom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-21 14:53:42

Faktycznie, wskazanie z linku Toma, jeśli brakuje serialVersionID jest obliczane przez serialization runtime tzn. nie podczas kompilacji

Jeśli Klasa serializowalna nie deklaruje jawnie serialVersionUID, wtedy środowisko uruchomieniowe serializacji wyliczy domyślna wartość serialVersionUID dla tej klasy w oparciu o różne aspekty klasy...

To sprawia, że sprawy są jeszcze bardziej skomplikowane mając różne wersje JRE.
 2
Author: Viktor Stolbin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-30 14:00:34

Koncepcyjnie, serializowane dane wyglądają tak:

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

Więc kiedy deserializujesz, niedopasowanie wersji w którejkolwiek z klas w hierarchii powoduje, że deserializacja nie powiedzie się. Nic nie jest przechowywane dla interfejsów, więc nie ma potrzeby określania ich wersji.

Odpowiedź: tak, musisz również podać serialVersionUID w podstawowej klasie abstrakcyjnej. Nawet jeśli nie ma pól (className + version są przechowywane nawet jeśli nie ma pól).

Zwróć również uwagę na po:

  1. jeśli klasa nie ma pola, które znajduje się w serializowanych danych (usuniętym polu), jest ono ignorowane.
  2. jeśli klasa ma pole, które nie jest obecne w serializowanych danych( nowe pole), jest ustawione na 0 / false / null(nie na wartość domyślną, jak można się spodziewać).
  3. jeśli pole zmienia typ danych, deserializowana wartość musi być przypisana do nowego typu. Np. jeśli posiadasz pole Object z wartością String, zmiana typu pola na String powiedzie się, ale zmiana na Integer nie zadziała. jednak zmiana pola z int na long nie zadziała, nawet jeśli można przypisać wartość int do zmiennej long.
  4. jeśli podklasa nie rozszerza już klasy nadrzędnej, którą rozszerza w serializowanych danych, jest ignorowana (jak w przypadku 1).
  5. Jeśli podklasa rozszerza klasę, która nie znajduje się w serializowanych danych, pola klasy nadrzędnej są przywracane z wartością 0/false/null (jak w przypadku 2).

W prostych słowach: możesz zmienić kolejność pól, dodać i usuń je, a nawet Zmień hierarchię klas. Nie należy zmieniać nazw pól ani klas (nie zawiedzie się, ale wartości nie zostaną deserializowane). Nie można zmienić typu pól za pomocą typu prymitywnego i można zmienić pola typu odniesienia, pod warunkiem, że nowy typ można przypisać ze wszystkich wartości.

Uwaga: jeśli klasa bazowa nie implementuje Serializowalnego, a jedynie podklasa, to pola z klasy bazowej zachowywałyby się jak transient.

 0
Author: Oliv,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-05 10:49:53