Mon site perso a changé d'adresse.
Vous allez être automatiquement redirigé
vers cette dernière dans quelques secondes ...

http://www.jmdoudoux.fr/

Merci de bien vouloir mettre à jour vos favoris.


Si rien ne se passe cliquez sur le lien ci-dessus.

  19. Les flux 21. L'interaction avec le réseau Imprimer Sommaire Consulter avec table des matières Développons en Java   v 0.85 béta  
Copyright (C) 1999-2005 Jean-Michel DOUDOUX  

 

20. La sérialisation

 

chapitre 2 0

 

La sérialisation est un procédé introduit dans le JDK version 1.1 qui permet de rendre un objet persistant. Cet objet est mis sous une forme sous laquelle il pourra être reconstitué à l'identique. Ainsi il pourra être stocké sur un disque dur ou transmis au travers d'un réseau pour le créer dans une autre JVM. C'est le procédé qui est utilisé par RMI. La sérialisation est aussi utilisée par les beans pour sauvegarder leurs états.

Au travers de ce mécanisme, Java fourni une façon facile, transparente et standard de réaliser cette opération : ceci permet de facilement mettre en place un mécanisme de persistance. Il est de ce fait inutile de créer un format particulier pour sauvegarder et relire un objet. Le format utilisé est indépendant du système d'exploitation. Ainsi, un objet sérialisé sur un système peut être réutilisé par un autre système pour récréer l'objet.

L'ajout d'un attribut à l'objet est automatiquement pris en compte lors de la sérialisation. Attention toutefois, la déserialisation de l'objet doit se faire avec la classe qui a été utilisée pour la sérialisation.

La sérialisation peut s'appliquer facilement à tous les objets.

Ce chapitre contient plusieurs sections :

 

20.1. Les classes et les interfaces de la sérialisation

La sérialisation utilise l'interface Serializable et les classes ObjectOutputStream et ObjectInputStream

 

20.1.1. L'interface Serializable

Cette interface ne définit aucune méthode mais permet simplement de marquer une classe comme pouvant être sérialisée.

Tout objet qui doit être sérialisé doit implémenter cette interface ou une de ses classes mères doit l'implémenter.

Si l'on tente de sérialiser un objet qui n'implémente pas l'interface Serializable, une exception NotSerializableException est levée.

Exemple ( code Java 1.1 ) : une classe serializable possédant trois attributs
public class Personne implements java.io.Serializable {
  private String nom = "";
  private String prenom = "";
  private int taille = 0;
  
  public Personne(String nom, String prenom, int taille) {
    this.nom = nom;
    this.taille = taille;
    this.prenom = prenom;
  }
  
  public String getNom() {
    return nom;
  }
  
  public void setNom(String nom) {
    this.nom = nom;
  }
  
  public int getTaille() {
    return taille;
  }
  
  public void setTaille(int taille) {
    this.taille = taille;
  }
  
  public String getPrenom() {
    return prenom;
  }
  
  public void setPrenom(String prenom) {
    this.prenom = prenom;
  }
}

 

20.1.2. La classe ObjectOuputStream

Cette classe permet de sérialiser un objet.

Exemple ( code Java 1.1 ) : sérialisation d'un objet et enregistrement sur le disque dur
import java.io.*;
   
public class SerializerPersonne {
   
  public static void main(String argv[]) {
    Personne personne = new Personne("Dupond","Jean",175);
    try {
      FileOutputStream fichier = new FileOutputStream("personne.ser");
      ObjectOutputStream oos = new ObjectOutputStream(fichier);
      oos.writeObject(personne);
      oos.flush();
      oos.close();
    }
    catch (java.io.IOException e) {
      e.printStackTrace();
    }
  }
}   

On définit un fichier avec la classe FileOutputStream. On instancie un objet de classe ObjectOutputStream en lui fournissant en paramètre le fichier : ainsi, le résultat de la sérialisation sera envoyé dans le fichier.

On appelle la méthode writeObject en lui passant en paramètre l'objet à sérialiser. On appelle la méthode flush() pour vider le tampon dans le fichier et la méthode close() pour terminer l'opération.

Lors de ces opérations une exception de type IOException peut être levée si un problème intervient avec le fichier.

Après l'exécution de cet exemple, un fichier nommé « personne.ser » est créé. On peut visualiser son contenu mais surtout pas le modifier car sinon il serait corrompu. En effet, les données contenues dans ce fichier ne sont pas toutes au format caractères.

La classe ObjectOutputStream contient aussi plusieurs méthodes qui permettent de sérialiser des types élémentaires et non des objets : writeInt, writeDouble, writeFloat ...

Il est possible dans un même flux d'écrire plusieurs objets les uns à la suite des autres. Ainsi plusieurs objets peuvent être sauvegardés. Dans ce cas, il faut faire attention de relire les objets dans leur ordre d'écriture.

 

20.1.3. La classe ObjectInputStream

Cette classe permet de déssérialiser un objet.

Exemple ( code Java 1.1 ) :
import java.io.*;

public class DeSerializerPersonne {

  public static void main(String argv[]) {
    try {
      FileInputStream fichier = new FileInputStream("personne.ser");
      ObjectInputStream ois = new ObjectInputStream(fichier);
      Personne personne = (Personne) ois.readObject();
      System.out.println("Personne : ");
      System.out.println("nom : "+personne.getNom());
      System.out.println("prenom : "+personne.getPrenom());
      System.out.println("taille : "+personne.getTaille());
    } 
    catch (java.io.IOException e) {
      e.printStackTrace();
    }
    catch (ClassNotFoundException e) {
      e.printStackTrace();
    }
   }
}

Résultat :
C:\dej>java DeSerializerPersonne
Personne :
nom : Dupond
prenom : Jean
taille : 175

On créer un objet de la classe FileInputStream qui représente le fichier contenant l'objet sérialisé. On créer un objet de type ObjectInputStream en lui passant le fichier en paramètre. Un appel à la méthode readObject() retourne l'objet avec un type Object. Un cast est nécessaire pour obtenir le type de l'objet. La méthode close() permet de terminer l'opération.

Si la classe a changée entre le moment ou elle a été sérialisée et le moment ou elle est déserialisée, une exception est levée :

Exemple : la classe Personne est modifiée et recompilée
C:\temp>java DeSerializerPersonne
java.io.InvalidClassException: Personne; Local class not compatible: stream class
desc serialVersionUID=-2739669178469387642 local class serialVersionUID=39870587
36962107851

atjava.io.ObjectStreamClass.validateLocalClass(ObjectStreamClass.java:4
38)
at java.io.ObjectStreamClass.setClass(ObjectStreamClass.java:482)
at java.io.ObjectInputStream.inputClassDescriptor(ObjectInputStream.java
:785)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:353)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:232)
at java.io.ObjectInputStream.inputObject(ObjectInputStream.java:978)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:369)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:232)
at DeSerializerPersonne.main(DeSerializerPersonne.java:9)

Une exception de type StreamCorruptedException peut être levée si le fichier a été corrompu par exemple en le modifiant avec un éditeur.

Exemple : les 2 premiers octets du fichier personne.ser ont été modifiés avec un éditeur hexa
C:\temp>java DeSerializerPersonne

java.io.StreamCorruptedException: InputStream does not containa serialized object
at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:731)
at java.io.ObjectInputStream.<init>(ObjectInputStream.java:165)
at DeSerializerPersonne.main(DeSerializerPersonne.java:8)

Une exception de type ClassNotFoundException peut être levée si l'objet est transtypé vers une classe qui n'existe plus ou pas au moment de l'exécution.

Exemple ( code Java 1.1 ) :
C:\temp>rename Personne.class Personne2.class
C:\temp>java DeSerializerPersonne

java.lang.ClassNotFoundException: Personne
at java.io.ObjectInputStream.inputObject(ObjectInputStream.java:981)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:369)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:232)
at DeSerializerPersonne.main(DeSerializerPersonne.java:9)

La classe ObjectInputStream possède de la même façon que la classe ObjectOutputStream des méthodes pour lire des données de type primitives : readInt(), readDouble(), readFloat ...

Lors de la deserialisation, le constructeur de l'objet n'est jamais utilisé.

 

20.2. Le mot clé transient

Le contenu des attributs est visible dans le flux dans lequel est sérialisé l'objet. Il est ainsi possible pour toute personne ayant accès au flux de voir le contenu de chaque attribut même si ceux si sont private. Ceci peut poser des problèmes de sécurité surtout si les données sont sensibles.

Java introduit le mot clé transient qui précise que l'attribut qu'il qualifie ne doit pas être inclus dans un processus de serailisation et donc de deserialisation.

Exemple ( code Java 1.1 ) :
...
private transient String codeSecret;
...

Lors de la deserialisation, les champs transient sont initialisés avec la valeur null. Ceci peut poser des problèmes à l'objet qui doit gérer cette état pour éviter d'avoir des exceptions de type NullPointerException.

 

20.3. La sérialisation personnalisée

Il est possible de personnaliser la serialisation d'un objet. Dans ce cas, la classe doit implémenter l'interface Externalizable qui hérite de l'interface Serializable.

 

20.3.1. L'interface Externalizable

Cette interface définit deux méthode : readExternal() et writeExternal().

Par défaut, la serialisation d'un objet qui implémente cette interface ne prend en compte aucun attribut de l'objet.

Remarque : le mot clé transient est donc inutile avec un classe qui implémente l'interface Externalisable

 

 

 

en construction
La suite de cette section sera développée dans une version future de ce document

 


  19. Les flux 21. L'interaction avec le réseau Imprimer Sommaire Consulter avec table des matières Développons en Java   v 0.85 béta  
Copyright (C) 1999-2005 Jean-Michel DOUDOUX