Le Touilleur ExpressLe Touilleur ExpressLe Touilleur ExpressLe Touilleur Express
  • Accueil
  • A propos de l’auteur
  • A propos du Touilleur Express

serialVersionUID mythes et légendes

    Home Dev Web serialVersionUID mythes et légendes

    serialVersionUID mythes et légendes

    Par Nicolas Martignole | Dev Web, Java | 7 commentaires | 26 janvier, 2008 | 0 | 35 774 affichages
         

    Suite à mon article sur la sérialisation, je souhaite préciser quelques détails sur la sérialisation et parler du champ « serialVersionUID ». Qu’est-ce que ce champ ? A quoi sert-il ? Faut-il le mettre ou non dans une class Serializable ?

    Quelques rappels:

    Toutes les classes dérivées d’une class sérialisable sont aussi sérialisables. L’interface java.io.Serializable ne sert qu’à marquer la classe comme étant sérialisable comme on l’a vu. Cependant pour permettre à des types dérivés d’être sérialisable alors que leur super classe n’est pas sérialisable, il faut que la super classe déclare au moins un constructeur vide sans argument. De plus, la classe dérivée doit prendre en compte la sérialisation des champs protected ou public de sa super classe.

    Lorqu’une classe est désérialisée par Java, les champs de la super-classe qui ne serait pas sérialisable seront initialisés dans le constructeur vide sans argument. D’où son importance. Faute de cela, vous aurez une exception au runtime alors que votre classe fille est marquée comme étant Serializable… Ce n’est pas toujours visible au premier coup d’oeil. Des outils comme PMD sont capables de détecter ce type de problèmes et de vous aider.

    Sachez aussi qu’il est possible de prendre en main le mécanisme de sérialisation/désérialisation en définissant 2 méthodes Java dans votre classe. Ce qui est intéressant à retenir, c’est que ces méthodes ne sont pas définies dans une interface… A votre avis pourquoi ? Il y a une raison très précise…

    Les 2 méthodes sont:

    private void writeObject(java.io.ObjectOutputStream out)
    private void readObject(java.io.ObjectInputStream in)
    

    Ne modifiez pas la signature de l’une de ses méthodes, Java utilise l’introspection pour les identifier lors de la sérialisation et désérialisation.

    En général, le code d’une méthode de sérialisation lorsque la classe contient des membres non sérialisables ressemble à ceci:

    private void readObject(ObjectInputStream in) {
     // Deserialise normalement la classe
     in.defaultReadObject();
     // Instancie un objet qui ne pouvait pas être sérialisé
     this.socket = new Socket();
    }
    

    Quoi d’autre ? Vous pouvez aussi avec Java 5 utiliser un autre mécanisme de sérialisation. Pour cela il faut définir 2 méthodes dans votre class:

    public Object writeReplace() throws ObjectStreamException;
    public Object readResolve() throws ObjectStreamException;
    

    Ce mécanisme permet de retourner un objet autre que le stream par défaut lors de la sérialisation et de la désérialisation. Vous pouvez même marquer ces 2 méthodes en tant que private, voir la javadoc à ce sujet.

    A propos de serialVersionUID :

    Qu’est-ce que le champ serialVersionUID ?

    C’est une clé de hachage SHA qui identifie de manière unique votre Classe. Cela permet lorsqu’elle est sérialisée, de la marquer avec une somme de contrôle (checksum) pour que lors de la désérialisation, votre programme soit certain de la version de la classe Java qu’il manipule. C’est un gestionnaire de version de votre classe si vous préférez.

    A quoi sert-il ?

    Imaginez un serveur et un client qui s’échangent des objets sérialisés. Ce système permet de s’assurer que les versions des classes envoyées d’un côté, existent bien de l’autre côté. Si la version est différente, Java lève une java.io.InvalidClassException pour vous dire qu’il y a un souci de version. Si je le déclare, est-ce que la sérialisation va plus vite ?Et non… c’est une des légendes urbaines en Java. Avant tout, le calcul de ce serialVersionUID n’est effectué que lors du chargement de la classe par le ClassLoader. Typiquement sur un serveur démarré, je ne pense pas que vous verrez un quelconque gain de performance… La sérialisation d’une classe est lente, ne confondez pas avec le calcul d’un checksum SHA, opération assez rapide.

    Comment le déclarer ?

    Il faut que cette variable respecte à la lettre la signature afin que Java puisse la voir lors de la sérialisation et de la désérialisation. En général on utilise des Long, en prenant soin de mettre un bon nombre de chiffres, histoire de ne pas tomber sur la même clé que le voisin.

    private static final long serialVersionUID = 354054054054L;
    
    

    Notez le « L » à la fin de la ligne pour indiquer qu’il s’agit d’un long et éviter un cast fantôme.A noter que vous pouvez déclarer ce champ protected. C’est une très mauvaise idée, sauf à savoir ce que vous faîtes.

    Faut-il déclarer ce champ ?

    Sur la nécessité de le faire, la réponse est non. Java se charge de calculer le serialVersionUID lors du chargement de la class pour sérialiser/désérialiser. C’est donc fait automatiquement et vous n’avez pas à le déclarer pour que la sérialisation fonctionne. C’est pour cela que je m’en suis passé dans l’article d’avant-hier.

    Maintenant sur l’opportunité, voici ce que dit la spécification Java 5 sur la sérialisation:Note ¯ It is strongly recommended that all serializable classes explicitly declare serialVersionUID values, since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected serialVersionUID conflicts during deserialization, causing deserialization to fail.

    Que se passe-t-il si je le déclare ?

    Je vous recommande de déclarer ce champ si vous savez ce que vous faîtes. En spécifiant ce tag, vous prenez en quelque sorte la responsabilité de gérer la version de votre classe.Il existe pleins de cas que j’ai rencontré où cela facilite la vie. Par exemple avec JBoss. Lorsque vous travaillez avec une arborescence explosé de votre EAR, et que vous ne modifiez qu’une partie, vous êtes souvent obligé de forcer ce tag pour être tranquille. Sans cela, JBoss et le ClassLoader (qui est lui aussi versionné, j’en parlerai une fois) va jeter des NoClassDefFoundError… difficile de faire le lien avec la sérialisation mais pourtant c’est le cas… C’est aussi pratique en phase de développement, lorsque vous travaillez sur la classe qui doit être sérialisée dans une architecture client-serveur avec RMI. Cela permet par exemple de déployer une nouvelle version de la classe sur le client 1 et ne vous force pas à mettre à jour le client 2 distant. Sans cela, à chaque nouvelle version vous seriez obligé de livrer la même version à vos différentes JVM ce qui n’est pas forcément possible. La Javadoc recommande de déclarer ce champ, car sa valeur est très sensible aussi au compilateur qui génère ce nombre. Afin de garantir que la valeur d’un serialVersionUID soit constante quelque soit le compilateur, il est recommandé de le fixer une fois pour toute.

    Mini quizz pour tester ses connaissances

    Pour terminer, quelques petites questions:

    • un champ static est-il sérialisable ?
    • un champ volatile est-il serialisable ?
    • Soit une classe Player qui implémente Serializable. Est-ce qu’une inner-class dans Player doit aussi implémenter cette interface ? Est-ce que l’inner-class sera aussi sérialisée ?

    Références:

    Article Java World « Into the mist of serialization »
    « Java Object Serialization » version 1.5.0

    Articles similaires:

    Default ThumbnailAstuces pour identifier la cause d'une java.io.NotSerializable et tuning JBoss
    Java, serialVersionUID
    • Avatar
      CyberEiffel 28 janvier 2008 at 21 h 24 min

      Plusieurs remarques par rapport à l’affirmation :
      « La sérialisation d’une classe est lente, ne confondez pas avec le calcul d’un checksum SHA, opération assez rapide. »

      * On sérialise un objet. On ne sérialise pas une classe.

      * Pour la comparaison sérialisation d’un objet/SHA, faut pas mélanger les choux et les carottes :
      * Le SHA est celui du code de la classe, et vraissemblablement calculé une seule fois au moment de la compilation.
      * La sérialisation nécessite d’accéder à tous les champs de l’objet un par un pour sauvegarder leur valeur. Si on devait calculer le SHA de l’objet (et non le SHA de la classe), ce serait forcément plus long que de la sérialiser. A moins bien sûr que le format de sérialisation soit particulièrement couteux (ce qui est le cas si on sérialise en XML… ;o) ).

    • Avatar
      Nicolas Delsaux 29 janvier 2008 at 9 h 28 min

      La première chose à noter, c’est que l’utilsiation de la sérialisation pour échanger des objets n’est pas forcément la meilleure façon de faire.

      La deuxième chose, c’est que pour contrôler la sérialisation, plutôt que de déclarer les deux méthodes, il vaut mieux implémenter l’interface Externalizable, qui définit ces deux méthodes dans l’interface, ce qui est plus propre et plus efficace.

      Quant à utiliser un grand nombre pour ce serialVersionUID, je suis plus que sceptique …
      Personnellmement, je fais démarrer les versions de mes obejts à 1, et j’incrémente pour chaque changement incompatible, et ça marche forcément, puisque le serialVersionUID identife une version de ma classe, et non mon objet par rapport à tous les objets Java de l’univers.

    • Avatar
      moqui 29 janvier 2008 at 9 h 56 min

      Bon, en gros, il est fortement recommander de déclarer explicitement serialVersionUID. Bien, mais maintenant, quelle valeur lui affectée ? Une valeur calculée ? La valeur 1L à la définition de la classe, puis on incrémente au fur et à mesure des modifications de la classe ?

    • Avatar
      Robert Gaudreau 24 mars 2008 at 15 h 49 min

      Site interressant, instructif, clair

    • Avatar
      Nicolas Martignole 24 juin 2008 at 9 h 06 min

      Par expérience je ne déclare pas ce champ et je laisse le compilateur s’en charger. Attention à ne pas être trompé par le Warning dans Eclipse qui vous recommande de déclarer ce champ et qui propose 1L…

    Recent Posts

    • Podcast à (re)découvrir

      On me demande parfois comment faire sa veille technologique… je suis assez

      20 janvier, 2021
    • Lorsque le réseau social Parler cherche un nouvel endroit pour poser ses encombrantes valises…

      Nous allons parler architecture et plus exactement, du nombre de machines nécessaires

      19 janvier, 2021
    • Veille technologique janvier 2021

      Nouveau format d’article cette semaine : je vous propose de partager mes

      17 janvier, 2021

    Recent Tweets

    • RT  @welovedevs : On lui a demandé s'il pensait que les développeurs étaient considérés dans son entreprise. Voilà ce qu'il a répondu 😩 #Enqu…

      2 hours ago
    • Lunatech a aussi eu l'excellente idée de devenir partenaire avec le projet FP Tower, et cela permettra à tous les e… https://t.co/wMfT5MhviI

      3 hours ago
    • Le site https://t.co/r90vLjZTQt propose un cours d'introduction sur la prog fonctionnelle avec Scala. La qualité de… https://t.co/cwOyzNhL0N

      3 hours ago
    • RT  @JulienTruffaut : I am so happy of this partnership. Lunatech is a fantastic IT consultancy which invests a lot in the training of its em…

      4 hours ago
    •  @k33g_org   @fcamblor   @waxzce   @bluxte  On a un Channel slack pour le hackbreakfast que nous organisions avant les conf… https://t.co/JfTJ2BAMlP

      9 hours ago

    Mots clés

    agile (18) ajax (11) Apple (11) architecture (5) barcamp (5) BarCampJavaParis (5) ddd (5) devoxx (33) esb (6) exo (6) flex (9) geek (5) google (11) grails (5) groovy (10) humeur (12) humour (7) independant (6) iphone (12) Java (76) javascript (7) jazoon (28) jboss (22) jboss seam (12) jsf (9) jug (16) Linux (11) mac (6) mule (5) parisjug (7) paris jug (22) pjug (6) play (8) playframework (6) portlet (5) ria (8) Scala (19) scrum (44) spring (23) Startup (10) twitter (5) usi (21) usi2010 (9) web (16) xebia (7)

    Le Touilleur Express

    Contactez-moi : nicolas@touilleur-express.fr

    Suivez-moi sur Twitter : @nmartignole

    Copyright© 2008 - 2020 Nicolas Martignole | Tous droits réservés
    • A propos de l’auteur
    • A propos du Touilleur Express
    Le Touilleur Express