[JAVA] Punkt 86: Implementieren Sie Serializable mit großer Vorsicht

86. Seien Sie sehr vorsichtig bei der Implementierung von Serializable

Kosten für die Serialisierung

Die Serialisierung kann in Sekunden erfolgen, kann aber auf lange Sicht sehr kostspielig sein.

Die Implementierung von Serializable verliert die Flexibilität, die Implementierungsklasse nach ihrer Veröffentlichung zu ändern

Die Tatsache, dass die Klasse Serializable implementiert, bedeutet, dass die bytestream-codierte Version (im Folgenden serialisierte Form) Teil der veröffentlichten API wird. Sobald eine Klasse veröffentlicht wurde, muss sie für immer unterstützt werden. In der nicht angepassten standardisierten serialisierten Form sind private Felder ebenfalls Teil der öffentlichen API, sodass das Design, mit dem zugängliche Felder (Element 15) minimiert werden, bedeutungslos wird.

Wenn Sie das standardmäßige Auswahlformular akzeptieren und später die interne Darstellung der Klasse ändern, treten inkompatible Änderungen auf. Probleme treten auf, wenn der Client eine Instanz mit einer älteren Version der Klasse serialisiert und mit einer neueren Version der Klasse deserialisiert. Es ist möglich, die Konvertierung durchzuführen, aber es ist schwierig und macht den Code schmutzig. Bei der Implementierung von Serializable muss die langfristige Verwendung berücksichtigt und versucht werden, eine qualitativ hochwertige zu entwerfen (Pos. 87,90).

Als Beispiel für die Einschränkungen bei der Implementierung von Serializable ist die UID der seriellen Version beteiligt. Alle serialisierbaren Klassen haben eine UID-Nummer der Serienversion, um sich zu beweisen. (** Es scheint festzustellen, ob die Klassenversion vor und nach der Wiederherstellung unterschiedlich ist. **) (http://www.ne.jp/asahi/hishidama/home/tech/java/serial.html) Wenn Sie dies nicht als statisches letztes langes Feld definieren, wird es zur Laufzeit automatisch zugewiesen. Dieser Wert wird durch den Klassennamen, die Mitglieder usw. beeinflusst. Wenn etwas geändert wird, ändert sich auch dieser Wert. Wenn Sie keine UID definieren und die Kompatibilität unterbrochen ist, tritt zur Laufzeit eine InvalidClassException auf.

Die Implementierung von Serializable erhöht das Potenzial für Fehler und Sicherheitslücken

Deserialisierung ist wie ein "unsichtbarer Konstruktor". Es ist leicht zu vergessen, dass Sie, da es unsichtbar ist, die vom Konstruktor festgelegte Invariante garantieren müssen, um zu verhindern, dass ein Angreifer auf das Objekt zugreift, das Sie erstellen. Wenn Sie sich auf den Standardmechanismus für die Deserialisierung verlassen, können Sie leicht einen unbefugten Zugriff auf eine Invariante zerstören oder erhalten.

Die Implementierung von Serializable erhöht den Aufwand für die Freigabe einer neuen Version der Klasse

Wenn eine Klasse, die Serializable implementiert, überarbeitet wird, muss überprüft werden, ob die Instanz der neuen Version serialisiert werden kann, die ältere Version deserialisiert werden kann und umgekehrt. Daher nimmt die Anzahl der Testfälle proportional zum Produkt aus der Anzahl der Versionen und der Implementierungsklasse für die Serialisierung zu. Die Testanforderungen können reduziert werden, wenn die erste Ausgabe der Implementierungsklasse Serializable sorgfältig entworfen und enthalten ist (Punkt 87,90).

Die Implementierung von Serializable ist nicht leicht zu entscheiden

Die Implementierung von Serializable ist wichtig, um Klassen in Frameworks einzubetten, die Java-Serialisierung für den Objekttransport und die Persistenz verwenden. Die Implementierung von Serializable erleichtert auch die Handhabung als Komponente aus anderen Klassen. Wie oben erwähnt, sind mit der Implementierung von Serializable jedoch verschiedene Kosten verbunden. Beim Entwerfen von Klassen müssen sie gewogen werden.

In der Vergangenheit implementieren Wertklassen wie die Klassen BigInteger und Instant Serializable, jedoch selten in Klassen, die aktive Entitäten wie den Thread-Pool darstellen.

Klassen und Schnittstellen, die für die Vererbung entwickelt wurden, sind einfach, implementieren Serializable und sollten nicht vererbt werden

Ein Verstoß gegen diese Regel stellt eine schwere Belastung für die Unterklassen dieser Klasse dar. Als Ausnahme von dieser Regel ist es verständlich, dass Klassen und Schnittstellen, die im Framework vorhanden sind und für alle Beteiligten eine serialisierbare Implementierung erfordern, Serializable implementieren. Throwable und Component implementieren Serializable. Da Throwable Serializable implementiert, kann RMI Ausnahmen vom Server an den Client senden. Da die Komponente Serializable implementiert, werden GUI-Elemente transportiert und gespeichert. Diese Funktion wurde in der Blütezeit von Swing und AWT nicht viel genutzt.

Seien Sie vorsichtig, wenn Sie eine Klasse implementieren, die serialisierbar und erweiterbar ist und Felder enthält

Unterklassen sollten die finalize-Methode nicht überschreiben, wenn das Instanzfeld eine Variante enthält. Andernfalls besteht die Gefahr eines Finalizer-Angriffs (Punkt 8).

Wenn die Invariante der Klasse durch die Initialisierung des Instanzfelds beschädigt wird, muss die Methode readObjectNoData hinzugefügt werden.

private void readObjectNoData()
     throws ObjectStreamException;

(** Geheimnis hier **)

Vorsichtsmaßnahmen, wenn es nicht serialisierbar wird

Das Schreiben von serialisierbaren Unterklassen ist mühsamer, wenn Sie sie in Ihrer Vererbungsklasse nicht serialisierbar machen. Die normale Deserialisierung solcher Klassen erfordert einen Konstruktor ohne Argumente mit Zugriff auf die Oberklasse. Wenn dies nicht verfügbar ist, verwenden Sie das Serialization Proxy Pattern (Item90).

Die innere Klasse sollte Serializable nicht implementieren

Die innere Klasse verwendet synthetische Felder. Synthetische Felder werden vom Compiler generiert und enthalten einen Verweis auf die einschließende Instanz und einen Verweis auf eine lokale Variable aus dem einschließenden Bereich.

Da die Form der Serialisierung für synthetische Felder nicht definiert ist, sollten serialisierbare Implementierungen in inneren Klassen vermieden werden. Statische Elementklassen können jedoch serialisierbar implementiert werden.

Recommended Posts

Punkt 86: Implementieren Sie Serializable mit großer Vorsicht
Implementieren Sie GraphQL mit Spring Boot
jCaptcha-Reload mit Ajax implementiert