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


Réponses:


267

Pour ajouter aux autres réponses, en implémentant java.io.Serializable, vous obtenez une capacité de sérialisation "automatique" pour les objets de votre classe. Pas besoin d'implémenter une autre logique, cela ne fera que fonctionner. Le runtime Java utilisera la réflexion pour comprendre comment marshaler et démarsaler vos objets.

Dans la version antérieure de Java, la réflexion était très lente, et donc la sérialisation de graphiques 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' java.io.Externalizableinterface a été fournie, qui est similaire java.io.Serializablemais avec des mécanismes personnalisés pour effectuer les fonctions de marshaling et de démarshalling (vous devez implémenter readExternaletwriteExternal méthodes sur votre classe). Cela vous donne les moyens de contourner le goulot d'étranglement des performances de réflexion.

Dans les versions récentes de Java (à partir de la version 1.3, certainement), les performances de réflexion sont bien meilleures qu'elles ne l'étaient auparavant, et c'est donc beaucoup moins un problème. Je soupçonne que vous auriez du mal à tirer un avantage significatif d' Externalizableune JVM 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 JBoss Serialization, qui est considérablement plus rapide et constitue un remplacement direct par défaut.

Un gros inconvénient Externalizableest que vous devez maintenir cette logique vous-même - si vous ajoutez, supprimez ou modifiez un champ dans votre classe, vous devez changer vos méthodes writeExternal/ readExternalpour en tenir compte.

En résumé, Externalizableest une relique des jours Java 1.1. Il n'y en a vraiment plus besoin.


61
Pas selon ces critères: [ code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking] , la sérialisation manuelle (en utilisant externalisable) est beaucoup, beaucoup plus rapide que d'utiliser la sérialisation par défaut de Java. Si la vitesse est importante pour votre travail, écrivez certainement votre propre sérialiseur.
volni

6
mise à jour vers le nouveau lien github.com/eishay/jvm-serializers/wiki suggéré par @Jack
noquery

3
Le "java-manual" dans github.com/eishay/jvm-serializers/wiki n'utilise pas Externalizable, ce qui impliquerait d'utiliser ObjectOutputStream. Voir github.com/eishay/jvm-serializers/wiki/ToolBehavior pour un lien vers le code. Au lieu de cela, c'est du code manuscrit qui utilise DataOutputStream, donc il ne souffre pas des choses qui rendent ObjectOutputStream lent (comme le suivi des instances d'objet et la prise en charge des cycles d'objet).
Esko Luontola

7
Devoir maintenir la logique vous-même n'est un inconvénient que si la classe ne change jamais et que vous n'avez jamais à lire des versions persistantes d'anciennes données. Si vous voulez la liberté de changer votre classe sans avoir à écrire du code infernal pour désérialiser les anciennes versions, cela Externalizableaide beaucoup .
Tim Boudreau

2
Je devais juste écrire une collection personnalisée et je dois dire que cela Externalizableme convient beaucoup mieux, car je ne veux pas générer de tableaux avec des espaces vides ou des objets d'espace réservé, ainsi qu'avec l'interface explicite, vous pouvez gérer l'héritage, ce qui signifie que mon sous synchronisé -class peut facilement ajouter un verrouillage autour de l'appel writeExternal(). Donc oui, l'externalisable est toujours très pertinent, certainement pour les objets grands ou complexes.
Haravikk

37

La sérialisation fournit des fonctionnalités par défaut pour stocker et recréer ultérieurement l'objet. Il utilise un format détaillé pour définir le graphique entier des objets à stocker, par exemple, supposons que vous ayez une liste liée et que vous codiez comme ci-dessous, puis la sérialisation par défaut découvrira tous les objets liés et sérialisera. 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. En travaillant 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 l'exception ci-dessous:

 java.io.InvalidClassException: javaserialization.MyExternalizable;     
 javaserialization.MyExternalizable; no valid constructor.

Nous pouvons observer que le mot de passe étant une information sensible, je ne le sérialise donc pas dans la méthode writeExternal (ObjectOutput oo) et ne fixe pas la valeur de celui-ci dans readExternal (ObjectInput oi). C'est la flexibilité offerte 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, il est donc nul.

La même chose peut également être obtenue en déclarant le champ de mot de passe comme transitoire.

private transient String passWord;

J'espère que ça aide. Je m'excuse si j'ai fait des erreurs. Merci.


22

Différences clés entre SerializableetExternalizable

  1. Interface de marqueur : Serializableest une interface de marqueur sans aucune méthode. ExternalizableL'interface contient deux méthodes: writeExternal()et readExternal().
  2. Processus de sérialisation : le processus de sérialisation par défaut sera lancé pour les classes implémentantSerializable interface. Le processus de sérialisation défini par le programmeur sera lancé pour les classes implémentant l' Externalizableinterface.
  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' Externalizableinterface. Vous pouvez prendre en charge différentes versions de votre objet. Si vous implémentez Externalizable, il est de votre responsabilité de sérialisersuper classe
  5. constructeur No-arg public : Serializableutilise la réflexion pour construire l'objet et ne nécessite pas de constructeur arg. MaisExternalizable exige un constructeur public sans argument.

Consultez le blog de Hitesh Gargpour plus de détails.


1
(3) est incorrect. Il existe un large répertoire de modifications que vous pouvez apporter à une classe sans interrompre la désérialisation des objets existants, et l'ajout de membres sérialisables en est certainement un. Les supprimer en est une autre. Reportez-vous au chapitre Version des objets de la spécification de sérialisation des objets Java.
Marquis de Lorne

1
Phrase reformulée.
Ravindra babu

Merci de partager le lien vers la spécification de sérialisation.
JL_SO

21

La sérialisation utilise certains comportements par défaut pour stocker et recréer ultérieurement 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 finalement cela revient à 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 vraiment 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 d'encodage et de compression.


5
Nous avons utilisé Externalizable pour de grandes collections d '"ID sélectionnés" - c'était une externalisation beaucoup plus efficace en tant que plus ou moins un compte et un tableau d'entiers primitifs, que la sérialisation par défaut. C'est un cas d'utilisation très simple, rien de "spécial" ou "unique".
Thomas W

9

La sérialisation d'objets utilise les interfaces sérialisables et externalisables. Un objet Java est uniquement sérialisable. si une classe ou l'une de ses superclasses implémente soit l'interface java.io.Serializable, soit sa sous-interface, java.io.Externalizable. La plupart des classes java sont sérialisables .

  • NotSerializableException: packageName.ClassName«Pour participer à un objet de classe dans le processus de sérialisation, la classe doit implémenter une interface sérialisable ou externalisable.

entrez la description de l'image ici


Interface sérialisable

La sérialisation des objets produit un flux contenant des informations sur les classes Java pour les objets qui sont enregistrés. 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 sérialisable 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 ou de champs et ne sert qu'à 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.
  • JVM aura un contrôle complet sur la sérialisation de l'objet. utilisez un 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 valeur de la classe serialVersionUID locale est différente de la classe de l'expéditeur correspondant. alors le résultat est en conflit comme 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 externalisables, 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 externalisable 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 des méthodes writeExternal et readExternal pour enregistrer / restaurer l'état d'un objet.
  • Le programmeur doit prendre soin des objets à sérialiser. En tant que programmeur, prenez soin de la sérialisation Donc, ici, 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 sans argument, puis la méthode readExternal est 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 type que nous les avons écrits. S'il y a une incompatibilité de type dans le flux, il lance 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 (exposés) pour ObjectOutputêtre 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


7

L'interface externalisable n'a pas été réellement fournie pour optimiser les performances du processus de sérialisation! mais pour fournir des moyens d'implémenter 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!

La mise en œuvre d'AMF (ActionScript Message Format) à distance pour transférer des objets de script d'action natifs sur le réseau en est un exemple.


7

https://docs.oracle.com/javase/8/docs/platform/serialization/spec/serialTOC.html

La sérialisation par défaut est quelque peu verbeuse et suppose le scénario d'utilisation le plus large possible de l'objet sérialisé, et en conséquence le format par défaut (sérialisable) 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 complet 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'objet et son consommateur (qui réifie l'objet du flux) sont mis en correspondance, et des métadonnées supplémentaires sur la classe ne servent à rien et dégradent les performances.

De plus (comme le souligne Uri), l'externalisation permet également un contrôle complet du codage des données dans le flux correspondant aux types Java. Pour un exemple (artificiel), vous souhaiterez peut-être enregistrer booléen vrai comme «Y» et faux comme «N». L'externalisation vous permet de le faire.


2

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 du moins assez bien, gratuitement , et fournir un support personnalisé pour ce qu'il fait mal. Il s'agit généralement de beaucoup moins de code que la prise en charge externe complète.


2

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 le cas externalisable, nous-mêmes fournir 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 externalisable

@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. veuillez archiver / retirer le boîtier d'objet d'Externalisable. Ceux-ci ne sont directement liés à aucune implémentation.
Là où Outstream / Instream sont étroitement liés aux classes. Nous pouvons étendre ObjectOutputStream / ObjectInputStream mais ce sera un peu difficile à utiliser.


1
Pourriez-vous élaborer davantage? En le lisant, je ne comprends pas ce que vous essayez de dire. De plus, si vous pouviez formater le texte avec quelques paragraphes et exemples, cela pourrait être une excellente réponse.
Shirkam

0

Fondamentalement, Serializablec'est une interface de marqueur qui implique qu'une classe est sûre pour la sérialisation et la JVM détermine comment elle est sérialisée. Externalizablecontient 2 méthodes, readExternalet writeExternal. Externalizablepermet à l'implémenteur de décider comment un objet est sérialisé, où comme Serializablesérialise les objets par défaut.


0

Quelques différences:

  1. Pour la sérialisation, il n'y a pas besoin de constructeur par défaut de cette classe car Object car JVM le construit à l'aide de Reflection API. En cas d'externalisation, un constructeur sans arg est nécessaire, car le contrôle est en main du programme et plus tard, affectez les données désérialisées à l'objet via des setters.

  2. Dans la sérialisation, si l'utilisateur souhaite ignorer certaines propriétés à sérialiser, il doit marquer ces propriétés comme transitoires, l'inverse n'est pas requis pour l'externalisation.

  3. Lorsqu'une prise en charge de la compatibilité descendante est attendue pour n'importe quelle classe, il est recommandé d'utiliser Externalizable. La sérialisation prend en charge la persistance de defaultObject et si la structure de l'objet est brisée, cela entraînera un problème lors de la désérialisation.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.