Das Buch Effective Java und andere Quellen bieten eine ziemlich gute Erklärung, wie und wann die readObject () -Methode bei der Arbeit mit serialisierbaren Java-Klassen verwendet werden soll. Die readResolve () -Methode bleibt dagegen ein Rätsel. Grundsätzlich erwähnen alle Dokumente, die ich gefunden habe, entweder nur eines der beiden oder beide nur einzeln.
Fragen, die unbeantwortet bleiben, sind:
- Was ist der Unterschied zwischen den beiden Methoden?
- Wann soll welche Methode implementiert werden?
- Wie sollte readResolve () verwendet werden, insbesondere im Hinblick auf die Rückgabe von was?
Ich hoffe, Sie können etwas Licht in diese Angelegenheit bringen.
java
serialization
singleton
Futter
quelle
quelle
String.CaseInsensitiveComparator.readResolve()
Antworten:
readResolve
wird zum Ersetzen des aus dem Stream gelesenen Objekts verwendet. Die einzige Verwendung, die ich jemals dafür gesehen habe, ist das Erzwingen von Singletons. Wenn ein Objekt gelesen wird, ersetzen Sie es durch die Singleton-Instanz. Dies stellt sicher, dass niemand eine andere Instanz erstellen kann, indem der Singleton serialisiert und deserialisiert wird.quelle
transient
Felder erwähnt.readResolve
wird zum Auflösen des Objekts nach dem Lesen verwendet. Eine beispielhafte Verwendung ist möglicherweise, dass ein Objekt einen Cache enthält, der aus vorhandenen Daten neu erstellt werden kann und nicht serialisiert werden muss. Die zwischengespeicherten Daten können deklarierttransient
undreadResolve()
nach der Deserialisierung neu erstellt werden. Für solche Dinge ist diese Methode gedacht.Serializable
heißt es: "Klassen, die einen Ersatz angeben müssen, wenn eine Instanz davon aus dem Stream gelesen wird, sollten diese [readResolve
] spezielle Methode implementieren ...".Punkt 90, Effective Java, 3rd Ed Cover
readResolve
undwriteReplace
für serielle Proxys - deren Hauptverwendung. Die Beispiele schreiben nicht ausreadObject
undwriteObject
Methoden , da sie die Standardserialisierung zum Lesen und Schreiben von Feldern verwenden.readResolve
wird aufgerufen, nachdemreadObject
zurückgekehrt ist (umgekehrtwriteReplace
wird vorherwriteObject
und wahrscheinlich für ein anderes Objekt aufgerufen ). Das Objekt, das die Methode zurückgibt, ersetzt dasthis
an den BenutzerObjectInputStream.readObject
zurückgegebene Objekt und alle weiteren Verweise auf das Objekt im Stream. BeidereadResolve
undwriteReplace
können Objekte desselben oder unterschiedlichen Typs zurückgeben. Die Rückgabe des gleichen Typs ist in einigen Fällen hilfreich, in denen Felder vorhanden sein müssenfinal
und entweder Abwärtskompatibilität erforderlich ist oder Werte kopiert und / oder validiert werden müssen.Die Verwendung von
readResolve
erzwingt die Singleton-Eigenschaft nicht.quelle
readResolve kann verwendet werden, um die Daten zu ändern, die über die readObject-Methode serialisiert werden. Beispielsweise verwendet die xstream-API diese Funktion, um einige Attribute zu initialisieren, die nicht im zu deserialisierenden XML enthalten waren.
http://x-stream.github.io/faq.html#Serialization
quelle
readResolve ist für den Fall gedacht, dass Sie möglicherweise ein vorhandenes Objekt zurückgeben müssen, z. B. weil Sie nach doppelten Eingaben suchen, die zusammengeführt werden sollen, oder (z. B. in eventuell konsistenten verteilten Systemen), weil es sich um ein Update handelt, das möglicherweise eintrifft, bevor Sie es bemerken ältere Versionen.
quelle
readObject () ist eine in ObjectInputStream vorhandene Methode Klasse. das Lesen des Objekts zum Zeitpunkt der Deserialisierung die readObject-Methode intern prüft, ob das zu deserialisierende Klassenobjekt die readResolve-Methode hat oder nicht, wenn die readResolve-Methode vorhanden ist, ruft es die readResolve-Methode auf und gibt dieselbe zurück Beispiel.
Die Absicht, die readResolve-Methode zu schreiben, ist daher eine gute Vorgehensweise, um ein reines Singleton-Entwurfsmuster zu erzielen, bei dem niemand durch Serialisierung / Deserialisierung eine andere Instanz erhalten kann.
quelle
readResolve () stellt den Singleton-Vertrag während der Serialisierung sicher.
Bitte beziehen Sie sich
quelle
Wenn die Serialisierung verwendet wird, um ein Objekt so zu konvertieren, dass es in einer Datei gespeichert werden kann, können wir die Methode readResolve () auslösen. Die Methode ist privat und wird in derselben Klasse gespeichert, deren Objekt während der Deserialisierung abgerufen wird. Es stellt sicher, dass nach der Deserialisierung das zurückgegebene Objekt das gleiche ist, das serialisiert wurde. Das ist,
instanceSer.hashCode() == instanceDeSer.hashCode()
Die Methode readResolve () ist keine statische Methode. Nachdem
in.readObject()
während der Deserialisierung aufgerufen wird, wird nur sichergestellt, dass das zurückgegebene Objekt mit dem Objekt identisch ist, das wie folgt serialisiert wurdeout.writeObject(instanceSer)
Auf diese Weise hilft es auch bei der Implementierung von Singleton-Entwurfsmustern , da jedes Mal dieselbe Instanz zurückgegeben wird.
quelle
Ich weiß, dass diese Frage sehr alt ist und eine akzeptierte Antwort hat, aber da sie in der Google-Suche sehr häufig auftaucht, dachte ich, ich würde abwägen, da keine Antwort die drei Fälle abdeckt, die ich für wichtig halte - meiner Meinung nach die primäre Verwendung für diese Methoden. Natürlich gehen alle davon aus, dass tatsächlich ein benutzerdefiniertes Serialisierungsformat erforderlich ist.
Nehmen Sie zum Beispiel Sammlungsklassen. Die Standard-Serialisierung einer verknüpften Liste oder einer BST würde zu einem enormen Platzverlust mit sehr geringem Leistungsgewinn führen, verglichen mit der einfachen Serialisierung der Elemente in der angegebenen Reihenfolge. Dies gilt umso mehr, wenn es sich bei einer Sammlung um eine Projektion oder eine Ansicht handelt. Dabei wird auf eine größere Struktur verwiesen, als durch die öffentliche API verfügbar gemacht wird.
Wenn das serialisierte Objekt unveränderliche Felder enthält, für die eine benutzerdefinierte Serialisierung erforderlich ist, reicht die ursprüngliche Lösung von
writeObject/readObject
nicht aus, da das deserialisierte Objekt vor dem Lesen des eingeschriebenen Teils des Streams erstellt wirdwriteObject
. Nehmen Sie diese minimale Implementierung einer verknüpften Liste:Diese Struktur kann serialisiert werden, indem das
head
Feld jedes Links rekursiv geschrieben wird, gefolgt von einemnull
Wert. Das Deserialisieren eines solchen Formats wird jedoch unmöglich:readObject
Die Werte der Mitgliedsfelder können nicht geändert werden (jetzt festgelegt aufnull
). Hier kommt daswriteReplace
/readResolve
Paar:Es tut mir leid, wenn das obige Beispiel nicht kompiliert wird (oder funktioniert), aber hoffentlich reicht es aus, um meinen Standpunkt zu veranschaulichen. Wenn Sie der Meinung sind, dass dies ein sehr weit hergeholtes Beispiel ist, denken Sie bitte daran, dass viele funktionale Sprachen auf der JVM ausgeführt werden und dieser Ansatz in ihrem Fall von wesentlicher Bedeutung ist.
Wir möchten vielleicht tatsächlich ein Objekt einer anderen Klasse deserialisieren, als wir an das geschrieben haben
ObjectOutputStream
. Dies wäre der Fall bei Ansichten wie einerjava.util.List
Listenimplementierung, die ein Slice von einem längeren verfügbar machtArrayList
. Offensichtlich ist es eine schlechte Idee, die gesamte Hintergrundliste zu serialisieren, und wir sollten nur die Elemente aus dem angezeigten Slice schreiben. Warum jedoch damit aufhören und nach der Deserialisierung ein nutzloses Maß an Indirektion haben? Wir könnten einfach die Elemente aus dem Stream in einen lesenArrayList
und ihn direkt zurückgeben, anstatt ihn in unsere Ansichtsklasse einzuschließen.Alternativ kann eine ähnliche Delegatenklasse, die der Serialisierung gewidmet ist, eine Entwurfswahl sein. Ein gutes Beispiel wäre die Wiederverwendung unseres Serialisierungscodes. Wenn wir beispielsweise eine Builder-Klasse haben (ähnlich dem StringBuilder für String), können wir einen Serialisierungsdelegierten schreiben, der jede Sammlung serialisiert, indem wir einen leeren Builder in den Stream schreiben, gefolgt von der Sammlungsgröße und den vom Iterator der Spalte zurückgegebenen Elementen. Bei der Deserialisierung wird der Builder gelesen, alle anschließend gelesenen Elemente angehängt und das Ergebnis des Finales
build()
von den Delegierten zurückgegebenreadResolve
. In diesem Fall müssten wir die Serialisierung nur in der Stammklasse der Auflistungshierarchie implementieren, und es wäre kein zusätzlicher Code für aktuelle oder zukünftige Implementierungen erforderlich, sofern sie abstrakt implementiereniterator()
undbuilder()
Methode (letztere zur Neuerstellung der Sammlung des gleichen Typs - was an sich eine sehr nützliche Funktion wäre). Ein anderes Beispiel wäre eine Klassenhierarchie, deren Code wir nicht vollständig steuern. Unsere Basisklasse (n) aus einer Drittanbieter-Bibliothek können eine beliebige Anzahl von privaten Feldern enthalten, von denen wir nichts wissen und die sich von einer Version zur anderen ändern können unsere serialisierten Objekte. In diesem Fall wäre es sicherer, die Daten zu schreiben und das Objekt bei der Deserialisierung manuell neu zu erstellen.quelle
Die readResolve-Methode
Bei serialisierbaren und externisierbaren Klassen ermöglicht die readResolve-Methode einer Klasse, das aus dem Stream gelesene Objekt zu ersetzen / aufzulösen, bevor es an den Aufrufer zurückgegeben wird. Durch die Implementierung der readResolve-Methode kann eine Klasse die Typen und Instanzen ihrer eigenen zu deserialisierenden Instanzen direkt steuern. Die Methode ist wie folgt definiert:
ANY-ACCESS-MODIFIER Das Objekt readResolve () löst eine ObjectStreamException aus.
Die readResolve- Methode wird aufgerufen, wenn ObjectInputStream ein Objekt aus dem Stream gelesen hat und sich darauf vorbereitet, es an den Aufrufer zurückzugeben. ObjectInputStream prüft, ob die Klasse des Objekts die readResolve-Methode definiert. Wenn die Methode definiert ist, wird die readResolve-Methode aufgerufen, damit das Objekt im Stream das zurückzugebende Objekt festlegen kann. Das zurückgegebene Objekt sollte von einem Typ sein, der mit allen Verwendungszwecken kompatibel ist. Wenn es nicht kompatibel ist, wird eine ClassCastException ausgelöst, wenn die Typinkongruenz festgestellt wird.
Zum Beispiel kann ein Symbol kann Klasse , für die geschaffen werden , um nur eine einzige Instanz jeden Symbol Bindung innerhalb einer virtuellen Maschine vorhanden ist. Die readResolve- Methode würde implementiert, um festzustellen, ob dieses Symbol bereits definiert wurde, und das bereits vorhandene äquivalente Symbolobjekt zu ersetzen, um die Identitätsbeschränkung beizubehalten. Auf diese Weise kann die Eindeutigkeit von Symbolobjekten über die Serialisierung hinweg beibehalten werden.
quelle
Wie bereits beantwortet,
readResolve
handelt es sich um eine private Methode, die in ObjectInputStream beim Deserialisieren eines Objekts verwendet wird. Dies wird unmittelbar vor der Rückgabe der tatsächlichen Instanz aufgerufen. Im Fall von Singleton können wir hier die Rückgabe einer bereits vorhandenen Singleton-Instanzreferenz anstelle einer deserialisierten Instanzreferenz erzwingen. Ähnlich haben wirwriteReplace
für ObjectOutputStream.Beispiel für
readResolve
:}}
Ausgabe:
quelle