Quelle est la différence entre Serializable
et Externalizable
en Java?
Pour ajouter aux autres réponses, en implémentant Java.io.Serializable
, vous obtenez une fonctionnalité de sérialisation "automatique" pour les objets de votre classe. Pas besoin d'implémenter une autre logique, ça va marcher. Le moteur d’exécution Java utilisera la réflexion pour déterminer comment marshaler et décomparer vos objets.
Dans les versions précédentes de Java, la réflexion était très lente et la sérialisation des graphes d'objets volumineux (par exemple dans les applications RMI client-serveur) était un peu un problème de performances. Pour gérer cette situation, l'interface Java.io.Externalizable
a été fournie, ce qui ressemble à Java.io.Serializable
mais avec des mécanismes personnalisés permettant d'exécuter les fonctions de marshalling et unmarshalling (vous devez implémenter les méthodes readExternal
et writeExternal
sur votre classe). Cela vous donne les moyens de contourner le goulot d'étranglement lié aux performances de réflexion.
Dans les versions récentes de Java (à partir de la version 1.3, bien sûr), les performances de la réflexion sont considérablement meilleures qu’elles ne l’étaient, et le problème est donc beaucoup moins grave. Je suppose que vous auriez du mal à tirer un avantage significatif de Externalizable
avec une machine virtuelle moderne.
De plus, le mécanisme de sérialisation Java intégré n'est pas le seul, vous pouvez obtenir des remplacements tiers, tels que la sérialisation JBoss, qui est beaucoup plus rapide et qui remplace sans problème la valeur par défaut.
Un inconvénient majeur de Externalizable
est que vous devez conserver vous-même cette logique: si vous ajoutez, supprimez ou modifiez un champ de votre classe, vous devez modifier vos méthodes writeExternal
/readExternal
pour en rendre compte.
En résumé, Externalizable
est une relique des jours Java 1.1. Il n'y en a vraiment plus besoin.
La sérialisation fournit une fonctionnalité par défaut pour stocker puis recréer l'objet. Il utilise un format détaillé pour définir le graphe entier des objets à stocker, par exemple. supposons que vous ayez une liste liée et que vous codiez comme ci-dessous, alors la sérialisation par défaut détectera tous les objets liés et sera sérialisée. Dans la sérialisation par défaut, l'objet est entièrement construit à partir de ses bits stockés, sans appel de constructeur.
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("/Users/Desktop/files/temp.txt"));
oos.writeObject(linkedListHead); //writing head of linked list
oos.close();
Mais si vous voulez une sérialisation restreinte ou si vous ne voulez pas qu'une partie de votre objet soit sérialisée, utilisez Externalizable. L’interface Externalizable étend l’interface Serializable et ajoute deux méthodes, writeExternal () et readExternal (). Ceux-ci sont automatiquement appelés lors de la sérialisation ou de la désérialisation. Lorsque nous travaillons avec Externalizable, nous devons nous rappeler que le constructeur par défaut doit être public, sinon le code lèvera une exception. Veuillez suivre le code ci-dessous:
public class MyExternalizable implements Externalizable
{
private String userName;
private String passWord;
private Integer roll;
public MyExternalizable()
{
}
public MyExternalizable(String userName, String passWord, Integer roll)
{
this.userName = userName;
this.passWord = passWord;
this.roll = roll;
}
@Override
public void writeExternal(ObjectOutput oo) throws IOException
{
oo.writeObject(userName);
oo.writeObject(roll);
}
@Override
public void readExternal(ObjectInput oi) throws IOException, ClassNotFoundException
{
userName = (String)oi.readObject();
roll = (Integer)oi.readObject();
}
public String toString()
{
StringBuilder b = new StringBuilder();
b.append("userName: ");
b.append(userName);
b.append(" passWord: ");
b.append(passWord);
b.append(" roll: ");
b.append(roll);
return b.toString();
}
public static void main(String[] args)
{
try
{
MyExternalizable m = new MyExternalizable("nikki", "student001", 20);
System.out.println(m.toString());
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("/Users/Desktop/files/temp1.txt"));
oos.writeObject(m);
oos.close();
System.out.println("***********************************************************************");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("/Users/Desktop/files/temp1.txt"));
MyExternalizable mm = (MyExternalizable)ois.readObject();
mm.toString();
System.out.println(mm.toString());
}
catch (ClassNotFoundException ex)
{
Logger.getLogger(MyExternalizable.class.getName()).log(Level.SEVERE, null, ex);
}
catch(IOException ex)
{
Logger.getLogger(MyExternalizable.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
Ici, si vous commentez le constructeur par défaut, le code lèvera une exception ci-dessous:
Java.io.InvalidClassException: javaserialization.MyExternalizable;
javaserialization.MyExternalizable; no valid constructor.
Nous pouvons observer que le mot de passe est une information sensible, je ne le sérialise donc pas dans la méthode writeExternal (ObjectOutput oo) et je ne mets pas la valeur de same dans readExternal (ObjectInput oi). C'est la flexibilité fournie par Externalizable.
La sortie du code ci-dessus est comme ci-dessous:
userName: nikki passWord: student001 roll: 20
***********************************************************************
userName: nikki passWord: null roll: 20
Nous pouvons observer que nous ne définissons pas la valeur de passWord, elle est donc nulle.
La même chose peut également être obtenue en déclarant le champ mot de passe comme transitoire.
private transient String passWord;
J'espère que ça aide. Je m'excuse si j'ai commis des erreurs. Merci.
Par souci d’exhaustivité, le mot clé transient
corrige également l’écart entre les deux.
Si vous souhaitez uniquement sérialiser une partie de votre objet, définissez simplement des champs spécifiques comme étant transient
, en les marquant comme ne devant pas être persistés, et implémentez Serializable
.
Principales différences entre Serializable
et Externalizable
Serializable
est une interface de marqueur sans aucune méthode. L'interface Externalizable
contient deux méthodes: writeExternal()
et readExternal()
.Serializable
. Le processus de sérialisation défini par le programmeur sera activé pour les classes implémentant l'interface Externalizable
.Externalizable
. Vous pouvez supporter différentes versions de votre objet. Si vous implémentez Externalizable
, il vous incombe de sérialiser la classe super
.Serializable
utilise la réflexion pour construire un objet et ne nécessite aucun constructeur arg. Mais Externalizable
exige le constructeur public sans argument.Reportez-vous à blog by Hitesh Garg
pour plus de détails.
La sérialisation utilise certains comportements par défaut pour stocker puis recréer l'objet. Vous pouvez spécifier dans quel ordre ou comment gérer les références et les structures de données complexes, mais cela revient finalement à utiliser le comportement par défaut pour chaque champ de données primitif.
L'externalisation est utilisée dans les rares cas où vous souhaitez réellement stocker et reconstruire votre objet d'une manière complètement différente et sans utiliser les mécanismes de sérialisation par défaut pour les champs de données. Par exemple, imaginez que vous disposiez de votre propre schéma de codage et de compression unique.
La sérialisation d'objet utilise les interfaces Serializable et Externalizable. Un objet Java est uniquement sérialisable. si une classe ou l'une de ses superclasses implémente l'interface Java.io.Serializable ou sa sous-interface, Java.io.Externalizable. _ {La plupart des classes Java sont sérialisables}.
NotSerializableException
: packageName.ClassName
«Pour faire participer un objet de classe au processus de sérialisation, la classe doit implémenter une interface Serializable ou Externalizable.La sérialisation des objets génère un flux contenant des informations sur les classes Java pour les objets en cours de sauvegarde. Pour les objets sérialisables, des informations suffisantes sont conservées pour restaurer ces objets même si une version différente (mais compatible) de l'implémentation de la classe est présente. L’interface Serializable est définie pour identifier les classes qui implémentent le protocole sérialisable:
package Java.io;
public interface Serializable {};
InvalidClassException
«Dans le processus de désérialisation, si la classe locale serialVersionUID est différente de la classe de l'expéditeur correspondante. alors le résultat est en conflit en tant que Java.io.InvalidClassException: com.github.objects.User; local class incompatible: stream classdesc serialVersionUID = 5081877, local class serialVersionUID = 50818771
Pour les objets Externalizable, seule l'identité de la classe de l'objet est enregistrée par le conteneur; la classe doit sauvegarder et restaurer le contenu. L’interface Externalizable est définie comme suit:
package Java.io;
public interface Externalizable extends Serializable
{
public void writeExternal(ObjectOutput out)
throws IOException;
public void readExternal(ObjectInput in)
throws IOException, Java.lang.ClassNotFoundException;
}
OptionalDataException
«Les champs DOIVENT ÊTRE DANS LE MÊME ORDRE ET LE TYPE} tels que nous les avons écrits. S'il existe une incompatibilité de type dans le flux, elle génère une exception OptionalDataException.
@Override public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt( id );
out.writeUTF( role );
out.writeObject(address);
}
@Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.id = in.readInt();
this.address = (Address) in.readObject();
this.role = in.readUTF();
}
Les champs d'instance de la classe qui ont écrit (expose) à ObjectOutput
sont sérialisés.
Exemple « implémente Serializable
class Role {
String role;
}
class User extends Role implements Serializable {
private static final long serialVersionUID = 5081877L;
Integer id;
Address address;
public User() {
System.out.println("Default Constructor get executed.");
}
public User( String role ) {
this.role = role;
System.out.println("Parametarised Constructor.");
}
}
class Address implements Serializable {
private static final long serialVersionUID = 5081877L;
String country;
}
Exemple « implémente Externalizable
class User extends Role implements Externalizable {
Integer id;
Address address;
// mandatory public no-arg constructor
public User() {
System.out.println("Default Constructor get executed.");
}
public User( String role ) {
this.role = role;
System.out.println("Parametarised Constructor.");
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt( id );
out.writeUTF( role );
out.writeObject(address);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.id = in.readInt();
this.address = (Address) in.readObject();
this.role = in.readUTF();
}
}
Exemple
public class CustomClass_Serialization {
static String serFilename = "D:/serializable_CustomClass.ser";
public static void main(String[] args) throws IOException {
Address add = new Address();
add.country = "IND";
User obj = new User("SE");
obj.id = 7;
obj.address = add;
// Serialization
objects_serialize(obj, serFilename);
objects_deserialize(obj, serFilename);
// Externalization
objects_WriteRead_External(obj, serFilename);
}
public static void objects_serialize( User obj, String serFilename ) throws IOException{
FileOutputStream fos = new FileOutputStream( new File( serFilename ) );
ObjectOutputStream objectOut = new ObjectOutputStream( fos );
// Java.io.NotSerializableException: com.github.objects.Address
objectOut.writeObject( obj );
objectOut.flush();
objectOut.close();
fos.close();
System.out.println("Data Stored in to a file");
}
public static void objects_deserialize( User obj, String serFilename ) throws IOException{
try {
FileInputStream fis = new FileInputStream( new File( serFilename ) );
ObjectInputStream ois = new ObjectInputStream( fis );
Object readObject;
readObject = ois.readObject();
String calssName = readObject.getClass().getName();
System.out.println("Restoring Class Name : "+ calssName); // InvalidClassException
User user = (User) readObject;
System.out.format("Obj[Id:%d, Role:%s] \n", user.id, user.role);
Address add = (Address) user.address;
System.out.println("Inner Obj : "+ add.country );
ois.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static void objects_WriteRead_External( User obj, String serFilename ) throws IOException {
FileOutputStream fos = new FileOutputStream(new File( serFilename ));
ObjectOutputStream objectOut = new ObjectOutputStream( fos );
obj.writeExternal( objectOut );
objectOut.flush();
fos.close();
System.out.println("Data Stored in to a file");
try {
// create a new instance and read the assign the contents from stream.
User user = new User();
FileInputStream fis = new FileInputStream(new File( serFilename ));
ObjectInputStream ois = new ObjectInputStream( fis );
user.readExternal(ois);
System.out.format("Obj[Id:%d, Role:%s] \n", user.id, user.role);
Address add = (Address) user.address;
System.out.println("Inner Obj : "+ add.country );
ois.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
@voir
https://docs.Oracle.com/javase/8/docs/platform/serialization/spec/serialTOC.html
La sérialisation par défaut est quelque peu détaillée et suppose le scénario d'utilisation le plus large possible de l'objet sérialisé. Par conséquent, le format par défaut (Serializable) annote le flux résultant avec des informations sur la classe de l'objet sérialisé.
L’externalisation donne au producteur du flux d’objets un contrôle total sur les métadonnées de classe précises (le cas échéant) au-delà de l’identification minimale requise de la classe (par exemple, son nom). Cela est clairement souhaitable dans certaines situations, telles que les environnements fermés, où le producteur du flux d'objets et son consommateur (qui réifie l'objet à partir du flux) sont mis en correspondance, et des métadonnées supplémentaires sur la classe sont inutiles et dégradent les performances.
En outre, l'externalisation (comme le souligne Uri) permet également un contrôle complet du codage des données dans le flux correspondant aux types Java. Pour un exemple (artificiel), vous voudrez peut-être enregistrer le booléen vrai comme "Y" et faux comme "N". L'externalisation vous permet de le faire.
L’interface Externalizable n’a pas été fournie pour optimiser les performances du processus de sérialisation! mais pour fournir les moyens de mettre en œuvre votre propre traitement personnalisé et offrir un contrôle complet sur le format et le contenu du flux pour un objet et ses super types!
À titre d'exemple, citons l'implémentation de l'accès distant AMF (ActionScript Message Format) pour transférer des objets de script d'action natifs sur le réseau.
Il y a tellement de différences entre Serializable et Externalizable, mais lorsque nous comparons la différence entre Serializable personnalisé (écrasé writeObject () & readObject ()) et Externalizable, nous constatons que l'implémentation personnalisée est étroitement liée à la classe ObjectOutputStream où, comme dans Externalizable, nous sommes nous-mêmes. fournit une implémentation d'ObjectOutput qui peut être une classe ObjectOutputStream ou une autre comme org.Apache.mina.filter.codec.serialization.ObjectSerializationOutputStream
En cas d'interface externalizable
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(key);
out.writeUTF(value);
out.writeObject(emp);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.key = in.readUTF();
this.value = in.readUTF();
this.emp = (Employee) in.readObject();
}
**In case of Serializable interface**
/*
We can comment below two method and use default serialization process as well
Sequence of class attributes in read and write methods MUST BE same.
// below will not work it will not work .
// Exception = Java.io.StreamCorruptedException: invalid type code: 00\
private void writeObject(Java.io.ObjectOutput stream)
*/
private void writeObject(Java.io.ObjectOutputStream Outstream)
throws IOException {
System.out.println("from writeObject()");
/* We can define custom validation or business rules inside read/write methods.
This way our validation methods will be automatically
called by JVM, immediately after default serialization
and deserialization process
happens.
checkTestInfo();
*/
stream.writeUTF(name);
stream.writeInt(age);
stream.writeObject(salary);
stream.writeObject(address);
}
private void readObject(Java.io.ObjectInputStream Instream)
throws IOException, ClassNotFoundException {
System.out.println("from readObject()");
name = (String) stream.readUTF();
age = stream.readInt();
salary = (BigDecimal) stream.readObject();
address = (Address) stream.readObject();
// validateTestInfo();
}
J'ai ajouté un exemple de code pour mieux expliquer. s'il vous plaît vérifier dans/dehors le cas d'objet de Externalizable. Ceux-ci ne sont liés à aucune implémentation directement.
Où Outstream/Instream sont étroitement liés aux classes. Nous pouvons étendre ObjectOutputStream/ObjectInputStream mais cela sera un peu difficile à utiliser.
Lorsque vous envisagez des options pour améliorer les performances, n'oubliez pas la sérialisation personnalisée. Vous pouvez laisser Java faire ce qu'il fait bien, ou au moins assez bien, gratuitement , et fournir un support personnalisé pour ce qu'il fait mal. C’est généralement beaucoup moins de code que le support complet Externalizable.
Serializable
est une interface de marqueur qui implique qu'une classe est sécurisée pour la sérialisation et que la machine virtuelle Java détermine la manière dont elle est sérialisée. Externalizable
contient 2 méthodes, readExternal
et writeExternal
. Externalizable
permet à l'implémenteur de décider de la façon dont un objet est sérialisé, alors que Serializable
sérialise les objets de la manière par défaut.