From 33613a85afc4b1481367fbe92a17ee59c240250b Mon Sep 17 00:00:00 2001 From: Sven Eisenhauer Date: Fri, 10 Nov 2023 15:11:48 +0100 Subject: add new repo --- .../hjp5/html/k100261.html | 731 +++++++++++++++++++++ 1 file changed, 731 insertions(+) create mode 100644 Master/Reference Architectures and Patterns/hjp5/html/k100261.html (limited to 'Master/Reference Architectures and Patterns/hjp5/html/k100261.html') diff --git a/Master/Reference Architectures and Patterns/hjp5/html/k100261.html b/Master/Reference Architectures and Patterns/hjp5/html/k100261.html new file mode 100644 index 0000000..79d6ea0 --- /dev/null +++ b/Master/Reference Architectures and Patterns/hjp5/html/k100261.html @@ -0,0 +1,731 @@ + + + +Handbuch der Java-Programmierung, 5. Auflage + + + + + + + + + +
 Titel  + Inhalt  + Suchen  + Index  + DOC  +Handbuch der Java-Programmierung, 5. Auflage +
 <<  +  <   +  >   + >>  + API  +Kapitel 41 - Serialisierung +
+
+ + + + +

41.1 Grundlagen

+
+ +
+ + + + +

41.1.1 Begriffsbestimmung

+ +

+Unter Serialisierung wollen wir die +Fähigkeit verstehen, ein Objekt, das im Hauptspeicher der Anwendung +existiert, in ein Format zu konvertieren, das es erlaubt, das Objekt +in eine Datei zu schreiben oder über eine Netzwerkverbindung +zu transportieren. Dabei wollen wir natürlich auch den umgekehrten +Weg einschließen, also das Rekonstruieren eines in serialisierter +Form vorliegenden Objekts in das interne Format der laufenden Java-Maschine. +

+ + + + + + + + + +
+ +

+Serialisierung wird häufig mit dem Begriff Persistenz +gleichgesetzt, vor allem in objektorientierten Programmiersprachen. +Das ist nur bedingt richtig, denn Persistenz bezeichnet genaugenommen +das dauerhafte Speichern von Daten auf einem externen Datenträger, +so dass sie auch nach dem Beenden des Programms erhalten bleiben. +Obwohl die persistente Speicherung von Objekten sicherlich eine der +Hauptanwendungen der Serialisierung ist, ist sie nicht ihre einzige. +Wir werden später Anwendungen sehen, bei der die Serialisierung +von Objekten nicht zum Zweck ihrer persistenten Speicherung genutzt +werden.

+ + + + +
 Hinweis 
+
+ + + + +

41.1.2 Schreiben von Objekten

+ +

+Während es vor dem JDK 1.1 keine einheitliche Möglichkeit +gab, Objekte zu serialisieren, gibt es seither im Paket java.io +die Klasse ObjectOutputStream, +mit der das sehr einfach zu realisieren ist. ObjectOutputStream +besitzt einen Konstruktor, der einen OutputStream +als Argument erwartet: +

+ + + + + +
+ +
+public ObjectOutputStream(OutputStream out)
+  throws IOException
+
+
+
+java.io.ObjectOutputStream
+ +

+Der an den Konstruktor übergebene OutputStream +dient als Ziel der Ausgabe. Hier kann ein beliebiges Objekt der Klasse +OutputStream +oder einer daraus abgeleiteten Klasse übergeben werden. Typischerweise +wird ein FileOutputStream +verwendet, um die serialisierten Daten in eine Datei zu schreiben. + +

+ObjectOutputStream +besitzt sowohl Methoden, um primitive Typen zu serialisieren, als +auch die wichtige Methode writeObject, +mit der ein komplettes Objekt serialisiert werden kann: + + + + +

+ + + + + +
+ +
+public final void writeObject(Object obj)
+  throws IOException
+public void writeBoolean(boolean data)
+  throws IOException
+public void writeByte(int data)
+  throws IOException
+public void writeShort(int data)
+  throws IOException
+public void writeChar(int data)
+  throws IOException
+public void writeInt(int data)
+  throws IOException
+public void writeLong(long data)
+  throws IOException
+public void writeFloat(float data)
+  throws IOException
+public void writeDouble(double data)
+  throws IOException
+public void writeBytes(String data)
+  throws IOException
+public void writeChars(String data)
+  throws IOException
+public void writeUTF(String data)
+  throws IOException
+
+
+
+java.io.ObjectOutputStream
+ +

+Während die Methoden zum Schreiben der primitiven Typen ähnlich +funktionieren wie die gleichnamigen Methoden der Klasse RandomAccessFile +(siehe Abschnitt 20.4), +ist die Funktionsweise von writeObject +wesentlich komplexer. writeObject +schreibt folgende Daten in den OutputStream: +

+ +

+Insbesondere der letzte Punkt verdient dabei besondere Beachtung. +Die Methode writeObject +durchsucht also das übergebene Objekt nach Membervariablen und +überprüft deren Attribute. Ist eine Membervariable vom Typ +static, +wird es nicht serialisiert, denn es gehört nicht zum Objekt, +sondern zur Klasse des Objekts. Weiterhin werden alle Membervariablen +ignoriert, die mit dem Schlüsselwort transient +deklariert wurden. Auf diese Weise kann das Objekt Membervariablen +definieren, die aufgrund ihrer Natur nicht serialisiert werden sollen +oder dürfen. Wichtig ist weiterhin, dass ein Objekt nur dann +mit writeObject +serialisiert werden kann, wenn es das Interface Serializable +implementiert. + +

+aufwändiger als auf den ersten Blick ersichtlich ist das Serialisieren +von Objekten vor allem aus zwei Gründen: +

+ +

+Wir wollen uns zunächst ein Beispiel ansehen. Dazu konstruieren +wir eine einfache Klasse Time, +die eine Uhrzeit, bestehend aus Stunden und Minuten, kapselt: + + +

+ + + + + +
+ +
+001 /* Time.java */
+002 
+003 import java.io.*;
+004 
+005 public class Time
+006 implements Serializable
+007 {
+008   private int hour;
+009   private int minute;
+010 
+011   public Time(int hour, int minute)
+012   {
+013     this.hour = hour;
+014     this.minute = minute;
+015   }
+016 
+017   public String toString()
+018   {
+019     return hour + ":" + minute;
+020   }
+021 }
+
+
+Time.java
+ +Listing 41.1: Eine serialisierbare Uhrzeitklasse

+ +

+Time besitzt einen öffentlichen +Konstruktor und eine toString-Methode +zur Ausgabe der Uhrzeit. Die Membervariablen hour +und minute wurden als private +deklariert und sind nach außen nicht sichtbar. Die Sichtbarkeit +einer Membervariable hat keinen Einfluss darauf, ob es von writeObject +serialisiert wird oder nicht. Mit Hilfe eines Objekts vom Typ ObjectOutputStream +kann ein Time-Objekt serialisiert +werden: + + +

+ + + + + +
+ +
+001 /* Listing4102.java */
+002 
+003 import java.io.*;
+004 import java.util.*;
+005 
+006 public class Listing4102
+007 {
+008   public static void main(String[] args)
+009   {
+010     try {
+011       FileOutputStream fs = new FileOutputStream("test1.ser");
+012       ObjectOutputStream os = new ObjectOutputStream(fs);
+013       Time time = new Time(10,20);
+014       os.writeObject(time);
+015       os.close();
+016     } catch (IOException e) {
+017       System.err.println(e.toString());
+018     }
+019   }
+020 }
+
+
+Listing4102.java
+ +Listing 41.2: Serialisieren eines Time-Objekts

+ +

+Wir konstruieren zunächst einen FileOutputStream, +der das serialisierte Objekt in die Datei test1.ser +schreiben soll. Anschließend erzeugen wir einen ObjectOutputStream +durch Übergabe des FileOutputStream +an dessen Konstruktor. Nun wird ein Time-Objekt +für die Uhrzeit 10:20 konstruiert und mit writeObject +serialisiert. Nach dem Schließen des Streams steht das serialisierte +Objekt in »test1.ser«. +

+ + + + + + + + + +
+ +

+Wichtig an der Deklaration von Time +ist das Implementieren des Serializable-Interfaces. +Zwar definiert Serializable +keine Methoden, writeObject +testet jedoch, ob das zu serialisierende Objekt dieses Interface implementiert. +Ist das nicht der Fall, wird eine Ausnahme des Typs NotSerializableException +ausgelöst.

+ + + + +
 Hinweis 
+
+ +

+Ein ObjectOutputStream +kann nicht nur ein Objekt serialisieren, sondern beliebig viele, sie +werden nacheinander in den zugrundeliegenden OutputStream +geschrieben. Das folgende Programm zeigt, wie zunächst ein int, +dann ein String +und schließlich zwei Time-Objekte +serialisiert werden: + + +

+ + + + + +
+ +
+001 /* Listing4103.java */
+002 
+003 import java.io.*;
+004 import java.util.*;
+005 
+006 public class Listing4103
+007 {
+008   public static void main(String[] args)
+009   {
+010     try {
+011       FileOutputStream fs = new FileOutputStream("test2.ser");
+012       ObjectOutputStream os = new ObjectOutputStream(fs);
+013       os.writeInt(123);
+014       os.writeObject("Hallo");
+015       os.writeObject(new Time(10, 30));
+016       os.writeObject(new Time(11, 25));
+017       os.close();
+018     } catch (IOException e) {
+019       System.err.println(e.toString());
+020     }
+021   }
+022 }
+
+
+Listing4103.java
+ +Listing 41.3: Serialisieren mehrerer Objekte

+ +

+Da ein int +ein primitiver Typ ist, muss er mit writeInt +serialisiert werden. Bei den übrigen Aufrufen kann writeObject +verwendet werden, denn alle übergebenen Argumente sind Objekte. +

+ + + + + + + + + +
+ +

+Es gibt keine verbindlichen Konventionen für die Benennung von +Dateien mit serialisierten Objekten. Die in den Beispielen verwendete +Erweiterung .ser ist allerdings recht +häufig zu finden, ebenso wie Dateierweiterungen des Typs .dat. +Wenn eine Anwendung viele unterschiedliche Dateien mit serialisierten +Objekten hält, kann es auch sinnvoll sein, die Namen nach dem +Typ der serialisierten Objekte zu vergeben.

+ + + + +
 Hinweis 
+
+ + + + +

41.1.3 Lesen von Objekten

+ +

+Nachdem ein Objekt serialisiert wurde, kann es mit Hilfe der Klasse +ObjectInputStream +wieder rekonstruiert werden. Analog zu ObjectOutputStream +gibt es Methoden zum Wiedereinlesen von primitiven Typen und eine +Methode readObject, +mit der ein serialisiertes Objekt wieder hergestellt werden kann: + + + +

+ + + + + +
+ +
+public final Object readObject()
+  throws OptionalDataException,
+         ClassNotFoundException,
+         IOException
+public boolean readBoolean()
+  throws IOException
+public byte readByte()
+  throws IOException
+public short readShort()
+  throws IOException
+public char readChar()
+  throws IOException
+public int readInt()
+  throws IOException
+public long readLong()
+  throws IOException
+public float readFloat()
+  throws IOException
+public double readDouble()
+  throws IOException
+public String readUTF()
+  throws IOException
+
+
+
+java.io.ObjectInputStream
+ +

+Zudem besitzt die Klasse ObjectInputStream +einen Konstruktor, der einen InputStream +als Argument erwartet, der zum Einlesen der serialisierten Objekte +verwendet wird: +

+ + + + + +
+ +
+public ObjectInputStream(InputStream in)
+
+
+
+java.io.ObjectInputStream
+ +

+Das Deserialisieren eines Objektes kann man sich stark vereinfacht +aus den folgenden beiden Schritten bestehend vorstellen: +

+ +

+Das erzeugte Objekt hat anschließend dieselbe Struktur und denselben +Zustand, den das serialisierte Objekt hatte (abgesehen von den nicht +serialisierten Membervariablen des Typs static +oder transient). +Da der Rückgabewert von readObject +vom Typ Object +ist, muss das erzeugte Objekt in den tatsächlichen Typ (oder +eine seiner Oberklassen) umgewandelt werden. Das folgende Programm +zeigt das Deserialisieren am Beispiel des in Listing 41.2 +serialisierten und in die Datei test1.ser +geschriebenen Time-Objekts: + + +

+ + + + + +
+ +
+001 /* Listing4104.java */
+002 
+003 import java.io.*;
+004 import java.util.*;
+005 
+006 public class Listing4104
+007 {
+008   public static void main(String[] args)
+009   {
+010     try {
+011       FileInputStream fs = new FileInputStream("test1.ser");
+012       ObjectInputStream is = new ObjectInputStream(fs);
+013       Time time = (Time)is.readObject();
+014       System.out.println(time.toString());
+015       is.close();
+016     } catch (ClassNotFoundException e) {
+017       System.err.println(e.toString());
+018     } catch (IOException e) {
+019       System.err.println(e.toString());
+020     }
+021   }
+022 }
+
+
+Listing4104.java
+ +Listing 41.4: Deserialisieren eines Time-Objekts

+ +

+Hier wird zunächst ein FileInputStream +für die Datei test1.ser geöffnet +und an den Konstruktor des ObjectInputStream-Objekts +is übergeben. Alle lesenden +Aufrufe von is beschaffen ihre +Daten damit aus test1.ser. Jeder Aufruf +von readObject +liest immer das nächste gespeicherte Objekt aus dem Eingabestream. +Das Programm zum Deserialisieren muss also genau wissen, welche Objekttypen +in welcher Reihenfolge serialisiert wurden, um sie erfolgreich deserialisieren +zu können. In unserem Beispiel ist die Entscheidung einfach, +denn in der Eingabedatei steht nur ein einziges Time-Objekt. +readObject +deserialisiert es und liefert ein neu erzeugtes Time-Objekt, +dessen Membervariablen mit den Werten aus dem serialisierten Objekt +belegt werden. Die Ausgabe des Programms ist demnach: + +

+10:20
+
+ +

+ + + + + + + + + + + +
+ +

+Es ist wichtig zu verstehen, dass beim Deserialisieren nicht der Konstruktor +des erzeugten Objekts aufgerufen wird. Lediglich bei einer serialisierbaren +Klasse, die in ihrer Vererbungshierarchie Superklassen hat, die nicht +das Interface Serializable +implementieren, wird der parameterlose Konstruktor der nächsthöheren +nicht-serialisierbaren Vaterklasse aufgerufen. Da die aus der nicht-serialisierbaren +Vaterklasse geerbten Membervariablen nicht serialisiert werden, soll +auf diese Weise sichergestellt sein, dass sie wenigstens sinnvoll +initialisiert werden. + +

+Auch eventuell vorhandene Initialisierungen einzelner Membervariablen +werden nicht ausgeführt. Wir könnten beispielsweise die +Time-Klasse aus Listing 41.1 +um eine Membervariable seconds +erweitern: + +

+private transient int seconds = 11;
+
+ + +

+Dann wäre zwar bei allen mit new +konstruierten Objekten der Sekundenwert mit 11 vorbelegt. Bei Objekten, +die durch Deserialisieren erzeugt wurden, bleibt er aber 0 (das ist +der Standardwert eines int, +siehe Tabelle 4.1), +denn der Initialisierungscode wird in diesem Fall nicht ausgeführt.

+ + + + +
 Warnung 
+
+ +

+Beim Deserialisieren von Objekten können einige Fehler passieren. +Damit ein Aufruf von readObject +erfolgreich ist, müssen mehrere Kriterien erfüllt sein: +

+ +

+Soll beispielsweise die in Listing 41.3 +erzeugte Datei test2.ser deserialisiert +werden, so müssen die Aufrufe der read-Methoden +in Typ und Reihenfolge denen des serialisierenden Programms entsprechen: + + +

+ + + + + +
+ +
+001 /* Listing4105.java */
+002 
+003 import java.io.*;
+004 import java.util.*;
+005 
+006 public class Listing4105
+007 {
+008   public static void main(String[] args)
+009   {
+010     try {
+011       FileInputStream fs = new FileInputStream("test2.ser");
+012       ObjectInputStream is = new ObjectInputStream(fs);
+013       System.out.println("" + is.readInt());
+014       System.out.println((String)is.readObject());
+015       Time time = (Time)is.readObject();
+016       System.out.println(time.toString());
+017       time = (Time)is.readObject();
+018       System.out.println(time.toString());
+019       is.close();
+020     } catch (ClassNotFoundException e) {
+021       System.err.println(e.toString());
+022     } catch (IOException e) {
+023       System.err.println(e.toString());
+024     }
+025   }
+026 }
+
+
+Listing4105.java
+ +Listing 41.5: Deserialisieren mehrerer Elemente

+ +

+Das Programm rekonstruiert alle serialisierten Elemente aus »test2.ser«. +Seine Ausgabe ist: + +

+123
+Hallo
+10:30
+11:25
+
+ +
+ + + +
 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 +
+ + + -- cgit v1.2.3