Java RMI Serialisierung - O'Reilly Verlag

Wie man eine Klasse Serializable

Bisher haben wir uns auf die Mechanik fokussiert, um ein Objekt von Serialisierung. Wir haben angenommen, wir haben ein serialisierbares Objekt und diskutiert wird, aus der Sicht von Client-Code, wie es serialisieren. Der nächste Schritt wird diskutiert, wie eine Klasse serialisierbar zu machen.

Es gibt vier grundlegende Dinge, die Sie tun müssen, wenn Sie eine Klasse serialisierbar machen. Sie sind:

  1. Implementieren Sie die Serializable-Schnittstelle.
  2. Stellen Sie sicher, dass Instanzebene, lokal definierten Zustand richtig serialisiert wird.
  3. Stellen Sie sicher, dass übergeordnete Klasse Zustand richtig serialisiert wird.
  4. Überschreiben equals () und hashCode ().

Lassen Sie uns an jedem dieser Schritte im Detail aussehen.

Implementieren Sie die Serializable Schnittstelle

Datei-file = new File ( "c: \\ Temp \\ foo");

Es ist nicht klar, was ausgeschrieben werden soll, wenn diese serialisiert wird. Das Problem ist, dass die Datei selbst einen anderen lifecyle als die serialisierten Daten hat. Die Datei kann vollständig bearbeitet oder gelöscht werden, während die serialisierten Informationen bleiben unverändert. Oder die serialisierten Informationen können verwendet werden, um die Anwendung auf einem anderen Computer neu zu starten, wobei „C: \\ Temp \\ foo“ ist der Name einer ganz anderen Datei.

Ein weiteres Beispiel ist die Thread-Klasse zur Verfügung gestellt. (Wenn Sie nicht viel über Themen wissen, nur ein paar Kapitel warten und dann dieses Beispiel überdenken. Es wird mehr Sinn machen, dann.) Ein Thread stellt einen Ablauf der Ausführung innerhalb einer bestimmten JVM. Sie würde nicht nur den Stapel speichern haben, und alle lokalen Variablen, sondern auch alle damit verbundenen Schlösser und Fäden, und starten Sie alle Fäden richtig, wenn die Instanz deserialisiert wird.

TIPP: Die Dinge werden schlimmer, wenn Sie Plattform Abhängigkeiten berücksichtigen. In der Regel, dass jede Klasse nativen Code beinhaltet ist nicht wirklich ein guter Kandidat für die Serialisierung.

Sicherstellen, dass Instanzebene, Regional aufbereiteter Zustand serialisiert ordnungsgemäß

Der Serialisierungsmechanismus hat ein schönes Standardverhalten - wenn alle Instanzebene, lokal definierten Variablen Werte haben, die entweder serialisierbare Objekte oder primitive Datentypen sind, dann wird der Serialisierungsmechanismus unsererseits ohne weiteren Aufwand arbeiten. Zum Beispiel unserer Implementierungen von Konto. wie Account_Impl. keine Probleme für den Standard-Serialisierungsmechanismus darstellen würde:

public class Account_Impl erstreckt UnicastRemoteObject Geräte privates Geld _balance Konto;
.
>

Während _balance keinen primitiven Typen hat, ist es zu einer Instanz von Geld beziehen. die eine serializable Klasse.

public class Arraylist erweitert AbstractList implementiert List, klonbar, java.io.
Serializable privates Objekt elementData [];
private int size;
.
>

Aber versteckt in hier ist ein großes Problem: Arraylist ist eine generische Container-Klasse, deren Zustand als ein Array von Objekten gespeichert. Während Arrays erstklassige Objekte in Java sind, sind sie nicht serialisierbar Objekte. Das bedeutet, dass Arraylist kann nicht nur die Serializable-Schnittstelle implementieren. Es hat zusätzliche Informationen zur Verfügung zu stellen der Serialisierungsmechanismus behandelt ihre nonserializable Felder zu helfen. Es gibt drei grundlegende Lösungen für dieses Problem:

public class Arraylist erweitert AbstractList implementiert List, klonbar, java.io.
Serializable privates transientes Objekt elementData [];
private int size;
.
>

Dies teilt den Standard Serialisierungsmechanismus die Variable zu ignorieren. Mit anderen Worten, überspringt der Serialisierungsmechanismus einfach die transienten Variablen über. Im Fall von Arraylist. der Standard-Serialisierungsmechanismus versuchen würde, eine Größe zu schreiben. aber ignorieren elementData vollständig.

Dies kann in zwei, in der Regel unterschiedliche Situationen nützlich sein:

Implementieren write () und readObject- ()

private void write (java.io.ObjectOutputStream out) throws IOException private void readObject- (java.io.ObjectInputStream in) throws IOException, ClassNotFoundException;

Wenn der Serialisierungsmechanismus ein Objekt zu schreiben beginnt, wird es prüfen, um zu sehen, ob die Klasse implementiert write (). Wenn ja, wird die Serialisierung Mechanismus nicht den Standardmechanismus verwendet werden und wird nicht der Instanzvariablen auszuschreiben. Stattdessen wird es write () und sind abhängig von der Methode aufrufen, die alle wichtigen Zustand speichern aus. Hier ist Arraylist ‚s Implementierung von write ():

Private synchronized void write (java.io.ObjectOutputStream Stream) wirft Java.
io.IOException stream.defaultWriteObject ();
stream.writeInt (elementData.length);
for (int i = 0; i stream.writeObject (elementData [i]);
>

Das erste, was das bedeutet ist defaultWriteObject () aufrufen. defaultWriteObject () ruft den Standard Serialisierungsmechanismus, der alle Variablen nontransient, nicht-statische Instanz serialisiert. Als nächstes schreibt die Methode aus elementData.length und ruft dann die write Stream () für jedes Element von elementData.

Es ist ein wichtiger Punkt, dass hier manchmal verpasst: readObject- () und write () ein Paar von Methoden, die gemeinsam umgesetzt werden müssen. Wenn Sie irgendeine Anpassung der Serialisierung innerhalb eines dieser Verfahren zu tun, müssen Sie die andere Methode implementieren. Wenn Sie dies nicht tun, wird die Serialisierung Algorithmus fehlschlagen.

Unit-Tests und Serialisierung

Wenn Sie eine Einheit-Testmethode annehmen, dann sollte jede serializable Klasse die folgenden drei Prüfungen bestehen:

  • Wenn es implementiert readObject- (). es sollte write () implementieren. und umgekehrt.
  • Es ist gleich einer serialisierten Kopie von sich selbst (die equals () Methode verwendet wird).
  • Es hat den gleichen Hash-Code als eine serialisierte Kopie von mir selbst.

Ähnliche Einschränkungen gelten für Klassen, die die Externalizable-Schnittstelle implementieren.

Bisher haben wir nur über Instanzebene Zustand gesprochen. Was ist auf Klassenebene Zustand? Angenommen, Sie in einer statischen Variablen gespeichert wichtige Informationen haben? Statische Variablen werden nicht durch Serialisierung gerettet werden, wenn Sie spezielle Code hinzufügen, dies zu tun. In unserem Zusammenhang (ohne Versand Objekte über den Draht zwischen Clients und Servern), Statik ist in der Regel eine schlechte Idee sowieso.

Stellen Sie sicher, dass übergeordnete Klasse Staat korrekt umgegangen wird

Ein weiterer Aspekt, den Zustand eines nonserializable übergeordnete Klasse der Handhabung ist, dass nonserializable Super einen Konstruktor ohne Argumente haben muss. Das ist nicht wichtig für die Serialisierung eines Objekts aus, aber es ist unglaublich wichtig, wenn ein Objekt deserialisiert. Deserialisierung funktioniert durch eine Instanz einer Klasse erstellen und korrekt ihre Felder ausfüllen. Während dieses Vorgangs wird der Deserialisierung Algorithmus nicht nennen tatsächlich jede der Konstrukteure der serialisierten Klasse, aber nicht rufen Sie den Konstruktor ohne Argumente des ersten nonserializable geordneten Klasse. Wenn es keine Null Argument Konstruktor ist, dann kann die Deserialisierung Algorithmus keine Instanzen der Klasse erstellen, und der gesamte Prozess fehlschlägt.

WARNUNG: Wenn Sie nicht einen Konstruktor ohne Argumente in der ersten nonserializable Super erstellen, werden Sie stattdessen die Externalizable Schnittstelle implementieren müssen.

ein Null-Argument Konstruktor einfaches Hinzufügen könnte ein wenig problematisch zu sein scheint. Angenommen, das Objekt bereits mehrere Konstrukteure hat, von denen alle Argumente. Wenn Sie einfach einen Konstruktor ohne Argumente hinzufügen, dann könnte der Serialisierungsmechanismus das Objekt in einem Halb initialisiert, verlassen und damit unbrauchbar, Zustand.

da die Serialisierung der Instanzvariablen jedoch mit korrekten Werten aus einer aktiven Instanz unmittelbar nach dem Objekt instanziiert wird, die einzige Möglichkeit, dieses Problem entstehen liefern könnte, wenn die Konstrukteure ihre Argumente etwas tun, eigentlich mit - neben Variablenwert zu setzen.

zu etwas, das wie folgt aussieht:

Nachdem dies geschehen ist, sollte write () / readObject- () implementiert werden und readObject- () sollte mit einem Anruf beenden, um () initialisiert werden. Manchmal wird dies in Code zur Folge hat, die einfach den Mechanismus Standardserialisierung aufruft, wie im folgenden Ausschnitt:

private void write (java.io.ObjectOutputStream Strom) throws
java.io.IOException stream.defaultWriteObject ();
>

private void readObject- (java.io.ObjectInputStream Strom) throws
java.io.IOException stream.defaultReadObject ();
intialize ();
>

TIPP: Wenn ein Konstruktor ohne Argumente zu schaffen schwierig ist (zum Beispiel, die Sie nicht den Quellcode für die übergeordnete Klasse haben), Ihre Klasse müssen die Externalizable Schnittstelle statt Serializable implementieren.

Überschreiben equals () und hashCode (), wenn notwendig

Die Standard-Implementierungen von equals () und hashCode (). welche von java.lang.Object geerbt. einfach eine Instanz Standort im Speicher verwenden. Dies kann problematisch sein. Betrachten wir unsere bisherigen tiefe Kopie Codebeispiel:

ByteArrayOutputStream memoryOutputStream = new ByteArrayOutputStream ();
Object Serializer = new Object (memoryOutputStream);
serializer.writeObject (serializableObject);
serializer.flush ();

ByteArrayInputStream memoryInputStream = new ByteArrayInputStream (memoryOutputStream.
toByteArray ());
Object Deserializer = new Object (memoryInputStream);
Objekt deepCopyOfOriginalObject = deserializer.readObject ();

Das potenzielle Problem hier umfasst die folgenden Booleschen Test:

Manchmal, wie im Fall von Geld und DocumentDescription. die Antwort wahr sein sollte. Wenn zwei Instanzen von Geld haben die gleichen Werte für _cents. sie sind dann gleich. Allerdings erbte die Implementierung von equals () von Object wird false zurück.

Das gleiche Problem tritt mit hashCode (). dieses Objekt implementiert Hinweis hashCode () mit der Speicheradresse der Instanz zurück. Daher haben keine zwei Instanzen immer die gleiche hashCode () Object ‚s Implementierung verwenden. Wenn zwei Objekte gleich sind, aber dann sollten sie den gleichen Hash-Code haben. Wenn Sie also equals außer Kraft zu setzen (). Sie müssen wahrscheinlich hashCode () als auch außer Kraft zu setzen.

In Verbindung stehende Artikel