Programmieren 2
11 – Objekt-Serialisierung
Bachelor Medieninformatik
Sommersemester 2015
Dipl.-Inform. Ilse Schmiedecke
schmiedecke@beuth-hochschule.de
1
Noch eine Persistenzalternative
Eine Adresse als Ganzes in den Strom schicken?
Ein ganzes Adressbuch in den Strom schicken?
oder einen Stammbaum?
Oder eine Prozessakte?
Oder eine Spielfigur?
22
Die Idee: Objekt raus, Objekt rein
Java.io unterstützt dieses Konzept mit Binärströmen:
ObjectOutputStream, ObjectInputStream
Verkettung wie gehabt:
out = new ObjectOutputStream(new File OutputStream("myfile.obj");
in = new ObjectInputStream(new FileInputStream("myfile.obj");
Schreiben und Lesen ganzer Objekte:
out.writeObject(myAddressBook);
myAddressBook = (AddressBook)in.readObject();
Könnte es einfacher sein?
keine Schleifen durch alle Einträge
keine Protokolle
33
Vielleicht mal etwas anderes?
Avatar hat einen Namen und eine Liste von Assets
unter anderem auch Bilder (Image-s)
44
Fragen? Bedenken?
Keine Sorge bei einfachen Objekten
Attribute sind primitive Datentypen oder Strings
Objektströme automatisieren das Protokoll, d.h. die Reihenfolge der
Binärinformation
Kompliziertere Attribute?
Avatar enthält ein Bild
Avatar hat Liste von Assets (wie lang?)
Avatar könnte weitere Avatare als Gefährten "kennen"
(Liste Baumstruktur, Zyklen?)
Vererbungshierarchien?
Assets sind Waffen, Fähigkeiten, Kenntnisse... (Unterklassen)
Liste von Assets enthält unterschiedliche Elemente!!
55
Die Antwort heißt Objekt-Serialisierung
Java-Objektserialisierung automatisiert:
die Binärcodierung von Daten (primitive Typen und String)
das Protokoll für die Binärcodierung von Objekte
Avatar1
Avatar2
Avatar3
eine Typkennung für binär codierte Objekte (Polymorphie!)
eine Referenzverfolgung zur Vermeidung von Duplikaten (Zyklen!)
1
2
3
1
2
3
4
6
5
5
4
6
66
Serialisierbarkeit
Eine Klasse, deren Objekte serialisierbar sein sollen, muss das
Interface Serializable implementieren.
public class Avatar implements Serializable {...}
public class Asset implements Serializable {...}
Die Attribute einer serialisierbaren Klassen müssen serialisierbar sein
Serializable ist ein Marker-Interface, es enthält keine Methoden
public interface Serializable { }
Alle primitiven Typen sind Serializable
Fast alle Klassen in den Java-Standardbibliotheken sind Serializable
(wichtige Ausnahme: Object)
Alle Unterklassen einer serialisierbaren Klasse sind (natürlich!)
Serializable
77
Spielprototyp mit Avatar
Player
+name
Avatar
Asset
+name
+coins
+image
0..*
Weapon
Wisdom
+image
+description
Dagger
+name
+value
+strength
+realm
Ein Avatar hat Name, Münzen,
Bild und eine Liste von
"Assets" (Aktivposten)
Eine Asset ist eine Waffe oder
Weisheit
Eine Waffe ist ein Dolch oder
ein Schwert
Weisheit gehört zu einem der
Bereiche Landwirtschaft,
Schmieden, Frieden, Alchimie
oder Kriegskunst
<<enumeration>>
Realm
+Farming
+Forging
+Peace
+Alchimy
+War
Sword
88
Enumeration
Eine Enumeration ist ein Typ
public Realm myRealm;
endliche Anzahl benannter Elemente
benutzbar als Realm.Forging, Realm.War
Reihenfolge liegt fest, d.h. sie sind vergleichbar
Gleichheit und Größer/ Kleiner sind definert
Arithmetische Operationen mit den Werten sind nicht möglich
zugehöriger Integerwert kann gelesen werden:
myRealm.ordinal();
per Definition Serializable, d.h. implements ... ist unnötig
99
Avatar
Avatar verwaltet Liste von
Assets
1010
Diskussion Assets
Liste von Assets hat Elemente verschiedener Struktur und
Länge, je nach Unterklasse
typsichere Serialisierung ist wichtig!
Es ist sinnvoll, Asset und Weapon als abstrakte Klassen zu
definieren, damit keine Instanzen gebildet werden
1111
Konstanten
Konstante definiert man als static final
static
final
der Wert soll in allen Instanzen gleich sein
nach der Initialisierung unveränderlich
1212
Spielprotoyp: Player
ein Player spielt (kontrolliert) eine Avatar
Insbesondere liest er ihn zu Beginn ein und definiert auch das
Herausschreiben
1313
Spielprototyp Payer-Persistenz
1414
Spielprototyp: Hilfsmethoden für die Simulation
1515
Spielprototyp: Main-Methode
Name wird als Aufruf-Argument übergeben
1616
Serialisierbarkeit - Ausnahmen
Klassenattribute werden nicht serialisiert
(gehören nicht zum Objektzustand, sondern zum Systemzustand)
Es kann Attribute geben, die nicht serialisiert werden sollen / müssen
abgeleitete Attribute – z.B. Alter errechenbar aus dem Geburtsdatum
ausführungsspezifische Attribute, z.B. IP-Adresse, Portnummer,
Verbindungsdauer...
Diese Attribute können als "transient" markiert werden
transient private int Dauer = 0;
Eine Belegung mit sinnvollen Werten muss in der Klasse programmiert
werden.
public int get Dauer() {
if (dauer != 0) return dauer;
return currentYear-yearOfBirth;
}
1717
Serialisierbarkeit - Grenzen
Eine Klasse kann grundsätzlich nur serialisierbar sein,
wenn es ihre Oberklasse auch ist.
Ausnahme nur für Spezialfälle: selbst implementierte Serialisierung.
die Serialisierungs-Methoden implementieren
writeObject
readObject
readObjectNoData
(Signaturen s.u.)
und dabei die Oberklassenattribute mit berücksichtigen
private void writeObject(java.io.ObjectOutputStream out) throws IOException
private void readObject(java.io.ObjectInputStream in) throws IOException,
ClassNotFoundException; private void readObjectNoData() throws
ObjectStreamException;
1818
Serialisierbarkeit: Versionsnummer
Ein Avatar wurde herausgeschrieben
Danach wurde die Avatar-Klasse geändert (Image-Attribut hinzugefügt)
Was passiert beim Einlesen?
rjava.io.InvalidClassException: rpg.Avatar; local class incompatible:
stream classdesc serialVersionUID = -3920281712614004619,
local class serialVersionUID = -4623769236742658137
at java.io.ObjectStreamClass.initNonProxy(Unknown Source)
at java.io.ObjectInputStream.readNonProxyDesc(Unknown Source)
at java.io.ObjectInputStream.readClassDesc(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at rpg.Player.getAvatar(Player.java:39)
at rpg.Player.<init>(Player.java:19)
at rpg.Main_1.main(Main_1.java:6)
1919
Serialisierbarkeit: Versionsnummer
Jedes Klasse erhält automatisch eine Versionsnummer, die bei
Änderung neu erzeugt wird
Diese Versionsnummer serialVersionUID wird mit serialisiert
Passt bei der Deserialisierung die Versionsnummer im Programm
nicht zur Versionsnummer im "stream" , wird eine Exception
geworfen.
rjava.io.InvalidClassException: rpg.Avatar; local class incompatible:
stream classdesc serialVersionUID = -3920281712614004619,
local class serialVersionUID = -4623769236742658137
SUPER! So werden versehentliche Fehldeutungen von Daten
vermieden!
2020
Serialisierung: Versionsnummer
Die Versionsnummer ändert sich auch bei Änderungen, die die
Serialisierung nicht betreffen:
Änderungen an Methoden
Änderungen an Attributnamen oder -sichtbarkeiten
Änderungen an statischen Attributen
Änderungen an Kommentaren
Eigene Versionsnummer vergeben und nur bei relevanten
Änderungen neu generieren
2121
Setzen der Versionsnummer
Default:
Aus der Klassenstruktur generiert:
kann selbst mit dem Werkzeug serialver erzeugt werden:
2222
Zusammenfassung
Durch Serialisierung ist es möglich, ganze Objekte auf
einmal zu lesen und zu schreiben
einfach und effizient
typ- und versionssicher
alte Daten nur mit alten Programmen wieder herstellbar!
ohne Probleme bei zyklischen Datenstrukturen
Das Mittel der Wahl für effiziente Persistenz
in der Testphase sind Textdateien vorzuziehen!
2323
Nächstes Mal geht's ins Netzwerk
24
24