Kapitel 8: Serialisierbarkeit

Werbung
Liste P: Programmieren mit Java
WS 2001/2002
Prof. Dr. V. Turau
FH Wiesbaden
Kapitel 8: Serialisierbarkeit
Folie 189 : Serialisierbarkeit
Die Umwandlung eines komplexen Objektes in ein Byte-Array nennt man Serialisierung
Serialisierte Objekte können zu einem späteren Zeitpunkt wieder reproduziert werden, dieser
Vorgang heißt Deserialisierung
Bei der Serialisierung werden alle Informationen über die Struktur des Objektes abgespeichert
Die binäre Darstellung kann
in einer Datei abgespeichert
über ein Netzwerk verschickt werden
Folie 190 : Serialisierbarkeit
Die Serialisierung wurde in Java Version 1.1 eingeführt
Die Serialisierung von Objekten hat viele Anwendungen:
Verteilte Systeme (RMI, Java Enterprise Beans, CORBA)
Persistente Speicherung von Objekten
Das Serialisierungskonzept in Java stützt sich auf wenige Klassen und Methoden und lässt
sich sehr einfach anwenden
Folie 191 : Serialisierbarkeit (Beispiel)
Instanz der Klasse Stack wird serialisiert und in einer Datei abgespeichert
Stack s = new Stack();
s.push(...);
...
FileOutputStream fos = new FileOutputStream("tmp.ser");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(s);
oos.flush();
oos.close();
In einen ObjectOutputStream können beliebig viele Objekte reingeschrieben werden, sie
müssen nur in der gleichen Reihenfolge wieder ausgelesen werden
Folie 192 : Serialisierbarkeit (Beispiel)
Die serialisierte Instanz der Klasse Stack wird aus der Datei ausgelesen und deserialisiert
Stack s;
FileInputStream fis = new FileInputStream("tmp.ser");
ObjectInputStream ois = new ObjectInputStream(fis);
s = (Stack) ois.readObject();
ois.close();
Folie 193 : Das Interface Serializable
Eine Klasse deren Instanzen serialisiert werden sollen, muss das Interface Serializable aus
dem Paket java.io implementieren
Das Interface Serializable enthält keine Methoden
Nicht jede Instanzvariable eignet sich zum abspeichern (z.B. Instanzen der Klasse File)
Instanzvariablen mit dem Schlüsselwort transient werden nicht serialisiert
Folie 194 : Serialisierbarkeit
Alle Instanzvariablen welche nicht das Schlüsselwort transient haben, werden serialisiert
Die Serialisierung ist ein rekursiver Prozess, d.h. ist der Wert einer Instanzvariablen ein
Objekt, so wird auch dieses serialisiert (d.h. im letzten Beispiel werden die Objekte im Stack
ebenfalls alle serialisiert)
Es wird ein Objekt-Graph gespeichert
Dopppelt referenzierte Objekte werden nur einmal serialisiert
Folie 195 : Komprimierte Speicherung
Zur Reduktion des Speicheraufwandes können Objekte auch komprimiert gespeichert werden
Beispiel:
StringStack st = new StringStack();
.....
FileOutputStream fos = new FileOutputStream("tmp.ser.gz");
GZIPOutputStream gos = new GZIPOutputStream(fos);
ObjectOutputStream oos = new ObjectOutputStream(gos);
oos.writeObject(s);
oos.flush();
oos.close();
Zum Einlesen wird die Klasse GZIPInputStream verwendet
Folie 196 : Spezielle Anwendungen
Der vorgestellte Defaultserialisierungssmechanismus erlaubt keinerlei Eingriffe in den
Serialisierungsprozess
Für manche Anwendungen ist dies nicht ausreichend
Für diesen Fall gibt es zwei Alternativen:
eine Klasse kann das Interface Externalizable implementieren
der Defaultserialisierungssmechanismus kann beeinflusst werden
Diese ermöglicht eine bessere Kontrolle des Serialisierungsprozesses
Anwender tragen aber auch mehr Verantwortung
Folie 197 : Das Interface Externalizable
Das Interface Externalizable
public interface Externalizable extends Serializable {
public void writeExternal(ObjectOutput out)
throws IOException;
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException;
}
Diese Interface sollte nur in speziellen Anwendungen verwendet werden
Folie 198 : Angepasste Serialisierung
Um von dem Defaultserialisierungssmechanismus abzuweichen, kann eine Klasse die
Methoden
private void writeObject(ObjectOutputStream oos)
throws IOException;
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException;
verwenden
In diesen Methoden wird das Serialisierungverfahren implementiert
Innerhalb dieser Methoden kann auf den Defaultmechanismus zurückgegriffen werden
Folie 199 : Angepasste Serialisierung
Hierzu stehen folgende Methoden
public void defaultWriteObject() throws IOException
public void defaultReadObject() throws IOException,
ClassNotFoundException, NotActiveException
aus den Klassen ObjectOutputStream und ObjectInputStream zur Verfügung.
Vor und nach dem Aufruf dieser Methoden können spezielle Anpassungen vorgenommen
werden
Folie 200 : Beispiel
Eine Klasse repräsentiert eine symmetrische Matrix, d.h. Werte oberhalb und unterhalb der
Diagonalen sind identisch
Beispiel:
1
4
7
15
4
3
-2
5
7
-2
-2
17
15
5
17
0
Beim Serialisieren genügt es, die obere "Hälfte" der Matrix zu speichern
Folie 201 : Beispiel
public class CustomDataExample implements Serializable {
transient int dimension;
transient int thearray[][];
private void writeObject(ObjectOutputStream s)
throws IOException {
// Für spätere Erweiterungen jetzt schon aufnehmen
s.defaultWriteObject();
// Die Dimension wird gespeichert
s.writeInt(dimension);
// die obere "Hälfte" der Matrix wird gespeichert
for (int i = 0; i < dimension; i++) {
for (int j = 0; j <= i; j++) {
s.writeInt(thearray[i][j]);
}
}
}
Folie 202 : Beispiel
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException
// Für spätere Erweiterungen jetzt schon aufnehmen
s.defaultReadObject();
// Die Dimension einlesen
dimension = s.readInt();
// Speicherplatz allozieren
thearray = new int[dimension][dimension];
// die obere Hälfte einlesen
for (int i = 0; i < dimension; i++) {
for (int j = 0; j <= i; j++) {
thearray[i][j] = s.readInt();
}
}
// Den Rest rüberkopieren
for (int i = 0; i < dimension; i++) {
for (int j = dimension - 1; j > i; j--) {
thearray[i][j] = thearray[j][i];
}
}
}
Folie 203 : Versionsverwaltung
{
Probleme entstehen, wenn nach der Serialisierung einer Instanz einer Klasse der Quellcode
der Klasse geändert und die Klasse neu übersetzt wird
Jede Änderung einer Instanzvariablen oder der Signatur einer öffentlichen Methode führt zu
Inkompatibilitäten
Kleinere Änderungen der Klasse beeinflussen nicht die Deserialiserbarkeit
Wie kann festgestellt werden, ob die Deserialiserbarkeit verloren geht?
Folie 204 : Versionsverwaltung
Lösung: Versionsnummern
Das Programm serialver bestimmt eine Versionsnummer für eine Klasse (Aufruf mit
serialver -show)
Diese Versionsnummer muss in einer statischen Instanzvariablen mit dem Namen
serialVersionUID vom Typ long abgespeichert werden
static final long serialVersionUID = 1224463164541339165L;
Folie 205 : Das Werkzeug serialver
Bei kleineren Änderungen bleibt diese Nummer unverändert
Das Werkzeug serialver erzeugt mittels des Secure Hash Algorithm (SHA) eine
Versionsnummer
D.h. wird nach einer Änderung der Klasse die gleiche Versionsnummer festgestellt, so ist eine
Deserialiserung weiterhin möglich
Herunterladen