La sérialisation

 

La sérialisation permet d'enregistrer ou de charger un objet dans un état précédent à partir d'un flux de données.

- La sérialisation permet donc la persistance des objets (la machine virtuelle peut être arrêtée, l'objet pourra être réinstancié plus tard come si rien n'était arrivé).

- Le flux de données pourra passer par un fichier ou réseau. L'objet pourra ainsi être exécuté sur une autre machine virtuelle java située n'importe où.

Image non trouvée !Seule contrainte, la recréation de l'objet doit se faire avec la même classe que celle de l'objet initialement enregistrée.

Image non trouvée !La sérialisation est évidemment déjà implémentée dans les classes Collections, String, ...

 

Interface Serializable

Pour être sérialisable, il faut et il suffit que la classe de votre objet (ou une de ses classes mères) ainsi que les classes objets qu'il mémorise (directement ou non) implémentent toutes l'interface java.io.Serializable.

public class maClasse implements java.io.Serializable {

[...]

}

Image non trouvée !Si une instance de classe n'implémente pas Serializable et que l'on tente malgré tout de la sérialiser, une exception NotSerializableException est levée.

Image non trouvée !Toutes les classes dérivées d’une classe sérialisable sont aussi sérialisables.

 

La sérialisation

La sérialisation (sauvegarde) d'un objet passe par un filtre sur flux de données de type binaire: ObjectOuputStream.

Image non trouvée !Cela signifie donc qu'il ne faut pas s'amuser à modifier ce type de fichier avec un bloc-notes par exemple sous peine d'avoir une exception StreamCorruptedException au chargement !

Il faudra:

- Instancier ObjectOutputStream en passant l'instance d'un flux binaire en paramètre. Exemple:

FileOutputStream fos = new FileOutputStream( "monFichierBin" );

ObjectOutputStream oos = new ObjectOutputStream(fos);

- Utiliser writeObject pour sauver l'instance de votre objet,

oos.writeObject(maClasse);

- Enfin faire un flush() pour vider les tampons et close() pour clôturer le flux.

oos.flush();

oos.close();

 

Le tout devant pouvoir gérer l'exception IOException

try {

FileOutputStream fos = new FileOutputStream( "monFichierBin" );
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(maClasse);
oos.flush();
oos.close();

}

catch (java.io.IOException e) {
e.printStackTrace();

}

 

Image non trouvée !Si vous tentez de sérialiser une objet n'implémentant Serializable, vous obtiendrez un exception java.io.NotSerializableException.

 

Désérialisation

 

La désérialisation (restaure) d'un objet passe par un filtre sur flux de données de type binaire: ObjectInputStream.

- Instancier ObjectInputStream en passant l'instance d'un flux binaire en paramètre. Exemple:

FileInputStream fis = new FileInputStream( "monFichierBin" );

ObjectInputStream ois = new ObjectInputStream(fis);

- Utiliser readObject() pour l'instancier votre objet, la méthode retournant cette instance.

MaClasse maClasse = (MaClasse) ois.readObject(); (un cast sera nécessaire !)

- Enfin close() pour clôturer le flux (mais le ramasse miette fera le travail aussi).

ois.close();

Là encore, des exceptions peuvent être levées:

try {

[...]

} catch (java.io.IOException e) {

e.printStackTrace();

}

catch (ClassNotFoundException e) {


e.printStackTrace();

}

D'autres exceptions peuvent se produire:

java.io.InvalidClassException: ... Local class not compatible. Cas où la classe a changé entre temps !

ou encore ClassNotFoundException si la classe n'existe pas sur la jvm.

 

Confidentialité des données

Le problème avec la sérialisation est que l'instance d'un objet est lisible via des éditeurs héxadécimaux par exemple. Il ne faudra donc pas y laisser des informations sensibles comme les mots de passe, ...

Pour éviter cela, le mot clef transient dans la construction d'un attribut va permettre de ne pas sauver cet attribut.

Exemple private transient String motDePasse;

 

Image non trouvée !Ces attributs seront positionnées à null lors de la désérialisation. Prévoir la possibilité de réinitialiser ces attributs ! Dans le cas d'un mot de passe en redemandant celui-ci par exemple.


Numéro de version

Vous pouvez obtenir un warning sur vos classes sérialisées:

serializable class Main has no definition of serialVersionUID

serialVersionUID est à définir de la manière suivante:

private static final long serialVersionUID = 99999L;

où 99999 est une valeur représentant le n° de version de votre classe (type Long). Donc vous pouvez le commencer à 1 puis l'incrémenter ensuite. Mais quand ?

Ce n° devra changer à chaque ajout ou suppression de champs à sérialiser dans la classe.

Ce n° n'est pas obligatoire. Dans ce cas, c'est le compilateur et non le développeur qui le génèrera. Mais il est fortement conseillé de gérer soit même le serialVersionUID de toutes classes sérialisables. Mais le risque étant d'oublier d'incrémenter la version lors d'une modification de la classe.

Image non trouvée ! C'est ce n° qui peut provoquer l'exception InvalidClassException. En effet, lors de la désérialisation, un teste est déclenché pour comparer le n° de version de la classe et celui de l'objet sérialisé. S'ils sont différents, l'exception est levée.

 

Exemple de code

Dans le code suivant, une instance va être sérialisée avec la valeur en cours. Puis cette valeur sera modifiée. Une désérialisation de cette instance est alors lancée et affichage de l'état des valeurs de l'ancienne instance et de celle qui a été modifiée.

MainClass.java

Et la classe sérialisable

 

Personnaliser la sérialisation

Première solution:

Ajouter deux méthodes dans la classe sérailisable:

private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {}

private void writeObject(ObjectOutptStream oos) throws IOException {}

 

A l'exécution, la machine Java regarde si ces méthodes existent dans la classe Serializable. Si c'est le cas, elle utilisera ces méthodes plutôt que les siennes.

Image non trouvée !Pas de fantaisie sur le nom des méthodes et le typage, sinon cela ne marchera pas ! La machine java explorant la classe à sérializer par introspection pour trouver ces méthodes.

 

il reste à utiliser ois ou oos pour charger ou sauver les données que vous désirez (transient n'a alors plus grand intérêt ici !) avec les méthodes correspondantes au typage des données.

 

Seconde solution:

A venir...

 

Note

Pour les développeurs sous Android, suivant les cas (par exemple rotation de l'écran), il peut être nécessaire de sauver les instances de vos objets. il existe une solution plus simple: onSaveInstance