web-dev-qa-db-fra.com

Pourquoi readObject et writeObject sont-ils privés et pourquoi devrais-je écrire des variables transitoires de manière explicite?

Je lis le chapitre sur la sérialisation dans Effective Java .

  1. Qui appelle readObject () et writeObject ()? Pourquoi ces méthodes sont-elles déclarées privées?

  2. Ce qui suit est un morceau de code du livre

    // StringList with a reasonable custom serialized form
    public final class StringList implements Serializable {
        private transient int size = 0;
        private transient Entry head = null;
    
        //Other code
    
        private void writeObject(ObjectOutputStream s)
            throws IOException {
            s.defaultWriteObject();
            s.writeInt(size);
            // Write out all elements in the proper order.
            for (Entry e = head; e != null; e = e.next)
               s.writeObject(e.data);
            }
        }
    }
    

    Existe-t-il une raison spécifique pour laquelle la variable size est déclarée comme transitoire, puis dans la méthode writeObject, elle est explicitement écrite? S'il n'avait pas été déclaré transitoire, il aurait été écrit de toute façon, non?

33
Vinoth Kumar C M

Mis à part ne doit pas être utilisé par de mauvaises parties, voici une autre raison pour la confidentialité de ces méthodes:

Nous ne voulons pas que ces méthodes soient remplacées par des sous-classes . Au lieu de cela, chaque classe peut avoir sa propre méthode writeObject et le moteur de sérialisation les appellera toutes les unes après les autres. Cela n'est possible qu'avec des méthodes privées (celles-ci ne sont pas remplacées). (La même chose est valable pour readObject.)

(Notez que cela ne s'applique qu'aux super-classes qui implémentent elles-mêmes Serializable.)

De cette façon, les sous-classes et les super-classes peuvent évoluer indépendamment, tout en restant compatibles avec les objets stockés d'anciennes versions.

17
Paŭlo Ebermann

A propos du fait que readObject ()/writeObject () soit privé, voici l'affaire: si votre classe Bar étend une classe Foo; Foo implémente également readObject ()/writeObject () et Bar implémente également readObject ()/writeObject ().

Maintenant, lorsqu'un objet Bar est sérialisé ou désérialisé, JVM doit appeler automatiquement readObject ()/writeObject () pour Foo et Bar (c'est-à-dire sans que vous ayez besoin de classer ces méthodes de super classe explicitement). private, la méthode devient prioritaire et JVM ne peut plus appeler les méthodes de la super-classe sur l'objet de la sous-classe.

Par conséquent, ils doivent être privés!

10
shrini1000

Les readObject et writeObject sont appelées par la ou les classes Object(Input/Output)Stream.

Ces méthodes sont (et doivent être) déclarées privées (lors de la mise en œuvre de la vôtre), ce qui prouve/indique qu'aucune des méthodes n'est héritée et remplacée ou surchargée par la mise en œuvre. L'astuce ici est que la JVM vérifie automatiquement si l'une ou l'autre des méthodes est déclarée lors de l'appel de la méthode correspondante. Notez que la machine virtuelle Java peut appeler des méthodes privées de votre classe à tout moment mais aucun autre objet ne peut. Ainsi, l’intégrité de la classe est maintenue et le protocole de sérialisation peut continuer à fonctionner normalement.

Et en ce qui concerne la variable transitoire int, il s'agit simplement de prendre le contrôle de la sérialisation de la sérialisation de l'objet dans son ensemble. Cependant, notez que techniquement, il n'est même pas nécessaire [ d'invoquer defaultWriteObject(), si tous les champs sont transitoires. Mais je pense qu'il est toujours recommandé de l'invoquer à des fins de flexibilité, afin que vous puissiez plus tard introduire des membres non transitoires dans votre classe, en préservant la compatibilité.

7
Saket

Supposons que vous avez une classe A qui fait référence à un socket. Si vous souhaitez sérialiser des objets de classe A, vous ne pouvez pas directement, car Socket n'est pas sérialisable. Dans ce cas, vous écrivez le code ci-dessous.

public class A implements  implements Serializable {

// mark Socket as transient so that A can be serialized

private transient Socket socket;

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

    // take out ip address and port write them to out stream
    InetAddress inetAddress = socket.getInetAddress();
    int port = socket.getPort();
    out.writeObject(inetAddress);
    out.writeObject(port);
}



private void readObject(ObjectInputStream in)
                  throws IOException, ClassNotFoundException{
    in.defaultReadObject();
    // read the ip address and port form the stream and create a frest socket.
    InetAddress inetAddress = (InetAddress) in.readObject();
    int port = in.readInt();
    socket = new Socket(inetAddress, port);
}
}

Ignorez les problèmes liés au réseau car l'objectif est de montrer l'utilisation des méthodes writeObject/readObject.

0
Saket

En ce qui concerne la variable transitoire, le meilleur moyen de comprendre pourquoi et si nous déclarons une variable transitoire puis les sérialisons dans la méthode writeobject est de vérifier/analyser/déboguer les méthodes readobject/writeobject des classes LinkedList/HashMap/etc.

Cela se fait généralement lorsque vous souhaitez sérialiser/désérialiser les variables de classe dans un ordre prédéfini et ne pas vous fier au comportement/ordre par défaut. 

0
Uday Ogra