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

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

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

  2. Le ci-dessous est un morceau de code du livre

    // SsortingngList with a reasonable custom serialized form public final class SsortingngList 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); } } } 

    Y a-t-il une raison spécifique pour laquelle la size la variable est déclarée comme transitoire et ensuite, 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?

(1) Les méthodes ne sont déclarées dans aucune classe ou interface. Une classe qui implémente l’interface Serializable et nécessite une gestion spéciale spéciale pendant le processus de sérialisation et de désérialisation doit implémenter ces méthodes et le sérialiseur / deserializer essaiera de refléter ces méthodes.

C’est l’un des coins les plus étranges de Java où l’API est en fait définie dans le javaDoc… Mais si les méthodes avaient été définies dans une interface, elles devaient être public (on ne peut pas implémenter une méthode d’interface avec un verrou) en ajoutant un modificateur private ).

Pourquoi private – le javaDoc ne donne aucun indice. Peut-être qu’ils sont spécifiés comme privés car aucune autre classe que l’implémenteur n’est destinée à les utiliser. Ils sont privés par définition .

(2) L’exemple montre simplement comment fonctionne la manipulation spéciale. Dans cet exemple, la size est transitoire et ne sera pas sérialisée. Mais maintenant, nous introduisons le gestionnaire spécial et ce gestionnaire ajoute la valeur de size au stream. La différence par rapport à l’approche normale avec des champs non transitoires pourrait être l’ordre des éléments dans le stream résultant (si c’est important …).

Cet exemple pourrait avoir un sens si le champ transitoire était défini dans une super classe et si une sous-classe souhaitait sérialiser la valeur.

En dehors de ne 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. Ceci n’est possible qu’avec les méthodes privées (elles 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 et restr compatibles avec les objects stockés des anciennes versions.

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 object Bar est sérialisé ou désérialisé, JVM doit appeler readObject () / writeObject () à la fois pour Foo et Bar (sans que vous ayez besoin de classer explicitement ces méthodes de classe super). Cependant, si ces méthodes sont tout sauf private, il devient la méthode de substitution, et JVM ne peut plus appeler les méthodes de classe super sur l’object de sous-classe.

Ils doivent donc être privés!

Les readObject et writeObject sont appelés par les writeObject Object(Input/Output)Stream .

Ces méthodes sont (et doivent être) déclarées privées (lors de l’implémentation de votre propre méthode), prouvant / indiquant qu’aucune méthode n’est héritée, remplacée ou surchargée par l’implémentation. L’astuce ici est que la JVM vérifie automatiquement si l’une ou l’autre méthode est déclarée lors de l’appel de méthode correspondant. Notez que la JVM peut appeler des méthodes privées de votre classe à tout moment, mais aucun autre object ne le 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 l’ int transitoire, il ne fait que prendre le contrôle de la sérialisation de la sérialisation d’object entière en tant que telle. Toutefois, notez que techniquement, il n’est même pas nécessaire d’appeler defaultWriteObject() si tous les champs sont transitoires. Mais je pense qu’il est toujours recommandé de l’invoquer pour des raisons de flexibilité, afin que vous puissiez plus tard introduire des membres non-transitoires dans votre classe, en préservant la compatibilité.

En ce qui concerne la variable transitoire, la meilleure façon de comprendre pourquoi nous déclarons une variable transitoire et ensuite de la sérialiser en méthode writeobject est de vérifier / parsingr / déboguer les méthodes readobject / writeobject des classes LinkedList / HashMap / etc.

Cela se produit 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 / à l’ordre par défaut.

Supposons que vous avez une classe A qui fait référence à un socket. Si vous voulez sérialiser des objects de classe A, vous ne pouvez pas directement parce que Socket n’est pas Serializable. 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); } } 

Ignorer tous les problèmes liés à la mise en réseau, car il s’agit de montrer l’utilisation des méthodes writeObject / readObject.