Que signifie Serializable?

136

Que signifie exactement pour une classe d'être Serializableen Java? Ou en général, d'ailleurs ...

Ritwik Bose
la source
10
@skaffman Voici ce qu'il dit pour la classe Serializable: Serializability of a class is enabled by the class implementing the java.io.Serializable interface. Classes that do not implement this interface will not have any of their state serialized or deserialized. All subtypes of a serializable class are themselves serializable. The serialization interface has no methods or fields and serves only to identify the semantics of being serializable.
Ritwik Bose
32
Une bonne explication si vous savez déjà ce que signifient sérialisé et désérialisé. (Pas un compliment.) Ces définitions vous aident à mieux comprendre le problème techniquement une fois, et une seule fois, vous en avez déjà quelques connaissances.
Xonatron

Réponses:

132

La sérialisation consiste à conserver un objet de la mémoire vers une séquence de bits, par exemple pour enregistrer sur le disque. La désérialisation est le contraire: lire les données du disque pour hydrater / créer un objet.

Dans le contexte de votre question, c'est une interface qui, si elle est implémentée dans une classe, cette classe peut être automatiquement sérialisée et désérialisée par différents sérialiseurs.

Oded
la source
2
Notez également que tous les champs non explicitement marqués seront également sérialisés. Cela signifie que vous pouvez facilement enregistrer une structure de données complexe simplement en sérialisant l'objet racine.
Thorbjørn Ravn Andersen
1
Donc, quand nous parlons d '«objets», entendons-nous l'objet instancié par une classe, ou simplement n'importe quel «objet logiciel» comme les assemblys, les fichiers, etc.? Et si c'est le dernier, s'agit-il simplement d'un moyen standardisé d'envoyer des données entre programmes et environnements?
Sunburst275
1
@ Sunburst275 - dans ce cas, il s'agit de la représentation en mémoire d'une classe en mémoire - c'est-à-dire une instance d'une classe (il n'y a pas vraiment de raison de parler de sérialisation d'assemblys, car ils sont généralement sur disque sous forme de fichiers qui peuvent être envoyé tel quel).
Oded
44

Bien que la plupart des utilisateurs aient déjà donné la réponse, mais je voudrais ajouter un exemple pour ceux qui en ont besoin afin d'expliquer l'idée:

Disons que vous avez une personne de classe comme celle-ci:

public class Person implements java.io.Serializable {
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    public String firstName;
    public String lastName;
    public int age;
    public String address;

    public void play() {
        System.out.println(String.format(
                "If I win, send me the trophy to this address: %s", address));
    }
    @Override
    public String toString() {
        return String.format(".....Person......\nFirst Name = %s\nLast Name = %s", firstName, lastName);
    }
}

puis vous créez un objet comme celui-ci:

Person william = new Person();
        william.firstName = "William";
        william.lastName = "Kinaan";
        william.age = 26;
        william.address = "Lisbon, Portugal";

Vous pouvez sérialiser cet objet en plusieurs flux. Je vais faire cela à deux flux:

Sérialisation vers la sortie standard:

public static void serializeToStandardOutput(Person person)
            throws IOException {
        OutputStream outStream = System.out;
        ObjectOutputStream stdObjectOut = new ObjectOutputStream(outStream);
        stdObjectOut.writeObject(person);
        stdObjectOut.close();
        outStream.close();
    }

Sérialisation vers un fichier:

public static void serializeToFile(Person person) throws IOException {
        OutputStream outStream = new FileOutputStream("person.ser");
        ObjectOutputStream fileObjectOut = new ObjectOutputStream(outStream);
        fileObjectOut.writeObject(person);
        fileObjectOut.close();
        outStream.close();
    }

Ensuite:

Désérialiser du fichier:

public static void deserializeFromFile() throws IOException,
            ClassNotFoundException {
        InputStream inStream = new FileInputStream("person.ser");
        ObjectInputStream fileObjectIn = new ObjectInputStream(inStream);
        Person person = (Person) fileObjectIn.readObject();
        System.out.println(person);
        fileObjectIn.close();
        inStream.close();
    }
William Kinaan
la source
Je vous remercie. Je pense que je l'ai maintenant.
Sunburst275
40

Cela signifie que les instances de la classe peuvent être transformées en un flux d'octets (par exemple, pour être enregistrées dans un fichier), puis reconverties en classes. Ce rechargement peut se produire dans une instance différente du programme, ou même sur une machine différente. La sérialisation (dans n'importe quelle langue) implique toutes sortes de problèmes, cependant, surtout lorsque vous avez des références à d'autres objets dans celui qui est sérialisable.

David
la source
15

Voici une explication détaillée de la sérialisation : (mon propre blog)

Sérialisation:

La sérialisation est le processus de sérialisation de l'état d'un objet représenté et stocké sous la forme d'une séquence d'octets. Cela peut être stocké dans un fichier. Le processus pour lire l'état de l'objet à partir du fichier et le restaurer est appelé désérialisation.

Quel est le besoin de sérialisation?

Dans l'architecture moderne, il est toujours nécessaire de stocker l'état de l'objet, puis de le récupérer. Par exemple dans Hibernate, pour stocker un objet, nous devons rendre la classe Serializable. Ce qu'il fait, c'est qu'une fois que l'état de l'objet est enregistré sous forme d'octets, il peut être transféré vers un autre système qui peut ensuite lire l'état et récupérer la classe. L'état de l'objet peut provenir d'une base de données ou d'un autre jvm ou d'un composant séparé. Avec l'aide de la sérialisation, nous pouvons récupérer l'état de l'objet.

Exemple de code et explication:

Jetons d'abord un coup d'œil à la classe d'objets:

public class Item implements Serializable{

    /**
    *  This is the Serializable class
    */
    private static final long serialVersionUID = 475918891428093041L;
    private Long itemId;
    private String itemName;
    private transient Double itemCostPrice;
    public Item(Long itemId, String itemName, Double itemCostPrice) {
        super();
        this.itemId = itemId;
        this.itemName = itemName;
        this.itemCostPrice = itemCostPrice;
      }

      public Long getItemId() {
          return itemId;
      }

     @Override
      public String toString() {
          return "Item [itemId=" + itemId + ", itemName=" + itemName + ", itemCostPrice=" + itemCostPrice + "]";
       }


       public void setItemId(Long itemId) {
           this.itemId = itemId;
       }

       public String getItemName() {
           return itemName;
       }
       public void setItemName(String itemName) {
            this.itemName = itemName;
        }

       public Double getItemCostPrice() {
            return itemCostPrice;
        }

        public void setItemCostPrice(Double itemCostPrice) {
             this.itemCostPrice = itemCostPrice;
        }
}

Dans le code ci-dessus, on peut voir que la classe Item implémente Serializable .

C'est l'interface qui permet à une classe d'être sérialisable.

Nous pouvons maintenant voir une variable appelée serialVersionUID est initialisée en variable Long. Ce nombre est calculé par le compilateur en fonction de l'état de la classe et des attributs de classe. C'est le nombre qui aidera le jvm à identifier l'état d'un objet lorsqu'il lit l'état de l'objet à partir du fichier.

Pour cela, nous pouvons consulter la documentation officielle d'Oracle:

Le runtime de sérialisation associe à chaque classe sérialisable un numéro de version, appelé serialVersionUID, qui est utilisé pendant la désérialisation pour vérifier que l'expéditeur et le destinataire d'un objet sérialisé ont chargé des classes pour cet objet qui sont compatibles avec la sérialisation. Si le destinataire a chargé une classe pour l'objet qui a un serialVersionUID différent de celui de la classe de l'expéditeur correspondant, la désérialisation entraînera une InvalidClassException. Une classe sérialisable peut déclarer son propre serialVersionUID explicitement en déclarant un champ nommé "serialVersionUID" qui doit être statique, final et de type long: ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L; Si une classe sérialisable ne déclare pas explicitement un serialVersionUID, puis le runtime de sérialisation calculera une valeur serialVersionUID par défaut pour cette classe en fonction de divers aspects de la classe, comme décrit dans la spécification de sérialisation d'objets Java (TM). Cependant, il est fortement recommandé que toutes les classes sérialisables déclarent explicitement les valeurs serialVersionUID, car le calcul serialVersionUID par défaut est très sensible aux détails de classe qui peuvent varier en fonction des implémentations du compilateur, et peuvent donc entraîner des InvalidClassExceptions inattendues pendant la désérialisation. Par conséquent, pour garantir une valeur serialVersionUID cohérente dans les différentes implémentations du compilateur Java, une classe sérialisable doit déclarer une valeur serialVersionUID explicite. Il est également fortement conseillé que les déclarations explicites serialVersionUID utilisent le modificateur private lorsque cela est possible,

Si vous avez remarqué qu'un autre mot-clé que nous avons utilisé est transitoire .

Si un champ n'est pas sérialisable, il doit être marqué comme transitoire. Ici, nous avons marqué le itemCostPrice comme transitoire et ne voulons pas qu'il soit écrit dans un fichier

Voyons maintenant comment écrire l'état d'un objet dans le fichier, puis le lire à partir de là.

public class SerializationExample {

    public static void main(String[] args){
        serialize();
       deserialize();
    } 

    public static void serialize(){

         Item item = new Item(1L,"Pen", 12.55);
         System.out.println("Before Serialization" + item);

         FileOutputStream fileOut;
         try {
             fileOut = new FileOutputStream("/tmp/item.ser");
             ObjectOutputStream out = new ObjectOutputStream(fileOut);
             out.writeObject(item);
             out.close();
             fileOut.close();
             System.out.println("Serialized data is saved in /tmp/item.ser");
           } catch (FileNotFoundException e) {

                  e.printStackTrace();
           } catch (IOException e) {

                  e.printStackTrace();
           }
      }

    public static void deserialize(){
        Item item;

        try {
                FileInputStream fileIn = new FileInputStream("/tmp/item.ser");
                ObjectInputStream in = new ObjectInputStream(fileIn);
                item = (Item) in.readObject();
                System.out.println("Serialized data is read from /tmp/item.ser");
                System.out.println("After Deserialization" + item);
        } catch (FileNotFoundException e) {
                e.printStackTrace();
        } catch (IOException e) {
               e.printStackTrace();
        } catch (ClassNotFoundException e) {
               e.printStackTrace();
        }
     }
}

Dans ce qui précède, nous pouvons voir un exemple de sérialisation et de désérialisation d'un objet.

Pour cela, nous avons utilisé deux classes. Pour sérialiser l'objet, nous avons utilisé ObjectOutputStream. Nous avons utilisé la méthode writeObject pour écrire l'objet dans le fichier.

Pour la désérialisation, nous avons utilisé ObjectInputStream qui lit l'objet à partir du fichier. Il utilise readObject pour lire les données d'objet à partir du fichier.

La sortie du code ci-dessus serait comme:

Before SerializationItem [itemId=1, itemName=Pen, itemCostPrice=12.55]
Serialized data is saved in /tmp/item.ser
After DeserializationItem [itemId=1, itemName=Pen, itemCostPrice=null]

Notez que itemCostPrice de l'objet désérialisé est nul car il n'a pas été écrit.

Pritam Banerjee
la source
2
Merci pour cette explication détaillée.
Promise Preston
C'est une belle explication! Je vous remercie! Mais pour être honnête, cette entrée semble beaucoup plus propre que celle de votre blog. Quoi qu'il en soit, cela a beaucoup aidé!
Sunburst275
11

La sérialisation implique l'enregistrement de l'état actuel d'un objet dans un flux et la restauration d'un objet équivalent à partir de ce flux. Le flux fonctionne comme un conteneur pour l'objet

Jigar Joshi
la source
1
Cette définition semble plus précise. Je vous remercie.
Promise Preston
6

Serializable est appelé comme une interface, mais c'est plutôt un indicateur du sous-système de sérialisation, au moment de l'exécution. Il dit que cet objet peut être enregistré. Toutes les variables d'instance Objects, à l'exception des objets non sérialisables et de celles marquées comme volatiles, seront enregistrées.

Imaginez que votre application puisse changer de couleur en option, sans garder ce paramètre externe, vous devrez changer la couleur à chaque fois que vous l'exécutez.

AphexMunky
la source
5
Ce n'est pas un «drapeau pour le compilateur». Il s'agit d'un indicateur du sous-système de sérialisation, au moment de l'exécution.
Marquis of Lorne
1
@EJP - Merci, je ne savais pas que
AphexMunky
Avec respect, pourquoi l'écrire alors que vous ne savez pas que c'est vrai? Vous avez également omis «transitoire». Dans l'ensemble, une mauvaise réponse, désolé.
Marquis of Lorne
19
Si je ne l'avais pas écrit, je n'aurais pas été corrigé et je serais pire. Toutes les autres réponses sont également restées transitoires. Vous n'avez même pas écrit de réponse, vous ne faites que troller les autres.
AphexMunky
4

La sérialisation est une technique de stockage ou d'écriture des objets et des données dans des fichiers. En utilisant ObjectOutputStreamet FileOutputStreamclasses. Ces classes ont leurs méthodes spécifiques pour conserver les objets. commewriteObject();

pour une explication claire avec des chiffres. Voir ici pour plus d'informations

Mdhar9e
la source
2

Présenter sous un autre angle. La sérialisation est une sorte d'interface appelée «interface de marqueur». Une interface de marqueur est une interface qui ne contient aucune déclaration de méthode, mais désigne simplement (ou «marque») une classe qui implémente l'interface comme ayant une propriété. Si vous comprenez le polymorphisme, cela aura beaucoup de sens. Dans le cas de l'interface de marqueur Serializable, la méthode ObjectOutputStream.write (Object) échouera si son argument n'implémente pas l'interface. Il s'agit d'une erreur potentielle en java, cela aurait pu être ObjectOutputStream.write (Serializable)

Hautement recommandé: Lisez l'article 37 de Effective Java de Joshua Bloch pour en savoir plus.

nanospeck
la source
2

Sérialisation: écriture de l'état de l'objet dans un fichier / réseau ou n'importe où. (Signalez le formulaire pris en charge par l'objet Java au formulaire pris en charge par fichier ou le formulaire pris en charge par le réseau)

Désérialisation: lecture de l'état de l'objet depuis un fichier / réseau ou n'importe où. (Formulaire moyen de fichier / réseau pris en charge vers formulaire pris en charge par objet Java)

Asif Mushtaq
la source
1

Juste pour ajouter aux autres réponses et en ce qui concerne la généralité. La sérialisation est parfois appelée archivage, par exemple en Objective-C.

Greg Sexton
la source