Was ist die serialVersionUID?

1. Übersicht

Einfach ausgedrückt ist die serialVersionUID eine eindeutige Kennung für serialisierbare Klassen.

Dies wird während der Deserialisierung eines Objekts verwendet, um sicherzustellen, dass eine geladene Klasse mit dem serialisierten Objekt kompatibel ist. Wenn keine übereinstimmende Klasse gefunden wird, wird eine InvalidClassException ausgelöst.

2. UID der Serienversion

Beginnen wir mit der Erstellung einer serialisierbaren Klasse und deklarieren eine serialVersionUID-ID :

public class AppleProduct implements Serializable { private static final long serialVersionUID = 1234567L; public String headphonePort; public String thunderboltPort; }

Als Nächstes benötigen wir zwei Dienstprogrammklassen: eine zum Serialisieren eines AppleProduct- Objekts in einen String und eine zum Deserialisieren des Objekts aus diesem String:

public class SerializationUtility { public static void main(String[] args) { AppleProduct macBook = new AppleProduct(); macBook.headphonePort = "headphonePort2020"; macBook.thunderboltPort = "thunderboltPort2020"; String serializedObj = serializeObjectToString(macBook); System.out.println("Serialized AppleProduct object to string:"); System.out.println(serializedObj); } public static String serializeObjectToString(Serializable o) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(o); oos.close(); return Base64.getEncoder().encodeToString(baos.toByteArray()); } }
public class DeserializationUtility { public static void main(String[] args) { String serializedObj = ... // ommited for clarity System.out.println( "Deserializing AppleProduct..."); AppleProduct deserializedObj = (AppleProduct) deSerializeObjectFromString( serializedObj); System.out.println( "Headphone port of AppleProduct:" + deserializedObj.getHeadphonePort()); System.out.println( "Thunderbolt port of AppleProduct:" + deserializedObj.getThunderboltPort()); } public static Object deSerializeObjectFromString(String s) throws IOException, ClassNotFoundException { byte[] data = Base64.getDecoder().decode(s); ObjectInputStream ois = new ObjectInputStream( new ByteArrayInputStream(data)); Object o = ois.readObject(); ois.close(); return o; } }

Wir beginnen mit der Ausführung von SerializationUtility.java , das das AppleProduct- Objekt in einer String- Instanz speichert (serialisiert) und die Bytes mit Base64 codiert.

Anschließend führen wir mit diesem String als Argument für die Deserialisierungsmethode DeserializationUtility.java aus , das das AppleProduct- Objekt aus dem angegebenen String wieder zusammensetzt (deserialisiert) .

Die generierte Ausgabe sollte ungefähr so ​​aussehen:

Serialized AppleProduct object to string: rO0ABXNyACljb20uYmFlbGR1bmcuZGVzZXJpYWxpemF0aW9uLkFwcGxlUHJvZHVjdAAAAAAAEta HAgADTAANaGVhZHBob25lUG9ydHQAEkxqYXZhL2xhbmcvU3RyaW5nO0wADmxpZ2h0ZW5pbmdQb3 J0cQB+AAFMAA90aHVuZGVyYm9sdFBvcnRxAH4AAXhwdAARaGVhZHBob25lUG9ydDIwMjBwdAATd Gh1bmRlcmJvbHRQb3J0MjAyMA==
Deserializing AppleProduct... Headphone port of AppleProduct:headphonePort2020 Thunderbolt port of AppleProduct:thunderboltPort2020

Lassen Sie uns nun die Konstante serialVersionUID in AppleProduct.java ändern und erneut versuchen, das AppleProduct- Objekt aus demselben zuvor erstellten String zu deserialisieren . Durch erneutes Ausführen von DeserializationUtility.java sollte diese Ausgabe generiert werden.

Deserializing AppleProduct... Exception in thread "main" java.io.InvalidClassException: com.baeldung.deserialization.AppleProduct; local class incompatible: stream classdesc serialVersionUID = 1234567, local class serialVersionUID = 7654321 at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616) at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1630) at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1521) at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1781) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:373) at com.baeldung.deserialization.DeserializationUtility.deSerializeObjectFromString(DeserializationUtility.java:24) at com.baeldung.deserialization.DeserializationUtility.main(DeserializationUtility.java:15)

Durch Ändern der serialVersionUID der Klasse haben wir ihre Version / ihren Status geändert. Infolgedessen wurden während der Deserialisierung keine kompatiblen Klassen gefunden, und eine InvalidClassException wurde ausgelöst.

3. Kompatible Änderungen

Angenommen , wir müssen unserer vorhandenen AppleProduct- Klasse einen neuen Feld- LightningPort hinzufügen :

public class AppleProduct implements Serializable { //... public String lightningPort; }

Da wir nur ein neues Feld hinzufügen, ist keine Änderung der serialVersionUID erforderlich . Dies liegt daran, dass während des Deserialisierungsprozesses null als Standardwert für das Feld lightningPort zugewiesen wird .

Ändern Sie unsere DeserializationUtility- Klasse, um den Wert dieses neuen Felds zu drucken:

System.out.println("LightningPort port of AppleProduct:" + deserializedObj.getLightningPort());

Wenn wir nun die DeserializationUtility- Klasse erneut ausführen , sehen wir eine Ausgabe ähnlich der folgenden:

Deserializing AppleProduct... Headphone port of AppleProduct:headphonePort2020 Thunderbolt port of AppleProduct:thunderboltPort2020 Lightning port of AppleProduct:null

4. Standardmäßige serielle Version

Wenn wir keinen serialVersionUID- Status für eine serialisierbare Klasse definieren, definiert Java einen basierend auf einigen Eigenschaften der Klasse selbst, wie z. B. dem Klassennamen, Instanzfeldern usw.

Definieren wir eine einfache serialisierbare Klasse:

public class DefaultSerial implements Serializable { }

Wenn wir eine Instanz dieser Klasse wie folgt serialisieren:

DefaultSerial instance = new DefaultSerial(); System.out.println(SerializationUtility.serializeObjectToString(instance));

Dadurch wird der Base64-Digest der serialisierten Binärdatei gedruckt:

rO0ABXNyACpjb20uYmFlbGR1bmcuZGVzZXJpYWxpemF0aW9uLkRlZmF1bHRTZXJpYWx9iVz3Lz/mdAIAAHhw

Nach wie vor sollten wir in der Lage sein, diese Instanz aus dem Digest zu deserialisieren:

String digest = "rO0ABXNyACpjb20uYmFlbGR1bmcuZGVzZXJpY" + "WxpemF0aW9uLkRlZmF1bHRTZXJpYWx9iVz3Lz/mdAIAAHhw"; DefaultSerial instance = (DefaultSerial) DeserializationUtility.deSerializeObjectFromString(digest);

Einige Änderungen an dieser Klasse können jedoch die Serialisierungskompatibilität beeinträchtigen. Wenn wir dieser Klasse beispielsweise ein privates Feld hinzufügen :

public class DefaultSerial implements Serializable { private String name; }

Wenn Sie dann versuchen, denselben Base64-Digest für eine Klasseninstanz zu deserialisieren, erhalten Sie eine InvalidClassException:

Exception in thread "main" java.io.InvalidClassException: com.baeldung.deserialization.DefaultSerial; local class incompatible: stream classdesc serialVersionUID = 9045863543269746292, local class serialVersionUID = -2692722436255640434

Aufgrund dieser Art unerwünschter Inkompatibilität ist es immer eine gute Idee, eine serialVersionUID in serialisierbaren Klassen zu deklarieren . Auf diese Weise können wir die Version behalten oder weiterentwickeln, während sich die Klasse selbst weiterentwickelt.

5. Schlussfolgerung

In diesem kurzen Artikel haben wir die Verwendung der Konstante serialVersionUID demonstriert , um die Versionierung serialisierter Daten zu erleichtern.

Wie immer finden Sie die in diesem Artikel verwendeten Codebeispiele auf GitHub.