Java >> Tutoriel Java >  >> Java

Apprenez l'ABC de la sérialisation Java avec des exemples

Dans cet article, nous allons explorer un concept Java important fréquemment demandé lors des entretiens de Job, à savoir la sérialisation Java. Nous parlerons exclusivement de la sérialisation et expliquerons ses fonctionnalités de base à avancées à l'aide d'exemples de code et d'un exemple de projet. En plus de connaître la sérialisation en Java, il est encore plus important d'apprendre les différentes façons d'implémenter la sérialisation. Nous les avons également abordés dans cet article. Vous avez probablement été confronté à des questions telles que la sérialisation nécessite-t-elle une gestion des versions ou rencontre-t-elle des problèmes de performances ? Alors, lisez cet article jusqu'à la fin, nous avons couvert toutes ces questions. Tout d'abord, passez en revue cette définition générique de la sérialisation.

"C'est un moyen de convertir les structures de données ou l'état de l'objet dans un format qui peut être conservé et récupéré au fur et à mesure des besoins. Le processus de reformation de l'objet à partir de l'état décomposé est appelé désérialisation. Aujourd'hui, la plupart des langages orientés objet (y compris Java) fournissent un support natif pour la sérialisation et la désérialisation."

Vous pouvez maintenant voir dans la section suivante la liste des questions et réponses les plus élémentaires sur la sérialisation Java. Cela augmentera sûrement vos connaissances sur le sujet de la sérialisation Java.

1- Principes de base de la sérialisation Java.

1.1- Qu'est-ce que le concept de sérialisation Java ?

Réponse : La sérialisation Java est le processus de transformation d'un objet en un flux d'octets ou un tableau d'octets. Le tableau d'octets représente la classe de l'objet, la version de l'objet et l'état interne de l'objet.

1.2- Qu'est-ce que la désérialisation en Java ?

Réponse : La désérialisation est le processus de reconstruction du flux d'octets dans un objet Java actif qui doit être utilisable.

1.3- À quoi sert la sérialisation Java ?

Réponse : Vous pouvez utiliser la sérialisation Java pour effectuer les tâches suivantes.

1.3.1- Stockage.

Plutôt que de conserver un objet volumineux en mémoire, il est préférable de le mettre en cache dans un fichier local via la sérialisation. Pour votre note, si vous essayez d'enregistrer un objet non sérialisable, la JVM échouera l'opération avec  .

1.3.2- Transmission de données.

Java permet de sérialiser un objet sur un réseau en utilisant RMI (Remote Method Invocation), une technologie distribuée de Java. RMI permet à un objet client Java de communiquer avec l'instance d'un serveur Java hébergé sur un système distant. Par exemple, un centre ATM de votre localité peut interagir avec un serveur bancaire situé dans un autre pays.

1.3.3- Persistance.

Si vous souhaitez conserver l'état d'une opération particulière dans une base de données, sérialisez-la simplement dans un tableau d'octets et enregistrez-la dans la base de données pour une utilisation ultérieure.

1.3.4- Clonage en profondeur.

En Java, il est également connu sous le nom de copie profonde. Il provoque la copie d'un objet avec les objets auxquels il se réfère. Vous devez écrire une classe de clone personnalisée pour y parvenir. La sérialisation Java peut vous éviter d'avoir à ajouter une classe clone. La sérialisation de l'objet dans un tableau d'octets, puis sa désérialisation en un autre objet rempliront l'objectif.

1.3.5- Communication entre JVM.

La sérialisation fonctionne de la même manière sur différentes JVM, quelles que soient les architectures sur lesquelles elles s'exécutent.

2- Comment implémenter la sérialisation Java ?

Java fournit une solution prête à l'emploi pour sérialiser un objet. Il décrit un ensemble d'interfaces génériques. Votre classe doit en mettre en œuvre un seul pour activer la sérialisation. Dans la section suivante, nous explorerons ces interfaces intégrées et certains des exemples prêts à l'emploi. Nous aborderons principalement les concepts de sérialisation Java suivants.

  • Mettre en œuvre la sérialisation à l'aide de l'interface sérialisable Java .
  • Mettre en œuvre la sérialisation avec héritage .
  • Comment effectuer la refactorisation du code pour les classes sérialisables.
  • Comment utiliser l'interface Java externalisable.
    • Lien de téléchargement pour un exemple de code sérialisable.

2.1- Mettre en œuvre la sérialisation Java à l'aide de l'interface sérialisable.

C'est le moyen le plus simple d'activer la sérialisation Java. Si vous souhaitez que votre classe soit sérialisée, implémentez simplement l'interface Java Serializable. Il s'agit d'une interface de marqueur qui ne fournit aucune méthode ou champ à implémenter.

Q-1 Qu'est-ce qu'une interface de marqueur en Java ?

Réponse : En Java, le marqueur Interface occupe une place particulière car il ne contient aucune méthode déclarée et la classe qui l'implémente n'a besoin de surcharger aucune méthode. Une interface de marqueur ordonne à la JVM de traiter l'objet adapté à une tâche spéciale. Par exemple, l'implémentation de l'interface Serializable permet à la JVM d'autoriser son objet à écrire dans un fichier.

C'était un bref résumé de Java Serializable Interface. Nous allons maintenant voir comment implémenter l'interface Serializable dans une classe Java et appliquer la sérialisation.

2.1.1- Exemple de sérialisation Java.

Nous utiliserons un exemple de base à des fins de démonstration. Pour l'exemple, vous allez créer les fichiers de classe suivants, comme indiqué ci-dessous.

1- .
2- .
3- .

2.1.1.1- SerializationDef.java.

Ce fichier définit la classe Java que nous utiliserons pour la sérialisation. Cette classe représente un bean Java simple portant des propriétés et des méthodes getter/setter. Par défaut, toutes les propriétés sont sérialisées. Mais vous pouvez modifier ce comportement. Voyons comment ?

Q-1 À quoi sert le mot-clé transitoire ?

Réponse : Le  est un mot clé en Java. Il marque un champ à exclure de la sérialisation. Vous pouvez utiliser ce mot-clé pour une variable dont vous ne voulez pas qu'elle fasse partie de l'état persistant d'un objet.

Q-2 Un membre statique de la classe serait-il sérialisé ?

Réponse : Non. Un membre statique est associé à la classe, pas à l'objet de la classe. Il obtient de la mémoire une fois lors du chargement de la classe. Et est stocké dans  section du tas.

Nous expliquerons les concepts ci-dessus à l'aide d'un exemple de projet. Veuillez suivre les parties de l'exemple de projet ci-dessous.

Si vous voyez le fichier de classe ci-dessous, nous avons marqué le variable comme transitoire. Il sera donc exclu de la sérialisation.

package com.techbeamers.serialization;

import java.io.Serializable;

public class SerializationDef implements Serializable {

	private	String Product;
	private	String Feature;
	transient private int FeatureCount;
	
    @Override
    public String toString(){
        return "Summary[Product("+Product+"),Feature("+Feature+"),FeatureCount("+FeatureCount+")]";
    }
	
	public String getProduct() {
		return Product;
	}

	public void setProduct(String product) {
		this.Product = product;
	}

	public String getFeature() {
		return Feature;
	}

	public void setFeature(String feature) {
		this.Feature = feature;
	}

	public int getFeatureCount() {
		return FeatureCount;
	}

	public void setFeatureCount(int count) {
		this.FeatureCount = count;
	}
}
2.1.1.2- SerializationLib.java.
Pour la sérialisation, vous devrez copier l'objet dans un fichier. Vous pouvez alors chercher à le désérialiser à partir du même fichier. Pour tout cela, vous aurez besoin de fonctions d'assistance, vous pouvez les trouver dans l'extrait de code ci-dessous. Vous remarquerez dans le code ci-dessous que nous utilisons le <ObjectOutputStream> et <ObjectInputStream> classes pour la sérialisation. Leurs méthodes utilisent <Object> variable de classe comme argument qui est la classe parent de toutes les classes en Java.
package com.techbeamers.serialization;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class SerializationLib {

    // Do serialize the Java object and save it to a file
    public static void doSerialize(Object obj, String outputFile)
            throws IOException {
        FileOutputStream fileTowrite = new FileOutputStream(outputFile);
        ObjectOutputStream objTowrite = new ObjectOutputStream(fileTowrite);
        objTowrite.writeObject(obj);
 
        fileTowrite.close();
    }

    // Do deserialize the Java object from a given file
    public static Object doDeserialize(String inputFile) throws IOException,
            ClassNotFoundException {
        FileInputStream fileToread = new FileInputStream(inputFile);
        ObjectInputStream objToread = new ObjectInputStream(fileToread);
        Object obj = objToread.readObject();
        objToread.close();
        return obj;
    }
 }
2.1.1.3- SerializationDemo.java.
Jusqu'à présent, nous avons défini la structure de base de l'échantillon de sérialisation. Créons maintenant le fichier principal pour illustrer le processus de sérialisation.
package com.techbeamers.serialization;

import java.io.IOException;

public class SerializationDemo {

	public static void main(String[] args) {

        String outputFile="serializationdemo.txt";
        SerializationDef def = new SerializationDef();
        def.setProduct("testProduct");
        def.setFeature("testFeature");
        def.setFeatureCount(10);
         
        // Serialize the object into a file.
        try {
            SerializationLib.doSerialize(def, outputFile);
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
         
        // Deserialize from a file into an object.
        SerializationDef defNext = null;
        try {
        	defNext = (SerializationDef) SerializationLib.doDeserialize(outputFile);
        } catch (ClassNotFoundException | IOException e) {
            e.printStackTrace();
        }
         
        System.out.println("def():\n --"+"\n  |\n  "+def);
        System.out.println(System.lineSeparator());
        System.out.println("defNext():\n --"+"\n  |\n  "+defNext);
	}

}

Maintenant, nous avons tous terminé. Et il est temps de créer un projet Java dans Eclipse. Ajoutez tous les fichiers ci-dessus dans le projet. Puisque nous avons entièrement vérifié le code, il ne devrait pas y avoir d'erreur de compilation. Si vous voyez toujours une erreur, veuillez vérifier que le chemin JDK est correctement défini dans les préférences Eclipse>>Java>>JRE installés .

Enfin, lorsque vous exécutez le projet, il donnera la sortie suivante. À partir du résultat, vous pouvez vérifier la valeur de n'a pas été enregistré car nous l'avons déclaré en tant que variable transitoire.

def():
 --
  |
  Summary[Product(testProduct),Feature(testFeature),FeatureCount(10)]

defNext():
 --
  |
  Summary[Product(testProduct),Feature(testFeature),FeatureCount(0)]

2.2- Sérialisation Java avancée avec héritage.

Deux cas se présentent lorsque nous utilisons la sérialisation avec héritage.

1- Lorsque la classe parent implémente l'interface Serializable, la classe enfant le fait automatiquement.
2- Si la classe parent n'implémente pas l'interface sérialisable, son état ne se transformera pas en flux d'octets lors de la sérialisation de l'instance de la classe enfant.

Pour gérer le 2ème cas, vous devez implémenter les deux méthodes suivantes dans la classe Child.

1- <readObject()> .
2- <writeObject()> .

Ces méthodes aideront à transformer l'état de la classe parente en flux, à le sérialiser et enfin à le récupérer. Voyons tout cela en action.

2.2.1- Exemple de sérialisation Java avec héritage.

Nous allons démontrer ce concept à travers l'exemple de projet ci-dessous. La première partie du projet est la fichier qui n'implémente pas l'interface sérialisable.

2.2.1.1- Fichier ParentClass.java.
package com.techbeamers.serialization.inheritancedemo;

public class ParentClass {

	private String Product;
	private int ProductId;

	public String getProduct() {
		return Product;
	}

	public void setProduct(String product) {
		this.Product = product;
	}

	public int getProductId() {
		return ProductId;
	}

	public void setProductId(int Id) {
		this.ProductId = Id;
	}
}

2.2.1.2- Fichier ChildClass.java.

Vient ensuite le fichier qui définit la méthode d'objet en lecture/écriture pour préparer le flux de l'état de la classe parent. Pour votre information, l'ordre de lecture/écriture des données dans le flux d'octets resterait le même. Un autre point important que vous remarquerez est que la classe enfant implémente interface. Cela permettra de remplacer certaines méthodes où vous pouvez ajouter une logique métier pour assurer l'intégrité des données.

package com.techbeamers.serialization.inheritancedemo;

import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectInputValidation;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class ChildClass extends ParentClass implements Serializable,
		ObjectInputValidation {

	private String Brand;

	public String getBrand() {
		return Brand;
	}

	public void setBrand(String brand) {
		this.Brand = brand;
	}

	@Override
	public String toString() {
		return "Summary[ ProductId=" + getProductId() + ", Product=" + getProduct()
				+ ", Brand=" + getBrand() + " ]";
	}

	// adding helper method for serialization to save/initialize parent class
	// state
	private void readObject(ObjectInputStream ois)
			throws ClassNotFoundException, IOException {
		ois.defaultReadObject();

		// notice the order of read and write should be same
		setProductId(ois.readInt());
		setProduct((String) ois.readObject());

	}

	private void writeObject(ObjectOutputStream oos) throws IOException {
		oos.defaultWriteObject();

		oos.writeInt(getProductId());
		oos.writeObject(getProduct());
	}

	@Override
	public void validateObject() throws InvalidObjectException {
		// validate the object here
		if (Brand == null || "".equals(Brand))
			throw new InvalidObjectException("Brand is not set or empty.");
		if (getProductId() <= 0)
			throw new InvalidObjectException("ProductId is not set or zero.");
	}
}

2.2.1.3- Fichier InheritanceDemo.java.

Créons maintenant un fichier de classe de démonstration pour sérialiser/désérialiser les données de la classe enfant, y compris le parent. Et voyez si nous pouvons récupérer l'état de la classe parent à partir du formulaire sérialisé.

package com.techbeamers.serialization.inheritancedemo;

import java.io.IOException;

import com.techbeamers.serialization.SerializationLib;

public class InheritanceDemo {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		String fileName = "childclass.txt";

		ChildClass childClass = new ChildClass();
		childClass.setProductId(21);
		childClass.setProduct("Blog");
		childClass.setBrand("TechBeamers");

		try {
			SerializationLib.doSerialize(childClass, fileName);
		} catch (IOException e) {
			e.printStackTrace();
			return;
		}

		try {
			ChildClass newChild = (ChildClass) SerializationLib
					.doDeserialize(fileName);
			System.out.println("ChildClass output:  \n  |\n   --" + newChild);
		} catch (ClassNotFoundException | IOException e) {
			e.printStackTrace();
		}
	}

}

Lorsque vous exécutez le code ci-dessus, vous obtenez le résultat suivant.

ChildClass output:  
  |
   --Summary[ ProductId=21, Product=Blog, Brand=TechBeamers ]

2.3- Apprenez à gérer la refactorisation de classe avec la sérialisation Java.

La sérialisation Java permet de refactoriser la classe sous-jacente. Voici la liste des modifications autorisées dans une classe et qui ne perturberont pas le processus de désérialisation.

  • Vous pouvez ajouter de nouveaux membres au cours.
  • Le passage d'une variable de transitoire à non transitoire est autorisé. Mais la sérialisation considérera ces variables comme nouvelles.
  • Faites passer une variable de statique à non statique. La sérialisation la comptera comme une nouvelle variable.

Cependant, Java impose une condition pour que tous ces changements fonctionnent. Vous pouvez le remplir en ajoutant un identifiant unique, le  dans la classe pour suivre les modifications sous une balise commune. Par défaut, la sérialisation calculera automatiquement le  en parcourant tous les domaines et méthodes. C'est pourquoi si vous essayez de modifier une variable de classe sans spécifier manuellement l'identifiant de version, la JVM lèvera le  car il détecte une modification de la valeur de l'identifiant.

2.3.1- Comment générer un  ?

Réponse :

Il existe trois façons de produire un valeur.

2.3.1.1- Utilisez la commande .

JDK regroupe un petit utilitaire de ligne de commande appelé  . Il vous suffit de passer le nom de la classe sérialisable en paramètre de commande pour obtenir son identifiant de version.

C:\Working\SerializationDemo>serialver com.techbeamers.serialization.SerializationDef
SerializationDemo:    static final long serialVersionUID = -2456709228636810878L;

Avant d'exécuter la commande ci-dessus, assurez-vous d'avoir défini le chemin d'accès au dossier bin du JDK qui contient l'outil de ligne de commande ci-dessus.

2.3.1.2- Utiliser l'IDE Eclipse.

Dans l'IDE Eclipse, survolez la classe de sérialisation. Il ouvrira un menu contextuel et affichera trois options. Choisissez l'une des deux premières options comme indiqué ci-dessous.

  • Ajouter un ID de version de série par défaut, ou
  • Ajouter l'ID de version de série généré.

2.3.1.3- Attribuez la valeur de votre choix.

Choisissez simplement un numéro que vous aimez et définissez-le comme ID de version série. Mais postfixez le numéro avec un "L".

private static final long serialVersionUID = 21L;

2.4- Apprenez à utiliser l'interface Java externalisable pour la sérialisation.

La méthode de sérialisation par défaut n'est pas sécurisée et présente des problèmes de performances. Vous pouvez voir la liste ci-dessous pour vérifier les problèmes de performances avec la sérialisation par défaut.

1- La sérialisation dépend du mécanisme de récursivité. Lorsque la sérialisation d'un objet de classe enfant commence, elle déclenche la sérialisation d'autres variables d'instance à partir de la chaîne de classes parentes qui se poursuit jusqu'à ce qu'elle atteigne la classe Object de ces variables. Cela entraîne de nombreux frais généraux.
2- Lors de la sérialisation d'une information de description de classe d'objets est attachée au flux. De nombreuses données et métadonnées réduisent les performances.
3- La sérialisation nécessite également un ID de version de série pour suivre les modifications au niveau de la classe. Si vous ne le définissez pas manuellement, la sérialisation le calcule automatiquement en parcourant tous les champs et méthodes. Plus la taille de la classe sera longue, plus il sera temps de calculer la valeur. Il s'agit donc à nouveau d'un problème de performances potentiel.
4- Nous pouvons résoudre tous les problèmes ci-dessus avec l'interface d'externalisation.

2.4.1- Qu'est-ce que l'interface externalisable et en quoi est-elle meilleure que l'interface sérialisable ?

Réponse : L'externalisation est une extension de l'interface sérialisable. Si vous souhaitez externaliser un objet, votre classe doit implémenter l'interface Externalizable et un constructeur public par défaut. Dans le processus d'externalisation, seule l'identité de la classe est ajoutée au flux de sérialisation. Et la classe est responsable du stockage et de la récupération des informations d'instance. Il donne un contrôle total sur ce qu'il faut ajouter et ce qu'il faut laisser pendant la sérialisation. La sérialisation de base n'offre pas la même flexibilité.

Pour votre information, Externalizable n'est pas une interface de marqueur mais expose deux méthodes - writeExternal et readExternal. La classe cible implémente ces méthodes pour prendre le contrôle total sur le format et le contenu du flux relatif à l'objet et à ses supertypes. Ces méthodes doivent avoir la capacité de se coordonner avec les supertypes de l'objet pour sauvegarder son état. Ils remplacent la mise en œuvre sur mesure de et méthodes.

Il est maintenant temps de créer un exemple de projet Eclipse pour démontrer l'utilisation de l'interface externalisable. Dans cet exemple de projet, nous ajouterons les deux fichiers de classe Java suivants.

  • UIMap.java , ce fichier définit une classe qui implémente l'interface Externalizable et fournit le et méthodes.
  • UIMapDemo.java , ce fichier créera l'objet UIMap et testera le processus de sérialisation Java via l'interface externalisable.

2.4.2- Exemple externalisable Java.

Nous intégrons le code source de ces fichiers afin que vous puissiez l'ajouter instantanément à votre projet Java local. Le premier extrait de code concerne fichier de classe, jetez un oeil au code ci-dessous.

2.4.2.1- Fichier de classe UIMap.java.
package com.techbeamers.externalization;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

public class UIMap implements Externalizable {

	private int id;
	private String locator;
	private String value;

	@Override
	public void writeExternal(ObjectOutput out) throws IOException {
		out.writeInt(id);
		out.writeObject(locator + "$$");
		out.writeObject("##" + value);
	}

	@Override
	public void readExternal(ObjectInput in) throws IOException,
			ClassNotFoundException {
		id = in.readInt();
		// Retrieve data in the same sequence as written
		locator = (String) in.readObject();
		if (!locator.endsWith("$$"))
			throw new IOException("data integrity failed.");
		locator = locator.substring(0, locator.length() - 2);
		value = (String) in.readObject();
		if (!value.startsWith("##"))
			throw new IOException("data integrity failed.");
		value = value.substring(2);
	}

	@Override
	public String toString() {
		return "UIMap[ id=" + id + ",locator=" + locator + ",value=" + value + " ]";
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getLocator() {
		return locator;
	}

	public void setLocator(String locator) {
		this.locator = locator;
	}

	public String getValue() {
		return value;
	}

	public void setValue(String value) {
		this.value = value;
	}
}

2.4.2.2- Fichier de classe UIMapDemo.java.

Maintenant, consultez le fichier pour tester la fonctionnalité de la sérialisation Java à l'aide de l'interface externalisable.

package com.techbeamers.externalization;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class UIMapDemo {

	public static void main(String[] args) {

		String fileName = "uimap.properties";
		UIMap uimap = new UIMap();
		uimap.setId(2);
		uimap.setLocator("cssSelector");
		uimap.setValue("input[id=email]");

		try {
			FileOutputStream fos = new FileOutputStream(fileName);
			ObjectOutputStream oos = new ObjectOutputStream(fos);
			oos.writeObject(uimap);
			oos.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		FileInputStream fis;
		try {
			fis = new FileInputStream(fileName);
			ObjectInputStream ois = new ObjectInputStream(fis);
			UIMap p = (UIMap) ois.readObject();
			ois.close();
			System.out.println("UIMap Object Summary:\n  |\n   -- " + p);
		} catch (IOException | ClassNotFoundException e) {
			e.printStackTrace();
		}

	}

}

Lorsque nous exécutons l'exemple de projet Java ci-dessus, il génère la sortie suivante.

UIMap Object Summary:
  |
   -- UIMap[ id=2,locator=cssSelector,value=input[id=email] ]

2.4.3- Télécharger des exemples de projets.

Il est maintenant temps de télécharger l'exemple de projet afin de pouvoir jouer facilement avec les exemples de sérialisation Java. Veuillez utiliser le lien ci-dessous pour commencer votre téléchargement.

Téléchargez des exemples de code de sérialisation.

⇓ Téléchargez le projet de sérialisation.

Nous avons récemment publié un quiz intéressant sur le concept de sérialisation Java. Et nous vous recommandons fortement d'essayer ce questionnaire. Il testera vos connaissances et vous aidera à établir votre emprise sur le sujet.

Lecture suggérée :

1- Test en ligne Java - 20 questions sur la sérialisation.

Réflexion finale.

Nous souhaitons que le didacticiel Java de A à Z sur la sérialisation de différentes manières vous ait aidé à franchir une étape supplémentaire sur l'échelle d'apprentissage de Java. Nous voulions couvrir l'ensemble du sujet de la sérialisation Java et les petites choses qui l'entourent dans un seul article. Notre objectif était de nous assurer que le message vous fournirait presque tous les détails sur la sérialisation en Java. Vous pouvez obtenir des détails plus spécifiques sur ce sujet sur le site Web d'Oracle.

Si vous avez aimé la façon dont nous avons couvert chaque petit détail du sujet, veuillez utiliser la zone de commentaires et partager vos commentaires. Veuillez également transférer ce message à votre cercle d'amis et sur les plateformes de médias sociaux que vous utilisez.

Tout le meilleur,

TechBeamers.


Balise Java