web-dev-qa-db-fra.com

Quelle est la différence entre Serializable et Externalizable en Java?

Quelle est la différence entre Serializable et Externalizable en Java?

268
Sasha

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.

257
skaffman

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.

34
Trying

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.

24
Benjamin Wootton

Principales différences entre Serializable et Externalizable

  1. Interface de marqueur: Serializable est une interface de marqueur sans aucune méthode. L'interface Externalizable contient deux méthodes: writeExternal() et readExternal().
  2. Processus de sérialisation: le processus de sérialisation par défaut sera activé pour les classes implémentant l'interface Serializable. Le processus de sérialisation défini par le programmeur sera activé pour les classes implémentant l'interface Externalizable.
  3. Maintenance: Modifications incompatibles peuvent interrompre la sérialisation. 
  4. Compatibilité et contrôle en amont: si vous devez prendre en charge plusieurs versions, vous pouvez avoir un contrôle total avec l'interface Externalizable. Vous pouvez supporter différentes versions de votre objet. Si vous implémentez Externalizable, il vous incombe de sérialiser la classe super.
  5. public No-arg constructor: 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. 

20
Ravindra babu

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. 

19
Uri

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.

 enter image description here


Interface sérialisable

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 {};
  • L'interface de sérialisation n'a pas de méthodes ni de champs et sert uniquement à identifier la sémantique d'être sérialisable. Pour sérialiser/désérialiser une classe, nous pouvons utiliser les méthodes writeObject et readObject par défaut (ou) nous pouvons remplacer les méthodes writeObject et readObject d'une classe.
  • La JVM aura le contrôle complet de la sérialisation de l'objet. utilisez mot-clé transitoire pour empêcher la sérialisation du membre de données.
  • Ici, les objets sérialisables sont reconstruits directement à partir du flux sans exécuter 
  • 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
  • Les valeurs des champs non transitoires et non statiques de la classe sont sérialisées.

Interface externalisable

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;
}
  • L'interface Externalizable a deux méthodes, un objet externalisable doit implémenter une méthode writeExternal et une méthode readExternal pour enregistrer/restaurer l'état d'un objet.
  • Le programmeur doit s'occuper des objets à sérialiser. En tant que programmeur, veillez à la sérialisation. Par conséquent, le mot clé transitoire ne restreindra aucun objet dans le processus de sérialisation.
  • Lorsqu'un objet Externalizable est reconstruit, une instance est créée à l'aide du constructeur public no-arg, puis de la méthode readExternal appelée. Les objets sérialisables sont restaurés en les lisant à partir d'un ObjectInputStream.
  • 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

8
Yash

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.

7
alphazero

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.

7
Ali Joudeh

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.

2
Ashish Sharma

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.

2
Ed Staub

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.