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.
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
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
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
To sprawia, że sprawy są jeszcze bardziej skomplikowane mając różne wersje JRE.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...
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:
- jeśli klasa nie ma pola, które znajduje się w serializowanych danych (usuniętym polu), jest ono ignorowane.
- 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ć).
- 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 naString
powiedzie się, ale zmiana naInteger
nie zadziała. jednak zmiana pola zint
nalong
nie zadziała, nawet jeśli można przypisać wartośćint
do zmiennejlong
. - jeśli podklasa nie rozszerza już klasy nadrzędnej, którą rozszerza w serializowanych danych, jest ignorowana (jak w przypadku 1).
- 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
.
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