[JAVA] Punkt 87: Erwägen Sie die Verwendung eines benutzerdefinierten serialisierten Formulars

87. Erwägen Sie die Verwendung eines benutzerdefinierten serialisierten Formulars

Wenn Sie ein reguläres serialisiertes Formular wählen, müssen Sie denken, dass Sie es in Zukunft nicht mehr reparieren können.

Wählen Sie nicht das standardisierte serialisierte Formular, ohne zu überlegen, ob es angemessen ist oder nicht

Im Allgemeinen sollten Sie das standardisierte serialisierte Formular nur wählen, wenn die Codierungsergebnisse fast dieselben sind wie beim Entwerfen eines benutzerdefinierten serialisierten Formulars. Die standardmäßige serialisierte Form ist eine effiziente Codierung der physischen Darstellung des Objektgraphen. Mit anderen Worten

Wenn das standardmäßige serialisierte Formular in Ordnung ist

** Die standardmäßige serialisierte Form ist häufig geeignet, wenn die physische Darstellung des Objekts und der logische Inhalt übereinstimmen. ** ** ** Das folgende serialisierte Formular eignet sich beispielsweise für eine Klasse, die einfach den Namen einer Person ausdrückt.

// Good candidate for default serialized form
public class Name implements Serializable {

    /**
     * must be non-null
     * @serial
     */
    private final String lastName;

    /**
     * must be non-null
     * @serial
     */
    private final String firstName;

    /**
     * Middle name, or null if there is none
     * @serial
     */
    private final String middleName;

    public Name(String lastName, String firstName, String middleName) {
        super();
        this.lastName = lastName;
        this.firstName = firstName;
        this.middleName = middleName;
    }

}

Das Instanzfeld Name entspricht dem logischen Inhalt.

** Auch wenn Sie entscheiden, dass das standardmäßige serialisierte Formular geeignet ist, müssen Sie möglicherweise eine readObject-Methode bereitstellen, um unveränderliche Bedingungen und Sicherheit zu gewährleisten. ** ** ** Im Fall des obigen Namens muss die readObject-Methode sicherstellen, dass lastName und firstName nicht null sind. Dies wird in den Punkten 88 und 90 ausführlich beschrieben.

Obwohl der Nachname, der Vorname und der zweite Vorname des Namens private Felder sind, werden sie dokumentiert. Der Grund ist, dass diese privaten Felder als serialisierte Form der Klasse verfügbar gemacht werden. Wenn Sie das Tag @serial hinzufügen, können Sie mit dem generierten Javadoc eine eigene Seite zum serialisierten Formular erstellen.

Wenn das standardmäßige serialisierte Formular nicht geeignet ist

Stellen Sie sich eine Klasse vor, die eine Liste von Zeichenfolgen darstellt, wie unten gezeigt, vorausgesetzt, sie befindet sich genau gegenüber der Name-Klasse.

//Darf nicht das standardmäßige serialisierte Formular sein!
public final class StringList implements Serializable {
    private int size = 0;
    private Entry head = null;

    private static class Entry {
        String data;
        Entry previous;
        Entry next;
    }
}

Logischerweise repräsentiert diese Klasse eine Folge von Zeichenfolgen. Physikalisch repräsentiert es eine Sequenz als bidirektionale Liste. Wenn Sie das standardisierte serialisierte Formular akzeptieren, spiegelt das serialisierte Formular alle Elemente der verketteten Liste und alle Verknüpfungen zwischen den Einträgen wider.

** Die Verwendung des standardmäßigen serialisierten Formulars hat vier Nachteile, wenn eine Diskrepanz zwischen physischer Darstellung und logischem Inhalt besteht. ** ** **

Überarbeitete String-Liste

Eine rational serialisierte Form einer StringList ist eine Liste, die aus mehreren Strings besteht. Diese Struktur ist eine logische Darstellung der Daten in einer StringList. Unten finden Sie eine überarbeitete StringList mit writeObject und readObject.

package tryAny.effectiveJava;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

//Verbesserte StringList
public final class StringList2 implements Serializable {
    private transient int size = 0;
    private transient Entry head = null;

    // No longer Serializable
    private static class Entry {
        String data;
        Entry previous;
        Entry next;
    }

    //Fügen Sie der Liste eine Zeichenfolge hinzu
    public final void add(String s) {

    }

    private void writeObject(ObjectOutputStream s) throws IOException {
        s.defaultWriteObject();
        s.writeInt(size);

        for (Entry e = head; e != null; e = e.next) {
            s.writeObject(e.data);
        }
    }

    private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
        s.defaultReadObject();
        int numElements = s.readInt();

        for (int i = 0; i < numElements; i++) {
            add((String) s.readObject());
        }
    }
}

In der obigen Klasse ruft writeObject zuerst die defaultWriteObject-Methode auf und readObject ruft zuerst die defatlReadObject-Methode auf. Es wird manchmal gesagt, dass es nicht erforderlich ist, die Methoden defaultWriteObject und defatlReadObject aufzurufen, wenn alle Felder in der Klasse vorübergehend sind. Dies ist jedoch nicht der Fall und muss aufgerufen werden. Diese Aufrufe ermöglichen das Hinzufügen nicht vorübergehender Felder in späteren Versionen unter Wahrung der Kompatibilität.

In Bezug auf die Leistung hat die überarbeitete StringList, gemessen mit einer Liste von 10 Zeichenfolgen mit 10 Zeichen, etwa die Hälfte des vorherigen Arbeitsspeichers, verdoppelt die Serialisierungsgeschwindigkeit und beseitigt die Sorge um einen Stapelüberlauf.

Wenn die invariante Bedingung von Object nicht an die Implementierung gebunden ist

Die physische Implementierung der Hash-Tabelle besteht aus einer Reihe wichtiger Hash-Werte in Hash-Buckets. Der Hash-Wert des Schlüssels hängt von der Implementierung ab. Daher führt die Verwendung des standardmäßigen serialisierten Formulars zu schwerwiegenden Fehlern.

transient Durch Aufrufen von defaultWriteObject werden alle Felder mit Ausnahme des vorübergehend angegebenen Felds serialisiert, unabhängig davon, ob das standardisierte serialisierte Formular verwendet wird. Versuchen Sie, den Transientenmodifikator so weit wie möglich hinzuzufügen, um unnötige Serialisierungen zu vermeiden.

Als vorübergehend gekennzeichnete Felder haben beim Deserialisieren Standardanfangswerte. Mit anderen Worten, es ist null für Referenztypvariablen, 0 für int und false für boolean. Wenn dies nicht akzeptabel ist, legen Sie es mit der readObject-Methode (Item88) fest oder verzögern Sie die Initialisierung, wenn Sie es verwenden (Item83).

Wenn andere Methoden für die Synchronisation verwendet werden, wird die Synchronisation auch während der Serialisierung durchgeführt.

Da zum Zeitpunkt der Serialisierung eine Synchronisierung erforderlich ist, muss auch eine Synchronisierung zu writeObject hinzugefügt werden.

Geben Sie die serialVersionUID an

Indem Sie es explizit machen, können Sie Inkompatibilitäten mit Quellen verhindern (Item86). Da die serialVersionUID zur Laufzeit nicht generiert werden muss, wird die Leistung geringfügig verbessert. Beim Schreiben einer neuen Klasse kann diese Zahl beliebig sein und muss kein eindeutiger Wert sein.

Wenn Sie einer vorhandenen Klasse eine UID zuweisen und die Kompatibilität beibehalten möchten, müssen Sie eine ältere Version der UID verwenden.

Recommended Posts

Punkt 87: Erwägen Sie die Verwendung eines benutzerdefinierten serialisierten Formulars
Punkt 90: Betrachten Sie Serialisierungs-Proxys anstelle von serialisierten Instanzen
So löschen Sie benutzerdefinierte Adapterelemente mithilfe eines benutzerdefinierten Modells
[Einführung in Spring Boot] Senden Sie ein Formular mit thymeleaf