Advanced   Java   Services Serialisierung  (Lesen und Schreiben von Objekten) Back Next Up Home


ObjectOutputStream

Mit ObjectOutputStream und ObjectInputStream kann man Objekte speichern oder einlesen. Voraussetzung dafür ist allerdings, daß die zugehörigen Klassen das Interface Serializable implementiert haben. Bei diesem Interface handelt es sich um ein sog. "tagging interface", also um ein Markierungsinterface, das zur Typprüfung veredent wird und keine Implementierung von Methoden fordert. Hat ein Objekt den Typ Serializable, so gilt es als dauerhaft speicherbar, also persistent. Das Speichern eines Objektes ist gewissermaßen ein Einfrieren des Zustands um zu einem späteren Zeitpunkt das Objekt in desem Zustand wiederherzustellen.

Der Versuch, ein Objekt zu speichern, das nicht vom Typ Serializable ist, endet zur Laufzeit mit einer NotSerializableException, einer Unterklasse von ObjectStreamException. Es folgt ein Auszug aus der API.


ObjectOutputStream (Apiauszug)
Konstruktor
ObjectOutputStream(OutputStream out) Creates an ObjectOutputStream that writes to the specified OutputStream.
Einige Methoden
ReturntypName der Methode
void close()
Closes the stream.
voidflush()
Flushes the stream.
voidreset()
Reset will disregard the state of any objects already written to the stream.
voidwrite(byte[] buf)
Writes an array of bytes.
voidwrite(int val)
Writes a byte.
voidwriteBoolean(boolean val)
Writes a boolean.
voidwriteByte(int val)
Writes an 8 bit byte.
voidwriteBytes(String str)
Writes a String as a sequence of bytes.
voidwriteChar(int val)
Writes a 16 bit char.
voidwriteChars(String str)
Writes a String as a sequence of chars.
voidwriteDouble(double val)
Writes a 64 bit double.
voidwriteFloat(float val)
Writes a 32 bit float.
voidwriteInt(int val)
Writes a 32 bit int.
voidwriteLong(long val)
Writes a 64 bit long.
voidwriteObject(Object obj)
Write the specified object to the ObjectOutputStream.
voidwriteShort(int val)
Writes a 16 bit short.
voidwriteUTF(String str)
Primitive data write of this String in modified UTF-8 format.

Beispiel: Speichern von Date-Objekten

Will man eine Reihe von Objekten gleichen Typs wegschreiben, so ist das Vorgehen sehr einfach und analog zu den bereits besprochenen Vorgängen. Das folgende Beispiel erzeugt Datumsobjekte für den Monat Januar 2001 und speichert sie in eine Datei.

/**
 * Date-Objekte für Januar 2001 wegschreiben
 */
private static void writeDateObjects(String fileName)
{
   System.out.println("write date objects");
   Calendar cal = Calendar.getInstance();

   // wegschreiben
   try(FileOutputStream fos = new FileOutputStream(fileName);
         BufferedOutputStream bos = new BufferedOutputStream(fos);
         ObjectOutputStream oos = new ObjectOutputStream(bos);)
   {
      for(int i = 1; i < 32; i++)
      {
         cal.set(2001, 0, i);
         oos.writeObject(cal.getTime()); // Date
      }
      System.out.println("date objects written");
   }
   catch(IOException ex)
   {
      System.out.println(ex);
   }
}

Beispiel: Speichern verschiedener Objekte

Das folgende Beispiel speichert die aktuelle Uhrzeit, danach die Anzahl der Millisekunden seit dem 01.01.1970 (Typ long) und danch den Namen der Zeitzone (Typ String). Verwendet wird das in Java 7 eingeführte try with resources.

private static void writeObjects(String fileName)
{
   Calendar cal = Calendar.getInstance();
   Date date = new Date();

   // wegschreiben
   try(ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(fileName)));)
   {
      Date time = cal.getTime();
      System.out.println("schreibe: " + time);
      oos.writeObject(time); // liefert Date, schreibt Date
      long millis = date.getTime();
      System.out.println("schreibe: " + millis);
      oos.writeLong(millis); // schreibt long
      TimeZone tz = cal.getTimeZone();
      System.out.println("schreibe: " + tz.getDisplayName());
      oos.writeObject(tz.getDisplayName()); // liefert String, schreibt String
      System.out.println("data written");
   }
   catch(IOException ex)
   {
      System.out.println(ex);
   }
}

Wie schon aus den Methoden eingangs ersichtlich kann man mit ObjectOutputStream auch Variable mit einem primitiven Datentyp schreiben.


ObjectInputStream

Das Einlesen von Objekten kann ein komplexer Vorgang werden. Wir werden die obigen Beispiele fortführen und die entsprechenden Objekte wieder "auftauen". Das Lesen von Objekten kommt einem Konstruktoraufruf gleich. Es werden Objekte initialisiert, Typ und Zustand werden der serialisierten Form entnommen. Zunächst wieder ein Auszug aus der API.







Konstruktor
ObjectInputStream(InputStream in) Creates an ObjectInputStream that reads from the specified InputStream.
Einige Methoden
ReturntypName der Methode
intavailable()
Returns the number of bytes that can be read without blocking.
voidclose()
Closes the input stream.
intread()
Reads a byte of data.
intread(byte[] buf, int off, int len)
Reads into an array of bytes.
booleanreadBoolean()
Reads in a boolean.
bytereadByte()
Reads an 8 bit byte.
charreadChar()
Reads a 16 bit char.
doublereadDouble()
Reads a 64 bit double.
floatreadFloat()
Reads a 32 bit float.
voidreadFully(byte[] buf)
Reads bytes, blocking until all bytes are read.
intreadInt()
Reads a 32 bit int.
longreadLong()
Reads a 64 bit long.
ObjectreadObject()
Read an object from the ObjectInputStream.
shortreadShort()
Reads a 16 bit short.
intreadUnsignedByte()
Reads an unsigned 8 bit byte.
intreadUnsignedShort()
Reads an unsigned 16 bit short.
StringreadUTF()
Reads a String in modified UTF-8 format.
intskipBytes(int len)
Skips bytes, block until all bytes are skipped.


Beispiel: Einlesen der Date-Objekte

Für das Einlesen unserer Ojekte setzen wir voraus, daß der Typ bekannt ist (Date), aber die Anzahl der Datensätze nicht bekannt ist. Wir wollen also bis zum Dateiende lesen. Die dafür zuständige Methode readObject() gibt nun nicht, wie vielleicht zu erwarten, am Dateiende null zurück (vgl. die Methode readLine() bei BufferedReader) sondern wirft eine EOFException.

private static void readDateObjects(String fileName)
{
   System.out.println("read date objects");

   ObjectInputStream ois = null;
   Object ob = null;
   int count = 0;

   try
   {
      ois = new ObjectInputStream(new BufferedInputStream(new FileInputStream(fileName)));
      for(;;)
      {
         ob = ois.readObject();
         count++;
         System.out.println(ob);
      }
      // System.out.println(ob);
   }
   catch(EOFException ex) // wg. readObject
   {
      System.out.println("--- EOF ---");
      System.out.println("Es wurden " + count + " Objekte eingelesen");
   }
   catch(IOException ex)
   {
      System.out.println(ex);
   }
   catch(ClassNotFoundException ex) // wg. readObject
   {
      System.out.println(ex);
   }
   finally
   {
      try
      {
         if (ois != null) ois.close();
      }
      catch(IOException ex)
      {
      }
   }
}

Da wir das Dateiende erst durch eine Exception erkennen, müssen wird Datei nach dieser Exception schließen. Dafür bietet sich finally an, wobei wir allerdings in finally noch einmal ein try catch Konstrukt unterbringen müssen, da die close()-Methode eine IOException wirft, eine typische Situation, auf die wir auch bei Datenbankzugriffen stoßen. des weiteren muß noch eine ClassCastException abgefangen werden, die readObject() werfen könnte. Will man keine abgestufte Reaktion, so kann man, wie immer, mehrere catch-Verzweigungen durch ein einziges catch unter Verwendung der ersten Oberklasse ersetzen.


Beispiel: Einlesen verschiedener Objekte

Der umgekehrte Vorgang des zweiten Beispiels erfordert nun ein Einlesen eines Dateobjekts, gefolgt von einer long-Zahl, gefolgt von einem String. Kennt man die Datentypen ist das Einlesen einfach. Auch hier kann man das elegante try with resources verwenden.

private static void readObjects(String fileName)
{
   System.out.println("read data");
   try(ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(new FileInputStream(fileName)));)
   {
      Object ob = ois.readObject();
      System.out.println("gelesen : " + ob); // java.util.Date
      long lon = ois.readLong();
      System.out.println("gelesen : " + lon);
      ob = ois.readObject(); // wirft OptionalDataException
      System.out.println("gelesen : " + ob);
      System.out.println("end read");
   }
   catch(IOException | ClassNotFoundException ex)
   {
      ex.printStackTrace();
   }
}

Anders ist es, wenn man die Reihenfolge nicht kennt. Wir machen nun absichtlich einen Fehler und versuchen zwei Objekte hintereinander einzulesen. Das zweite readObject() wirft nämlich eine OptionalDataException, wenn es nicht auf Objektdaten stößt. Da der interne Dateipositionszeiger stehenbleibt, haben wir im catch()-Block die Chance, die long-zahl einzulesen. readLong() liest genau 8 byte und interpretiert sie als long-Zahl. Das nächste readObject() liest dann den String ein.

/**
 * Einlesen mit Fehler
 */
private static void readObjects2(String fileName)
{
   System.out.println("read data");
   Object ob = null;
   ObjectInputStream ois = null;

   try
   {
      ois = new ObjectInputStream(new BufferedInputStream(new FileInputStream(fileName)));
      ob = ois.readObject();
      System.out.println("gelesen : " + ob); // java.util.Date
      // falsches Lesen
      ob = ois.readObject(); // wirft OptionalDataException
   }
   catch(OptionalDataException ex) // Primitive data was found in the stream instead of objects.
   {
      System.out.println("OptionalDataException"); //
      try
      {
         long lo = ois.readLong();
         System.out.println("gelesen : " + lo);
         ob = ois.readObject();
         System.out.println("gelesen : " + ob);
      }
      catch(IOException ex2)
      {
         System.out.println(ex2);
      }
      catch(ClassNotFoundException ex2) // wg. readObject
      {
         System.out.println(ex2);
      }
   }
   catch(IOException ex)
   {
      System.out.println(ex);
   }
   catch(ClassNotFoundException ex) // wg. readObject
   {
      System.out.println(ex);
   }
   finally
   {
      try
      {
         if (ois != null) ois.close();
      }
      catch(IOException ex)
      {
      }
   }
}
Valid XHTML 1.0 Strict top Back Next Up Home