Vorlesung Informatik 1 Fachhochschule für Technik Esslingen Studiengang Wirtschaftsinformatik Teil 4: Bibliotheken Dr. rer. nat. Andreas Rau http://www.hs-esslingen.de/~rau [email protected] © Andreas Rau, 14.12.07 D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-bibliotheken.odp #1 Inhalt Dieser Teil der Vorlesung behandelt die wichtigsten Standardpakete von Java und die darin enthaltenen Klassen. Davon ausgenommen sind die Pakete und Klassen zur Oberflächenprogrammierung. Diese werden zu einem späteren Zeitpunkt eigenständig behandelt. Kein Mensch kann sich alle Details merken. Daher ist es beim Umgang mit den hier beschriebenen Klassen immer wieder wichtig und richtig, auf die Klassenreferenz zurückzugreifen um dort die Methoden der jeweiligen Interfaces und Klassen nachzuschlagen (Wink mit dem Zaunpfahl!). © Andreas Rau, 14.12.07 D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-bibliotheken.odp #2 Klassen für Datenstrukturen - Hintergrund Die fundamentale Datenstruktur in Java ist die Klasse. Diese stellt eine Erweiterung der in anderen Klassen bekannten Strukturen oder Records dar. Alle weiteren Datenstrukturen sind mit Hilfe von Klassen realisiert. Dies gilt auch für Arrays, die einfachste Datenstruktur. Jedoch wird der Einsatz von Arrays in Java durch besondere syntaktische Konstrukte unterstützt. Dadurch sind Arrays nicht direkt als Objekte erkennbar. Ungeachtet dessen spielt die Objekteigenschaft von Arrays an vielen, z.B. bei der Parameterübergabe, eine wichtige Rolle. Arrays sind aber nicht immer die beste Lösung. Deshalb gibt es in Java unter der Sammelbezeichnung "Collections" eine Reihe weiterer Klassen, die andere Datenstrukturen implementieren. Diese sind im Paket java.util untergebracht (Arrays sind Bestandteil des Kerns bzw. von java.lang). Kennzeichnend für alle Collection Klassen ist außerdem, dass Sie anders als Arrays nicht individuell typisiert sind, sondern Objekte verwalten und dabei auch verschiedene Typen mischen können. © Andreas Rau, 14.12.07 D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-bibliotheken.odp #3 Eigenschaften von Datenstrukturen(1) Klassen wie die Collections in Java werden in anderen Programmiersprachen auch als Container bezeichnet. Dieser Begriff hat in Java jedoch bereits eine andere Bedeutung bzgl. grafischer Oberflächen. Bei der Charakterisierung dieser Klassen unterscheidet man oftmals zwischen der Schnittstelle ("wie kann die Datenstruktur genutzt werden?", "Wie verhält sich die Datenstruktur?") und der Implementierung ("Wie ist die Datenstruktur intern organisiert?"). Diese beiden Aspekte sind zunächst weitgehend voneinander unabhängig, d.h. man kann durch geeignete Kombinationen von Schnittstellen und Implementierungen maßgeschneiderte Container erzeugen. Historischer Hinweis: Collections waren bereits in Smalltalk-80 enthalten(!) © Andreas Rau, 14.12.07 D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-bibliotheken.odp #4 Eigenschaften von Datenstrukturen(2) Eine Datenstruktur kann durch folgende Eigenschaften charakterisiert werden: Zugriff ● Wahlfrei über Index oder Schlüssel ● Sequentiell durch "abklappern" ● Verhalten ● Fähigkeit automatisch zu wachsen oder zu schrumpfen ● Fähigkeit zum Einfügen in der Mitte ● Logische Organisation ● Mit / Ohne Duplikate ● Geordnet / ungeordnet ● Sortiert / unsortiert ● Physikalische Struktur ● Lineare Anordnung (statisch) ● Verzeigerung (dynamisch, verschiedene Strukturen) ● Dabei bestehen natürlich auch Abhängigkeiten zwischen den verschiedenen Eigenschaften – nicht alle Kombinationen sind ohne weiteres möglich. © Andreas Rau, 14.12.07 D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-bibliotheken.odp #5 Collection Klassen(1) - Überblick In Java gibt es 3 große Familien von Collection Klassen Listen ● Sets ● Maps ● Alle diese Klassen implementieren das Interface Collection und sind von der gemeinsamen Oberklasse AbstractCollection abgeleitet die einige elementare Methoden zur Verfügung stellt. Daneben gibt es noch die Klasse Collections die eine Reihe von nützlichen Hilfsmethoden implementiert. © Andreas Rau, 14.12.07 D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-bibliotheken.odp #6 Collection Klassen(2) - Listen Als Listen bezeichnet man Klassen, die das Interface List implementieren. Dies sind die Klassen ArrayList, Vector, Stack und LinkedList. Die gemeinsame Oberklasse dieser Klassen ist AbstractList. Kennzeichnend für Listen ist, dass man Elemente leicht einfügen oder anhängen kann und direkt über einen Index auf Sie zugreifen kann. Diese gemeinsame Schnittstelle entspricht auch der Anschauung einer Liste. Die Elemente werden intern geordnet abgelegt. Die Klasse Stack bietet außerdem spezielle Zugriffsmethoden für die Betrachtung als Stapel. Jedoch bestehen wesentliche Unterschiede bzgl. der internen Implementierung: Während ArrayList und Vector ihre Elemente linear im Speicher ablegen (schneller Zugriff) sind diese bei der LinkedList explizit verzeigert (schnelles Einfügen*). vgl. Experimente zur Geschwindigkeitsmessung über System.currentTimeMillis() * Die Navigation zur Einfügestelle kann hier allerdings länger dauern (besser: Einfügen mit ListIterator)! © Andreas Rau, 14.12.07 D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-bibliotheken.odp #7 Collection Klassen(2a) - Listen List AbstractList (Interface) © Andreas Rau, 14.12.07 (Basisklasse) ArrayList LinkedList (Implementierung 1) (Implementierung 2) D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-bibliotheken.odp #8 Collection Klassen(2b) - Listen Implementierungsvariante 1: ArrayList angehängt Schneller Zugriff über Indexberechnung verschieben Dynamisches Wachsen Einfaches Anhängen eingefügt © Andreas Rau, 14.12.07 Einfügen durch Verschieben (langsam) D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-bibliotheken.odp #9 Collection Klassen(2c) - Listen Implementierungsvariante 2: LinkedList null null Langsamer Zugriff durch "Durchhangeln" Dynamisches Wachsen Einfaches Anhängen Einfügen durch Einklinken (schnell) © Andreas Rau, 14.12.07 D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-bibliotheken.odp #10 Collection Klassen(3) - Sets Als Sets bezeichnet man Klassen, die das Interface Set implementieren. Dies sind die Klassen HashSet und TreeSet. Die gemeinsame Oberklasse dieser Klassen ist AbstractSet. Kennzeichnend für Sets ist, dass keine doppelten Elemente gespeichert werden können. Die Elemente werden intern ungeordnet abgelegt. Es ist leicht Elemente einzufügen, zu entfernen und die Anwesenheit eines bestimmten Elements zu überprüfen. Dafür ist kein direkter Zugriff auf einzelne Elemente möglich. Diese gemeinsame Schnittstelle entspricht auch der Anschauung einer mathematischen Menge. Wieder bestehen wesentliche Unterschiede bzgl. der internen Implementierung der Elemente (unterschiedliche Implementierungen): Während die Klasse HashSet ihre Elemente in einer Tabelle verwalten sind diese beim TreeSet in einer Baumstruktur hinterlegt. © Andreas Rau, 14.12.07 D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-bibliotheken.odp #11 Collection Klassen(3a) - Mengen Set AbstractSet (Interface) © Andreas Rau, 14.12.07 (Basisklasse) HashSet TreeSet (Implementierung 1) (Implementierung 2) D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-bibliotheken.odp #12 Collection Klassen(4) - Maps Als Maps bezeichnet man Klassen, die das Interface Map implementieren. Dies sind die Klassen HashMap, IdentityHashMap, TreeMap und WeakHashMap Die gemeinsame Oberklasse dieser Klassen ist AbstractMap und ist nicht von Collection abgeleitet. Kennzeichnend für Maps ist, dass über einen Schlüssel direkt auf Objekte zugegriffen werden kann. Dieser Schlüssel kann ein beliebiges Objekt sein. Die Elemente werden intern ungeordnet abgelegt. Es ist leicht Elemente einzufügen, zu entfernen und die Anwesenheit eines bestimmten Elements zu überprüfen. Diese gemeinsame Schnittstelle entspricht auch der Anschauung einer Zuordnungstabelle (Mapping). Ähnliche Klassen werden in anderen Programmiersprachen auch als Assoziative Arrays oder Dictionaries bezeichnet. Auch hier (gähn!) bestehen wesentliche Unterschiede bzgl. der internen Implementierung der Elemente (unterschiedliche Implementierungen): Während die Klassen HashMap, IdentityHashMap und WeakHashMap ihre Elemente in einer Tabelle verwalten sind diese bei der TreeMap in einer Baumstruktur hinterlegt. © Andreas Rau, 14.12.07 D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-bibliotheken.odp #13 Collection Klassen(4a) - Maps Map AbstractMap (Interface) © Andreas Rau, 14.12.07 (Basisklasse) HashMap TreeMap (Implementierung 1) (Implementierung 2) D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-bibliotheken.odp #14 Einschub: Hashing Verfahren(1) Bei der Datenspeicherung mittels Hashing wird zunächst für jedes Objekt der sog. Hashwert berechnet. Anhand dieses Werts wird ähnlich wie der Index bei einem Array verwendet, um einen Speicherplatz auszuwählen. Da jedoch verschiedene Objekte den selben Hashwert haben können, ist an diesem Speicherplatz kein einzelnes Objekt sondern eine Liste von SchlüsselWertepaaren hinterlegt. Beim Zugriff wird diese Liste nach einem Paar durchsucht, dessen Schlüssel gleich dem angefragten Schlüssel ist. Dabei kann durch Sortierung und binäre Suche eine weitere Beschleunigung erreicht werden (lineare Suche: Zugriff in n/2 Schritten, binäre Suche: Zugriff in log(n)/log(2) Schritten). Damit dies funktioniert muss sichergestellt sein, dass gleiche Objekte auch denselben Hashwert haben. Dies muss beim Überschreiben der Methoden equals() bzw. hashCode() beachtet werden (siehe dazu auch die entsprechenden Beispielprogramme). Die Zugriffsgeschwindigkeit ist umso höher, je weniger Objekte denselben Hashwert haben. Damit kommt der Hashfunktion die diesen Wert berechnet eine wichtige Bedeutung zu (Anforderungen: schnelle Berechnung und breite Streuung). © Andreas Rau, 14.12.07 D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-bibliotheken.odp #15 Einschub: Hashing Verfahren(2) Zugriff Teil 2 durch Suche mit equals() Zugriff Teil 1 mit index lt. hashCode() Liste(n) mit Objekte die den gleichen hashCode haben interne Hashtable Wenn nicht "o1.equals( o2) => o1.hashCode() == o2.hashCode()", d.h. wenn gleiche Objekte unterschiedliche hashCodes haben ist funktioniert dieses zweischrittige Verfahren nicht! © Andreas Rau, 14.12.07 D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-bibliotheken.odp #16 Elementare Datentypen und Collections – Wrapper Klassen Im Unterschied zu Arrays speichern Collection Klassen allgemeine Objekte. Dies bedeutet zunächst, daß beim Zugriff auf Elemente vor deren Verwendung i.d.R. ein cast auf den speziellen Objekttyp notwendig ist. Außerdem ist es nicht möglich, elementare Datentypen in einer Collection zu speichern. Ein ähnliches Problem stellt sich auch bei verallgemeinerten Algorithmen, die auf Objekten operieren und kann durchaus als Designfehler von Java betrachtet werden. In beiden Fällen müssen von elementaren Datentypen als Objekt verpackt werden. Um dies nicht immer wieder selbst tun zu müssen, steht für jeden elementaren Datentyp eine Wrapperklasse zur Verfügung. Die Namen der Wrapperklassen ergeben sich aus der großgeschriebenen Langform des Typnamens, z.B. int / Integer, boolean / Boolean. Instanzen dieser Klassen kapseln einen Wert des zugehörigen elementaren Datentyps und stellen daneben eine Reihe von Hilfsmethoden, u.a. zum Einlesen und Ausgeben von Werten zur Verfügung. © Andreas Rau, 14.12.07 D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-bibliotheken.odp #17 Autoboxing Mit Java 5.0 packt der Compiler die Werte automatisch aus und ein. import java.util.*; import java.util.*; public class NewSchool { // ab Java 1.5 public class OldSchool { // bis Java 1.4 public static void main( String[] args) { public static void main( String[] args) { List l = new ArrayList(); List l = new ArrayList(); } l.add( 5); // Autobox-in int i = (Integer)l.get(0); // Autobox-out l.add( new Integer( 5)); int i = ((Integer)l.get(0)).intValue(); // new Integer(1); Integer A = 1; // new Integer(2); Integer B = 2; // int s = A.intValue() + B.intValue() int s = A+B; int a = 5; int b = 6; int s = a+b; } } } © Andreas Rau, 14.12.07 D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-bibliotheken.odp #18 Bearbeiten von Collections - Iteration Collections dienen nicht nur zum Verwalten sondern auch zum Bearbeiten von Elementen. Dabei kommt es häufig vor, daß eine Operation auf alle Elemente einer Collection angewendet werden soll. Bei Arrays würde man hierfür eine forSchleife verwenden. for (int i=0; i<objects.length; i++) { // objects ist ein Array Object element = objects[i]; System.out.println( element); } Diese Iteration läßt sich für Collections folgendermaßen verallgemeinern. Iterator iter = objects.iterator(); // objects ist eine Collection while (iter.hasNext()) { Object element = iter.next(); System.out.println( element); } Dabei entspricht der Iterator dem Array-Index. Zu jeder Collection Klasse gibt es eine eigene Iteratorklasse die das Interface Iterator implementiert. © Andreas Rau, 14.12.07 D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-bibliotheken.odp #19 Bearbeiten von Collections - Iteration Der Iterator ist dabei ein für den jeweiligen Typ der Collection spezifisches Objekt, wird aber genau wie die Collections selbst nur über das entsprechende Interface verwendet. Noch einfacher geht das ganze mit der neuen for-Schleife: Object[] objects = { "la", "li", "lu" }; for (Object element : objects) { // objects ist ein Array System.out.println( element); } Diese Art der Iteration läßt sich 1:1 auf Collections anwenden List objects = new ArrayList(); objects.add( "la"); objects.add( "li"); objects.add( "lu"); for (Object element : objects) { // objects ist eine Collection System.out.println( element); } Ist doch genial, wie sich hier alles bisher gelernte zusammenfügt... © Andreas Rau, 14.12.07 D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-bibliotheken.odp #20 Collection Klassen (5) – Vergleich mit Arrays Die wesentlichen Einschränkungen bei Arrays sind: feste Größe (Abhilfe: alle Collections) ● fester Datentyp für Elemente (Abhilfe: alle Collections*) ● numerischer Index (Abhilfe: Maps) ● Dies muss nicht immer von Nachteil sein. Gerade diese Eigenschaften sind es nämlich, die eine sehr effiziente Implementierung von Arrays ermöglichen. Man kann eben im Leben wie in der Programmierung einfach nicht alles haben (hier: Flexibilität und Geschwindigkeit). * Verwaltung über Klasse Object. Bei Entnahme von Objekten ist ein cast notwendig. Primitive Datentypen nur mit Hilfe von Wrapperklassen speicherbar. © Andreas Rau, 14.12.07 D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-bibliotheken.odp #21 Collection Klassen (6) – Illustration und Merkhilfe Arrays Listen a[0] l.get(0) a[1] l.get(1) a[2] l.get(2) Größe: beschränkt Zugriff: wahlfrei Struktur: geordnet © Andreas Rau, 14.12.07 variabel variabel geordnet Sets variabel variabel ungeordnet Maps variabel variabel ungeordnet D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-bibliotheken.odp #22 Collection Klassen(7) - Fazit Folgende Eigenschaften gelten für alle Collections Collections bieten "Datenstrukturen für alle Lebenslagen" ● Collections können mit beliebigen Objekten verwendet werden. ● Zu jeder Familie von Collections gibt es Interface und Basisklasse ● Variablen sollten stets mit Hilfe der Interfaces deklariert werden. Dies gilt auch für Übergabeparameter von Algorithmen. Da es normalerweise nur auf das Verhalten der Collection ankommt und dieses über das Interface voll zugänglich ist ermöglicht dies einen problemlosen Wechsel innerhalb der Varianten eines Collection-Typs (z.B. Ersatz von ArrayList durch LinkedList). Beispiel // problemloser Ersatz durch new LinkedList() List myList = new ArrayList(); Die konkrete Klasse sollte also nur verwendet werden, wenn spezielle Eigenschaften der Klasse verwendet bzw. gefordert werden (kommt so gut wie nie vor). Die Basisklasse kann ggf. für eigene Implementierungen verwendet werden (kommt noch seltener vor). © Andreas Rau, 14.12.07 D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-bibliotheken.odp #23 Ein-/Ausgabe mit Streams - Hintergrund Die Flexibilität von Software gründet sich neben ihrer Anpassbarkeit darauf, dass man Sie mit unterschiedlichsten Daten füttern kann. Daher ist die Ein- und Ausgabe von Daten ein elementarer Bestandteil von Programmen. Doch Eingabe woher und Ausgabe wohin? Tastatur, Maus, Netzwerk, Laufwerke, Bildschirm, Drucker, ... obwohl es sich hier physikalisch um unterschiedliche Geräte handelt, kann man sie alle logisch verallgemeinert als Datenquellen und -senken mit bestimmten Eigenschaften betrachten. Daraus folgt der Wunsch, diese Geräte auch in der Software möglichst gleich zu behandeln und ihre Gemeinsamkeiten zu einer einheitlichen Schnittstelle zusammenzufassen bzw. ihre Unterschiede dahinter zu verstecken. Dadurch steigt die Flexibilität der Software und es ist sehr einfach eine Datenquelle oder -senke gegen eine andere auszutauschen. © Andreas Rau, 14.12.07 D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-bibliotheken.odp #24 Ein-/Ausgabe mit Streams - Konzept(1) Das Konzept zur Verallgemeinerung von Datenquellen und -senken sind (nach alter Unix-Tradition) sogenannte Datenströme oder Streams. Ein Stream ist zunächst einmal ein Kanal, der Daten liefern oder abtransportieren kann. Auf der elementarsten Ebene werden diese Daten als Folge von Bytes betrachtet. Eine wesentliche Eigenschaft des Stream Konzepts ist die Möglichkeit, mehrere Streams zu verketten, d.h. wie Rohre ineinanderzustecken. Die aufgesteckten Streams agieren quasi als Filter (dieser Begriff ist tatsächlich gebräuchlich) und erlauben es, den ursprünglichen Bytestrom als eine Folge von Zeichen, Zahlen oder Objekten zu interpretieren oder weitere Eigenschaften wie Pufferung, Kompression, Verschlüsselung o.ä. zu realisieren. Dies funktioniert sowohl bei der Eingabe als auch bei der Ausgabe von Daten. Quelle Senke © Andreas Rau, 14.12.07 Stream (Bytes) Filter 1 (Zip) Filter 2 (Objects) Stream (Bytes) Filter 1 (Zip) Filter 2 (Objects) read write D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-bibliotheken.odp #25 Ein-/Ausgabe mit Streams - Konzept(2) Die konkrete Datenquelle oder -senke für den grundlegenden Stream kann entweder ein Gerät (Tastatur/Bildschirm), eine Datei, das Netzwerk oder ein Objekt im Hauptspeicher (String oder Array) sein. Das Tolle ist, das die konkrete Datenquelle oder -senke mit Ausnahme der Streamerzeugung praktisch keinen Unterschied macht, da alle grundlegenden Streams die gleiche Schnittstelle besitzen und die nachfolgenden Filter mit jedem von Ihnen arbeiten können. Damit kann ein Programm auf ein und dieselbe Art und Weise mit Daten aus unterschiedlichsten Quellen arbeiten. Die Kombinationsmöglichkeiten der verschiedenen Filter ermöglichen dabei ein Höchstmaß an Flexibilität und Wiederverwendung. © Andreas Rau, 14.12.07 D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-bibliotheken.odp #26 Stream Klassen(1) Die Stream Klassen in Java sind im Paket java.io untergebracht und lassen sich in InputStreams (für die Eingabe) und OutputStreams (für die Ausgabe) einteilen die jeweils von der gleichnamigen abstrakten(!) Basisklasse InputStream bzw. OutputStream abgeleitet sind. Diese Basisklasse definieren elementare Methoden für die Ein-/Ausgabe von Bytes. Ihre konkreten Subklassen wie z.B. FileInputStream implementieren diese Methoden für die Arbeit mit verschiedenen Datenquellen bzw. -senken. Daneben gibt es ein je eine weitere Klasse (z.B. FilterInputStream), die als abstrakte Basisklasse für diverse Filter dient. Diese erlauben die Bearbeitung des elementaren Bytestroms, z.B. Pufferung (z.B. BufferedInputStream), Komprimierung (z.B. ZipInputStream) oder "Interpretation" als Werte elementarer Datentypen (z.B. DataInputStream). Die Klassen zur Ein-/Ausgabe von Objekten, ObjectInputStream und ObjectOutputStream, sind eigentlich Filter, erben aber trotzdem direkt von den jeweiligen abstrakten Streamklassen. © Andreas Rau, 14.12.07 D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-bibliotheken.odp #27 Stream Klassen(2) Zur Verkettung von Filtern bzw. Streams muss bei der darunterliegende Stream bei der Konstruktion des darüberliegenden angegeben werden. Quelle Stream (Bytes) Filter (Data) read Beispiel: import java.io.FileInputStream; import java.io.DataInputStream; // ... FileInputStream fi = new FileInputStream( "myfile.dat"); DataInputStream di = new DataInputStream( fi); double d = di.readDouble(); di.close(); Die Klasse System stellt mit den Klassenvariablen err, in und out drei Streams für die Ein-/Ausgabe über Tastatur und Bildschirm zur Verfügung. Die beiden Ausgabestreams sind in Wirklichkeit Filterketten, der Eingabestream ist direkt. © Andreas Rau, 14.12.07 D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-bibliotheken.odp #28 Serialisierung Bei der Ein-/Ausgabe von Objekten muss unter anderem die Klasse sowie der Wert aller Attribute einzeln verarbeitet werden. Glücklicherweise muß man dies nicht manuell implementieren: Über das Interface Serializable können beliebige Klassen für die automatische Ein-/Ausgabe markiert werden. Beispiel public class Auto implements Serializable { // ... } // in einer anderen Klasse: Auto a = new Auto( "Ford", "Fiesta"); FileOutputStream fos = new FileOutputStream( "objects.dat") ObjectOutputStream oos = new ObjectOutputStream( fos); oos.writeObject( a); oos.close(); Dieser automatische Vorgang kann bei Bedarf durch überschreiben der Methoden readObject() und writeObject() angepasst werden. Dies ist jedoch nur in Ausnahmefällen notwendig. © Andreas Rau, 14.12.07 D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-bibliotheken.odp #29 Reader und Writer Die Ein-/Ausgabe von Zeichen ist wegen deren unterschiedlicher Kodierung nur in Ausnahmefällen direkt über Streams möglich. Während der westeuropäische ISO-Latin-1 Zeichensatz weitgehend dem ASCII Zeichensatz entspricht und mit einem Byte pro Zeichen auskommt, sind im allgemeinen Fall mit Unicode zwei oder mehr Bytes pro Zeichen notwendig. Daher gibt es in Java spezielle Klassen zur Ein-/Ausgabe von Zeichen, die sog. Reader und Writer die wiederum von zwei gleichnamigen abstrakten Basisklassen abgeleitet sind. Reader und Writer können Text zeichen- und/oder zeilenweise ein- und ausgeben und berücksichtigen dabei automatisch die jeweils gültige Kodierung. Die Systematik der darunterliegenden Klassen ist ähnlich wie bei den Streams: Es gibt direkte konkrete Subklassen für die Anbindung verschiedener Datenquellen und -senken sowie eine Zwischenklasse für Filter. Durch die Fokussierung auf Text ist jedoch die Vielfalt, insbesondere bei den Filtern, nicht so zahlreich. Reader bzw. Writer sind jedoch selbst auch Filter und können auf Streams und deren Filter "aufgesteckt" werden. © Andreas Rau, 14.12.07 D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-bibliotheken.odp #30 Ein-/Ausgabe - Überblick Ein-/Ausgabe (I/O) Paket java.io Byteorientiert Zeichenorientiert Eingabe Ausgabe Eingabe Ausgabe (abstrakt) InputStream (abstrakt) OutputStream (abstrakt) Reader (abstrakt) Writer (konkret) FileInputStream (konkret) FileOutputStream (konkret) FileReader (konkret) FileWriter (filter) BufferedInputStream ByteArrayInputStream ByteArrayOutputStream CharArrayReader InputStreamReader CharArrayWriter OutputStreamWriter (filter) (filter) (filter) BufferedOutputStream BufferedReader BufferedWriter DataInputStream DataOutputStream ObjectInputStreamObjectOutputStream © Andreas Rau, 14.12.07 D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-bibliotheken.odp #31 Ein-/Ausgabe - Fazit Streams und damit die zugehörigen Datenquellen und -senken können ähnlich wie Collections ausgetauscht werden, ohne das am Code viel geändert werden muss, da die Schnittstelle stets dieselbe bleibt. Aufbauend auf dieser gemeinsamen Schnittstelle können den elementaren Streams über Filter spezifische Eigenschaften flexibel aufgeprägt werden. Dabei lassen sich auch verschiedene Filter durch Verkettung kombinieren. Auf diese Art und Weise ist ein Höchstmaß an Flexibilität und Wiederverwendung möglich. Damit stellen die Streams und die zugehörigen Reader und Writer und Filter eine vielseitige, mächtige und erweiterbare Menge von Klassen dar. Der Aufbau der zugehörigen Klassenbibliothek folgt einer einfachen Systematik: Abstrakte Basisklassen zur Festlegung der elementaren Schnittstelle ● Konkrete Subklassen zur Anbindung verschiedener Datenquellen und -senken ● Abstrakte Subklasse für Filter ● Konkrete Subklassen zur Implementierung der Filter ● © Andreas Rau, 14.12.07 D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-bibliotheken.odp #32