Montag, 9. August 2010

Java Serialisierung

Serialisierung ist die Möglichkeit ein Objekt aus dem Hauptspeicher in einen persistenten Zustand zu transformieren. Entweder handelt es sich um immutable (unveränderliche) Werte aus den Java Standardtypen oder die betreffende Klasse muss das Interface Serializable implementieren. Dieses Interface besitzt keine Methoden, sondern dient als sogenanntes Marker-Interface zur Freischaltung der Serialisierbarkeit. Außerdem müssen in der aufsteigenden Vererbungshierarchie (in Richtung Object) alle Superklassen ebenfalls Serializable implementieren, außer man definiert eigene Methoden für die Serialisierung, welche dann aber nicht die Default-Serialisierung (s.u.) aufrufen dürfen. Alle Sub-Klassen einer serialisierbaren Klasse sind automatisch als serialisierbar markiert. Um als Subklasse diese Serialisierbarkeit wieder zu entfernen kann man folgenden Trick anwenden:
/**
* Modified from: "Discover the secrets of the Java Serialization API"
* by Todd Greanier July 2000
*/
private void writeObject(ObjectOutputStream out) throws IOException
{
  throw new NotSerializableException("Serialisierung wird nicht unerstützt");
}

private void readObject(ObjectInputStream in) throws IOException
{
  throw new NotSerializableException("Serialisierung wird nicht unerstützt");
}

Alle Membervariablen einer Instanz einer serialisierbaren Klasse werden rekursiv serialisert. Sollte bei dieser Rekursion ein Element auftauchen, das die Serialisation nicht unterstützt, dann wird eine Exception geworfen. Um dies zu verhindern, kann man betreffende nicht zu serialiserende Member der Instanz mit transient kennzeichen. Solche Elemente werden bei der rekursiven Serialisierung ausgelassen und müssen mit entsprechendem Programmcode beim Deserialisieren manuell wiederhergestellt werden.

Beispiel:

/**
* Modified from: "Discover the secrets of the Java Serialization API"
* by Todd Greanier July 2000
*/
import java.io.Serializable;

public class PersistentAnimation implements Serializable, Runnable {
    transient private Thread animator;
    private int animationSpeed;

    public PersistentAnimation(int animationSpeed) {
        this.animationSpeed = animationSpeed;
        startAnimation();
    }

    private void startAnimation() {
        animator = new Thread(this);
        animator.start();
    }

    public void run() {
        while (true) {
            // Hier steht Code für eigentliche Animation
        }
    }

    /**
     * Hook-Methode == Inverse of Control (IOC)
     * PersistentAnimation aPA = ...;
     * ObjectOutputStream aOOS = ...;
     * 
     * aOOs.writeObject(aPA);
     *      //Pseudcode der Implementierung
     *      { if(aPA besitzt Methode "private void writeObject(ObjectOutputStream)")
     *        { 
     *              //speichere aktuelles Argument in Membervariable
     *              aktiveObject = aPA;
     *              aPA.writeObject(this);
     *         } else writeUnhooked(aPA);
     *      }
     * 
     * aOOs.defaultWriteObject();
     *      //Pseudocode
     *      { 
     *          //schreibe Aufrufer der Methode
     *          writeUnhooked(aktiveObject);
     *      }
     * 
     * @param out
     *            ObjectOutputStream, welches this serialisiert:
     *            out.writeObject(this);
     * @throws IOException
     */
    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
    }

    /**
     * @param in
     *            ObjectInputStream, welches this per Deserialisierung
     *            initialisiert.
     * @throws IOException
     * @throws ClassNotFoundException
     *             Wenn Klassendatei nicht zur Verfügung steht
     */
    private void readObject(ObjectInputStream in) throws IOException,
        ClassNotFoundException {
        // Initialisiere this mit Zustand aus vorheriger Serialisation
        in.defaultReadObject();
        // transient Member wurde nicht serialisert, also manuell besetzen
        startAnimation();

    }
}

Man beachte, dass der Aufruf von out.defaultWriteObject() alle Felder der aktuellen Objektinstanz, unabhängig von einer manuell erfolgenden Serialisierung, serialisiert. Dabei serialisiert es alle Felder der Instanz aus den Klassen der Vererbungshierarchie beginnend mit der Basisklasse (Tiefensuche) und endend mit der Laufzeitklasse. Analog verhält es sich mit in.defaultReadObject().


Quellen:

Keine Kommentare:

Kommentar veröffentlichen