Programmieren 2 11 – Objekt-Serialisierung Bachelor Medieninformatik Sommersemester 2015 Dipl.-Inform. Ilse Schmiedecke [email protected] 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