Sérialisation des objets en Java : des bases aux techniques avancées
\n\nLa sérialisation est le processus de conversion de l'état d'un objet en un flux d'octets pour le sauvegarder (dans un fichier, une base de données) ou le transmettre sur un réseau. Le processus inverse — la désérialisation — restaure l'objet à partir du flux d'octets. En Java, ce mécanisme est intégré à la plateforme et largement utilisé dans les systèmes distribués, la mise en cache et les applications web. Dans cet article, nous allons voir comment fonctionne la sérialisation, quelles interfaces et classes sont impliquées, et comment implémenter correctement une sérialisation personnalisée.
\n\nLes bases : l'interface Serializable
\n\nPour qu'un objet puisse être sérialisé, sa classe doit implémenter l'interface marqueur java.io.Serializable. Cette interface ne contient aucune méthode — elle indique simplement à la machine virtuelle que l'objet est autorisé à être sérialisé.
import java.io.Serializable;\n\npublic class User implements Serializable {\n private static final long serialVersionUID = 1L;\n private String name;\n private int age;\n\n public User(String name, int age) {\n this.name = name;\n this.age = age;\n }\n\n @Override\n public String toString() {\n return "User{name='" + name + "', age=" + age + "}";\n }\n}\n\nLe champ serialVersionUID est un identifiant unique de version de la classe. S'il n'est pas déclaré explicitement, la JVM le calculera automatiquement, mais cela peut entraîner une InvalidClassException lors de la désérialisation si la classe a changé. Il est recommandé de toujours le définir manuellement.
Processus de sérialisation et de désérialisation
\n\nPour écrire un objet dans un flux, on utilise ObjectOutputStream, pour le lire — ObjectInputStream. Voici un exemple de base :
import java.io.*;\n\npublic class SerializationDemo {\n public static void main(String[] args) {\n User user = new User("Alexei", 30);\n\n // Sérialisation\n try (FileOutputStream fos = new FileOutputStream("user.ser");\n ObjectOutputStream oos = new ObjectOutputStream(fos)) {\n oos.writeObject(user);\n System.out.println("Objet sérialisé : " + user);\n } catch (IOException e) {\n e.printStackTrace();\n }\n\n // Désérialisation\n try (FileInputStream fis = new FileInputStream("user.ser");\n ObjectInputStream ois = new ObjectInputStream(fis)) {\n User restoredUser = (User) ois.readObject();\n System.out.println("Objet restauré : " + restoredUser);\n } catch (IOException | ClassNotFoundException e) {\n e.printStackTrace();\n }\n }\n}\n\nLors de la désérialisation, le constructeur de l'objet n'est pas appelé — l'état est restauré directement à partir du flux. C'est une différence importante par rapport à la création normale d'un objet via new.
Le mot-clé transient
\n\nIl est parfois nécessaire d'exclure certains champs de la sérialisation, par exemple les mots de passe ou les calculs temporaires. Pour cela, on utilise le modificateur transient. Les champs marqués ainsi reçoivent des valeurs par défaut (0, null, false) lors de la sérialisation.
public class UserWithPassword implements Serializable {\n private static final long serialVersionUID = 1L;\n private String login;\n private transient String password; // ne sera pas sérialisé\n\n public UserWithPassword(String login, String password) {\n this.login = login;\n this.password = password;\n }\n\n @Override\n public String toString() {\n return "User{login='" + login + "', password='" + password + "'}";\n }\n}\n\nAprès la désérialisation, le champ password sera null. C'est pratique pour la sécurité, mais nécessite une logique de restauration supplémentaire.
Sérialisation personnalisée : les méthodes writeObject et readObject
\n\nSi le comportement standard ne convient pas, il est possible de redéfinir les méthodes privées
Aussi dans la bibliothèque
KeychainAccess Swift : guide complet avec exemples
migrate Go : guide complet avec exemples