Ce este serialVersionUID?

1. Prezentare generală

Pur și simplu pune, serialVersionUID este un identificator unic pentru serializabil clase.

Aceasta este utilizată în timpul deserializării unui obiect, pentru a se asigura că o clasă încărcată este compatibilă cu obiectul serializat. Dacă nu se găsește nicio clasă potrivită, este aruncată o InvalidClassException .

2. UID versiune serial

Să începem prin a crea o clasă serializabilă și să declarăm un identificator serialVersionUID :

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

Apoi, vom avea nevoie de două clase de utilitate: una pentru a serializa un obiect AppleProduct într-un șir, și alta pentru a deserializa obiectul din acel șir:

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

Începem prin rularea SerializationUtility.java , care salvează (serializează) AppleProduct obiect într - un șir de instanc e, care codifică bytes folosind base64.

Apoi, folosind acel șir ca argument pentru metoda de deserializare, rulăm DeserializationUtility.java, care reasamblează (deserializează) obiectul AppleProduct din șirul dat .

Ieșirea generată ar trebui să fie similară cu aceasta:

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

Acum, să modifice serialVersionUID constanta din AppleProduct.java, și reattempt la deserializati AppleProduct obiectul din același șir produs mai devreme. Reexecutarea DeserializationUtility.java ar trebui să genereze această ieșire.

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)

Prin modificarea serialVersionUID a clasei, i-am modificat versiunea / starea. Ca urmare, nu s-au găsit clase compatibile în timpul deserializării și s-a aruncat o excepție InvalidClassException .

3. Modificări compatibile

presupunem că trebuie să adăugăm un nou câmp lightningPort la clasa noastră existentă AppleProduct :

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

Deoarece tocmai adăugăm un câmp nou, nu va fi necesară nicio modificare în serialVersionUID . Acest lucru se datorează faptului că, în timpul procesului de deserializare, valoarea nulă va fi atribuită ca valoare implicită pentru câmpul LightningPort .

Să modificăm clasa DeserializationUtility pentru a imprima valoarea acestui nou câmp:

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

Acum, când reluăm clasa DeserializationUtility , vom vedea o ieșire similară cu:

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

4. Versiune de serie implicită

Dacă nu definim o stare serialVersionUID pentru o clasă Serializabilă , atunci Java o va defini pe baza unor proprietăți ale clasei în sine, cum ar fi numele clasei, câmpurile de instanță etc.

Să definim o clasă serializabilă simplă :

public class DefaultSerial implements Serializable { }

Dacă serializăm o instanță din această clasă, după cum urmează:

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

Aceasta va imprima rezumatul Base64 al binarului serializat:

rO0ABXNyACpjb20uYmFlbGR1bmcuZGVzZXJpYWxpemF0aW9uLkRlZmF1bHRTZXJpYWx9iVz3Lz/mdAIAAHhw

La fel ca înainte, ar trebui să putem deserializa această instanță din rezumat:

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

Cu toate acestea, unele modificări ale acestei clase pot rupe compatibilitatea serializării. De exemplu, dacă adăugăm un câmp privat la această clasă:

public class DefaultSerial implements Serializable { private String name; }

Și apoi încercați să deserializați același rezumat Base64 într-o instanță de clasă, vom obține o InvalidClassException:

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

Din cauza acestui tip de incompatibilitate nedorită, este întotdeauna o idee bună să declarați un serialVersionUID în clasele serializabile . Astfel putem păstra sau evolua versiunea pe măsură ce clasa în sine evoluează.

5. Concluzie

În acest articol rapid, am demonstrat utilizarea constantei serialVersionUID pentru a facilita versiunea datelor serializate.

Ca întotdeauna, mostrele de cod utilizate în acest articol pot fi găsite pe GitHub.