From 33613a85afc4b1481367fbe92a17ee59c240250b Mon Sep 17 00:00:00 2001
From: Sven Eisenhauer
+Die folgende Klasse TrivialObjectStore
+stellt ein einfaches Beispiel für einen persistenten Objektspeicher
+dar. Sie besitzt eine Methode putObject,
+mit der beliebige String-Object-Paare
+angelegt und später mit getObject
+wieder abgerufen werden können. Durch Aufruf von save
+kann der komplette Objektspeicher serialisiert werden, ein Aufruf
+von load lädt ihn von der
+Festplatte. Die Klasse TrivialObjectStore
+verwendet eine Hashtable
+zur Ablage der String-Object-Paare.
+Diese implementiert bereits standardmäßig das Interface
+Serializable
+und kann daher sehr einfach auf einem externen Datenträger gespeichert
+oder von dort geladen werden.
+
+
+
+
+
+
+ Titel
+ Inhalt
+ Suchen
+ Index
+ DOC
+ Handbuch der Java-Programmierung, 5. Auflage
+
+ <<
+ <
+ >
+ >>
+ API
+ Kapitel 41 - Serialisierung
+
+
+
+
+
+41.3 Anwendungen
+
+
+
+
+
+
+
+41.3.1 Ein einfacher Objektspeicher
+
+
+
+
+Listing 41.9: Ein einfacher Objektspeicher
+
+
+
+
+
+001 /* TrivialObjectStore.java */
+002
+003 import java.io.*;
+004 import java.util.*;
+005
+006 /**
+007 * Trivialer Objektspeicher, der Mengen von Name-Objekt-
+008 * Paaren aufnehmen und persistent speichern kann.
+009 */
+010 public class TrivialObjectStore
+011 {
+012 //Instance variables
+013 private String fname;
+014 private Hashtable objects;
+015
+016 /**
+017 * Erzeugt einen neuen Objektspeicher mit dem angegebenen
+018 * Namen (die Erweiterung ".tos" ("trivial object store")
+019 * wird ggfs. automatisch angehängt.
+020 */
+021 public TrivialObjectStore(String fname)
+022 {
+023 this.fname = fname;
+024 if (!fname.endsWith(".tos")) {
+025 this.fname += ".tos";
+026 }
+027 this.objects = new Hashtable(50);
+028 }
+029
+030 /**
+031 * Sichert den Objektspeicher unter dem im Konstruktor
+032 * angegebenen Namen.
+033 */
+034 public void save()
+035 throws IOException
+036 {
+037 FileOutputStream fs = new FileOutputStream(fname);
+038 ObjectOutputStream os = new ObjectOutputStream(fs);
+039 os.writeObject(objects);
+040 os.close();
+041 }
+042
+043 /**
+044 * Lädt den Objektspeicher mit dem im Konstruktor
+045 * angegebenen Namen.
+046 */
+047 public void load()
+048 throws ClassNotFoundException, IOException
+049 {
+050 FileInputStream fs = new FileInputStream(fname);
+051 ObjectInputStream is = new ObjectInputStream(fs);
+052 objects = (Hashtable)is.readObject();
+053 is.close();
+054 }
+055
+056 /**
+057 * Fügt ein Objekt in den Objektspeicher ein.
+058 */
+059 public void putObject(String name, Object object)
+060 {
+061 objects.put(name, object);
+062 }
+063
+064 /**
+065 * Liest das Objekt mit dem angegebenen Namen aus dem
+066 * Objektspeicher. Ist es nicht vorhanden, wird null
+067 * zurückgegeben.
+068 */
+069 public Object getObject(String name)
+070 {
+071 return objects.get(name);
+072 }
+073
+074 /**
+075 * Liefert eine Aufzählung aller gespeicherten Namen.
+076 */
+077 public Enumeration getAllNames()
+078 {
+079 return objects.keys();
+080 }
+081 }
+
+
+TrivialObjectStore.java
+
+Objekte der Klasse TrivialObjectStore +können nun verwendet werden, um beliebige serialisierbare Objekte +unter Zuordnung eines Namens auf einem externen Datenträger zu +speichern. Das folgende Listing zeigt dies am Beispiel eines fiktiven +»Tamagotchi-Shops«, dessen Eigenschaften Name, Besitzer +und Liste der Produkte im Objektspeicher abgelegt, in die Datei +shop.tos geschrieben und anschließend +wieder ausgelesen werden: + + +
+
+
+
+001 /* Listing4110.java */
+002
+003 import java.io.*;
+004 import java.util.*;
+005
+006 public class Listing4110
+007 {
+008 public static void main(String[] args)
+009 {
+010 //Erzeugen und Speichern des Objektspeichers
+011 TrivialObjectStore tos = new TrivialObjectStore("shop");
+012 tos.putObject("name", "Tami-Shop Norderelbe");
+013 tos.putObject("besitzer", "Meier, Fridolin");
+014 Vector products = new Vector(10);
+015 products.addElement("Dinky Dino");
+016 products.addElement("96er Classic");
+017 products.addElement("Black Frog");
+018 products.addElement("SmartGotchi");
+019 products.addElement("Pretty Dolly");
+020 tos.putObject("produkte", products);
+021 try {
+022 tos.save();
+023 } catch (IOException e) {
+024 System.err.println(e.toString());
+025 }
+026
+027 //Einlesen des Objektspeichers
+028 TrivialObjectStore tos2 = new TrivialObjectStore("shop");
+029 try {
+030 tos2.load();
+031 Enumeration names = tos2.getAllNames();
+032 while (names.hasMoreElements()) {
+033 String name = (String)names.nextElement();
+034 Object obj = tos2.getObject(name);
+035 System.out.print(name + ": ");
+036 System.out.println(obj.getClass().toString());
+037 if (obj instanceof Collection) {
+038 Iterator it = ((Collection)obj).iterator();
+039 while (it.hasNext()) {
+040 System.out.println(" " + it.next().toString());
+041 }
+042 } else {
+043 System.out.println(" " + obj.toString());
+044 }
+045 }
+046 } catch (IOException e) {
+047 System.err.println(e.toString());
+048 } catch (ClassNotFoundException e) {
+049 System.err.println(e.toString());
+050 }
+051 }
+052 }
+
+ |
++Listing4110.java | +
+Hier wird zunächst ein neuer Objektspeicher tos +erstellt und mit den Objekten aus dem Tamagotchi-Shop gefüllt. +Neben zwei Strings name und +besitzer wird dabei unter der +Bezeichnung produkte eine weitere +Collection, der Vector +mit den Produkten, eingefügt. Das durch Aufruf von save +ausgelöste Serialisieren der Hashtable +bewirkt, dass alle darin gespeicherten Elemente serialisiert werden, +sofern sie das Interface Serializable +implementieren. + +
+Ab Zeile 027 wird +dann der Objektspeicher wieder eingelesen, in diesem Fall in die Variable +tos2. Mit getAllNames +beschafft das Programm zunächst eine Enumeration +über alle Objektnamen und durchläuft sie elementweise. Zu +jedem Namen wird mit getElement +das zugehörige Element geholt, und sein Name und der Name der +zugehörigen Klasse werden ausgegeben (Zeile 036). +Anschließend wird überprüft, ob das gefundene Objekt +das Interface Collection +implementiert und ggfs. über alle darin enthaltenen Elemente +iteriert. Andernfalls wird das Objekt direkt mit toString +ausgegeben. + +
+Die Ausgabe des Programms ist:
+
+
+produkte: class java.util.Vector
+ Dinky Dino
+ 96er Classic
+ Black Frog
+ SmartGotchi
+ Pretty Dolly
+besitzer: class java.lang.String
+ Meier, Fridolin
+name: class java.lang.String
+ Tami-Shop Norderelbe
+
+
+
+
![]() |
+
+
+ +Die Klasse TrivialObjectStore +verdeutlicht eine mögliche Vorgehensweise bei der persistenten +Speicherung von Objekten. Für einen echten Praxiseinsatz (etwa +in der Anwendungsentwicklung) fehlen aber noch ein paar wichtige Eigenschaften: +
|
+
+
|
+![]() |
+
+Leider ist die Implementierung dieser Features nicht trivial. Ein +gutes Beispiel für die Implementierung des ersten und dritten +Punkts findet sich in »Java Algorithms« von Scott Robert +Ladd. Der Autor zeigt zunächst, wie man Objekte auf der Basis +von Random-Access-Dateien (anstelle der üblichen Streams) serialisiert. +Anschließend zeigt er die Verwendung von Indexdateien am Beispiel +der Implementierung von B-Trees. + + + + +
+Eine auf den ersten Blick überraschende Anwendung der Serialisierung +besteht darin, Objekte zu kopieren. Es sei noch einmal daran erinnert, +dass die Zuweisung eines Objektes an eine Objektvariable lediglich +eine Zeigeroperation war; dass also immer nur ein Verweis geändert +wurde. Soll ein komplexes Objekt kopiert werden, wird dazu üblicherweise +das Interface Cloneable +implementiert und eine Methode clone +zur Verfügung gestellt, die den eigentlichen Kopiervorgang vornimmt. +Sollte ein Objekt kopiert werden, das Cloneable +nicht implementiert, blieb bisher nur der umständliche Weg über +das manuelle Kopieren aller Membervariablen. Das ist insbesondere +dann mühsam und fehlerträchtig, wenn das zu kopierende Objekt +Unterobjekte enthält, die ihrerseits kopiert werden müssen +(deep copy anstatt shallow copy). + +
+Der Schlüssel zum Kopieren von Objekten mit Hilfe der Serialisierung +liegt darin, anstelle der üblichen dateibasierten Streamklassen +solche zu verwenden, die ihre Daten im Hauptspeicher halten. Am besten +sind dazu ByteArrayOutputStream +und ByteArrayInputStream +geeignet. Sie sind integraler Bestandteil der OutputStream- +und InputStream-Hierarchien +und man kann die Daten problemlos von einem zum anderen übergeben. +Das folgende Programm implementiert eine Methode seriaClone(), +die ein beliebiges Objekt als Argument erwartet und in einen ByteArrayOutputStream +serialisiert. Das resultierende Byte-Array wird dann zur Konstruktion +eines ByteArrayInputStream +verwendet, dort deserialisiert und als Objektkopie an den Aufrufer +zurückgegeben: + + +
+
+
+
+001 /* Listing4111.java */
+002
+003 import java.io.*;
+004 import java.util.*;
+005
+006 public class Listing4111
+007 {
+008 public static Object seriaClone(Object o)
+009 throws IOException, ClassNotFoundException
+010 {
+011 //Serialisieren des Objekts
+012 ByteArrayOutputStream out = new ByteArrayOutputStream();
+013 ObjectOutputStream os = new ObjectOutputStream(out);
+014 os.writeObject(o);
+015 os.flush();
+016 //Deserialisieren des Objekts
+017 ByteArrayInputStream in = new ByteArrayInputStream(
+018 out.toByteArray()
+019 );
+020 ObjectInputStream is = new ObjectInputStream(in);
+021 Object ret = is.readObject();
+022 is.close();
+023 os.close();
+024 return ret;
+025 }
+026
+027 public static void main(String[] args)
+028 {
+029 try {
+030 //Erzeugen des Buchobjekts
+031 Book book = new Book();
+032 book.author = "Peitgen, Heinz-Otto";
+033 String[] s = {"Jürgens, Hartmut", "Saupe, Dietmar"};
+034 book.coAuthors = s;
+035 book.title = "Bausteine des Chaos";
+036 book.publisher = "rororo science";
+037 book.pubyear = 1998;
+038 book.pages = 514;
+039 book.isbn = "3-499-60250-4";
+040 book.reflist = new Vector();
+041 book.reflist.addElement("The World of MC Escher");
+042 book.reflist.addElement(
+043 "Die fraktale Geometrie der Natur"
+044 );
+045 book.reflist.addElement("Gödel, Escher, Bach");
+046 System.out.println(book.toString());
+047 //Erzeugen und Verändern der Kopie
+048 Book copy = (Book)seriaClone(book);
+049 copy.title += " - Fraktale";
+050 copy.reflist.addElement("Fractal Creations");
+051 //Ausgeben von Original und Kopie
+052 System.out.print(book.toString());
+053 System.out.println("---");
+054 System.out.print(copy.toString());
+055 } catch (IOException e) {
+056 System.err.println(e.toString());
+057 } catch (ClassNotFoundException e) {
+058 System.err.println(e.toString());
+059 }
+060 }
+061 }
+062
+063 class Book
+064 implements Serializable
+065 {
+066 public String author;
+067 public String[] coAuthors;
+068 public String title;
+069 public String publisher;
+070 public int pubyear;
+071 public int pages;
+072 public String isbn;
+073 public Vector reflist;
+074
+075 public String toString()
+076 {
+077 String NL = System.getProperty("line.separator");
+078 StringBuffer ret = new StringBuffer(200);
+079 ret.append(author + NL);
+080 for (int i = 0; i < coAuthors.length; ++i) {
+081 ret.append(coAuthors[i] + NL);
+082 }
+083 ret.append("\"" + title + "\"" + NL);
+084 ret.append(publisher + " " + pubyear + NL);
+085 ret.append(pages + " pages" + NL);
+086 ret.append(isbn + NL);
+087 Enumeration e = reflist.elements();
+088 while (e.hasMoreElements()) {
+089 ret.append(" " + (String)e.nextElement() + NL);
+090 }
+091 return ret.toString();
+092 }
+093 }
+
+ |
++Listing4111.java | +
+Das Programm verwendet zum Testen ein Objekt der Klasse Book,
+das mit den Daten eines Buchtitels initialisiert wird. Anschließend
+wird mit seriaClone eine Kopie
+hergestellt und der Variable copy
+zugewiesen. Um zu verdeutlichen, dass wirklich eine Kopie hergestellt
+wurde, modifizieren wir nun einige Angaben der Kopie und geben anschließend
+beide Objekte aus:
+
+
+Peitgen, Heinz-Otto
+Jürgens, Hartmut
+Saupe, Dietmar
+"Bausteine des Chaos"
+rororo science 1998
+514 pages
+3-499-60250-4
+ The World of MC Escher
+ Die fraktale Geometrie der Natur
+ Gödel, Escher, Bach
+---
+Peitgen, Heinz-Otto
+Jürgens, Hartmut
+Saupe, Dietmar
+"Bausteine des Chaos - Fraktale"
+rororo science 1998
+514 pages
+3-499-60250-4
+ The World of MC Escher
+ Die fraktale Geometrie der Natur
+ Gödel, Escher, Bach
+ Fractal Creations
+
+
+
+
+An der Programmausgabe kann man erkennen, dass das Objekt tatsächlich +ordnungsgemäß kopiert wurde. Auch alle Unterobjekte wurden +kopiert und konnten anschließend unabhängig voneinander +geändert werden. Ohne Serialisierung wäre der manuelle Aufwand +um ein Vielfaches größer gewesen. Das Verfahren findet +dort seine Grenzen, wo die zu kopierenden Objekte nicht serialisierbar +sind oder nicht-serialisierbare Unterobjekte enthalten. Zudem muss +im echten Einsatz das Laufzeitverhalten überprüft werden, +denn der Vorgang des Serialisierens/Deserialisierens ist um ein Vielfaches +langsamer als das direkte Kopieren der Objektattribute. +
| Titel + | Inhalt + | Suchen + | Index + | DOC + | Handbuch der Java-Programmierung, 5. Auflage, Addison +Wesley, Version 5.0.1 + |
| << + | < + | > + | >> + | API + | © 1998, 2007 Guido Krüger & Thomas +Stark, http://www.javabuch.de + |