Qu'est-ce que serialVersionUID?

1. Vue d'ensemble

Autrement dit, le serialVersionUID est un identifiant unique pour Serializable classes.

Ceci est utilisé lors de la désérialisation d'un objet, pour s'assurer qu'une classe chargée est compatible avec l'objet sérialisé. Si aucune classe correspondante n'est trouvée, une InvalidClassException est levée.

2. UID de la version série

Commençons par créer une classe sérialisable et déclarons un identifiant serialVersionUID :

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

Ensuite, nous aurons besoin de deux classes d'utilitaires: une pour sérialiser un objet AppleProduct dans une chaîne, et une autre pour désérialiser l'objet de cette chaîne:

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

Nous commençons par exécuter SerializationUtility.java , qui enregistre (sérialise) l' objet AppleProduct dans une instance String , en encodant les octets à l'aide de Base64.

Ensuite, en utilisant cette String comme argument pour la méthode de désérialisation, nous exécutons DeserializationUtility.java , qui réassemble (désérialise) l' objet AppleProduct à partir de la String donnée .

La sortie générée doit être similaire à ceci:

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

Maintenant, nous allons modifier la serialVersionUID constante AppleProduct.java, et une nouvelle tentative pour désérialiser l' AppleProduct objet de la même chaîne produit plus tôt. La réexécution de DeserializationUtility.java devrait générer cette sortie.

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)

En changeant le serialVersionUID de la classe, nous avons modifié sa version / état. Par conséquent, aucune classe compatible n'a été trouvée lors de la désérialisation et une exception InvalidClassException a été levée.

3. Modifications compatibles

Disons que nous devons ajouter un nouveau champ lightningPort à notre classe AppleProduct existante :

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

Puisque nous ajoutons simplement un nouveau champ, aucune modification de serialVersionUID ne sera requise . En effet, pendant le processus de désérialisation, null sera affecté comme valeur par défaut pour le champ lightningPort .

Modifions notre classe DeserializationUtility pour afficher la valeur de ce nouveau champ:

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

Maintenant, lorsque nous réexécutons la classe DeserializationUtility , nous verrons une sortie similaire à:

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

4. Version série par défaut

Si nous ne définissons pas un état serialVersionUID pour une classe Serializable , alors Java en définira un en fonction de certaines propriétés de la classe elle-même, telles que le nom de la classe, les champs d'instance, etc.

Définissons une classe Serializable simple :

public class DefaultSerial implements Serializable { }

Si nous sérialisons une instance de cette classe comme suit:

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

Cela imprimera le condensé Base64 du binaire sérialisé:

rO0ABXNyACpjb20uYmFlbGR1bmcuZGVzZXJpYWxpemF0aW9uLkRlZmF1bHRTZXJpYWx9iVz3Lz/mdAIAAHhw

Tout comme avant, nous devrions pouvoir désérialiser cette instance du condensé:

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

Cependant, certaines modifications apportées à cette classe peuvent interrompre la compatibilité de sérialisation. Par exemple, si nous ajoutons un champ privé à cette classe:

public class DefaultSerial implements Serializable { private String name; }

Et puis essayez de désérialiser le même condensé Base64 en une instance de classe, nous obtiendrons une InvalidClassException:

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

En raison de ce type d'incompatibilité indésirable, il est toujours judicieux de déclarer un serialVersionUID dans les classes Serializable . De cette façon, nous pouvons conserver ou faire évoluer la version à mesure que la classe elle-même évolue.

5. Conclusion

Dans cet article rapide, nous avons démontré l'utilisation de la constante serialVersionUID pour faciliter la gestion des versions des données sérialisées.

Comme toujours, les exemples de code utilisés tout au long de cet article peuvent être trouvés sur GitHub.