Dateien und Streams

Werbung
Kapitel 1
Dateien und Streams
1.1
Dateien und Dateiverzeichnisse
Die Klassen File und URL
Die Daten, welche von Programmen bearbeitet werden, sowie die Programme
selbst werden in Dateien abgespeichert. Dateien können angelegt werden und der
Übersicht halber in Dateiverzeichnisse strukturiert werden und sie können umbenannt und auch wieder gelöscht werden.
Die Verwaltung von Dateien und Dateiverzeichnissen wird in Java von der Klasse
File übernommen. Sie liefert eine abstrakte, systemunabhängige Repräsentation
für derartige Objekte. Der Zugriff auf die in einer Datei gespeicherten Daten erfolgt
über Datenströme, die in der Java-Literatur auch als Streams bezeichnet werden
und in den folgenden Unterkapiteln beschrieben werden.
Ein File-Objekt kann über den mehrfach überladenen Konstruktor der Klasse
File instantiiert werden. In den Konstruktordefinitionen werden Referenzen vom
Typ der Klassen String, File und URI übergeben. Die String- und File-Referenzen verweisen auf konkrete bzw. abstrakte Verzeichnis- oder Dateinamen.
Über URI (»uniform resource identifier«)-Referenzen können Ressourcen im Netzwerk eindeutig identifiziert werden. URIs können vom Typ URL (»uniform
resource locator«) oder URN (»uniform resource name«) sein. URL-Referenzen
können auf Ressourcen im World Wide Web (WWW) wie Dateien und Dateiverzeichnisse, aber auch auf kompliziertere Objekte wie Abfragen auf Datenbanken
oder Suchmaschinen zeigen. URLs beinhalten die Location (Ort) eines Dokuments
und die wichtigsten unterstützten Zugriffsarten. URNs spezifizieren die Art von
Ressourcen, um diese zu identifizieren, ohne sie gleichzeitig zu referenzieren. Beispiele dafür sind die Protokolle http (HypertextTransfer Protocol), ftp (File Transfer Protocol), file (Dateien im lokalen Dateisystem), mailto (E-Mail-Adressen)
und news (NewsGroup oder Newsartikel).
Ein Beispiel für eine URL ist http://java.sun.com/index.jsp, die das Dokument index.jsp auf einem Host von Sun referenziert. Ein weiteres Beispiel ist
file:C:/Programme/Java/jdk1.6/docs/api/index.html, welche die Datei
index.html im angegebenen Directory auf dem lokalen Rechner referenziert.
http ist ein Protokoll, das im Allgemeinen keine Authentifizierung des Benutzers
verlangt. Bei anderen wie z.B. ftp ist meistens eine Benutzer-/Passwort-Identifizierung notwendig, damit eine Datei übertragen werden kann. Diese Beispiele zei-
27
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
gen, dass eine URL über einen String dargestellt werden kann, der eine Ressource
im Internet eindeutig identifiziert.
Die Methode toURL() der File-Klasse wurde mit Java 6 als »deprecated« gekennzeichnet; weil diese falsche URLs liefert, falls Datei- oder Dateiverzeichnisnamen
Leerzeichen beinhalten. Der einfachste Weg, eine korrekte URL-Instanz für eine
File-Instanz zu erzeugen ist der gekettete Methodenaufruf toURI().toURL() an
einem File-Objekt.
Aufgabe 1.1
Die Konstruktoren und Methoden der Klasse File
Definieren Sie eine Klasse DateiundVerzeichnisVerwaltung, in der Sie FileObjekte über die unterschiedlichen Konstruktoren erzeugen.
Im Konstruktor der Klasse File kann ein Verzeichnisname als String-Referenz
übergeben werden, der in einen abstrakten Pfadnamen konvertiert wird. Anschließend muss die Methode mkdirs() aufgerufen weden, die das entsprechende
Unterverzeichnis erzeugt. Es können aber auch der Verzeichnisname gefolgt vom
Dateinamen bzw. Verzeichnis- und Dateinamen als String-Referenzen übergeben
werden. Anschließend muss die Methode createNewFile() aufgerufen werden,
welche die Datei erzeugt.
Lassen Sie sich die Syntax von URL- und URI-Schemata mit Hilfe der Methoden
toURL() und toURI() der Klasse File anzeigen, um herauszufinden, wie das im
Konstruktoraufruf übergebene Argument in diesem Fall auszusehen hat. So liefert
z.B. new File("C:/EJ_Uebungsbuch2/").toURI().toURL(); die URL file:/
C:/EJ_Uebungsbuch2/ und genau diese Syntax muss im Konstruktoraufruf verwendet werden.
Erzeugen Sie ein weiteres File-Objekt, indem Sie im Konstruktoraufruf einen als
Argument im Programmaufruf angegebenen Datei- oder Verzeichnisnamen übergeben.
Definieren Sie der Einfachheit halber ein File-Array, um darin alle erzeugten Verzeichnisse und Dateien zu hinterlegen.
Die Klasse File stellt Methoden zur Verfügung, mit denen Informationen über
eine Datei oder ein Dateiverzeichnis geholt werden können. Rufen Sie die Methoden exists(), isFile() und isDirectory() an allen von Ihnen erzeugten
File-Objekten auf, um zu sehen, ob die Konstruktoraufrufe zu den gewünschten
Ergebnissen geführt haben. Zeigen Sie die Namen aller Verzeichnisse und Dateien
am Bildschirm an, indem Sie die Methode getName() der Klasse File an den von
Ihnen erzeugten Instanzen der Klasse aufrufen.
Testen Sie auch andere Methoden der Klasse File, wie z. B. renameTo() und
delete(), mithilfe derer Sie Dateien umbennen bzw. löschen können.
28
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.1
Dateien und Dateiverzeichnisse
Hinweise für die Programmierung:
Auf manchen Systemen liefert die Methode delete() den Rückgabewert true,
obwohl die Datei nicht gelöscht wurde, und beim Aufruf der Methode getName()
nach renameTo() wird noch immer der alte Dateiname angezeigt, obwohl die
Datei umbenannt wurde.
Java-Dateien: DateiundVerzeichnisVerwaltung.java
Programmaufruf: java DateiundVerzeichnisVerwaltung
C:/EJ_Uebungsbuch2/, java DateiundVerzeichnisVerwaltung
DateiundVerzeichnisVerwaltung.java
Aufgabe 1.2
Auflisten von Einträgen aus Dateiverzeichnissen
Über den Methodenaufruf getProperty("user.home") der Klasse System kann
das Home-Verzeichnis des aktuellen Benutzers ermittelt werden, wobei
user.home eine Systemeigenschaft (»system property«) bezeichnet.
Erstellen Sie eine Klasse DateiundVerzeichnisListen, in der Sie mithilfe der
Methode listFiles() der Klasse File sowohl die Unterverzeichnisse und
Dateien des Home-Verzeichnisses auflisten als auch die eines von Ihnen definierten Verzeichnisses. Dieses soll ein weiteres Unterverzeichnis enthalten und darin
sollen mehrere Dateien hinterlegt werden.
Definieren Sie eine Klassenmethode anzeige(), um die Namen von Verzeichnissen und Dateien aus den als Rückgabewert der Methode listFiles() erzeugten
File-Arrays am Bildschirm auszugeben.
Ermitteln Sie alle verfügbaren Laufwerke auf Ihrem Rechner mithilfe der Methode
listRoots() der Klasse File und deren Speicherkapazität wie auch den darauf
noch vorhandenen Platz mithilfe der Methoden getTotalSpace() und getFreeSpace() der gleichen Klasse. Diese Methoden wurden mit der Version 6.0
von Java eingeführt.
Java-Dateien: DateiundVerzeichnisListen.java
Programmaufruf: java DateiundVerzeichnisListen
Aufgabe 1.3
Die Klassen FileFilter und FilenameFilter
Über den Methodenaufruf getProperty("user.dir") der Klasse System kann
das aktuelle Verzeichnis des Benutzers ermittelt werden.
29
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
Erstellen Sie eine Klasse DateiundVerzeichnisFilter, in der Sie das aktuelle
Verzeichnis ermitteln und dieses nach vorgegebenen Filterdefinitionen mithilfe
der Methoden list(FilenameFilter filter) und listFiles(FileFilter
filter) der Klasse File durchsuchen.
Definieren Sie dazu mittels anonymer Klassen drei benutzerdefinierte Filter, die
das Interface FilnameFilter implementieren und aus dem aktuellen Verzeichnis
jeweils alle Dateien mit den Endungen .java und .class filtern.
Ein weiterer benutzerdefinierter Filter vom Typ der Klasse FileFilter wird mittels einer weiteren anonymen Klasse implementiert und soll dazu dienen,
bestimmte Dateien oder Unterverzeichnisse über ihren Pfadnamen zu ermitteln.
In den anonymen Klassen sollen die Methoden accept() des Interfaces
FilenameFilter und der Klasse FileFilter implementiert bzw. überschrieben
werden.
Weil die Methoden list() und listFiles() Arrays von unterschiedlichen Datentypen (String bzw. File) liefern, soll eine generische Methode anzeigeArray()
definiert werden, der beide Arten von Arrays für die Anzeige der Datei- und Verzeichnisnamen als Argumente im Methodenaufruf übergeben werden können.
Java-Dateien: DateiundVerzeichnisFilter.java
Programmaufruf: java DateiundVerzeichnisFilter
1.2
Die Definition und Klassifikation von Streams
Streams (Datenströme) sind in Java Objekte, die uns erlauben, in einem Programm
Daten von einer Informationsquelle einzulesen und zu einem Ziel zu senden. Ein
Stream kann von einem peripheren Gerät, einer Datei, aus dem Speicher des Rechners, von einem Thread, einem Netzwerk-Socket sowie von einem anderen Stream
kommen. Ein solcher wird auch als Input-Stream bezeichnet. Die Ziele eines
Streams können die gleichen wie seine Quellen sein, denen wäre nur noch der
Drucker hinzuzufügen. In diesem Fall wird von Output-Streams gesprochen. Ein
Stream muss geöffnet werden, seine Daten (Bytes oder Zeichen) gelesen oder
geschrieben werden und der Stream wieder geschlossen werden.
Alle Stream-Klassen von Java befinden sich im Paket java.io. Die einzelnen Klassen unterscheiden sich dadurch, dass die einen Byte- und die anderen Characterorientiert (was auch als zeichenorientiert bezeichnet wird) arbeiten.
Nützliche Bemerkungen zu Datenkonvertierungen für Ein-/Ausgaben
An dieser Stelle sei noch mal darauf hingewiesen, dass ein Bit (»binary digit«) die
kleinste Dateneinheit in einem Computer ist und die Werte 0 und 1 aufnehmen
kann. Ein Byte besteht aus 8 Bits und ein Java-Unicode-Zeichen besteht aus zwei
Bytes.
30
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.2
Die Definition und Klassifikation von Streams
Unicode ist ein international standardisierter Zeichensatz, der als Untermenge den
ASCII-Zeichensatz enthält. Mit dem Unicode-Zeichensatz wird einem Character
eine 16 Bit breite ganze Zahl (die in der Java-Literatur auch als Unicode-Code-Point
bezeichnet wird) zugeordnet. Der Unicode-Code-Point eines Zeichens kann mithilfe der mit der Version 5.0 von Java implementierten Methode codePointAT()
der Klasse String ermittelt werden.
Dadurch, dass im Unicode-Zeichensatz jedes Zeichen mit einer Breite von 16 Bits
dargestellt wird, kann eine sehr große Anzahl von Zeichen erfasst und damit der
Zeichensatz vieler Sprachen dieser Welt abgedeckt werden.
Im Gegensatz dazu ordnet der altbekannte ASCII-Zeichensatz 128 ganze Zahlen
mit einer Breite von 7 Bits den Englisch-orientierten Characters zu, wie zum Beispiel den Großbuchstaben des Alphabetes von A bis Z die Zahlen 65 bis 91 und den
Kleinbuchstaben von a bis z die Zahlen 97 bis 123. Daher kommt auch die Bezeichnung 7-Bit ASCII-Key-Code, die oft verwendet wird. Zum Verständnis der weiteren
Darbietungen ist wichtig zu wissen, dass die Zeichen des ASCII-Zeichensatzes im
Unicode-Zeichensatz mit dem gleichen Code-Wert (der gleichen ganzen Zahl) dargestellt werden.
Ähnliches gilt für den 8-Bit-Code »ISO-8859-1« für die westeuropäischen Sprachen, bei dem die Codes von 0 bis 127 die 128 ASCII-Zeichen enthalten und die
Codes 128 bis 255 weitere Sonderzeichen wie z.B. deutsche Umlaute und französische Akzente. Es gibt aber auch andere 8-Bit-Codes, z.B. für die osteuropäischen
Sprachen, Griechisch, Kyrillisch u.a.
Die meisten Rechnersysteme unterstützen nicht den gesamten Unicode-Zeichensatz, sondern nur Teile davon, wie z.B. ISO-8859-1. Java-Programme werden meistens mit einem ASCII- oder ISO-8859-1-basierten Editor erfasst. Um die Anzahl
der Zeichen, die in einem solchen Editor eingegeben werden können, zu vergrößern, definiert Java die Unicode-Escape-Sequenz, welche eine Folge von ASCII-Zeichen in Form von \uxxxx für die Repräsentation eines Unicode-Zeichens nutzt. In
dieser Darstellung steht jedes x für eine hexadezimale Zahl und es können damit
alle Unicode-Zeichen repräsentiert werden (so ist z.B. für die Großbuchstaben von
A bis Z der Wertebereich von \u0041 bis \u005a reserviert, für die Kleinbuchstaben von a bis z der Wertebereich von \u0061 bis \u007a und für die Zahlen von 1
bis 9 der Wertebereich von \u0030 bis \u0039).
Eigentlich gibt es eine 1:1-Abbildung zwischen Unicode-Zeichen und den Zeichen
dieser Zeichensätze. So entsprechen die Unicode-Zeichen \u0020 bis \u007E den
ASCII- und ISO8859-1-Zeichen 0x20 bis 0x7E und die Unicode-Zeichen von
\u00A0 bis \u00FF sind mit den ISO8859-1-Zeichen 0xA0 und 0xFF identisch.
Bevor es mit dem Compilieren losgeht, prüft der Java-Compiler ob der Programmcode in Unicode-Zeichencodes vorliegt. Werden 7-Bit ASCII- oder 8-Bit ISO-88591-Zeichencodes vorgefunden, werden diese durch das Hinzufügen von Bits gleich 0
in Unicode-Zeichencodes umgesetzt. Eine Ausnahme bilden nur die Unicode-
31
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
Escape-Sequenzen. Für diese werden die 6 ASCII-Zeichen, welche die Sequenz
repräsentieren, in einen 16 Bit Unicode-Zeichencode umgesetzt.
Aufgabe 1.4
Operationen mit primitiven Datentypen
Wir wollen eine Testklasse TestLossofPrecision für Operationen mit primitiven
Datentypen erstellen, um deren Ergebnisse in den weiteren Aufgaben beim Konvertieren von Daten zu nutzen.
Die Klasse soll eine Methode mit der Signatur public static Number[]
add(Number zahl1, Number zahl2) definieren, die zwei beliebige Instanzen von
Wrapper-Klassen übergeben bekommt, mithilfe der Methoden intValue(),
floatValue(), byteValue() etc. deren primitive Werte ermittelt und diese in
unterschiedlichen Kombinationen addiert. Alle errechneten Ergebnisse werden in
einem Number-Array für die Rückgabe hinterlegt.
In der main()-Methode der Klasse sollen lokale Variablen von allen primitiven
Datentypen definiert, initialisiert und addiert werden, um herauszufinden, welche
Operationen möglich sind und welche Art von Castings dafür erforderlich sind.
Gleichzeitig soll die Methode add() mit Referenzen auf Objekte von Unterklassen
des Parametertyps mehrmals aufgerufen werden.
In der Klasse TestLossPrecision wird auch ein byte-Array mit 26 Elementen
definiert, denen der 7-Bit ASCII-Key-Code (gleich dem Unicode-Code-Point) für
alle Großbuchstaben des Alphabetes zugewiesen wird (d.h. die Werte 65 bis 91).
Diese sollen mithilfe der Methode Character.toLowerCase() in Kleinbuchstaben und mit der Methode byteValue() der Wrapper-Klasse Byte sowie der Addition des int-Wertes 32 auf die ursprünglichen Werte in deren 7-Bit ASCII-KeyCode (d.h. die Werte von 97 bis 123) umgesetzt werden.
Zeigen Sie alle erzielten Ergebnisse am Bildschirm an.
Java-Dateien: TestLossPrecision.java
Programmaufruf: java TestLossPrecision
Byteorientierte Streams
Byte-Streams können als Instanzen der Klassen InputStream und OutputStream
gebildet werden. Die als Instanzen dieser Klassen erstellten Streams benutzen als
kleinste Ein- und Ausgabeeinheit 1 Byte.
InputStream ist die Oberklasse aller byteorientierten Klassen zum Einlesen von
Daten und OutputStream die Oberklasse aller byteorientierten Klassen zum
Schreiben von Daten.
32
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.2
Die Definition und Klassifikation von Streams
Diese Klassen werden in der Regel für den Zugriff auf binäre Daten verwendet, da
ein Zugriff auf Textdaten sich damit nicht allzu einfach gestalten lässt und Probleme beim Konvetieren entstehen können.
Zeichenorientierte Streams
Die Klassen Reader und Writer existieren parallel zu den byteorientierten Klassen
und dienen der Definition von Character-Streams. Diese ermöglichen eine korrekte
Konvertierung zwischen den von Java intern genutzten Unicode-Zeichen und den
jeweiligen Betriebssystem-Zeichensätzen.
Reader ist die Oberklasse aller zeichenorientierten Klassen zum Einlesen von
Daten und Writer die Oberklasse aller zeichenorientierten Klassen zum Schreiben
von Daten.
Da die meisten Betriebssysteme noch nicht den Unicode-Zeichsatz unterstützen,
muss eine Konvertierung der Zeichen bei Ein-/Ausgaben erfolgen, die automatisch
von den Unterklassen dieser Klassen, InputStreamReader und OutputStreamWriter, durchgeführt wird. Diese beiden Klassen stellen eine Verbindung zwischen den beiden vorher definierten Stream-Arten (byte- und zeichenorientiert) dar
und werden in der Java-Literatur auch als Brückenklassen oder Adapterklassen
bezeichnet. Für die Umwandlung der Daten wird eine Default-Kodierung benutzt,
die der Kodierung des Zeichensatzes auf der verwendeten Betriebssystem-Plattform entspricht und automatisch von den Brückenklassen ermittelt wird.
Parallel zur Default-Kodierung kann eine plattformabhängige Kodierung eingesetzt
werden, welche über die Systemeigenschaft »file.encoding« abgefragt werden kann
und dann im Konstruktor von Brückenklassen als String übergeben werden muss.
Wie andere Systemeigenschaften auch, wird auch diese von der Java-StandardKlasse Properties zur Verfügung gestellt, deren Feld defaults aus Sicherheitsgründen mit dem Modifikator protected definiert ist. Über den Aufruf der
Methode getProperty() der Klasse System kann der Name der Zeichenkodierung ermittelt werden.
Über die Konstruktoren der Klassen InputStreamReader und OutputStreamWriter, die beide mehrfach überladen sind, wird ein Byte-Stream vom Typ InputStream über eine angegebene Charset- oder CharsetDecoder-Instanz in eine für
die Reader-Klassen lesbare Instanz umgesetzt bzw. eine als Instanz der Klasse OutputStream übergebene Anzahl von Zeichen mit Angabe eines Zeichensatzes in
Bytes umgewandelt.
Die abstrakte Klasse Charset definiert eine Beziehung zwischen Folgen von 16-BitUnicode-Zahlen und Folgen von Bytes. Beispiele von Standard-Charsets in Java
sind US-ASCII, ISO-8859-1 und UTF-8.
In Java wird eine leicht modifizierte Version der Unicode Transformation Format
(UTF)-8-Kodierung genutzt, mit welcher 16 Bit-Unicode-Zahlen auf 8 Bit-Zahlen
abgebildet werden, die auch in Dateien abgespeichert und wieder zurückgewonnen
33
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
werden können. Die Verschlüsselung der Unicode-Zeichen erfolgt in Abhängigkeit
von deren Wert. So werden die Unicode-Zeichen, die durch den ASCII-Zeichensatz
von \u0001 bis \u007F repräsentiert werden, mit einem Byte kodiert, das UnicodeZeichen mit dem Wert \u0000 sowie die Zeichen, deren Wert zwischen \u0080
und \u07FF liegt, mit zwei Bytes und die Zeichen mit einem Wert von \u0800 bis
\uFFFF mit drei Bytes kodiert.
Die abstrakte Klasse CharsetDecoder kann eine Folge von Bytes mit dem spezifizierten Charset in eine Folge von 16-Bit-Unicode-Zahlen umwandeln.
Für das Umsetzen von Byte-Folgen in Strings können auch die entsprechenden
Konstruktoren der String-Klasse benutzt werden. Die Klasse CharsetDecoder
sollte laut Java-Dokumentation dann genutzt werden, wenn eine zusätzliche Kontrolle beim Dekodieren erforderlich ist.
Character-Streams sind einfacher zu benutzen und darum wird empfohlen, auf
diese zurückzugreifen, wenn keine binären Daten verarbeitet werden sollen. Da
eine sprachspezifische Zeichendarstellung nur beim Lesen und Schreiben von Texten eine Rolle spielt, ist es jedoch sinnvoll, ansonsten die byteorientierten Streams
zu benutzen. Die Unterklassen der vorher beschriebenen Stream-Klassen werden
im Nachfolgenden immer parallel dargestellt, da es in ihrer Funktionalität keine
großen Unterschiede gibt. Der Datentyp, auf dem diese Streams operieren, ist zwar
verschieden, aber sie definieren in fast allen Fällen die gleichen oder ähnliche
Methoden.
Die Unterklassen von InputStream und OutputStream
Die abstrakten Klassen InputStream und OutputStream stellen dem Programmierer über die Objekte ihrer konkreten Unterklassen die wichtigsten Streams für
die Ein- und Ausgabe von binären Daten zur Verfügung.
Für das Schreiben und Lesen von binären Daten in (aus) eine (einer) Datei werden
die Klassen FileInputStream und FileOutputStream verwendet. Die Bearbeitung der Daten aus den Dateien kann hiermit nur sequentiell erfolgen.
Das Schreiben und Lesen von Bytes in (aus) den (dem) Arbeitsspeicher wird von den
Klassen ByteArrayInputStream und ByteArrayOutputStream bewerkstelligt.
Die abstrakten Unterklassen FilterInputStream und FilterOutputStream dienen der Umwandlung von Daten, um für diese neue Funktionalitäten zu ermöglichen. Deren Unterklassen BufferedInputStream und BufferedOutputStream
dienen der Pufferung von Daten.
Die Klasse StringBufferInputStream ermöglicht, Daten aus einem String mithilfe eines Streams vom Typ der Klasse zu lesen. Sie liest die Daten aus einem
StringBuffer, der als Objekt einer gleichnamigen Klasse erzeugt wird.
Methoden zum Schreiben und Lesen von primitiven Datentypen sind in den
Schnittstellen DataOutput und DataInput spezifiziert, die von den Klassen Data-
34
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.2
Die Definition und Klassifikation von Streams
OutputStream und DataInputStream (zwei weitere Unterklassen von FilterInputStream und FilterOutputStream) implementiert werden.
Die Klassen ObjectInputStream und ObjectOutputStream ermöglichen ein
vorher mithilfe von Instanzen der Klasse ObjectOutputStream gespeichertes
Objekt durch eine Instanz vom Typ ObjectInputStream wieder einzulesen bzw.
über das Netz zu transportieren. Dieser Vorgang ist unter dem Begriff Objektserialisierung bekannt.
Mithilfe von print-Methoden der Klasse PrintStream (ebenfalls von FilterOutputStream abgeleitet) können primitive Datentypen in einem Textformat (mit und
ohne Zeilenumbruch) ausgegeben werden. Hierfür werden die Methoden
print(), println() und ab der Version 5.0 von Java die Methode printf() zur
Verfügung gestellt.
Der Vollständigkeit halber erwähnen wir an dieser Stelle auch die Klasse PushbackInputStream, die das Zurückschreiben eines bereits gelesenen Streams ermöglicht, und die Klassen PipedOutputStream und PipedInputStream, die für einen
Datenaustausch zwischen Threads eingesetzt werden können. Die mit der Version
1.4 des JDK eingeführten Interfaces ImageOutputStream und ImageInputStream, welche die Interfaces DataOutputStream und DataInputstream erweitern, sowie die abstrakten Klassen ImageReader und ImageWriter können für
Dekodierungsaufgaben beim Schreiben und Lesen von Bildern im Kontext der Java
Image I/O API benutzt werden. Um konkrete Instanzen zu bilden, müssen diese
Klassen erweitert werden. Auf die Piped-Streams werden wir in Kapitel 2 zurückkommen.
Die Unterklassen von Reader und Writer
Die Klassen FileWriter und FileReader sind nicht direkt von Writer und Reader abgeleitet, sondern von deren Unterklassen, den Brückenklassen OutputStreamWriter und InputStreamReader. Die Bearbeitung der Daten aus einer
Datei kann auch in diesem Fall nur sequentiell erfolgen.
Die Klassen CharArrayReader und CharArrayWriter können für das Lesen und
Schreiben von char-Arrays genutzt werden und die Klassen StringReader und
StringWriter für das Lesen und Schreiben von Strings.
Um die Performance von Zugriffen auf Textdaten zu steigern, können die Klassen
BufferedReader und BufferedWriter eingesetzt werden, welche ebenso wie die
Klassen BufferedInputStream und BufferedOutputStream der Pufferung von
Daten dienen, aber im Unterschied zu diesen nicht von den Filterklassen, sondern
direkt von den Klassen Reader und Writer abgeleitet sind. Die Filterklassen für
Textdaten, FilterReader und FilterWriter, sind abstrakte Klassen und ebenfalls direkte Erweiterungen der Klassen Reader und Writer.
Mit Character-Streams sind auch formatierte Ausgaben möglich; dazu kann die
Klasse PrintWriter benutzt werden. Sie ermöglicht mithilfe von print()-,
35
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
println()- und printf()-Methoden die Ausgabe einer String-Repräsentation
für beliebige primitive Typen und Objekte.
Streams, zu deren Erzeugung die Instanz einer anderen Stream-Klasse benötigt
wird, werden auch als »abgeleitete Streams« bezeichnet. Sie erweitern den Stream,
der im Konstruktor ihrer Klasse als Referenz übergeben wird, um weitere Funktionen oder filtern die Daten, die über diesen Stream transportiert werden. Dazu
gehören alle von FilterOutputStream bzw. FilterWriter und FilterInputStream bzw. FilterReader abgeleitete Klassen. Wenn mit abgeleiteten Streams
weitere abgeleitete Streams durch die Übergabe von deren Referenzen in Konstruktoraufrufen nacheinander gebildet werden, wird auch von Kettungen oder Verknüpfungen von Streams gesprochen. Wie die nachfolgenden Beispiele zeigen,
sind viele Kettungen von Streams realisierbar.
1.3
Die gemeinsamen Methoden zum Schreiben und Lesen
der Oberklassen OutputStream und Writer bzw. InputStream und Reader
Die Methode zum Schreiben in einen OutputStream (bzw. Writer) ist write(),
welche Bytes (bzw. Zeichen) in das Ziel hinterlegt. Die drei write()-Methoden,
welche sich in der Klasse OutputStream überladen, nehmen einzelne Bytes vom
primitiven Typ byte oder byte-Arrays entgegen. In den fünf überladenen Methoden der Klasse Writer werden einzelne Zeichen oder Arrays von Zeichen bzw.
String- oder CharSequence-Objekte über ihre Referenzen übergeben.
CharSequence ist ein Interface, das von Klassen wie String, CharBuffer und
StringBuffer implementiert wird und eine lesbare Folge von char-Werten reprä-
sentiert. Zum Zugriff auf Zeichenfolgen wird eine Indizierung der Zeichen vorgenommen, die wie bei Arrays mit 0 beginnt.
Die im Methodenaufruf angegebenen Daten werden nicht sofort geschrieben, sondern erst zwischengespeichert. Ist der angegebene Schreibpuffer voll oder wird die
Methode flush() der Klassen aufgerufen, werden die Daten in den AusgabeStream gelegt und der Schreibpuffer gelöscht.
Die Methode zum Lesen eines InputStreams (bzw. Readers) ist read(), welche
Bytes (bzw. Zeichen) von einer Quelle liest. Von den drei read()-Methoden, die
sich überladen, werden in beiden Klassen einzelne Bytes bzw. Characters oder
byte- bzw. char-Arrays zurückgeliefert. Der Rückgabewert der parameterlosen
Methode gibt das gelesene Byte oder Zeichen zurück bzw. die Anzahl der gelesenen
Zeichen für die restlichen read()-Methoden. Letzteren muss ein Puffer für das
Speichern der eingelesenen Daten als Referenz im Methodenaufruf übergeben
werden. Ist der Rückgabewert einer read()-Methode gleich -1, bedeutet dies, dass
das Ende des Streams erreicht wurde.
Die Methode skip() kann benutzt werden, um eine angegebene Anzahl Zeichen
beim Lesen zu überspringen und reset(), um zu einer vorher über eine mit der
36
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.4
FileInputStream, FileReader, FileOutputStream, FileWriter
Methode mark() markierten Position zurückzukehren. Der Aufruf von close()
schließt den Stream.
1.4
Die Klassen FileInputStream und FileReader bzw. FileOutputStream und FileWriter und der Zugriff auf
Dateien
Die Instanzen dieser Klassen werden für das Lesen und Schreiben von Bytes und
Zeichen aus (in) einer (eine) Datei genutzt. Wie bereits erwähnt, erfolgt der Zugriff
auf die Datei damit immer sequentiell.
Die Klassen FileInputStream und FileOutputStream definieren einen mehrfach überladenen Konstruktor, in dem ein Dateiname als String-Instanz, die Datei
selbst als Objekt der Klasse File oder eine FileDescriptor-Instanz per Referenz
übergeben werden können. Bei der Übergabe einer Datei oder ihres Namens wird
eine FileNotFoundException geworfen, falls diese nicht vorhanden ist, welche
abgefangen oder an die aufrufende Methode weitergereicht werden muss.
Bei der Ein- und Ausgabe von Zeichen mithilfe der Character-Streams vom Typ
FileReader und FileWriter erfolgt eine Konvertierung von Zeichen in Bytes und
umgekehrt. Diese Arbeit wird von den Brückenklassen, von denen diese Klassen
abgeleitet sind, übernommen. Die Klassen FileReader und FileWriter besitzen
je drei Konstruktoren mit gleicher Syntax wie die byteorientierten FileStreams.
Die Klassen FileOutputStream und FileWriter definieren noch zwei zusätzliche Konstruktoren mit einem Parameter vom Typ boolean, über den festgelegt
werden kann, ob die zu schreibenden Daten zu den schon vorhandenen hinzugefügt werden oder diese vorher gelöscht werden sollen. Ist eine angegebene Datei
nicht vorhanden, wird diese neu angelegt. Wird der boolean-Wert gleich false
gesetzt oder ist dieser im Konstruktoraufruf nicht vorhanden, wird der vorherige
Dateiinhalt beim Öffnen gelöscht.
Aufgabe 1.5
Die Klassen FileOutputStream und FileInputStream
Ein byteorientierter Stream vom Typ der Klasse FileOutputStream stellt Methoden zur Verfügung, die einzelne Bytes bzw. Arrays von Bytes in Dateien schreiben.
Ein solcher Stream soll in einer Klasse ByteFileStreams mit einer Datei namens
»Binärdatei« verbunden werden, die zuerst nicht vorhanden ist und bei Schreibzugriffen immer weiter fortgeschrieben werden soll.
Initialisieren Sie ein byte-Array array mit den Werten, welche die 7-Bit ASCIIKey-Code Repräsentation der Buchstaben von A bis Z darstellen, und übergeben
Sie dieses im Aufruf der write()-Methode.
37
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
Ein byteorientierter Stream vom Typ der Klasse FileInputStream stellt Methoden
zur Verfügung, die Bytes aus Dateien einlesen. Ein solcher Stream soll mit der vorher erstellten Datei verbunden werden, um die darin gespeicherten binären Daten
zu lesen und am Bildschirm anzuzeigen.
Java-Dateien: ByteFileStreams.java
Programmaufruf: java ByteFileStreams
Aufgabe 1.6
Die Klassen FileWriter und FileReader
Erstellen Sie in Analogie zur Aufgabe 1.5 eine Klasse CharFileStreams, die
Instanzen der Character-Stream-Klassen FileWriter und FileReader erzeugt
und diesen im Konstruktoraufruf den String »Textdatei« als Dateiname übergibt.
Auch damit sollen die Buchstaben A bis Z erstmals in die Datei geschrieben werden und danach aus der Datei gelesen werden. Schreiben Sie diesmal die Zeichen
einzeln in die Datei und benutzen Sie zum Einlesen der Zeichen ein char-Array
mit dem Namen array, dessen Elemente im Nachhinein am Bildschirm angezeigt
werden sollen.
Java-Dateien: CharFileStreams.java
Programmaufruf: java CharFileStreams
1.5
Der Zugriff auf Daten aus dem Arbeitsspeicher mithilfe
der Klassen ByteArrayOutputStream und CharArrayWriter bzw. ByteArrayInputStream und CharArrayReader
Mit den drei write()-Methoden der Klasse ByteArrayOutputStream können
Daten in Form eines ByteArray-Streams in einen Bereich des Arbeitsspeichers
geschrieben werden, der mit der Anzahl von hinzugefügten Daten immer weiter
wächst.
Die in einem solchen Stream hinterlegten Daten können mit der Methode
toByteArray() als byte-Array zurückgewonnen werden.
Die Klasse überschreibt die Methode toString() der Klasse Object, um die im
Stream enthaltenen Bytes nach den für die entsprechende System-Plattform spezifizierten Dekodierungsmethoden in Zeichen umzusetzen.
Das Pendant dieser Klasse für Character-Streams ist die Klasse CharArrayWriter,
welche auch die write()-Methoden ihrer Oberklasse überschreibt und eine äquivalente Methode toCharArray() für das Kopieren der im Stream eingegebenen
Daten in ein char-Array definiert. Die Methode toString() liefert diese Daten als
String zurück.
38
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.5
ByteArrayOutputStream, CharArrayWriter, ByteArrayInputStream, CharArrayReader
Das Gegenstück der Klasse ByteArrayOutputStream ist die Klasse ByteArrayInputStream, deren Methoden für das Lesen der in einem ByteArray-Stream
hinterlegten Daten aus dem Arbeitsspeicher zuständig sind. Sie überschreibt die
von ihrer Oberklasse definierten Methoden read(), mark(), reset(), skip()
und close(). Ihr Pendant, die von der Klasse Reader abgeleitete Klasse CharArrayReader, kann für das Lesen von Zeichen aus einem Character-Stream genutzt
werden.
Die Unterklassen StringWriter und StringReader der Writer- und ReaderKlassen sind den Klassen CharArrayWriter und CharArrayReader sehr ähnlich.
Auch diese beiden Klassen überschreiben die Methoden ihrer Oberklassen und die
Klasse StringWriter implementiert die zusätzliche Methode getBuffer(), die
den Streaminhalt als Instanz der Klasse StringBuffer zurückliefert.
Die Klasse StringBuffer liefert eine Thread-sichere modifizierbare Folge von
Characters: D.h. ein StringBuffer ist ein modifizierbarer String, der kreiert wurde,
um in Zusammenhang mit Multithreading (siehe das nachfolgende Kapitel)
genutzt zu werden.
Aufgabe 1.7
Die Klassen ByteArrayOutputStream und ByteArrayInputStream
Definieren Sie in der main()-Methode einer Klasse ByteArrayStreams ein byteArray und initialisieren Sie seine Elemente mit den Zahlenwerten, welche die
Codes der ASCII-Zeichen J, a, v, a darstellen (74, 97, 118, 97). Erzeugen Sie zwei
Ausgabe-Streams vom Typ der Klasse ByteArrayOutputStream und schreiben Sie
in den ersten die Zahlenwerte 49 bis 56, welche die Codes der ASCII-Zeichen 1 bis
6 darstellen, und in den zweiten die Werte der Elemente des vorher definierten
byte-Arrays, jeweils mithilfe der write()-Methoden der Klasse. Die Größe eines
ByteArray-Streams wächst nach Bedarf. Vergewissern Sie sich, dass einzeln
geschriebene Bytes am Ende der vorher geschriebenen Arrayelemente nacheinander angefügt werden, indem Sie nach den Zeichen des Wortes »Java« auch noch die
Zeichenkette »-Stream« anfügen (ASCII-Codes 45, 63, 116, 114, 101, 97, 109).
Rufen Sie die Methoden writeTo() und toString() der Klasse ByteArrayOutputStream auf, um den Inhalt des zweiten Streams in den ersten zu kopieren und
die Inhalte beider Streams für die Bildschirmanzeige in einen String zu konvertieren.
Setzen Sie eine der ByteArrayOutputStream-Instanzen in ein byte-Array um,
indem Sie die Methode toByteArray() der Klasse aufrufen, und übergeben Sie
dieses bzw. eine beliebige Untermenge seiner Elemente in den Konstruktoren der
Klasse ByteArrayInputStream, um zwei Instanzen dieser Klasse zu erzeugen.
Lesen Sie aus diesen die gespeicherten Daten mithilfe von read()-Methoden der
Klasse.
39
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
Zeigen Sie die in (aus) die (den) Streams geschriebenen bzw. gelesenen Daten am
Bildschirm an.
Hinweise für die Programmierung:
Die Erzeugung der Streams erfolgt im Arbeitsspeicher und im eigentlichen Sinne
geht es darum, in/aus einen(m) Puffer durch dessen Umwandlung in/aus
einen(m) Stream schreiben und lesen zu können. Erst bei einer Kettung mit anderen Streams kann die Existenzberechtigung derartiger Streams besser verdeutlicht
werden (siehe dazu auch die Aufgabe 1.31).
Weil das Schließen von ByteArray-Streams keine Auswirkung zeigt, braucht die
close()-Methode dafür nicht aufgerufen zu werden.
Weil einige der read/write-Methoden eine IOException werfen, müssen deren
Aufrufe in einen try/catch-Block eingebettet werden.
Java-Dateien: ByteArrayStreams.java
Programmaufruf: java ByteArrayStreams
Aufgabe 1.8
Die Klassen CharArrayWriter und CharArrayReader
Definieren Sie analog zur Klasse ByteArrayStreams aus der vorigen Aufgabe eine
Klasse CharArrayStreams, die Instanzen vom Typ der Klassen CharArrayWriter
und CharArrayReader erzeugt und die gleichen Daten wie in der Aufgabe 1.7 von
einer Quelle einliest und in ein Ziel schreibt. Achten Sie darauf, dass die Klasse
CharArrayWriter einen dritten Konstruktor besitzt, der eine String-Referenz als
Parameter definiert und erstellen Sie damit eine zusätzliche dritte Instanz der
Klasse.
Rufen Sie die gleichnamigen Methoden dieser Stream-Klassen für das Schreiben
und Einlesen von Daten wie in der Aufgabe 1.7 auf und schließen Sie danach die
Streams mit der close()-Methode.
Java-Dateien: CharArrayStreams.java
Programmaufruf: java CharArrayStreams
1.6
Die Filterklassen FilterInputStream und FilterReader
bzw. FilterOutputStream und FilterWriter
Die Filterklassen FilterInputStream und FilterOutputStream sind konkrete
Klassen, die im Konstruktor eine Referenz vom Typ ihrer abstrakten Oberklasse
übergeben bekommen und deren Methoden überschreiben. D.h. es können Instanzen von diesen Klassen gebildet werden, und die an diesen Instanzen aufgerufenen
Methoden der Klassen geben alle Anforderungen über den im Konstruktor überge-
40
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.6
FilterInputStream, FilterReader, FilterOutputStream, FilterWriter
benen InputStream an ihre Oberklasse weiter. Ihre Unterklassen definieren über
ihre Felder und Methoden zusätzliche Funktionalitäten.
Ihre Pendants unter den Character-Streams, die Klassen FilterReader und FilterWriter, verfügen über die gleiche Struktur, sind jedoch in Java als abstrakte
Klassen definiert. Darum müssen diese Klassen erweitert werden, um konkrete
Unterklassen zu bilden, deren Konstruktoren die Konstruktoren der Oberklasse
und in ihren read()- und write()-Methoden die gleichnamigen Methoden der
Oberklasse aufrufen.
Soll der Einsatz von Instanzen von Filterklassen demonstriert werden, führt dies zu
einem Ketten mit anderen Streams, weil mit Filter-Streams keine direkte Ein-/Ausgabe von Daten erfolgen kann, diese dienen lediglich einer Zwischenbehandlung
von Ein-/Ausgaben. Die zu bearbeitenden Streams werden, wie bereits erwähnt, im
Konstruktur der Filterklassen beim Instantiieren über ihre Referenzen übergeben,
woher auch die Bezeichnung als »abgeleitete Streams« für diese Klassen hervorgeht.
Aufgabe 1.9
Das Ketten von FilterOutputStreams mit FileOutputStreams
Die Klasse FilterOutputStream definiert einen »Null-Filter«. Sie liest Daten von
einem im Konstruktor übergebenen OutputStream und gibt diese unverändert
zurück. Um eine gewünschte Filterung durchführen zu können, muss eine Unterklasse die write()-Methoden überschreiben, welche dazu erforderlich sind.
Definieren Sie eine von FilterOutputStream abgeleitete Klasse UpperCaseOutputFilter, mit deren Instanzen ein Filter zum Umsetzen von Klein- in Großbuchstaben erzeugt werden kann. Überschreiben Sie dazu die write()-Methoden
der Oberklasse zum Schreiben von einzelnen Bytes bzw. eines byte-Arrays und
rufen Sie in diesen mit dem Präfix out. die write()-Methoden der Klasse auf, die
im Konstruktor als Argument übergeben wird. Das Feld out ist in der Oberklasse
mit protected definiert und damit für alle Unterklassen zugänglich.
Die Klasse ByteOutputFilterfuerFileStreams soll zum Testen des Filters eingesetzt werden. Sie definiert ein byte-Array, dessen Elemente mit den Buchstaben
von a bis z initialisiert werden.
Erzeugen Sie einen FilterStream vom Typ der Klasse UpperCaseOutputFilter
zum Umsetzen der Daten von Klein- in Großbuchstaben, die in einen FileOutputStream geschrieben werden sollen. Bilden Sie eine Instanz vom Typ der Klasse
FileOutputStream, der im Konstruktor der Dateiname »Filter« übergeben wird
und übergeben Sie eine Referenz auf diese im Konstruktor der Klasse UpperCaseOutputFilter.
41
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
Rufen Sie an dem so erzeugten FilterStream die Methode write() auf, in der eine
Referenz auf das vorher definierte byte-Array übergeben werden kann, um diese
Daten in die Datei zu schreiben.
Zwecks Bildschirmanzeige sollen die Daten mithilfe einer FileInputStreamInstanz über den Aufruf der read()-Methode eingelesen werden. Benutzen Sie im
Methodenaufruf die gleiche Arrayreferenz wie beim Schreiben der Daten.
Hinweise für die Programmierung:
Die Kettung der Streams mithilfe der FileOutputStream-Instanz kann wie folgt
erfolgen: UpperCaseOutputFilter byteFilterOut1 = new UpperCaseOutputFilter (new FileOutputStream("Filter")); und das Umsetzen von Klein- in
Großbuchstaben in den überschriebenen write()-Methoden kann wie folgt
durchgeführt werden: out.write(b-32);
Java-Dateien:
UpperCaseOutputFilter.java, ByteOutputFilterfuerFileStreams.java
Programmaufruf: java ByteOutputFilterfuerFileStreams
Aufgabe 1.10
Das Ketten von FilterOutputStreams mit der System.out-Instanz
Die Klasse ByteOutputFilterfuerPrintStream aus dieser Aufgabe soll zum
weiteren Testen des mit der Klasse UpperCaseOutputFilter definierten Filters
eingesetzt werden. Sie definiert ebenfalls ein byte-Array, das mit den Buchstaben
von a bis z initialisiert wird.
Erzeugen Sie eine Instanz der Klasse UpperCaseOutputFilter, welche im Konstruktor die in der Klasse System definierte System.out-Instanz vom Typ der
Klasse PrintStream (die standardmäßig der Konsole zugeordnet ist) übergeben
bekommt.
Testen Sie auch den von der Klasse FilterOutputStream definierten Null-Filter,
indem Sie anstelle einer Referenz vom Typ UpperCaseOutputFilter eine Referenz vom Typ der FilterOutputStream-Klasse übergeben.
Hinweise für die Programmierung:
Für die Ausgabe der Daten am Bildschirm, die mit den FilterOutputStreams umgesetzt wurden, kann eine Kettung von Streams genutzt werden: FilterOutputStream byteFilterOut1 = new FilterOutputStream(System.out)); bzw.
UpperCaseOutputFilter byteFilterOut2 = new UpperCaseOutputFilter
(System.out);
Java-Dateien:
UpperCaseOutputFilter.java ByteOutputFilterfuerPrintStream.java
Programmaufruf: java ByteOutputFilterfuerPrintStream
42
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.6
FilterInputStream, FilterReader, FilterOutputStream, FilterWriter
Aufgabe 1.11
Das Ketten von FilterWriter-Streams mit der System.out-Instanz
Die Klassen UpperCaseFilterWriter und CharOutputFilterfuerPrintWriter sollen das Pendant zu den Klassen aus der Aufgabe 1.10 für Character-FilterStreams darstellen.
So wird die Klasse UpperCaseFilterWriter von FilterWriter abgeleitet, die
ebenso wie ihre Writer-Oberklasse als abstrakt definiert ist. Die Klasse FilterWriter erbt die Methoden der Oberklasse, ohne diese neu zu implementieren, so
dass dies den davon abgeleiteten Klassen überlassen bleibt. Implementieren Sie
auch die write()-Methode, welche als Parameter eine String-Referenz definiert,
um diese für das Schreiben von Strings in den Ausgabe-Stream zu nutzen.
Erzeugen Sie in der main()-Methode der Klasse CharOutputFilterfuerPrintWriter eine Instanz der Klasse UpperCaseFilterWriter, die analog zur Aufgabe
1.10 im Konstruktor die System.out-Instanz, die per Voreinstellung eine Instanz
der Klasse PrintStream ist, übergeben bekommt.
Achten Sie darauf, dass diese Instanz über eine Referenz vom Typ der Klasse OutputStreamWriter übergeben werden muss, um eine Brücke zwecks Datenkonvertierung zwischen dem Byte-Stream vom Typ PrintStream und dem CharacterStream vom Typ FileWriter zu definieren. Übergeben Sie diese im Konstruktoraufruf der Klasse PrintWriter für das Umsetzen von Klein- in Großbuchstaben
der Bildschirmausgaben.
Hinweise für die Programmierung:
Die in der Klasse UpperCaseOutputFilter implementierten write()-Methoden,
welche eine Array- bzw. Stringreferenz als Parameter besitzen, definieren zwei
zusätzliche Parameter vom Typ int, in denen Anfangsposition und Länge der zu
schreibenden Daten übergeben werden. Außerdem erfolgt erst nach dem Schließen des PrintWriter-Streams eine Ausgabe am Bildschirm.
Das Umsetzen von Klein- in Großbuchstaben in den überschriebenen write()Methoden kann mit Character.toUpperCase((char)b); durchgeführt werden,
falls b einen int-Wert bezeichnet und mit Character.toUpperCase(c[i]); bzw.
String.valueOf(c).toUpperCase(); für den Fall, dass c ein char-Array
bezeichnet.
Die Klasse FilterWriter ist eine abstrakte Klasse, von der keine Instanzen gebildet werden können, so dass der damit definierte Null-Filter nur über eine davon
abgeleitete Klasse, die selbst einen Null-Filter definiert, getestet werden kann.
Java-Dateien:
UpperCaseFilterWriter.java, CharOutputFilterfuerPrintWriter.java
Programmaufruf: java CharOutputFilterfuerPrintWriter
43
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
Aufgabe 1.12
Das Ketten von FilterInputStreams mit FileInputStreams
Die Klasse FilterInputStream wird von der Klasse InputStream abgeleitet und
dient dem Filtern von Daten eines byteorientierten Eingabe-Streams. Sie liest Zeichen von einem im Konstruktor übergebenen InputStream und gibt diese unverändert zurück, so dass ihre Instanzen nur als »Null-Filter« eingesetzt werden können.
Wie beim Schreiben, bleibt auch beim Lesen dem Programmierer selbst überlassen, wie er die Filterung von eingelesenen Daten den jeweiligen Anforderungen
anzupassen hat. Deshalb muss eine Unterklasse von FilterInputStream die
read()-Methoden der Oberklasse überschreiben und daraus die gleichnamigen
Methoden der im Konstruktor übergebenenen Streams, die erforderlich sind, um
eine gewünschte Filterung durchführen zu können, aufrufen.
Definieren Sie eine von FilterInputStream abgeleitete Klasse LowerCaseInputFilter, mit deren Instanzen diesmal ein Filter zum Umsetzen von Groß- in
Kleinbuchstaben erzeugt werden kann. Überschreiben Sie dazu die read()Methode der Oberklasse zum Einlesen eines byte-Arrays und rufen Sie in dieser
mit dem Präfix in. die read()-Methode der Klasse auf, deren Instanzen im Konstruktor als Argument übergeben werden. Das Feld in ist in der Oberklasse mit
protected definiert und damit für alle Unterklassen zugänglich.
Die Klasse ByteInputFilterfuerFileStreams soll zum Testen des Filters eingesetzt werden.
Ein byteorientierter Stream vom Typ der Klasse FileInputStream wird mit einer
Datei namens »ByteFilter« verbunden und in diese Datei werden die 7-Bit ASCIIKey-Codes für die Buchstaben von von A bis Z geschrieben.
Erzeugen Sie einen FilterStream vom Typ der Klasse LowerCaseInputFilter, bilden Sie eine Instanz vom Typ FileInputStream, der im Konstruktor der Dateiname »ByteFilter« übergeben wird und übergeben Sie diese im Konstruktor der
Klasse LowerCaseInputFilter zum Einlesen der dadurch gefilterten, vorher
geschriebenen Daten. Selbstverständlich können Sie zum Testen weitere beliebige
Dateien nutzen.
Hinweise für die Programmierung:
Achten Sie beim Umsetzen der Daten darauf, dass die Summe von zwei byte-Werten nur als int-Wert ermittelt werden kann: int zahl = (new
Byte(b[i])).byteValue() + (new Integer(32)).byteValue(); und dieser
wiederum zurück konvertiert werden muss: b[i] = new Byte(""+zahl);, falls Sie
diesen Weg für die Konvertierung gewählt haben.
Java-Dateien:
LowerCaseInputFilter.java, ByteInputFilterfuerFileStreams.java
Programmaufruf: java ByteInputFilterfuerFileStreams
44
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.7
BufferedOutputStream, BufferedWriter, BufferedInputStream, BufferedReader
Aufgabe 1.13
Das Ketten von FilterReader-Streams mit FileReader-Streams
Wie schon angesprochen, ist das Pendant der Klasse FilterInputStream für
Character-Streams die Klasse FilterReader, eine abstrakte Klasse, die ebenso wie
die Klasse FilterWriter einen »Null-Filter« definiert. Sie liest Zeichen von einem
angegebenen Reader und gibt sie unverändert zurück. D. h. sie erbt alle Methoden
der Reader-Klasse, ohne diese neu implementieren zu müssen. Um eine
gewünschte Filterung durchführen zu können, muss eine Unterklasse die read()Methoden, welche erforderlich sind, überschreiben.
Definieren Sie in Analogie zur Aufgabe 1.12 eine von FilterReader abgeleitete
Klasse LowerCaseFilterReader, mit deren Instanzen ein Filter zum Umsetzen
von Groß- in Kleinbuchstaben erzeugt werden kann, um diesen beim Einlesen von
Daten aus einem zeichenorientierten Stream einzusetzen.
Implementieren Sie dazu die read()-Methode der Oberklasse FilterReader zum
Einlesen von Zeichen in ein char-Array und rufen Sie in dieser mit dem Präfix in.
die read()-Methode der Klasse auf, die im Konstruktor als Argument übergeben
wird.
Die Klasse CharInputFilterfuerFileReader soll zum Testen des Filters eingesetzt werden. In eine Datei namens »CharFilter« werden erstmals alle Buchstaben
von A bis Z mithilfe eines FileWriter-Streams geschrieben. Sie können diese
Aktion überspringen und eine schon vorhandene Datei zum Testen benutzen.
Bilden Sie eine Instanz vom Typ LowerCaseFilterReader, die im Konstruktoraufruf eine FileReader-Instanz mit dem Dateinamen »CharFilter« als Argument
übergeben bekommt, und rufen Sie an dem so erzeugten FilterReader-Stream
seine read()-Methode auf.
Zeigen Sie die aus dem FileReader-Stream eingelesenen Daten am Bildschirm
an.
Java-Dateien:
LowerCaseFilterReader.java, CharInputFilterfuerFileReader.java
Programmaufruf: java CharInputFilterfuerFileReader
1.7
Das Puffern von Daten und die Klassen BufferedOutputStream und BufferedWriter bzw. BufferedInputStream
und BufferedReader
Die Aufgabe aller Buffered-Klassen besteht darin, die zu lesenden bzw. zu schreibenden Daten zu puffern, um die Geschwindigkeit im Zugriff auf die Daten der
zugrunde liegenden Streams zu beschleunigen.
45
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
Auch die Instanzen dieser Klassen werden wie Instanzen von Filterklassen mit
anderen Streams gekettet.
Die Klassen BufferedOutputStream und BufferedWriter bzw. BufferedInputStream und BufferedReader verfügen über je zwei Konstruktoren, die den zu
puffernden bzw. gepufferten Stream entgegennehmen, der über eine Referenz
vom Typ ihrer OuputStream- bzw. InputStream-Oberklasse übergeben wird. Der
in Java definierte »überladene Polymorphismus« macht möglich, dass auch Referenzen vom Typ der Unterklassen übergeben werden können. Im zweiten Konstruktor der Klassen kann auch die Größe des internen Puffers angegeben werden.
Die Zwischenspeicherung der Daten erfolgt intern über ein byte- bzw. char-Array,
dass für den Programmierer völlig transparent ist. Mithilfe der Methoden flush()
und close() kann der Programmierer das Schreiben von Inhalten aus dem Puffer
ansteuern, der ansonsten immer nur dann automatisch geleert wird, wenn er voll
ist.
Ein sinnvoller Einsatz von diesen Klassen bietet sich in Zusammenhang mit dem
Zugriff auf Dateien an. Auch wenn viele einzelne Schreibzugriffe auf einen Stream
erfolgen sollen, ist der Einsatz dieser Puffer zu empfehlen.
In diesem Zusammenhang wollen wir auch explizit auf die Methoden readLine()
bzw. newLine() der Klassen BufferedReader bzw. BufferedWriter hinweisen.
Die Methode readLine() ermöglicht einen zeilenweisen Zugriff auf Textdaten
und die Methode newLine() das Hinzufügen eines Separators für den Zeilenumbruch (das Zeilenende-Zeichen: ‚\n’ oder ‚\r\n’ je nachdem, auf welchem Betriebssystem das Programm läuft). Weil die readLine()-Methode ein zeilenweises
Einlesen von Strings ermöglicht, was beim Einlesen von Daten von einer Konsole
oder aus einer Datei von großer Bedeutung ist, ist sie eine der mit am häufigsten
benutzten Methoden beim Einlesen von textorientierten Daten.
Die von BufferedReader abgeleitete Klasse LineNumberReader überschreibt die
read()- und readLine()-Methoden ihrer Oberklasse und definiert zwei zusätzliche Methoden setLineNumber() und getLineNumber(), mit deren Hilfe ein
Zähler für Zeilen gesetzt bzw. abgefragt werden kann.
Die read-Methoden dieser Klassen werfen eine Exception vom Typ IOException,
wenn ein Ein-/Ausgabe-Fehler auftritt.
Aufgabe 1.14
Das Ketten von BufferedInputStreams und BufferedOutputStreams mit
FileInputStreams
Um die Zeitdauer bei gepufferten und nicht gepufferten Dateizugriffen zu vergleichen, sollen die in der Klasse ByteFileStreams verwendeten FileStreams für den
Zugriff auf Daten mit Buffered-Streams verknüpft werden.
46
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.7
BufferedOutputStream, BufferedWriter, BufferedInputStream, BufferedReader
Definieren Sie dazu eine Klasse ByteFileundBufferedStreams, die in ihrer
main()-Methode Instanzen vom Typ BufferedOutputStream und BufferedInputStream erzeugt und rufen Sie an diesen die read()- und write()-Methoden
dieser Klassen auf.
Lesen Sie mithilfe der Methode currentTimeMilles() der Klasse System beim
Starten sowie nach dem Beenden von Dateizugriffen die aktuelle Zeit in Millisekunden und berechnen Sie die Zeitdauer der Zugriffe, indem Sie die Differenz der
so ermittelten Werte bilden.
Hinweise für die Programmierung:
Wenn zwei Streams gekettet werden, soll immer die close()- Methode für den
Stream, der sich an einen anderen kettet, aufgerufen werden (d.h. die Methode des
äußeren Streams).
Java-Dateien: ByteFileundBufferedStreams.java
Programmaufruf: java ByteFileundBufferedStreams
Aufgabe 1.15
Das Ketten von BufferedReader-Streams und BufferedWriter-Streams
mit FileReader-Streams
Definieren Sie eine Klasse CharFileundBufferedStreams als Pendant zur Klasse
ByteFileundBufferedStreams aus der Aufgabe 1.14 für BufferedWriter- und
BufferedReader-Streams. Da bei Ein- und Ausgaben für Character-Streams
immer eine Umwandlung zwischen Bytes und Characters stattfindet, muss für
diese Streams die Umsetzung nicht für jedes geschriebene bzw. gelesene Zeichen
einzeln vorgenommen werden, sondern immer nur dann, wenn der Puffer voll ist.
Darum wird eine Instanz der Klasse BufferedWriter auch von der Klasse PrintWriter intern genutzt.
Java-Dateien: CharFileundBufferedStreams.java
Programmaufruf: java CharFileundBufferedStreams
Aufgabe 1.16
Die readLine()-Methode der Klasse BufferedReader und das Selektieren
von Textzeilen
Definieren Sie eine von BufferedReader abgeleitete Klasse SelektierenvonTextZeilen, welche die readLine()-Methode ihrer Oberklasse überschreibt und
diese gleichzeitig mit super. aufruft, um die Daten aus dem Stream zeilenweise
zu lesen. Der Konstruktor der Klasse definiert als Parameter eine Referenz vom Typ
der Klasse Reader. Die mit dieser Methode eingelesenen Textzeilen sollen nach
47
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
dem ersten darin enthaltenen Buchstaben selektiert werden. Ist dies der Buchstabe
E (wie Error), werden die entsprechenden Textzeilen an die aufrufende Methode
returniert, ansonsten sollen diese einfach übersprungen werden.
Zum Testen der Klasse SelektierenvonTextZeilen soll eine weitere Klasse
SelektierenvonTextZeilenTest erstellt werden. Für das Selektieren der Einträge aus einer Datei mit dem Namen »Meldungsdatei« wird eine FileReaderInstanz gebildet, die als Argument im Konstruktor der Klasse SelektierenvonTextZeilen übergeben wird.
Schreiben Sie mit einem Editor in die »Meldungsdatei« mehrere Textzeilen, die mit
den Buchstaben E oder I beginnen und Meldungen wie »E Datei nicht gefunden«,
»E Ein-/Ausgabe-Fehler«, »I Datei wurde bearbeitet«, etc. beinhalten.
Die aus der Datei gelesenen Fehlermeldungen sollen über die Kettung einer FileWriter-Instanz mit einem BufferedWriter-Stream in eine Datei namens »Fehlerdatei« geschrieben werden. Über den Aufruf der Methode newLine() an dem
BufferedWriter-Objekt kann das Ende für Zeilen gesetzt werden.
Sehen Sie sich den Inhalt der so erzeugten Datei auf Kommandozeilenebene mit
dem Befehl type Fehlerdatei an.
Java-Dateien:
SelektierenvonTextZeilen.java, SelektierenvonTextZeilenTest.java
Programmaufruf: java SelektierenvonTextZeilenTest
1.8
Die Schnittstellen DataOutput und DataInput und
deren Methoden zum Schreiben und Lesen von primitiven Datentypen
Wie schon erwähnt, sind die Methoden der Klassen zum Lesen und Schreiben von
primitiven Datentypen DataOutputStream und DataInputStream in den Schnittstellen DataOutput und DataInput spezifiziert, welche von diesen Klassen implementiert werden. Die mithilfe von Methoden der Klasse DataOutputStream
geschriebenen Daten werden so abgelegt, dass diese problemlos von den Methoden
der Klasse DataInputStream wieder zurückgewonnen werden können.
Die meisten Methoden der Klassen tragen den Namen des übergebenen primitiven
Datentyps. Sie schreiben und lesen entweder einen Wert vom angegebenen Typ wie
z.B. readBoolean(), writeBoolean(), readInt(), writeInt() etc. oder ganze
Zeichenketten in Unicode- oder UTF-8-Format (mit /n bzw. /n/r beendet für das
Lesen) wie z.B. readLine(), readUTF(), writeBytes(), writeChars() und
writeUTF().
Die mit readLine() eingelesenen Textzeilen werden automatisch in einen String
konvertiert, wobei aber nicht die unterschiedlichen Zeichensätze auf den verschiedenen System-Plattformen berücksichtigt werden. Diese Aufgabe wird von der
Klasse BufferedReader übernommen, so dass eine Kettung mit einem Stream
48
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.8
Die Schnittstellen DataOutput und DataInput
vom Typ dieser Klasse, welche auch eine readline()-Methode definiert, sich als
durchaus sinnvoll erweisen kann.
Die Methoden writeChars() und readChar() arbeiten mit Unicode-Zeichen. Es
werden damit immer zwei Bytes pro Zeichen geschrieben und gelesen. Die
Methode writeUTF() speichert den String nicht im 2-Byte-Unicode-Format, sondern in dem in der theoretischen Einführung beschriebenen modifizierten UTF-8Format, in dem die Unicode-Zeichen mit einer Sequenz von ASCII-Zeichen dargestellt werden. Darum muss bei einer Nutzung dieser Klassen immer darauf geachtet werden, dass die parallel eingesetzten Schreib- und Lesemethoden auf dem
gleichen Datentyp operieren. Auf die Klasse DataOutputStream soll nur dann
zurückgegriffen werden, wenn mit unterschiedlichen Datentypen gearbeitet werden soll. Für die Speicherung in Form von Texten sind Streams vom Typ der Klassen PrintStream und PrintWriter dieser Art von Streams wegen des
einfacheren Umgangs vorzuziehen.
Von allen readxxx-Methoden für primitive Datentypen und von der readUTF()Methode für das Lesen von Strings als auch von den readFully()-Methoden wird
eine Ausnahme vom Typ EOFException erzeugt, falls das Ende des Streams
erreicht wurde. Die readline()-Methode liefert den Wert null zurück und die
read()-Methoden von Bytes den Wert -1.
Wie im Fall von Buffered- und Filter-Klassen, sind auch die Instanzen von diesen
Klassen dazu prädestiniert, mit anderen Streams gekettet zu werden. So kann
durch die Kettung mit FileOutput- und FileInput-Streams, die nur auf ByteEbene arbeiten, gewährleistet werden, dass int-, float-, double-Werte sowie
Werte von anderen primitiven Datentypen manipuliert werden können. Gleichzeitig kann mithilfe der Methoden dieser Klassen das Speichern und Zurückgewinnen
von primitiven Datentypen in und aus Dateien wesentlich vereinfacht werden.
DataOutputStream- und DataInputStream-Objekte können aber nicht direkt mit
Objekten vom Typ FileWriter und FileReader oder PipedWriter und PipedReader gekettet werden.
Eine Kettung von mehreren Streamtypen ist bei der Verwendung dieser Streams
für das Schreiben und Einlesen von Daten meistens auch deswegen sinnvoll, weil
dadurch ein Beitrag zur Erhöhung der Performance geleistet werden kann.
Aufgabe 1.17
Das Schreiben und Lesen von primitiven Datentypen
Definieren Sie eine Klasse DataOutputundInputStreams, die in ihrer main()Methode lokale Variablen vom Typ aller primitiver Datentypen definiert und initialisiert.
In dieser Klasse soll ein byteorientierter Stream vom Typ der Klasse FileOutputStream mit einer Datei namens »DateimitprimitivenDatentypen« verbunden und
49
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
über einen BufferedOutputStream mit einem DataOutputStream gekettet werden.
Schreiben Sie mithilfe von gleichnamigen Methoden die Werte der entsprechenden primitiven Datentypen in die Datei und definieren Sie zusätzlich zwei StringReferenzen, um diese mit den unterschiedlichen write()-Methoden und der
Methoden writeChars() und writeUTF() in die gleiche Datei zu schreiben.
Ein byteorientierter Stream vom Typ der Klasse FileInputStream wird mit der
vorher erstellten Datei verbunden und mit einem DataInputStream und einem
BufferedInputStream gekettet, um die geschriebenen Daten daraus einzulesen und
am Bildschirm anzuzeigen.
Benutzen Sie die Methode readFully(), um alle geschriebenen Daten einzulesen,
oder die Pendants der read()-Methoden zu den vorher aufgerufenen write()Methoden, um die Daten einzeln einzulesen (ansonsten muss der Stream für ein
wiederholtes Lesen neu geöffnet werden).
Für das Lesen der mit writeChars() gespeicherten Strings soll die Methode
readline() der Klasse DataInputStream genutzt werden und für das Lesen des
UTF-Strings die von der Klasse überladene Methode readUTF().
Hinweise für die Programmierung:
Dadurch, dass die readline()-Methode der Klasse DataInputStream nicht
immer korrekt die Bytes in Character konvertiert, wurde diese als »deprecated«
gekennzeichnet. In der Online-Dokumentation von Java wird empfohlen, für das
Lesen von Textzeilen die Methode readline() der Klasse BufferedReader zu
benutzen.
Java-Dateien: DataOutputundInputStreams.java
Programmaufruf: java DataOutputundInputStreams
Aufgabe 1.18
Das Filtern von primitiven Datentypen
Die Klassen DataOutputStream und DataInputStream können erweitert werden.
Dadurch, dass alle ihre Methoden als final definiert sind, können diese jedoch
nicht überschrieben werden.
Definieren Sie eine Klasse UmlauteSuchen, die alle Wörter mit Umlauten aus
einer Datei über einen DataInputStream selektiert. Schreiben Sie mithilfe der
Methode writeUTF() der Klasse DataOutputStream mehrere Strings mit und
ohne Umlaut in eine Datei namens »DateimitUmlauten«.
Initialisieren Sie in der main()-Methode der Klasse ein String-Array namens
worte mit den von Ihnen ausgewählten Strings und ein weiteres namens umlaute
50
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.9
Formatierte Ein-/Ausgaben
mit allen Umlauten aus dem deutschen Alphabet. Die lokale String-Referenz
buchstaben soll mit der Zeichenkette »äöüßÄÖÜ« initialisiert werden.
Erzeugen Sie mithilfe der Kettung DataOutputStream byteOut1 = new DataOutputStream(System.out) einen DataOutputStream und rufen Sie an diesem die
Methode writUTF(buchstaben) auf, um die Umlaute auf die Konsole zu schreiben.
Analog zur Klasse DataOutputundInputStreams aus der Aufgabe 1.17 soll auch in
dieser Klasse ein byteorientierter Stream vom Typ der Klasse FileOutputStream
mit der Datei namens »DateimitUmlauten« verbunden und ein DataOutputStream über einen byteorientierten Stream vom Typ der Klasse BufferedOutputStream mit diesem Stream gekettet werden.
Zum Einlesen der Daten wird ein byteorientierter Stream vom Typ der Klasse
FileInputStream mit der vorher erstellten Datei verbunden und ebenfalls mit
einem DataInputStream und einem BufferedInputStream gekettet.
Die gelesenen UTFStrings sollen auf Umlaute untersucht werden und alle, die
Umlaute enthalten, sollen zusammen mit dem Unicode-Code-Point (gleich dem 7Bit ASCII-Key-Code) für den gefundenen Umlaut am Bildschirm angezeigt werden.
Hinweise für die Programmierung:
Der Unicode-Code-Point, der dem Zeichen auf Position i aus einer Zeichenkette
entspricht, kann wie bereits erwähnt mit der Methode codePointAt(i) der Klasse
String ermittelt werden.
Lesen Sie die Datei »DateimitUmlauten« mit einem Hexa-Editor wie z.B. DF HEXEditor 1.1 ein, um die hexadezimale Verschlüsselung von Umlauten nachzuvollziehen (die Kodierung für ä Ä ü Ü ö Ö ß ist C4 E4 DC FC D6 F6 DF).
Java-Dateien: UmlauteSuchen.java
Programmaufruf: java UmlauteSuchen
1.9
Formatierte Ein-/Ausgaben
Wie so oft in Java (und in vielen anderen Programmiersprachen) sind Begriffe so
eng miteinander verknüpft, dass die richtige Reihenfolge in ihrer Behandlung
manchmal nicht einfach zu finden ist.
So sieht es auch mit der formatierten Aufbereitung von Daten aus. Werden Daten
für eine Ausgabe formatiert, kann man sich das Ergebnis dieser Formatierungen
nur über die Methoden von Print-Klassen ansehen. Umgekehrt benötigt man die
mithilfe von Formatierungsklassen erworbenen Kenntnisse über Format-Spezifizierer, um Daten mit den Methoden von Print-Klassen auszugeben.
Wie schon in den Beispielen zur Demonstration des Einsatzes von Filterklassen
erwähnt wurde, stehen für die formatierte Ausgabe von Daten die Klassen Print-
51
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
Stream und PrintWriter zur Verfügung. Sie definieren mehrere print()- und
println()-Methoden, die sich überladen und einen primitiven Datentyp, ein
char-Array oder eine String- bzw. Object-Referenz entgegennehmen können.
Im Unterschied zu print()-Methoden, die kein Zeilenende setzen, fügen die
println()-Methoden einen Zeilenumbruch hinzu. Die println()-Methoden liefern, wie auch die Methode newLine() der Klasse BuferredWriter, ein betriebs-
systemabhängiges Zeilenende-Zeichen, das über die Systemeigenschaft (»system
property«) line.separator definiert ist und verschieden von ‚\n’ sein kann. Die
readLine()-Methode der Klasse BuferredReader funktioniert ihrerseits mit Zeilenenden von verschiedenen Betriebssystemen.
In den Konstruktoren beider Klassen kann eine OutputStream- oder WriterInstanz übergeben werden, aber auch eine Datei als String- bzw. File-Referenz.
Beide Klassen besitzen je zwei Konstruktoren mit einem zusätzlichen Parameter
vom Typ boolean, der mit dem Namen autoFlush bezeichnet ist und dessen Wert
intern ausgewertet wird. Wird der Wert dieses Parameters gleich true gesetzt,
erfolgt sofort eine über print()- und println()-Methoden angestossene Ausgabe, ohne dass abgewartet wird, bis ein dafür eingesetzter Puffer vollgeschrieben
wurde. Der Wert dieses Parameters kann nur über einen Konstruktoraufruf eingestellt werden und nachdem eine PrintWriter-Instanz erzeugt wurde, auch nicht
mehr abgeändert werden. Wir werden die Auswirkung dieses Parametres auf Ausgaben im nächsten Kapitel in Thread-Klassen beobachten.
Für eine Formatierung von Daten für die Ausgabe wurden mit der Version 5.0 von
Java in der Klasse PrintStream die Methoden append(), format() und
printf() hinzugefügt, für welche auch die Bemerkungen bzgl. des autoFlushParameters gelten.
Die Methode append() fügt ein Zeichen bzw. eine Zeichenkette der Ausgabe hinzu
und bekommt diese als Referenz vom Typ der Klasse CharSequence übergeben.
Die in der Methode format() übergebenen Argumente vom Typ Object ersetzen
die in der Zeichenkette enthaltenen Platzhalter, welche den Formatierungsstring
definiert und als Referenz vom Typ String übergeben wird.
Mit printf() wird ein formatierter String unter Nutzung des im Methodenaufruf
spezifizierten Formats und der übergebenen Argumente ausgegeben.
Die Klasse PrintWriter definiert mit Java 5 auch die zusätzlichen gleichnamigen
Methoden append(), format() und printf() wie die Klasse PrintStream. Diese
Methoden werden auf die gleiche Weise eingesetzt.
Formatierte Ausgaben von Daten können auch mithilfe der Klasse java.util.
Formatter erreicht werden, die in ihren Konstruktoren Stream- und File-Instanzen entgegennehmen kann. Diese Klasse wurde mit der Version 5.0 von Java eingeführt und definiert zwei format()-Methoden, die sich überladen und über die
gleiche Parameterliste wie die Print-Klassen verfügen, aber anstelle einer PrintStream- bzw. PrintWriter-Instanz ein Formatter-Objekt zurückliefern.
52
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.9
Formatierte Ein-/Ausgaben
Das Gegenstück zu Formatter ist die Klasse Scanner. Im Konstruktor der ebenfalls mit der Version 5.0 eingeführten Klasse kann eine InputStream- oder
String-Referenz sowie eine Referenz von allen Objekten, deren Klassen die
Schnittstellen Readable und ReadableByteChannel implementieren, darunter
auch die FileReader-Klasse, übergeben werden. Die Klasse Scanner wird
genutzt, um formatierte Eingaben in einen binären Datenstrom umzusetzen. Mit
Ihrer Hilfe werden Zeichen aus einem Stream eingelesen, und sie stellt Methoden
wie nextInt(), nextLong(), etc. für das Einlesen von primitiven Datentypen und
Strings zur Verfügung. Der Typ der eingelesenen Daten kann im Vorhinein mit
den Methoden hasNextInt(), hasNextDouble(), etc. sowie für Strings hasNext() über ein im Methodenaufruf übergebenes Suchmuster geprüft werden.
Beide Klassen befinden sich im Paket java.util und sind als final deklariert,
d.h. diese Klassen können nicht erweitert werden.
Benutzen Sie ein Referenzbuch von Java oder die sehr detaillierte Beschreibung
aus der Online-Dokumentation zur Klasse Formatter, um die Vielzahl von Format-Spezifizierern und deren Einsatzmöglichkeiten in der Konvertierung von
Daten zu erforschen. Wir wollen in den folgenden Aufgaben nur auf die am häufigsten verwendeten eingehen.
Mit den Format-Spezifizierer können die zugehörigen Argumente auf drei Arten
referenziert werden:
쐽
Eine »explizite Indizierung« wird genutzt, wenn die Format-Spezifizierer einen
Index für die Argumentenspezifikation enthalten, der als ganzzahliger Wert die
Position des Argumentes in der Argumentenliste bestimmt: So führt z.B. die
Formatierung formatter.format("%4$s %3$s %2$s %1$s %4$s %3$s %2$s
%1$s", "1", "2", "3", "4"); zur String-Ausgabe »4 3 2 1 4 3 2 1«.
쐽
Von einer »relativen Indizierung« wird gesprochen, wenn der Format-Spezifizierer das Zeichen »<« beinhaltet, welches bewirkt, dass das Argument des vorangegangenen Format-Spezifizierers wiederholt genutzt wird. Existiert ein solches nicht, wird die Ausnahme MissingFormatArgumentException geworfen. So erfolgt mit der Formatierung formatter.format("%s %s %<s %<s",
"1", "2", "3", "4"); die String-Ausgabe »1 2 2 2«. Die Strings »3« und »4«
werden ignoriert, weil diese nicht referenziert wurden.
쐽
Mit dem Ausdruck »normale Indizierung« wird eine Indizierung bezeichnet,
falls der Format-Spezifizierer weder das Zeichen »<« noch einen Index für die
Argumentenspezifikation beinhaltet. Dem Format-Spezifizierer wird in diesem Fall ein sequentieller impliziter Index zugeordnet. So führt die Formatierung formatter.format("%s %s %s %s", "1", "2", "3", "4"); zur Ausgabe
»1 2 3 4«.
All diese Formen der Indizierung können nach Belieben kombiniert werden. Mit
formatter.format("%2$s %s %<s %s", "1", "2", "3", "4"); wird der String »2
53
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
1 1 2« ausgegeben. »3« und »4« werden ignoriert, weil diese nicht referenziert wurden.
Die maximale Anzahl von Argumenten ist durch die maximale Dimension eines
Java-Arrays limitiert, die über die JVM-Spezifikation von Java definiert wird.
Die Klassen PrintStream und PrintWriter besitzen kein Pendant im Bereich
der Eingabe-Streams für das Einlesen von formatierten Daten; dazu kann, wie
schon angesprochen, die Klasse Scanner genutzt werden.
1.10 Standard Ein-/Ausgabe-Kanäle
Die Kommandozeilen-Schnittstelle von Betriebsystemen unterstützt einen Mechanismus, der mit dem Namen Standard-Ein-/Ausgabe (»standard I/O«) bezeichnet
wird. Die Standard-Ein-/Ausgabe besteht aus drei Kanälen, die in Java als Streams
betrachtet werden: Standard-Input-, Standard-Output- und Standard-Error-Kanal.
Diese Kanäle sind in der Regel über einen Gerätetreiber der Tastatur bzw. dem Bildschirm oder dem Drucker zugeordnet.
Die drei Java-Standard-Streams, welche über die globalen statischen Referenzen
in, out und err in der Klasse java.lang.System definiert werden, sind, wie
bereits schon durch deren Benutzung in den vorangegangenen Aufgaben festgestellt werden konnte, Instanzen der Klassen java.io.InputStream und
java.io.PrintStream. So müssen diese im Gegensatz zu allen anderen Streams
nicht explizit vom Programm erzeugt werden. Dadurch, dass die Klasse System im
Paket java.lang enthalten ist, stehen diese Objekte jeder Applikation von Anfang
an zur Verfügung.
Über die Referenz System.out können die Methoden der Klasse PrintStream für
Standard-Ausgaben (in der Regel auf die Konsole) aufgerufen werden. Die Ausgabe
auf den Standard-Fehlerkanal kann über die System.err-Referenz erfolgen und ist
standardmäßig auch der Konsole zugeordnet.
Über einen Programmaufruf, gefolgt von einem »>«-Zeichen und dem Dateinamen, kann der Standard-Output-Kanal in eine Datei oder auf den Drucker umgelenkt werden. Im Gegensatz dazu kann unter Windows der Standard-Error-Kanal
nicht auf Kommandozeilenebene umgelenkt werden (wie z.B. unter Unix und
Linux).
Die Instanz der Klasse InputStream, auf welche die System.in-Referenz zeigt,
stellt dem Programmierer nur read()-Methoden zur Verfügung, die Eingaben in
einzelne int-Werte oder ein byte-Array speichern. D.h. Tastatureingaben können
mit dieser Instanz nicht direkt in einen String eingelesen werden, sondern müssen
in einen solchen umgewandelt werden, ebenso wie die einzelnen int-Codes, die
über ein Casting mit (char) in das eingelesene Zeichen umgesetzt werden können.
Weil diese Vorgehensweisen recht umständlich sind, werden die Streams zum
Lesen von Tastatureingaben oft gekettet. Die nachfolgenden Beispiele zeigen, wie
54
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.10
Standard Ein-/Ausgabe-Kanäle
nacheinander eingelesene Daten für die Weiterverarbeitung in Strings umgewandelt werden können.
Mit Java 6 wurde in der Java-Standard-Klasse System die Methode console() hinzugefügt. Sie liefert ein Objekt vom Typ der Klasse Console zurück, die ebenfalls
mit der Version 6.0 implementiert wurde. Diese Klasse befindet sich im Paket
java.io.package und definiert Methoden zum Schreiben und Lesen auf und von
einer zeichenorientierten Konsole, die einer Java Virtual Machine zugeordnet ist,
wenn eine solche überhaupt existiert. Ob eine JVM eine Konsole besitzt, ist abhängig von der darunterliegenden System-Plattform und der Art und Weise, wie sie
gestartet wird. Wenn die JVM über die Kommandozeile mithilfe des java-Kommandos gestartet wird und dabei die Output- und Input-Streams nicht umgelenkt
werden, ist eine Konsole definiert und diese ist standardmäßig mit dem Bildschirm
und der Tastatur, an welchem(r) die JVM wartet, verbunden. Weil die Klasse Console als final definiert ist, kann diese nicht erweitert werden und die einzige
Instanz vom Typ dieser Klasse muss über den Aufruf der statischen Methode console() der Klasse System erzeugt werden.
Die Methode readLine() dieser Klasse liefert eine von der Tastatur eingelesene
Zeile zurück, die nicht durch ein Zeilenende-Zeichen abgeschlossen wurde, und
null, falls das Ende des Streams erreicht ist.
Ihre printf()-Methode führt eine korrekte Umsetzung von Bytes in Characters
durch, so dass auch Umlaute (und andere sprachspezifische Zeichen) korrekt am
Bildschirm dargestellt werden können (siehe die Aufgaben 1.34 und 1.35).
Aufgabe 1.19
Die Klasse Formatter und das Formatieren von Ausgaben
Testen Sie die normale, explizite und relative Indizierung in der Referenzierung
von Formatierungsargumenten mithilfe von Format-Spezifizierern in einer Klasse
ZahlenCharacterundStringFormatierung.
Deklarieren und initialisieren Sie dazu mehrere Variablen von primitiven Datentypen, auch Arrayvariablen.
Erzeugen Sie mithilfe des parameterlosen Konstruktors der Klasse Formatter ein
Objekt dieser Klasse und rufen Sie an diesem seine format()-Methode auf, um die
Daten für die Ausgabe am Bildschirm zu formatieren.
Die Anzeige der Daten soll über den Aufruf der Methode System.out.println()
erfolgen, der als Argument die Formatter-Instanz als Referenz übergeben werden
kann.
Hinweise für die Programmierung:
Benutzen Sie für String-Ausgaben die Format-Spezifizierer %s oder %S und für
char-Daten die Format-Spezifizierer %C oder %c. Zeigen Sie byte- und short-
55
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
Werte als hexadezimale bzw. oktale Zahlen mit den Format-Spezifizierer %x (%X)
und %o an. Mit dem Format-Spezifizierer %d kann eine beliebige Dezimalzahl
angezeigt werden, %E steht für eine Anzeige von Gleitkommazahlen in wissenschaftlicher Schreibweise (mit Exponent) und %b bzw. %B für den Typ boolean.
Benutzen Sie diese Format-Spezifizierer und viele andere Ihrer Wahl, um eine Formatierung der Daten vor der Ausgabe zu testen.
Java-Dateien: ZahlenCharacterundStringFormatierung.java
Programmaufruf: java ZahlenCharacterundStringFormatierung
Aufgabe 1.20
Die Klasse Scanner und das Suchen, Ersetzen und Zerlegen von Zeichenketten
Die Instanzen der Klasse Scanner sind einfache Text-Scanner, die primitive Datentypen und Strings mithilfe von regulären Ausdrücken umwandeln können.
Reguläre Ausdrücke sind Zeichenfolgen, die als Suchmuster verwendet werden
und als Strings oder als Instanzen der Java-Standard-Klasse Pattern angegeben
werden können.
Initialisieren Sie in der von Ihnen definierten Klasse SuchenundZerlegenvonZeichenketten drei verschiedene Stringvariablen mit den vorgegebenen Werten
charString = "A a B b C c D d E e F f G g", intString = "1 2 3 4 5 6 7 8 9" und
doubleString = "1,2 34,5 678,910". Übergeben Sie diese nacheinander im
Konstruktor der Scanner-Klasse, um ihren Inhalt mit den Methoden hasNext(),
hasNextInt() und hasNextDouble() zu prüfen und mit den Methoden next(),
nextInt() und nextDouble() in einzelne Strings, int- und double-Werte zu
zerlegen. Zeigen Sie die so ermittelten Werte am Bildschirm an.
Erweitern Sie die Zeichenketten charString und intString mit den Sonderzeichen »$« und »&« und fügen Sie diese als einen neuen String zusammen, der zur
Untersuchung im Konstruktor der Klasse Scanner übergeben wird. An der so
erzeugten Scanner-Instanz soll die Methode useDelimiter() aufgerufen werden,
die als Argument ein Suchmuster übergeben bekommt.
Im Suchmuster kann mithilfe von Zeichenklassen eine Liste von Zeichen hinterlegt werden, die im Text vorkommen dürfen. Testen Sie die Ausgaben mit einigen
der gebräuchlisten Zeichenklassen, wie »\\D« für eine beliebige Ziffer und »\\W«
für ein Wortzeichen. Definieren Sie auch eigene Muster wie z.B. »\\$|\\&«, welche
direkt im Methodenaufruf übergeben werden können, aber auch als Referenz eines
Objektes der Klasse Pattern. Diese Klasse besitzt keine Konstruktoren und die
Instanzen der Klasse werden über ihre Klassenmethode compile() erzeugt, die als
Argument den entsprechenden regulären Ausdruck entgegennimmt.
56
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.10
Standard Ein-/Ausgabe-Kanäle
Nutzen Sie die Methode matches() der Klasse Pattern, um nach bestimmtem
Zeichenfolgen in einem Text zu suchen. Diese Methode returniert ein MatcherObjekt, das den Vergleich des Suchmusters mit der im Aufruf der Methode übergebenen Zeichenkette durchführt. Ersetzen Sie mithilfe der Methode replaceAll()
der Klasse Matcher im Text »PatternMatcherScanner« alle Buchstaben von A bis M
und a bis m mit dem Zeichen ! und fügen Sie im gleichen Text in einem zweiten
Anlauf mit der Methode appendReplacement() der Klasse zu den Buchstaben a
und e ein ! hinzu.
Hinweise für die Programmierung:
Die Klassen Pattern und Matcher befinden sich im Paket java.util.regex. Mit
der Methode find() der Klasse Matcher kann auf die nächste Zeichenfolge, die
dem angegebenen Suchmuster entspricht, zugegriffen werden. Ihre Methode
group() returniert eine vorher ermittelte Zeichenfolge aus dem übergebenen Text.
Java-Dateien: SuchenundZerlegenvonZeichenketten.java
Programmaufruf: java SuchenundZerlegenvonZeichenketten
Aufgabe 1.21
Die System.out-Instanz der Klasse PrintStream
Definieren Sie eine Klasse ZahlenMatrixmitprintln, in der für alle natürlichen
Zahlen von 1 bis n deren Potenzen von 1 bis m berechnet werden und in der Form
1**1 = 1, 2**3 = 8, usw. mittels einer nxm Matrix am Bildschirm angezeigt werden.
Analog zu der Vorgehensweise aus der Aufgabe 1.19 sollen mithilfe einer Instanz
der Klasse Formatter und dem Aufruf deren format()-Methode die Elemente der
Matrix nach Ihren Vorstellungen formatiert werden.
Die so erzeugte Formatter-Instanz wird im Aufruf der Methode println() am
immer geöffneten Output-Stream System.out vom Typ der Klasse PrintStream
für die Anzeige der Daten übergeben.
Hinweise für die Programmierung:
Für das Berechnen von Potenzen kann die Klassenmethode pow() der Java-Standard-Klasse Math aufgerufen werden.
Hinweise zur Programmausführung:
Der Standard-Output-Kanal kann auf Kommandozeilenebene in eine Datei oder
auf den Drucker umgelenkt werden. Über den Programmaufruf java ZahlenMatrixmitprintln > ZahlenMatrix werden die Programmausgaben in eine Datei
mit dem Namen »ZahlenMatrix« geschrieben. Sehen Sie sich den Inhalt dieser
Datei mit dem Befehl type auf Kommandozeilenebene an.
Java-Dateien: ZahlenMatrixmitprintln.java
Programmaufruf: java ZahlenMatrixmitprintln
57
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
Aufgabe 1.22
Andere PrintStream-Instanzen
In einen PrintStream kann auch mit einem Formatter-Objekt eine Ausgabe erfolgen. Erzeugen Sie in Analogie zur Klasse ZahlenMatrixmitprintln aus der Aufgabe 1.21 eine Klasse ZahlenMatrixmitFormatter, in der im Konstruktor der
Klasse Formatter eine PrintStream-Instanz übergeben wird.
Für die Ausgabe muss dann keine print- oder write-Methode mehr aufgerufen werden.
Java-Dateien: ZahlenMatrixmitFormatter.java
Programmaufruf: java ZahlenMatrixmitFormatter
Aufgabe 1.23
Die printf()-Methode
Definieren Sie eine Klasse ZahlenMatrixmitprintf zum Testen von Ausgabeformatierungen mit der Methode printf() anstelle von format().
Erzeugen Sie eine PrintStream-Instanz, um daran die Methode printf() aufzurufen. Definieren Sie die Parameterliste der printf()-Methode analog zu der aus
der format()-Methode aus den vorangegangenen Aufgaben.
Hinweise für die Programmierung:
Weil System.out eine Instanz der Klasse PrintStream ist, kann die printf()Methode auch an dieser aufgerufen werden, ohne dass eine neue Instanz der
Klasse PrintStream erzeugt werden muss.
Java-Dateien: ZahlenMatrixmitprintf.java
Programmaufruf: java ZahlenMatrixmitprintf
Aufgabe 1.24
Die Klasse PrintWriter
Zeigen Sie mit der Klasse ZahlenMatrixmitPrintWriter, dass anstelle der
PrintStream-Instanz aus der Aufgabe 1.23 eine PrintWriter-Instanz genutzt
werden kann, um daran die Methode printf() aufzurufen.
Eine PrintWriter-Instanz kann mithilfe der System.out-Instanz durch Kettung
mit einer OutputStreamWriter-Instanz erzeugt werden. In diesem Fall kann
jedoch auf die Brückenklasse verzichtet werden, weil die Umwandlung intern im
Konstruktor der Klasse PrintWriter automatisch vorgenommen wird.
58
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.10
Standard Ein-/Ausgabe-Kanäle
Vergessen Sie nicht, dass ein PrintWriter-Stream im Gegensatz zu einem Stream
vom Typ der Klasse PrintStream immer geschlossen werden muss, damit eine
Ausgabe erfolgen kann.
Java-Dateien: ZahlenMatrixmitPrintWriter.java
Programmaufruf: java ZahlenMatrixmitPrintWriter
Aufgabe 1.25
Die Klasse PrintWriter als Filterklasse einsetzen
Wie in der Aufgabe 1.24 gezeigt wurde, kann die Klasse PrintWriter eingesetzt
werden, um formatierte Daten in einen Output-Stream zu schreiben. Dabei wird
die PrintWriter-Instanz benutzt, um den Ausgabe-Stream, der auch ein FileStream sein kann, wie eine Filterklasse zu umwickeln, obwohl diese keine Unterklasse von FilterWriter ist, sondern direkt von Writer abgeleitet wird. Dann
kann in die Datei mit den Methoden beider Streams geschrieben werden.
Zeigen Sie mit der Klasse FileWriterundPrintWriter, worin der Unterschied
besteht, wenn in eine Datei mit einem FileWriter-Stream bzw. einem PrintWriter-Stream geschrieben wird, um klarzustellen, warum und wann solche Kettungen einzusetzen sind.
Zu diesem Zweck soll ein zeichenorientierter Stream der Klasse FileWriter mit
der Datei namens »Printdatei« verbunden werden. Ketten Sie den FileWriterStream mit einem PrintWriter-Stream und schreiben Sie mit den Methoden von
beiden Streams die in einem char-Array hinterlegten Zeichen und verschiedene
einzelne Zeichen, darunter auch das Zeichen für eine neue Zeile (’\n’), in die Datei.
Lesen Sie die Daten über einen FileReader-Stream in ein weiteres Array ein und
zeigen Sie diese auf zwei Arten am Bildschirm an: indem Sie ein PrintWriterObjekt erzeugen, mit einer OutputStreamWriter-Instanz für die Ausgabe zwecks
Konvertierung ketten und daran die printf()-Methode aufrufen bzw. die
printf()-Methode an der System.out-Instanz aufrufen.
Java-Dateien: FileWriterundPrintWriter.java
Programmaufruf: java FileWriterundPrintWriter
Aufgabe 1.26
Die Klasse StringWriter
Die Klasse StringWriter definiert einen Character-Stream, der ebenso wie die
Instanzen der Klasse CharArrayWriter einen Ausgabe-Stream für einen String
bereitstellt. Eine Instanz der Klasse StringWriter schreibt ihre Ausgaben in einen
String-Puffer. Mit ihrer Methode getBuffer() wird dieser als Instanz der Klasse
59
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
StringBuffer returniert und mit der Methode toString() als Instanz der Klasse
String.
Definieren Sie eine Klasse PrintWriterundStringWriter, die in geschachtelten
try/catch-Blöcken Ausnahmen vom Typ MissingFormatArgumentException,
IllegalFormatConversionException und UnsupportedEncodingException
abfängt und bearbeitet. Erfassen Sie mehrere fehlerhafte Statements im Programm, die solche Ausnahmen auslösen.
Dabei soll erstens der StackTrace in jedem catch-Block über die Methode printStackTrace() ausgegeben werden, welche die Java-Standard-Klasse Exception
von ihrer Oberklasse Throwable erbt.
Zweitens soll der StackTrace einer Ausnahme in einen StringWriter-Stream
mithilfe eines PrintWriter-Streams geschrieben und mit dessen Methode
toString() in einen String für die Weiterverarbeitung umgesetzt werden.
Die Methode traceStack() der Klasse PrintWriterundStringWriter, welche
diese Aufgabe übernimmt, soll aus dem so erzeugten String den Programmfehler
und Dateinamen mit Zeilennummer mithilfe von Methoden der Klasse String
ermitteln und diese als zusätzliche Fehlermeldung am Bildschirm anzeigen.
Hinweise für die Programmierung:
Die printStackTrace()-Methode von Throwable (die Oberklasse aller Exception- und Error-Klassen in Java) schreibt die Trace-Ausgaben von Ausnahmen
und Fehlern in einen PrintStream. Daraus ist ersichtlich, in welcher Klasse, welcher Methode und bei welcher Zeilennummer die Ausnahme ausgelöst wurde
sowie welche Ausnahme oder welcher Fehler dazu geführt hat.
Java-Dateien: PrintWriterundStringWriter.java
Programmaufruf: java PrintWriterundStringWriter
Aufgabe 1.27
Die System.in-Instanz der Klasse InputStream
Definieren Sie eine Klasse UngepufferteTastaturEingaben1 zum Lesen von
Tastatureingaben mithilfe der System.in-Instanz und ihrer read()-Methode zum
Einlesen von einzelnen Bytes. Das in der Java-Standard-Klasse System definierte
Klassenfeld System.in referenziert eine Instanz der Klasse InputStream.
Der Benutzer soll aufgefordert werden, über die Bildschirmausgabe »Beliebige Zeichen ueber die Tastatur eingeben und die Enter-Taste druecken«, Eingaben über
die Tastatur zu machen.
Zeigen Sie analog zur Klasse ByteFileStreams aus der Aufgabe 1.5 die eingelesenen Daten und ihren 7-Bit ASCII-Key-Code am Bildschirm an.
60
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.10
Standard Ein-/Ausgabe-Kanäle
Hinweise für die Programmierung:
Die parameterlose Methode read() der Klasse InputStream returniert eine intZahl. Diese Zahl ist ein 32-Bit breiter int-Wert, in den der 7-Bit ASCII-Key-Code
von dieser Methode konvertiert wurde, falls die Tastatur als Standard-Eingabe
diente, bzw. ein 8-Bit langes Byte, falls die Eingabe in eine Datei umgelenkt wurde.
Um die Eingabe abzuschließen, muss (Enter) gedrückt werden. Dabei setzt Windows den »carriage return« Zeichen-Code (in ASCII die 13), was sich über die
Anzeige der Daten am Bildschirm nachvollziehen lässt.
Java-Dateien: UngepufferteTastaturEingaben1.java
Programmaufruf: java UngepufferteTastaturEingaben1
Aufgabe 1.28
Das Umlenken des Standard-Eingabe-Kanals
In Java wird dem Programmierer nicht die Möglichkeit gegeben, herauszufinden,
ob die mithilfe von System.in.read() eingelesenen Zeichen vom StandardInput-Kanal, von der Tastatur oder aus einer Datei eingelesen wurden.
Mit der Methode setIn() der Java-Standard-Klasse System kann jedoch festgelegt
werden, dass der Standard-Eingabe-Kanal einem bestimmten Input-Stream zugeordnet wird.
In die Datei »Eingabedatei« wird mit einem Editor ein beliebiger Text geschrieben.
Ändern Sie die Klasse UngepufferteTastaturEingaben1 aus der Aufgabe 1.27 in
eine Klasse StandardEingabeKanalUmlenken so ab, dass mit den Anweisungen
FileInputStream byteFileIn = new FileInputStream ("Eingabedatei");
und System.setIn (byteFileIn); der Standard-Eingabe-Kanal so gesetzt wird,
dass Eingaben nur aus dieser Datei empfangen werden können.
Java-Dateien: StandardEingabeKanalUmlenken.java
Programmaufruf: java StandardEingabeKanalUmlenken
Aufgabe 1.29
Das Einlesen von Tastatureingaben in ein Array
Durch den Aufruf der Methode read(byte[] array) an einer Instanz der Klasse
InputStream können die Eingaben von der Tastatur in ein byte-Array eingelesen
werden.
Definieren Sie die Klasse UngepufferteTastaturEingaben2 zum Lesen von Tastatureingaben mithilfe dieser Methode.
61
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
Über den Konstruktor der Klasse String, der eine Arrayreferenz als Parameter
definiert, können die Bytes des Arrays in ein String-Objekt überführt werden.
Übergeben Sie dessen Referenz im Aufruf der Methode System.out.println()
für die Anzeige der Daten am Bildschirm.
Java-Dateien: UngepufferteTastaturEingaben2.java
Programmaufruf: java UngepufferteTastaturEingaben2
Aufgabe 1.30
Das Puffern von Tastatureingaben beim Einlesen
Mit der Klasse GepufferteTastaturEingaben soll die Möglichkeit der Kettung
des System.in-InputStreams mit einem Stream der Klasse BufferedReader
demonstriert werden. Die Klasse BufferedReader definiert die Methode readLine(), die einen String mit dem Inhalt der gepufferten Daten zurückgibt und
eine weitere Umsetzung überflüssig macht.
Auch in diesem Fall muss die System.in-Instanz zwecks Datenkonvertierung über
eine Instanz vom Typ der Brückenklasse InputStreamReader im Konstruktor der
Klasse BufferedReader übergeben werden.
Die Aufforderung zur Tastatureingabe und die Anzeige der eingelesenen Daten am
Bildschirm soll wie auch in den vorangegangenen Aufgaben über den Aufruf der
Methode System.out.println() erfolgen.
Java-Dateien: GepufferteTastaturEingaben.java
Programmaufruf: java GepufferteTastaturEingaben
Aufgabe 1.31
Das Schreiben auf den Fehlerkanal mit der System.err-Instanz
Mit der Klasse StandardFehlerKanalUmlenken sollen über die Tastatur eingegebene Zeichen über einen ByteArrayOutputStream in eine Datei mit dem Namen
»Ausgabedatei« geschrieben werden.
Erzeugen Sie einen Ausgabe-Stream vom Typ ByteArrayOutputStream, um die
vom Standard-Eingabe-Kanal eingelesenen Zeichen reinzuschreiben.
Wie schon mit der Klasse ByteArrayStreams aus der Aufgabe 1.7 gezeigt wurde,
kann ein ByteArrayOutputStream mit seiner Methode toString() für eine Bildschirmanzeige in einen String konvertiert und mit der Methode writeTo() in
einen anderen Stream kopiert werden.
Um Ausgaben auf den Fehlerkanal zu testen, soll der Dateiname »Ausgabedatei«
im Konstruktoraufruf eines FileInputStreams übergeben werden, bevor diese Datei
62
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.10
Standard Ein-/Ausgabe-Kanäle
angelegt wird. Damit soll gezeigt werden, dass dabei eine Ausnahme vom Typ
FileNotFoundException ausgelöst wird und diese mithilfe der Methode System.err.println() auf den Fehlerkanal, der unter Windows auch der Konsole
zugeordnet ist, geschrieben werden kann. Diese Ausnahme wird ausgelöst, wenn
es sich um ein Dateiverzeichnis und keine gültige Datei handelt, diese nicht existiert oder zum Lesen nicht geöffnet werden kann. Danach soll der Standard-ErrorKanal in eine Datei »StdErrDatei« umgelenkt werden und eine weitere Ausnahmesituation hervorgerufen werden (z.B. eine NullPointerException), um das Schreiben der Fehlermeldung in diese Datei zu prüfen.
Beim Erzeugen eines Streams vom Typ der Klasse FileOutputStream wird eine
noch nicht vorhandene Datei angelegt. Die vom Konstruktoraufruf geworfene Ausnahme vom Typ FileNotFoundException muss aber auch abgefangen oder weitergereicht werden. Die Ausnahme wird in diesem Fall ausgelöst, wenn es sich um
ein Dateiverzeichnis und keine gültige Datei handelt, diese nicht existiert und nicht
erzeugt werden kann oder aus irgendeinem Grund nicht geöffnet werden kann.
Übergeben Sie im Konstruktoraufruf der Klasse FileOutputStream den Namen
»Ausgabedatei« und schreiben Sie den Inhalt des ByteArrayOutputStream in den
damit erzeugten Stream.
Sehen Sie sich den Inhalt der Dateien »Ausgabedatei« und »StdErrDatei« mit dem
Befehl type auf Kommandozeilenebene an.
Hinweise für die Programmierung:
Wie bereits erwähnt, kann unter Windows der Standard-Fehlerkanal nicht auf
Kommandozeilenebene in eine Datei umgelenkt werden. Dies kann jedoch mithilfe der Methode setErr() der Java-Standard-Klasse System erreicht werden, der
als Argument ein Output-Stream übergeben werden kann, der mit einer Datei verbunden ist.
Java-Dateien: StandardFehlerKanalUmlenken.java
Programmaufruf: java StandardFehlerKanalUmlenken
Aufgabe 1.32
Einlesen von primitiven Datentypen mithilfe der Klasse Scanner
Wie schon erwähnt, kann für das Einlesen von primitiven Datentypen, die in einem
Zeichenformat vorliegen, die Klasse Scanner benutzt werden.
In der Klasse ScannerfuerStringsundStreams soll ein zeichenorientierter
Stream vom Typ der Klasse FileWriter mit der Datei »Scannerdatei« verbunden
werden. Der FileWriter-Stream soll mit einem PrintWriter-Stream gekettet
werden, um die vom Programm definierten Strings in die Datei als einzelne Zeilen
zu schreiben.
63
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
Diese Strings sollen ganze Zahlen, Gleitkommazahlen und Buchstaben – durch
Leerzeichen getrennt – enthalten wie z. B. zeile1 = "1 2 3 4 5", zeile2 = "1,2 34,5
678,910 745,11 23,45" und zeile3 = "A B C D E".
Die Datei »Scannerdatei« soll mithilfe der Methode nextLine() der ScannerKlasse zeilenweise gelesen werden. Übergeben Sie dazu beim Instantiieren im
Konstrukor der Klasse Scanner eine FileReader-Instanz mit dem Dateinamen.
Die gelesenen Zeilen werden in einen String gespeichert, der zwecks Zeilenzerlegung im Konstruktor einer weiteren Scanner-Instanz übergeben wird. Rufen Sie
an dieser Instanz die Methoden der Klasse Scanner für das Überprüfen und Einlesen von primitiven Datentypen und Strings wie hasNextInt(), nextInt(), hasNextDouble(), nextDouble(), hasNext() und next()auf.
Java-Dateien: ScannerfuerStringsundStreams.java
Programmaufruf: java ScannerfuerStringsundStreams
Aufgabe 1.33
Einlesen von Tastatureingaben mithilfe der Klasse Scanner
Die Vorgehensweise aus der Aufgabe 1.32 kann auch das Einlesen von Tastatureingaben wesentlich erleichtern.
Definieren Sie zur Demonstration eine Klasse GescannteTastaturEingaben, in
der Sie die Methode nextLine() der Klasse Scanner aufrufen, um Tastatureingaben, die mit der Enter-Taste abgeschlossen werden, zeilenweise entgegenzunehmen.
Dazu soll die System.in-Instanz im Konstruktor der Scanner-Klasse über ihre
Referenz übergeben werden.
Der Benutzer soll über die Bildschirmausgabe »Zeichenfolgen ueber die Tastatur
eingeben und die Enter-Taste druecken« aufgefordert werden, Eingaben über die
Tastatur zu machen.
Zeigen Sie die so eingelesenen Daten mit der Methode System.out.println()
am Bildschirm an.
Hinweise für die Programmierung:
Mithilfe der Methode hasNextLine() der Klasse Scanner kann überprüft werden,
ob eine weitere Zeile beim Einlesen zur Verfügung steht. Diese Methode blockiert
jedoch die Eingabe. Definieren Sie, um dies zu verhindern, eine while-Schleife,
die bei der Eingabe der Zeichenkette »Ende« verlassen wird.
Java-Dateien: GescannteTastaturEingaben.java
Programmaufruf: java GescannteTastaturEingaben
64
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.11
ObjectStreams und die Serialisierung von Objekten
Aufgabe 1.34
Die printf()-Methode der Klasse Console
Definieren Sie eine Klasse AusgabenmitderKlasseConsole, die eine Instanz der
Klasse Console erzeugt und an dieser ihre printf()-Methode aufruft, um den
String »äöüÄÖÜß« am Bildschirm anzuzeigen.
Zeigen Sie den gleichen String auch über den Aufruf der printf()-Methode an
einer System.out-Instanz an.
Java-Dateien: AusgabenmitderKlasseConsole.java
Programmaufruf: java AusgabenmitderKlasseConsole
Aufgabe 1.35
Die readLine()-Methode der Klasse Console
Definieren Sie eine Klasse EingabenmitderKlasseConsole, die eine Instanz der
Klasse Console erzeugt und an dieser ihre readLine()-Methode aufruft, um mit
der Tastatur eingegebene Zeichen zu lesen.
Eine BufferedReader-Instanz soll mithilfe der Brückenklasse InputStreamReader mit der System.in-Instanz gekettet werden, um an dieser die readLine()Methode der Klasse BufferedReader aufzurufen.
Zeigen Sie die eingelesenen Strings in beiden Fällen über den Aufruf der
printf()-Methode an der Console-Instanz und der System.out-Instanz am Bild-
schirm an.
Hinweise für die Programmierung:
Achten Sie darauf, dass die Methode readLine() der Klasse Console kein Zeilenende-Zeichen setzt und die Methode readLine() der Klasse BufferedReader
eine Ausnahme vom Typ IOException wirft.
Java-Dateien: EingabenmitderKlasseConsole.java
Programmaufruf: java EingabenmitderKlasseConsole
1.11
ObjectStreams und die Serialisierung von Objekten
Der Vorgang, durch den über das Speichern und Zurückgewinnen von Objektzuständen (damit sind wie immer die Werte ihrer Instanzfelder gemeint), Objekte zu
einem späteren Zeitpunkt wieder rekonstruiert werden können, wird in der JavaLiteratur als Serialisierung von Objekten bezeichnet.
Dazu werden die Java-Standard-Klassen ObjectOutputStream und ObjectInputStream benutzt. Mit der Klasse ObjectOutputStream wird der Inhalt von
65
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
Objekten in einen byteorientierten Stream konvertiert, um z.B. in eine Datei
geschrieben oder über das Netz übertragen zu werden. Dieser Vorgang wird als
Serialisierung bezeichnet. Es können nur Objekte von Klassen serialisiert werden,
die das Interface Serializable implementieren. Dies ist ein Marker-Interface,
d.h. es enthält weder Konstantendefinitionen noch Methodenspezifikationen und
dient lediglich dazu, Klassen, die eine Serialisierung unterstützen, zu kennzeichnen.
Bei einer Serialisierung werden die Werte der Instanzfelder von Objekten in der
über den Methodenaufruf writeObject() übergebenen ObjectOutputStreamInstanz gespeichert. Gleichzeitig werden der Klassenname mit der Klassensignatur, die Feldnamen und der Typ von Feldern darin festgehalten. Im Fall von Referenzfeldern werden die entsprechenden Daten für alle damit referenzierten
Instanzen rekursiv ermittelt und gespeichert.
Mit der Klasse ObjectInputStream und deren Methode readObject() wird die
Deserialisierung von Objekten realisiert. Weil beim Serialisieren die Methoden von
Klassen nicht gespeichert werden, muss die .class-Datei vorhanden sein, um mit
deren Hilfe und den gespeicherten Daten ein Objekt vollständig rekonstruieren zu
können. Die Methode readObject() returniert eine Referenz auf das wiederhergestellte Objekt.
Die Instanzfelder einer Klasse, deren Werte nicht gespeichert werden sollen (wie
z.B. Passwortfelder), müssen mit dem Modifikator transient deklariert werden.
Klassenfelder (mit dem Modifikator static in einer Klasse definiert) werden auch
nicht gespeichert, da diese nicht zu den Instanzen einer Klasse gehören.
Die Klassen ObjectOutputStream und ObjectInputStream implementieren die
Interfaces DataOutput und DataInput. D.h., sie implementieren auch deren
Methoden, die zum Schreiben und Lesen von primitiven Datentypen und Strings
in (aus) diese (diesen) Streams benutzt werden können.
1.12 Die Versionsverwaltung von Klassen
Beim Serialisieren von Objekten wird eine Versionsnummer (SerialVersionUID)
vergeben, die zur Identifikation dient. Diese wird als long-Wert definiert und enthält eine Verschlüsselung von Daten aus der Klasse, zu der das serialisierte Objekt
gehört. Sie kann mit dem Java-Tool serialver gefolgt vom Namen der Klasse auf
Kommandozeilenebene angezeigt werden.
Die Versionsnummer einer Klasse wird abgeändert, wenn in der Klassendefinition
Änderungen erfolgen, auch wenn eine Methode abgeändert oder neu hinzugefügt
wird und die Werte von Feldern gleich bleiben. Es sollen ja schließlich beim Deserialisieren von Objekten der Klasse keine falschen Daten zurückgeliefert werden.
So wird beim Deserialisieren die Versionsnummer des serialisierten Objekts mit
der aktuellen Versionsnummer der Klasse verglichen und bei ungleichen Werten
eine Ausnahme vom Typ InvalidClassException ausgelöst.
66
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.12
Die Versionsverwaltung von Klassen
Auf Programmebene kann die Versionsnummer mithilfe von Methoden der Klasse
ObjectStreamClass abgefragt werden. Diese Klasse ist von der Object-Klasse
abgeleitet und implementiert auch die Serializable-Schnittstelle. Ihre Methode
lookup(), die als Parameter eine Referenz vom Typ der parametrisierten Klasse
Class<T> definiert, liefert eine Referenz vom Typ der eigenen Klasse zurück, an
der die Methode getSerialVersionUID() aufgerufen werden kann.
Aufgabe 1.36
Das Speichern und Einlesen eines Objektes
Definieren Sie eine Klasse Punkt mit zwei Instanzfeldern vom Typ double, welche
die Koordinaten x und y eines Punktes im zweidimensionalen kartesischen Koordinatensystem beschreiben. Die Klasse definiert die Zugriffsmethoden setX(),
setY(), getX() und getY() für das Schreiben und Lesen der Koordinatenwerte und
eine Methode anzeige() für eine Punktanzeige am Bildschirm in der Form (x, y).
In einer weiteren Klasse mit dem Namen ObjectStreams sollen Objekte vom Typ
der Klasse Punkt in einer Datei so abgespeichert werden, dass sie aus dieser Datei
in ihrem ursprünglichen Zustand wieder zurückgewonnen werden können.
Die Klasse Punkt, deren Objekte serialisiert werden sollen, muss, wie bereits
erwähnt, die Schnittstelle Serializable implementieren.
Die Klasse ObjectStreams definiert zwei globale Referenzen vom Typ der Klasse
Punkt, originalPunkt und serialPunkt. Für das Serialisieren eines Objektes
wird ein byteorientierter FileOutputStream mit einem ObjectOutputStream gekettet und an diesem die Methode writeObject() aufgerufen, in der eine PunktInstanz über die originalPunkt-Referenz übergeben wird. Ein FileInputStream
soll mit der vorher erzeugten Datei verbunden und mit einem ObjectInputStream
gekettet werden. Die an diesem Stream aufgerufenen Methode readObject()
returniert mit serialPunkt eine Referenz auf das wiederhergestellte Objekt. Am
originalen und deserialisierten Objekt soll die Methode anzeige() der Klasse
Punkt aufgerufen werden, um die Feldinhalte der Instanzen zu vergleichen.
Java-Dateien: Punkt.java, ObjektStreams.java
Programmaufruf: java ObjektStreams
Aufgabe 1.37
Das Speichern und Einlesen von Objekten, deren Klassen Referenzfelder
definieren
Die Klasse Kreis definiert die globalen Referenzen p und s vom Typ der Klassen
Punkt und String, die Instanzfelder r vom Typ double und instanzZaehler
67
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
vom Typ int sowie das Klassenfeld klassenZaehler vom Typ int. Sie soll
demonstrieren, dass im Fall von Referenzfeldern die entsprechenden Daten für alle
damit referenzierten Instanzen auch abgespeichert und zurückgewonnen werden
können.
Das Instanzfeld instanzZaehler, das wie auch das Klassenfeld klassenZaehler
im Konstruktor der Klasse inkrementiert wird, soll nicht mit serialisiert werden
und muss deswegen mit dem Modifikator transient deklariert werden.
Definieren Sie Zugriffsmethoden für alle Instanzfelder der Klasse und überschreiben Sie die toString()-Methode der Klasse Object, in der Sie den im Konstruktor übergebenen String anzeigen, die Methode anzeige() der Klasse Punkt
aufrufen und die Gleichung des Kreises mit dem im Konstruktor übergebenen
Wert für Radius und Mittelpunkt returnieren.
Zum Testen der Serialisierung soll die Klasse ErweiterteSerialisierung
erstellt werden, die eine Instanz der Klasse Kreis erzeugt und auf die gleiche Art
und Weise wie in der Aufgabe 1.36 diese serialisiert und deserialisiert. Am originalen und deserialisierten Objekt sollen die Methoden der Klasse Kreis aufgerufen
werden.
Java-Dateien: Punkt.java, Kreis.java, ErweiterteSerialisierung.java
Programmaufruf: java ErweiterteSerialisierung
Aufgabe 1.38
Die SerialVersionUID einer Klasse
Erstellen Sie eine Klasse Schule, welche mit dem Schlüsselwort enum die Enumerationen mit den Namen SchuelerListe, NotenListe und FachListe, mit beliebigen Namen für Schüler, Noten und Fachangaben, definiert. Die Enumeration
NotenListe soll einen Konstruktor mit einem int-Parameter und die Zugriffsmethoden setNote() und getNote() definieren, die es ermöglichen, den Aufzählungskonstanten unterschiedliche Werte zuzuweisen bzw. deren Werte zu lesen.
Eine weitere Klasse SchulemitVersionUID definiert die Instanzfelder vom Typ
String name und fach und ein Instanzfeld vom Typ int note. Ein weiteres
Instanzfeld testVersionUID vom Typ String soll zum Testen der Vergabe einer
Versionsnummer beim Serialisieren dienen.
Anhand der Methode pruefen() dieser Klasse soll abgefragt werden, ob die im
Konstruktor übergebenen Werte für Name, Fach und Note unter den Aufzählungskonstanten der Enumerationen zu finden sind und entsprechende Meldungen am
Bildschirm angezeigt werden.
Mithilfe der Klasse SerialisierungTest sollen zwei Objekte vom Typ der Klasse
SchulemitVersionUID serialisiert und deserialisiert werden. Definieren Sie zu
diesem Zweck zwei eigene Methoden writeObject() und readObject(), in
68
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.13
Die Klasse RandomAccessFile
denen Sie die dazu erforderlichen Statements aus den vorangegangenen Aufgaben
entsprechend zusammenfassen, und rufen Sie diese in der main()-Methode der
Klasse auf.
Rufen Sie an den so erzeugten Instanzen die Methode pruefen() auf und zum
Anzeigen der SerialVersionUID die Methoden lookup() und getSerialVersionUID() der Klasse ObjectStreamClass.
Ändern Sie den Feldtyp von testVersionUID auf int ab und entwerten Sie den
Methodenaufruf für Ihre writeObject()-Methode durch das Setzen von Kommentarzeichen. Dadurch wird verhindert, dass die Objekte erneut serialisiert
werden. Mit dem Aufruf der Methode readObject() wird versucht, die im vorhinein schon gespeicherten Werte zu deserialisieren. Beim Lesen der Werte wird
die vorher gespeicherte SerialVersionUID der Klasse mit der nach dem erneuten
Übersetzen neu vergebenen verglichen und eine Ausnahme vom Typ InvalidClassException ausgelöst.
Hinweise für die Programmierung:
Für den Vergleich mit den im Konstruktoraufruf übergebenen Werten können die
Aufzählungskonstanten mithilfe der Methode values() der generischen Klasse
EnumMap<K,V> über eine erweiterte for-Schleife aus den Enumerationen gelesen
werden.
Die serialVersionUID kann auch vom Programmierer selbst in der Klassendefinition als long-Wert definiert werden. Dieser Wert wird vom System berücksichtigt
und sollte bei einer Änderung der Klassendefinition immer mit abgeändert werden, siehe dazu die im Lösungsvorschlag auskommentierte Konstantendefinition.
Java-Dateien:
Schule.java, SchulemitVersionUID.java, SerialisierungTest.java
Programmaufruf: java SerialisierungTest
1.13 Die Klasse RandomAccessFile
In und aus Dateien kann nicht nur mithilfe von Streams geschrieben bzw. gelesen
werden, wobei die Bearbeitung der Daten sequentiell erfolgt, sondern auch über
einen sogenannten »wahlfreien Zugriff«. Die Klasse RandomAccessFile, über
welche dieser Zugriff ermöglicht wird, implementiert die Schnittstellen DataOutput und DataInput und ihre Instanzen verhalten sich ähnlich wie große Arrays,
die in einem Dateisystem gespeichert sind. Im Gegensatz zu Streams ist dabei ein
abwechselndes Lesen und Schreiben von Daten erlaubt.
Die Klasse besitzt zwei Konstruktoren, in denen über eine File- bzw. String-Referenz eine Datei bzw. deren Name übergeben werden kann. Beide Konstruktoren
definieren einen zweiten Parameter vom Typ String, der den Eröffnungsmodus
der angegebenen Datei definiert. Ist dieser »r«, wird die Datei zum Lesen, mit »rw«
zum Lesen und Schreiben und mit »rws« und »rwd« zu einem gesicherten Lesen
und Schreiben geöffnet.
69
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
Die Schreib- und Leseposition wird über einen Dateizeiger (»file pointer«) verwaltet. Dadurch, dass die Klasse die write()- und read()-Methoden des DataOutput- und DataInput-Interface implementiert, können damit wie auch im Fall der
Klassen DataOutputStream und DataInputStream sowohl primitive Datentypen
und Strings als auch Bytes manipuliert werden.
Der Dateizeiger steht nach dem Öffnen einer Datei immer am Dateianfang und
wird von den write()- und read()-Methoden immer eine Position weiter gesetzt.
Die Methode getFilePointer() ermittelt die aktuelle Position des Dateizeigers,
indem sie die Anzahl der Bytes vom Dateianfang bis zur Zeigerposition zurückliefert. Die Methoden seek() und skipBytes() ermöglichen ein gezieltes Setzen
des Dateizeigers. Während die Methode seek() den Dateizeiger absolut, d.h. ausgehend vom Beginn der Datei positioniert, dient die Methode skipBytes() einer
relativen Positionierung, die von der gerade aktuellen Position des Zeigers ausgeht.
Beim Schreiben über das Dateiende hinaus wird eine Datei mit wahlfreiem Zugriff
automatisch erweitert. Die Länge der Datei kann mithilfe der Methode length()
ermittelt werden und entspricht der Anzahl der in der Datei gespeicherten Bytes,
die von der Anzahl der gespeicherten Zeichen abweichen kann. Damit kann beim
Lesen das Dateiende überprüft werden. Wird versucht, darüber hinaus zu lesen,
wird von allen readxxx-Methoden für primitive Datentypen und der readUTF()Methode für das Lesen von Strings sowie auch von den readFully()-Methoden
eine Ausnahme vom Typ EOFException ausgelöst. Die readLine()-Methode liefert den Wert null zurück und die read()-Methoden für Bytes den Wert -1.
Aufgabe 1.39
Wahlfreier Zugriff auf eine Datei
Definieren Sie eine Klasse RandomAccessDatei, die in ihrer main()-Methode eine
Instanz vom Typ RandomAccessFile über einen der Konstruktoren dieser Klasse
erzeugt. In die so bereitgestellte Datei sollen mehrere Textzeilen mit den Inhalten:
»ByteZeile1«, »CharZeile1«, »UTFZeile1«, »ByteZeile2«, »CharZeile2«, »UTFZeile2«
etc. mithilfe der Methoden writeBytes(), writeChars() und writeUTF() der
Klasse RandomAccessFile geschrieben werden. Nutzen Sie das Newline-Zeichen
(‘\n’), um ein Zeilenende zu markieren.
Ermitteln Sie die Größe der Datei und die Zeigerposition nach der Durchführung
aller Schreiboperationen und zeigen Sie diese Werte am Bildschirm an. Danach soll
der Dateizeiger an den Dateianfang für das Lesen der Dateieinträge positioniert
werden.
Definieren Sie eine Methode lesenDatei(), in der Sie mithilfe der Methoden
readLine() und readUTF() die Textzeilen nacheinander lesen und danach ausgeben.
70
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.14
Erweiterung der I/O-Funktionalität mit Java 7
Anstelle des Textes »ByteZeile« soll in jede der Textzeilen über absolute oder relative Zeigerpositionierung der Text »NeueZeile« geschrieben werden. Benutzen Sie
dazu die Methoden seek() bzw. skipBytes() und rufen Sie erneut die Methode
leseDatei() auf, um die durchgeführten Änderungen zu prüfen.
Java-Dateien: RandomAccessDatei.java
Programmaufruf: java RandomAccessDatei
1.14 Erweiterung der I/O-Funktionalität mit Java 7
Die mit NIO.2 bezeichneten I/O-Erweiterungen von Java 7 wurden dem Package
java.nio von Java 1.4 hinzugefügt und beinhalten die zwei größeren Themen:
쐽
Das neue File-System-API
쐽
Asynchrone I/O-Erweiterungen
Auf ein Dateisystem kann in Java 7 mit Methoden der Klassen FileSytem, FileSystems und FileStore aus dem Paket java.nio.file zugegriffen werden. Die
zwei wichtigsten Methoden, die der Konstruktion von Dateisystemen dienen, sind
die Factory-Methoden getDefault() und getFileSystem(URI uri). Die Methode getDefault() gibt die FileSystem-Instanz zurück, auf der ein Programm
gerade ausgeführt wird.
Zwei weitere Implementierungen des JDK, das Interface Path und die Klasse
Files aus dem gleichen Paket, ergänzen das File-System-API, indem sie die Funktionalität der Klasse File komplett ersetzen und gleichzeitig erweitern. Die FileKlasse selbst bleibt auch bestehen (wurde nicht als deprecated gekennzeichnet)
und Java 7 bietet mit der Methode toPath() die Möglichkeit, ein File-Objekt in
ein neues Path-Objekt umzusetzen. Laut Hinweisen aus der Java-Literatur sollte
von alten und neuen Klassen parallel Gebrauch gemacht werden, um die I/O-Funktionalität von Java sinnvoll nutzen zu können.
An einer mit getDefault() ermittelten FileSytem-Instanz kann die Methode
getPath() aufgerufen werden, um den Pfadnamen für eine/ein im Aufruf übergebene Datei oder Dateiverzeichnis zu ermitteln.
Die einfachste Möglichkeit, eine Path-Referenz zu ermitteln, besteht darin, eine
der get()-Methoden der Klasse Paths aufzurufen. Damit können absolute und
relative Pfadnamen bestimmt oder auch Pfade über Shortcuts mit den Bezeichnungen ».« (für das aktuelle Directory) bzw. »..« (für ein übergeordnetes Directory) definiert werden.
Die Klasse Files stellt Klassenmethoden zum Ausführen von Operationen auf
Dateien und Dateiverzeichnissen zur Verfügung wie z.B. delete(), move() und
copy(), die im Fehlerfall eine Exception werfen. So wird das API-Design dahin
gehend verbessert, dass vom Programm aus Fehler näher beschrieben werden können und darauf reagiert werden kann. Weitere Methoden der Klasse Files erlau-
71
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
ben den Zugriff auf Dateiattribute, die vom NIO.2 in Views gruppiert werden, weil
die unterschiedlichen Dateisysteme verschiedene Bezeichnungen nutzen, um
Attribute zu definieren. Die neuen Methoden ermöglichen zusätzlich zu den
gleichartigen Methoden der File-Klasse so genannte Bulk-Zugriffe, mit denen
mehrere Attribute gleichzeitig gelesen oder geschrieben werden können.
In vielen der Beispiele aus den vorangegangenen Aufgaben konnten wir sehen, wie
Streams für I/O-Operationen eingesetzt werden, um Daten in ein Java-Programm
von einer Informationsquelle einzulesen und zu einem Ziel zu senden. Weil in
Stream-orientierten I/O-Systemen ein Java-Programm immer nur ein Byte von
einem InputStream lesen bzw. in einen OutputStream schreiben kann, leidet die
Performance von Lese/Schreibzugriffen.
Mit Java 1.4 wurde mit dem NIO (»new imput/output«) ein neues I/O-API eingeführt, das auf Channels und Buffers basiert. Channels sind Streams ähnlich, unterscheiden sich jedoch von diesen in drei wichtigen Merkmalen:
쐽
Während Streams entweder zum Lesen oder zum Schreiben geöffnet werden
können, unterstützen Channels Reads und Writes gleichzeitig.
쐽
Channels können nur aus einem Puffer lesen bzw. in einen Puffer schreiben.
쐽
Channels können asynchron gelesen und geschrieben werden.
Ein Channel ist eine offene Verbindung zwischen einem Java-Programm und einer
Quelle bzw. einem Ziel, die/das benutzt wird, um Daten zu lesen oder zu schreiben. Die Implementierung von Channels basiert auf dem Channel-Interface aus
dem Paket java.nio.channels. Ein ReadableByteChannel-Objekt kann benutzt
werden, um Daten aus einer Quelle in einen ByteBuffer zu lesen und ein WritableByteChannel-Objekt, um Daten aus einem ByteBuffer in ein Ziel zu schreiben.
Ohne auf alle Details einzugehen, wollen wir noch erwähnen, dass die Klassen
FileInputStream und FileOutputStream für das NIO für die Arbeit mit Channels angepasst wurden. Sie definieren eine Methode getChannel(), die ein FileChannel-Objekt zurückgibt, das genutzt werden kann, um Daten aus/in einer/
eine Datei zu lesen bzw. zu schreiben. Die Klasse FileChannel wurde mit Java 7
ebenfalls abgeändert und an das neue File-System-API angepasst. Wird ein FileChannel-Objekt mit Hilfe eines Path-Objekts ermittelt, können zwei neue
open()-Methoden zum Öffnen des Channels aufgerufen werden. Die Klasse implementiert mit Java 7 auch das neue SeekableByteChannel-Interface, das für
die Nutzung von RandomAccessFiles eingesetzt werden kann.
Im Gegensatz zu synchronen I/O-Operationen, bei denen stets auf eine Antwort
gewartet wird, geben asynchrone Operationen sofort die Kontrolle an das Java-Programm zurück, ohne auf eine Antwort zu warten. Ansätze für eine asynchrone I/OVerarbeitung gibt es in Java schon seit der Version 1.4. Die mit dem Einsatz verbundenen Schwierigkeiten dienten als Anlass, mit Java 7 drei neue Typen von asynchronen Channels für den Zugriff auf Dateien und in der Client-ServerKommunikation einzuführen:
72
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.14
Erweiterung der I/O-Funktionalität mit Java 7
쐽
AsynchronousFileChannel
쐽
AsynchronousSocketChannel
쐽
AsynchronousServerSocketChannel
Während synchrone I/Os in einer Multithread-Umgebung (siehe dazu das nachfolgende Kapitel) die Performance negativ beeinflussen können, unterstützen asynchrone I/Os die Nebenläufigkeit und Skalierbarkeit von Programmen, was im
heutigen Zeitalter von Multi-Prozessor- und Multicore-Architekturen immer mehr
an Bedeutung gewinnt. Asynchrone Channels unterstützen und überwachen multiple I/O-Operationen (wie Verbindungsaufbau, Lesen und Schreiben), die parallel
von mehreren Threads ausgeführt werden.
Wie bereits erwähnt, basieren die Methoden zum Lesen und Schreiben aus/in
Channels auf so genannte ByteBuffers. Im Gegensatz zu einem synchronen FileChannel verfügen asynchrone FileChannels nicht über eine aktuelle Zeigerposition
bzw. einen Offset beim Lesen und Schreiben aus/in Dateien, weshalb bei jedem
neuen Vorgang diese Position spezifiziert werden muss.
Die Klasse ByteBuffer wird von der abstrakten Oberklasse Buffer abgeleitet, die
eine endliche Sequenz von Elementen von einem angegebenen primitiven Typ definiert. Die drei wichtigsten Eigenschaften einer Buffer-Instanz sind:
쐽
Die Größe (»capacity«), die die maximale Anzahl von Elementen, die darin gespeichert werden können, spezifiziert.
쐽
Das Limit (»limit«), das dem Index des ersten Elements, das nicht geschrieben
bzw. gelesen werden kann, gleich ist. Mit anderen Worten, wenn aus einem Puffer heraus geschrieben wird, spezifiziert das Limit, wie viele Daten zum Lesen
übrig geblieben sind, und wenn daraus gelesen wird, wie viel Platz zum Speichern von Daten noch vorhanden ist.
쐽
Die Position, die den Index des nächsten Elements, das geschrieben bzw. gelesen werden soll, vorgibt.
Die flip()-Methode setzt das Limit auf die aktuelle Position und danach die Position auf 0. Die Methode clear() setzt das Limit gleich der Puffergröße und die
Position auf 0. Darum sollte, wenn aus einem Puffer nur geschrieben wird, vor
dem Schreiben das Limit mit flip() gesetzt werden, um die korrekte Anzahl von
Bytes zu übertragen.
Die Klasse ByteBuffer verfügt über keinen Konstruktor und definiert mehrere
Methoden für das Lesen und Schreiben von Daten aus/in ByteBuffer-Instanzen.
Derartige Instanzen werden mit der Methode allocate() der Klasse erzeugt, in
deren Aufruf die Größe des Puffers übergeben wird.
Wir weisen an dieser Stelle auf die nachfolgenden Aufgaben hin, um die Syntax
und Benutzung von neuen Klassen und Methoden einzuüben. Eine Benutzung der
Klasse AsynchronousFileChannel in einer Multithreading-Umgebung wird mit
73
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
der Klasse ExecutorsmitAsynchronousFileChannel aus der Aufgabe 2.46 illustriert.
Die AsynchronousServerSocketChannel- und AsynchronousSocketChannelKlassen werden in Kapitel 7 näher beschrieben.
Aufgabe 1.40
Das Interface Path und die Klasse Files von Java 7
In der Klasse DateiundVerzeichnisVerwaltungmitJava7 soll die Klasse
DateiundVerzeichnisVerwaltung aus der Aufgabe 1.1 mit Hilfe des neuen FileSystem-APIs überarbeitet werden. Erzeugen Sie mehrere File-Objekte wie gehabt
über die unterschiedlichen Konstruktoren der Klasse File und rufen Sie daran die
static-Methode toPath() der Klasse Files auf, um diese in ein Path-Objekt
umzusetzen. Die neue Referenz kann im Aufruf der Methoden createFile()
bzw. createDirectory() zum Erzeugen von Dateien und Verzeichnissen übergeben werden. Alternativ kann eine Path-Referenz auf eine Datei oder ein Dateiverzeichnis aus dem aktuellen Dateisystem über den geketteten Methodenaufruf
FileSystems.getDefault().getPath() ermittelt werden. Beachten Sie dabei,
dass ein im Methodenaufruf als String übergebenes Verzeichnis (oder eine Datei)
nicht vorhanden sein muss und eine Ausnahme vom Typ FileAlreadyExistsException geworfen wird, wenn Dateien oder Verzeichnisse schon existieren.
Weil verschiedene Dateisysteme unterschiedliche Sichten auf die Art und Weise,
wie Dateiattribute ausfindig gemacht werden, haben können, gruppiert das NIO.2
die Attribute in Views. Der Zugriff auf eine View kann für die Ermittlung aller
Attribute mit der Methode readAttributes() erfolgen oder mit der Methode
getAttribute() für einzelne Attribute. Die vom NIO.2 definierten sechs Views
können über den Aufruf der Methode supportedFileAttributeViews() ermittelt werden. Richten Sie sich nach dem Lösungsvorschlag zu dieser Aufgabe, um
z.B. alle oder einzelne Attribute aus dem BasicFileAttributeView und dem
DosFileAttributeView zu bestimmen bzw. eigene Attribute für Verzeichnisse
und Dateien im UserDefinedFileAttributeView zu setzen (und ggf. wieder zu
löschen).
Rufen Sie zum Vergleich Methoden wie z.B. delete() der Klassen File und Files
auf und achten Sie darauf, dass die Methoden der Klasse Files eine IOException
werfen, die es ermöglicht, auf eventuelle Fehler zu reagieren.
Benutzen Sie den mit Java 7 eingeführten Disjunction-Typ für Exceptions, der die
Möglichkeit bietet, mehrere Ausnahmen gleichzeitig in einem catch()-Block zu
behandeln.
Java-Dateien: DateiundVerzeichnisVerwaltungmitJava7.java
Programmaufruf: java DateiundVerzeichnisVerwaltungmitJava7
74
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.14
Erweiterung der I/O-Funktionalität mit Java 7
Aufgabe 1.41
Die Klassen FileChannel und AsynchronousFileChannel
In Analogie zu der Klasse ByteFileStreams aus der Aufgabe 1.5 sollen zwei neue
Klassen SynchroneFileChannels und AsynchroneFileChannels zum Schreiben und Lesen von char-Werten in/aus eine/einer Datei definiert werden.
In beiden Klassen wird ein byte-Array mit den Großbuchstaben des Alphabets initialisiert und die Methode ByteBuffer byteBuffer = ByteBuffer.allocate(26)
aufgerufen, um einen ByteBuffer zu erzeugen. Schreiben Sie in diesen den Inhalt
des Arrays mit der Methode wrap().
In der Klasse SynchroneFileChannels sollen für den Zugriff auf die Datei C:/
EJ_Uebungsbuch3/Datei2, die mit der vorangegangenen Aufgabe angelegt wurde,
sowohl die Möglichkeiten von Java 1.4 als auch die von Java 7 zum Ermitteln eines
FileChannels genutzt werden: FileChannel fileChannel = new FileOutputStream(file).getChannel() bzw. FileChannel fileChannel = FileChannel.open(path, Enumset.of(StandardOpenOption.READ, StandardOpenOption.WRITE), wobei Path path = Paths.get("C:/EJ_Uebungsbuch3",
"Datei2") eine Referenz auf den Pfadnamen der Datei definiert. In beiden Fällen
kann der Inhalt eines ByteBuffers mit fileChannel.write(byteBuffer) auf den
Channel geschrieben werden und mit fileChannel.read(byteBuffer) aus der
Datei über den Channel in einen ByteBuffer gespeichert werden.
In der Klasse AsynchroneFileChannels wird für das Schreiben in eine Datei eine
AsynchronousFileChannel-Instanz erzeugt und zum Schreiben geöffnet: AsynchronousFileChannel asynchronousFileChannel = AsynchronousFileChannel.open(path,StandardOpenOption.WRITE), wobei die Referenz Path path =
Paths.get("C:/EJ_Uebungsbuch2", "Datei5") vorher ermittelt werden muss.
Der Inhalt des Puffers soll mit der Methode write(ByteBuffer src, long position) ab der Position 0 auf den Channel geschrieben werden.
Die write()-Methode der Klasse AsynchronousFileChannel liefert in einer
Future<Integer>-Instanz die Anzahl der geschriebenen Bytes, die mit ihrer
get()-Methode ermittelt werden kann.
Um das Ergebnis zu prüfen, wird eine AsynchronousFileChannel-Instanz zum
Lesen mit der Option StandardOpenOption.READ geöffnet, die Bytesequenz aus
dem Channel ab der Position 0 gelesen und in einen weiteren ByteBuffer übertragen. Die read()-Methode liefert ebenfalls in einer Future<Integer>-Instanz die
Anzahl der gelesenen Bytes zurück.
Dekodieren Sie in beiden Klassen die Inhalte der ByteBuffers für eine Anzeige am
Bildschirm wie folgt: Charset.forName(System.getProperty("file.encoding")).decode(byteBuffer)), nachdem Sie im Vorhinein deren Position auf 0
und das Limit gleich der aktuellen Position zurückgestellt haben.
75
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
Hinweise für die Programmierung:
Das Schreiben und Lesen von Bytes in/aus die/der Datei mit einem AsynchronousFileChannel wird über den Aufruf der Methoden write() und read() angestoßen
und danach asynchron ausgeführt. Mit dem Aufruf der Methode isDone() des
Future-Interface kann gewartet werden, bis der Vorgang beendet wurde. Diese
Methode gibt true zurück, wenn ein Task komplett abgearbeitet wurde.
Das generische Interface Future wurde mit Java 5 dem Paket java.util.concurrent hinzugefügt. Eine detaillierte Beschreibung und Anwendung seiner Methoden sowie der Klassen, die das Interface implementieren, finden Sie in Kapitel 2.
Weil die Klassen FileChannel und AsynchronousFileChannel das AutoCloseable-Interface implementieren, kann zum Schließen von Channels alternativ zur
close()-Methode das neue Feature von Java 7 »try with ressource« benutzt werden, indem der entsprechende Programmcode in einen derartigen try/catchBlock gepackt wird.
Java-Dateien: SynchroneFileChannels.java, AsynchroneFileChannels.java
Programmaufruf: java AsynchroneFileChannels, java SynchroneFileChannels
1.15 Lösungen
Lösung 1.1
Die Klasse DateiundVerzeichnisVerwaltung
import java.io.*;
import java.net.*;
public class DateiundVerzeichnisVerwaltung {
public static void main(String argFile[]) {
File[] f = new File[11];
try {
// File-Objekte über die unterschiedlichen Konstruktoren erzeugen
// Im Konstruktor der Klasse File kann ein Pfadname als String// Referenz übergeben werden, welcher in einen abstrakten
// Pfadnamen konvertiert wird
f[0] = new File("C:/EJ_Uebungsbuch1/");
// Anschließend muss die Methode mkdirs() aufgerufen werden,
// welche das Unterverzeichnis erzeugt
f[0].mkdirs();
f[1] = new File("C:/EJ_Uebungsbuch2/");
f[1].mkdirs();
f[2] = new File("C:/EJ_Uebungsbuch3/");
f[2].mkdirs();
// Datei oder Verzeichnisname als Argument im Programmaufruf
// übergeben
f[3] = new File(argFile[0]);
// Im Konstruktor der Klasse File wird der Pfadname und der
76
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
// Dateiname als String-Referenz übergeben
f[4] = new File("C:/EJ_Uebungsbuch1/", "TestDatei");
// Anschließend muss die Methode createNewFile() aufgerufen
// werden, welche die Datei erzeugt
f[4].createNewFile();
// Im Konstruktor der Klasse File wird ein abstrakter Pfadname
// als File-Referenz übergeben
f[5] = new File(f[2], "TestDateiNeu");
f[5].createNewFile();
// Die Syntax von URL- und URI-Schemata anzeigen
URL url = new File("C:/EJ_Uebungsbuch2/").toURI().
toURL();
System.out.println(url.toString());
f[6] = new File(url.toURI());
URI uri = new File("C:/EJ_Uebungsbuch3/").toURI();
System.out.println(uri.toString());
// Den Konstruktor mit einer URI-Referenz als Parameter aufrufen
// Das über file:URI angegebene URI-Objekt wird in einen
// abstrakten Pfadnamen umgesetzt
f[7] = new File(uri);
URL url1 = new URL("file:/C:/EJ_Uebungsbuch1/TestDatei");
URI uri1 = new URI(
"file:/C:/EJ_Uebungsbuch3/TestDateiNeu");
f[8] = new File(url1.toURI());
f[9] = new File(uri1);
// Pfadname als String-Referenz übergeben
f[10] = new File("C:/EJ_Uebungsbuch2/Datei");
f[10].createNewFile();
// Informationen über die erstellten Dateien und Verzeichnisse
// ermitteln und anzeigen
for(int i=0; i<11; i++) {
if(f[i].exists() && f[i].isFile()) {
System.out.println(f[i].getName()
+ " ist eine Datei");
}
else if(f[i].exists() && f[i].isDirectory()) {
System.out.println(f[i].getName()
+ " ist ein Dateiverzeichnis");
}
else {
// Ist das im Konstruktoraufruf übergebene Argument fehlerhaft?
System.out.println("Fehler: " + f[i]);
}
}
// Dateien umbenennen und löschen
boolean b = f[10].renameTo(new File(
"C:/EJ_Uebungsbuch2/DateiNeu"));
77
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
System.out.println(f[10].getName() + " " + b);
f[10].delete();
System.out.println(f[10].getName());
}
catch(NullPointerException e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
catch(ArrayIndexOutOfBoundsException e) {
System.out.println("Im Programmaufruf einen Verzeichnis"
+ " oder Dateinamen angeben");
e.printStackTrace();
}
catch(IOException e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
catch(URISyntaxException e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
}
}
Programmausgaben
file:/C:/EJ_Uebungsbuch2/
file:/C:/EJ_Uebungsbuch3/
EJ_Uebungsbuch1 ist ein Dateiverzeichnis
...
EJ_Uebungsbuch2 ist ein Dateiverzeichnis
...
test.ser ist eine Datei
TestDatei ist eine Datei
...
Datei true
Datei
file:/C:/EJ_Uebungsbuch2/
file:/C:/EJ_Uebungsbuch3/
EJ_Uebungsbuch1 ist ein Dateiverzeichnis
...
DateiundVerzeichnisVerwaltung.java ist eine Datei
...
test.ser ist eine Datei
TestDatei ist eine Datei
...
Datei true
Datei
78
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
Lösung 1.2
Die Klasse DateiundVerzeichnisListen
import java.io.*;
import java.net.*;
public class DateiundVerzeichnisListen {
public static void main(String argFile[]) {
try {
File v1, v2;
File[] f1 = new File[5];
File[] fListe1, fListe2, fRoots;
// Das Home-Verzeichnis ermitteln
File fHome = new File(System.getProperty("user.home"));
// Das Home-Verzeichnis auflisten
File[] f2 = fHome.listFiles();
// Eigenes Dateiverzeichnis als File-Instanz erzeugen
v1 = new File("C:/EJ_Uebungsbuch/");
// Anschließend muss die Methode mkdirs() aufgerufen werden,
// welche dass Unterverzeichnis von C:\ erzeugt
v1.mkdirs();
// Unterverzeichnis von C:/EJ_Uebungsbuch/ erzeugen
v2 = new File(v1, "Dateien/");
v2.mkdirs();
// Dateien als File-Instanzen im Unterverzeichnis erzeugen
for(int i=0; i<f1.length; i++) {
f1[i] = new File(v2, "Datei"+i);
f1[i].createNewFile();
}
// Die Verzeichnisse v1 und v2 auflisten
fListe1 = v1.listFiles();
fListe2 = v2.listFiles();
// Verzeichnisse und Dateien anzeigen
System.out.println();
System.out.println("Unterverzeichnisse und Dateien "
+ "von " + v1.getName() + ":");
anzeige(fListe1);
anzeige(fListe2);
// Leerzeile schreiben
System.out.println();
System.out.println("Unterverzeichnisse und Dateien "
+ "von " + fHome.getName() + ":");
anzeige(f2);
// Verfügbare Laufwerke ermitteln und anzeigen
fRoots = File.listRoots();
// Leerzeile schreiben
System.out.println();
System.out.println("Verfuegbare Laufwerke:");
79
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
for(int i=0; i<fRoots.length; i++)
System.out.print(fRoots[i].getPath() + " " + fRoots[i].
getTotalSpace() + " " + fRoots[i].getFreeSpace()
+ "; ");
}
catch(IOException e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
}
// Anzeige von File-Listen
public static void anzeige(File[] file) {
for(File fileName: file) {
if(fileName.isFile()) {
System.out.println(fileName.getName()
+ " ist eine Datei");
}
else if(fileName.isDirectory()) {
System.out.println(fileName.getName()
+ " ist ein Dateiverzeichnis");
}
}
}
}
Programmausgaben
Unterverzeichnisse und Dateien von EJ_Uebungsbuch:
Dateien ist ein Dateiverzeichnis
Datei0 ist eine Datei
Datei1 ist eine Datei
...
Unterverzeichnisse und Dateien von ...:
.appletviewer ist eine Datei
.netbeans ist ein Dateiverzeichnis
...
Verfuegbare Laufwerke:
C:\ ...; E:\ ...; F:\...
Lösung 1.3
Die Klasse DateiundVerzeichnisFilter
import java.io.*;
import java.util.*;
public class DateiundVerzeichnisFilter {
80
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
public static void main(String argFile[]) {
String[] fListe1, fListe2, fListe3;
File[] fListe4;
// Das aktuelle Verzeichnis ermitteln
File fDir = new File(System.getProperty("user.dir"));
// Das aktuelle Verzeichnis nach vorgegebenen Filterdefinitionen
// durchsuchen
fListe1 = fDir.list(new FilenameFilter() {
public boolean accept(File file, String string) {
return string.toLowerCase().endsWith(".java");
}
});
fListe2 = fDir.list(new FilenameFilter() {
public boolean accept(File file, String string) {
return string.toLowerCase().endsWith(".class");
}
});
fListe3 = fDir.list(new FilenameFilter() {
public boolean accept(File file, String string) {
return file.isDirectory();
}
});
fListe4 = fDir.listFiles(new FileFilter() {
public boolean accept(File pathname) {
return ((pathname.getName()).equals("test.ser"));
}
});
// Die Namen von gefilterten Dateien anzeigen
System.out.println(".java-Dateien:");
anzeigeArray(fListe1);
System.out.println(".class-Dateien:");
anzeigeArray(fListe2);
System.out.println("Alle Dateien:");
anzeigeArray(fListe3);
System.out.println("Bestimmte Dateien:");
anzeigeArray(fListe4);
}
// Generische Methode für die Anzeige von unterschiedlichen
// Array-Typen
public static <T> void anzeigeArray(T[] ein) {
// Die Elemente des Arrays werden über eine for-each-Schleife
// ausgegeben
for(T eingabeArray: ein) {
System.out.print(Arrays.asList(eingabeArray));
}
System.out.println();
}
}
81
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
Programmausgaben
.java-Dateien:
[ByteArrayStreams.java]...
.class-Dateien:
[ByteArrayStreams.class]...
.Alle Dateien:
[Ausgabedatei][ByteArrayStream.java]...
.Bestimmte Dateien:
[C:\...\java7uebungsbuch2sourcecode\kapitel1\test.ser]
Lösung 1.4
Die Klasse TestLossofPrecision
import java.util.Arrays;
// Testklasse für Operationen mit primitiven Datentypen
public class TestLossofPrecision {
public static Number[] add(Number zahl1, Number zahl2) {
Number[] zahlen = new Number[7];
zahlen[0] = zahl1.intValue() + zahl2.intValue();
zahlen[1] = zahl1.floatValue() + zahl2.floatValue();
zahlen[2] = zahl1.doubleValue() + zahl2.doubleValue();
zahlen[3] = zahl1.intValue() + zahl2.floatValue();
zahlen[4] = zahl1.floatValue() + zahl2.doubleValue();
zahlen[5] = zahl1.shortValue() + zahl2.shortValue();
zahlen[6] = zahl1.byteValue() + zahl2.byteValue();
return zahlen;
}
public static void main(String[] args) {
byte[] b = new byte[26];
int int1 = 1, int2 =2;
short short1 = new Short("1").shortValue();
short short2 = new Short("2").shortValue();
long long1 = new Long("1").longValue();
long long2 = new Long("2").longValue();
byte byte1 = new Byte("1").byteValue();
byte byte2 = new Byte("2").byteValue();
int int3 = int1 + int2;
int short3 = short1 + short2;
long long3 = long1 + long2;
int byte3 = byte1 + byte2;
// Fehler
// short short7 = short1 + short2;
// byte byte7 = byte1 + byte2;
// short short3 = new Short(short1 + short2).shortValue();
// byte byte3 = new Byte(byte1 + byte2).byteValue();
// Korrekte Additionen
82
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
int short4 = (new Short(short1)).shortValue() +
(new Short(short2)).shortValue();
int byte4 = (new Byte(byte1)).byteValue() +
(new Byte(byte2)).byteValue();
System.out.println(int3 + " " + short3 + " " + long3 + " "
+ byte3 + " " + short4 + " " + byte4);
short short5 = (new Integer(short4)).shortValue();
byte byte5 = (new Byte(""+byte4)).byteValue();
System.out.println(byte5+"*"+ short5);
// Der Konstruktor der Klasse Byte und das Rechnen mit byte-Werten
System.out.println("Die Grossbuchstaben und ihr 7-Bit "
+ "ASCII-Key-Code:");
for(int i=65; i<91; i++) {
b[i-65] = new Byte(""+i);
System.out.print(b[i-65] + " ");
}
System.out.println();
System.out.println(new String(b));
System.out.println("Die Kleinbuchstaben und ihr 7-Bit "
+ "ASCII-Key-Code:");
for(int i=65; i<91; i++) {
String s = Character.toString(Character.toLowerCase(
(char)b[i-65]));
System.out.print(" " + s + " ");
int zahl = (new Byte(b[i-65])).byteValue()+
(new Integer(32)).byteValue();
b[i-65] = new Byte(""+zahl);
System.out.print(b[i-65]);
}
System.out.println();
// Casting von primitiven Datentypen
// Fehler
// short short6 = (short)int1 + (short)int2;
// byte byte6 = (byte)long1 + (byte)long2;
// Korrekt, aber kein Casting erforderlich,
int int6 = (int)short1 + (byte)short2;
int int7 = (int)byte1 + (short)byte2;
// weil eine implizite Konvertierung stattfindet
int int8 = short1 + short2;
int int9 = byte1 + byte2;
System.out.println(int6 + "*" + int7 + "*" + int8+ "*"
+ int9);
// Im Methodenaufruf von add() werden Referenzen auf Objekte von
// Unterklassen des Parametertyps übergeben (ein Beispiel
// für den "impliziten Polymorphismus")
Number[] zahlen1 = add(new Integer(1), new Integer(2));
System.out.println("Ergebnisse der Addition:"+
83
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
Arrays.asList(zahlen1));
Number[] zahlen2 = add(new Float(-0.56f), new Float(2E-2));
System.out.println("Ergebnisse der Addition:"+
Arrays.asList(zahlen2));
Number[] zahlen3 = add(new Double(-0.56d),
new Double(2E-2));
System.out.println("Ergebnisse der Addition:"+
Arrays.asList(zahlen3));
Number[] zahlen4 = add(new Short("1"), new Short("2"));
System.out.println("Ergebnisse der Addition:"+
Arrays.asList(zahlen4));
Number[] zahlen5 = add(new Byte("1"), new Byte("2"));
System.out.println("Ergebnisse der Addition:"+
Arrays.asList(zahlen5));
}
}
Programmausgaben
3 3 3 3 3 3
3*3
Die Grossbuchstaben und ihr 7-Bit ASCII-Key-Code:
65 66 67 ...
ABC...
Die Kleinbuchstaben und ihr 7-Bit ASCII-Key-Code:
a 97 b 98 c 99 ...
3*3*3*3*3*3
Ergebnisse der Addition:[3, 3.0, 3.0, 3.0, 3.0, 3, 3]
...
Lösung 1.5
Die Klasse ByteFileStreams
import java.io.*;
public class ByteFileStreams {
//Schreiben und Lesen von binären Dateien
public static void main(String args[]) {
byte[] array = new byte[26];
int byteCode;
// Das byte-Array initialisieren
for(int i=65; i<91; i++)
array[i-65] = new Byte(""+i);
try {
// Ein byteorientierter Stream vom Typ der Klasse FileOutputStream
// wird mit einer Datei namens Binärdatei verknüpft
FileOutputStream byteFileOut =
new FileOutputStream("Binärdatei", true);
84
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
byteFileOut.write(array);
// File-Stream schliessen
byteFileOut.close();
// Ein byteorientierter Stream vom Typ der Klasse FileInputStream
// wird mit der vorher erstellten Datei verknüpft um die
// Daten daraus einzeln einzulesen und am Bildschirm anzuzeigen
FileInputStream byteFileIn =
new FileInputStream("Binärdatei");
while((byteCode = byteFileIn.read()) != -1)
System.out.println("byte- und char-Wert der aus der "
+ " Datei eingelesenen Daten: "
+ byteCode + "*" + (char)byteCode);
byteFileIn.close();
}
catch(IOException e) {
e.getMessage();
}
}
}
Programmausgaben
byte- und char-Wert der aus der Datei eingelesenen Daten: 65*A
byte- und char-Wert der aus der Datei eingelesenen Daten: 66*B
...
Lösung 1.6
Die Klasse CharFileStreams
import java.io.*;
public class CharFileStreams {
//Schreiben und Lesen von Textdateien
public static void main(String args[]) {
char[] array = new char[26];
int zeichen;
try {
// Ein zeichenorientierter Stream vom Typ der Klasse FileWriter
// wird mit einer Datei namens Textdatei verknüpft
FileWriter charFileOut =
new FileWriter("Textdatei");
for(int i=65; i<91; i++)
charFileOut.write(i);
// File-Stream schliessen
charFileOut.close();
// Ein zeichenorientierter Stream vom Typ der Klasse FileReader
// wird mit der vorher erstellten Datei verknüpft, um die Daten
// in ein char-Array einzulesen und am Bildschirm anzuzeigen
85
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
FileReader charFileIn =
new FileReader("Textdatei");
zeichen = charFileIn.read(array);
String s = new String(array);
System.out.println("Aus der Datei wurden " + zeichen
+ " Zeichen eingelesen: " + s);
// File-Stream schliessen
charFileIn.close();
}
catch (IOException e) {
e.getMessage();
}
}
}
Programmausgaben
Aus der Datei wurden 26 Zeichen eingelesen: ABCDEFGHIJKLMNOPQRSTUVWXYZ
Hinweise zu den Programmausgaben
Es ist nicht garantiert, dass mit der einen read-Anweisung tatsächlich der gesamte
Inhalt der Datei in das Array eingelesen wird. Falls die Eingabe so langsam und in
so kleinen Blöcken erfolgt, dass beim ersten Lesevorgang weniger als die gesamten
26 Zeichen eingelesen werden, dann würde die Programmausgabe entsprechend
weniger Zeichen enthalten. Man müsste eine Schleife von mehreren read-Aufrufen bis zum Dateienbde ausführen, um garantiert den kompletten Dateiinhalt zu
erhalten.
Lösung 1.7
Die Klasse ByteArrayStreams
import java.io.*;
public class ByteArrayStreams {
public static void main(String args[]) {
byte[] array1 = {74, 97, 118, 97};
byte[] array2, array3;
String s;
int anzBytes, byteCode;
try {
// Byteorientierte Ausgabe-Streams erzeugen
ByteArrayOutputStream byteArrayOut1 =
new ByteArrayOutputStream();
ByteArrayOutputStream byteArrayOut2 =
new ByteArrayOutputStream();
// Die Zahlen 1 bis 6 und das Wort Java-Stream in die Streams
// schreiben
86
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
for(int i=49; i<=54; i++)
byteArrayOut1.write(i);
byteArrayOut2.write(array1);
// Die Größe eines ByteArray-Stream wächst nach Bedarf; die
// hiermit einzeln geschriebenen Bytes werden am Ende der vorher
// geschriebenen Arrayelemente nacheinander angefügt
byteArrayOut2.write(45);
byteArrayOut2.write(83);
byteArrayOut2.write(116);
byteArrayOut2.write(114);
byteArrayOut2.write(101);
byteArrayOut2.write(97);
byteArrayOut2.write(109);
// ByteArrayOutputStreams für die Anzeige am Bildschirm in einen
// String konvertieren
s = byteArrayOut1.toString();
System.out.println("Inhalt des ersten Output-"
+ "Stream = " + s);
s = byteArrayOut2.toString();
System.out.println("Inhalt des zweiten Output-"
+ "Stream = " + s);
// Ausgabe-Streams kopieren
byteArrayOut2.writeTo(byteArrayOut1);
s = byteArrayOut2.toString();
System.out.println("Inhalt des kopierten Output-"
+ "Stream = " + s);
// ByteArrayOutputStream in ein byte-Array umsetzen, um damit ein
// ByteArrayInputStream zu erzeugen
array2 = byteArrayOut2.toByteArray();
// Array für das Speichern von eingelesenen Daten erzeugen
array3 = new byte[11];
// Der zweite Stream beinhaltet nur die Bytes von Position 0 bis
// 4, d.h. auch nur diese können daraus gelesen werden
ByteArrayInputStream byteArrayIn1 =
new ByteArrayInputStream(array2);
ByteArrayInputStream byteArrayIn2 =
new ByteArrayInputStream(array2,0,4);
// Einlesen der binären Daten aus den Eingabe-Streams und diese
// in Strings bzw. char-Werte umsetzen
anzBytes = byteArrayIn1.read(array3);
String s1 = new String(array3);
System.out.println("Inhalt des ersten Input-Stream = "
+ s1);
System.out.println("Anzahl der aus dem Stream gelesenen"
87
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
+" Bytes = " + anzBytes);
while((byteCode = byteArrayIn2.read()) != -1)
System.out.println("byte- und char-Wert der aus dem "
+ " zweiten Input-Stream eingelesenen Daten: "
+ byteCode + "*" + (char)byteCode);
}
catch(IOException e) {
System.out.println("Fehler: " + e.getMessage());
}
}
}
Programmausgaben
Inhalt des ersten Output-Stream = 123456
Inhalt des zweiten Output-Stream = Java-Stream
...
byte- und char-Werte der aus der Datei eingelesenen
byte- und char-Werte der aus der Datei eingelesenen
byte- und char-Werte der aus der Datei eingelesenen
byte- und char-Werte der aus der Datei eingelesenen
Daten:
Daten:
Daten:
Daten:
74*J
97*a
118*v
97*a
Lösung 1.8
Die Klasse CharArrayStreams
import java.io.*;
public class CharArrayStreams {
public static void main(String args[]) {
char[] array1 = {'J', 'a', 'v', 'a'};
char[] array2, array3;
String string = "Java";
String s;
int anzZeichen, zeichen;
try {
// Zeichenorientierte Ausgabe-Streams erzeugen
CharArrayWriter charArrayOut1 =
new CharArrayWriter();
CharArrayWriter charArrayOut2 =
new CharArrayWriter();
CharArrayWriter charArrayOut3 =
new CharArrayWriter();
// Die Zahlen 1 bis 6 und das Wort Java-Stream in die Streams
// schreiben
for(int i=49; i<=54; i++)
charArrayOut1.write(i);
charArrayOut2.write(array1);
88
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
// Auch die Größe eines CharArray-Stream wächst nach Bedarf; die
// hiermit einzeln geschriebenen Bytes werden am Ende der vorher
// geschriebenen Arrayelemente nacheinander angefügt
charArrayOut2.write(45);
charArrayOut2.write(83);
charArrayOut2.write(116);
charArrayOut2.write(114);
charArrayOut2.write(101);
charArrayOut2.write(97);
charArrayOut2.write(109);
// Das Wort Java in den 3. Stream als String schreiben
charArrayOut3.write(string);
// CharArrayWriter-Streams für die Anzeige am Bildschirm in einen
// String konvertieren
s = charArrayOut1.toString();
System.out.println("Inhalt des ersten Output-"
+ "Stream = " + s);
s = charArrayOut2.toString();
System.out.println("Inhalt des zweiten Output-"
+ "Stream = " + s);
s = charArrayOut3.toString();
System.out.println("Inhalt des dritten Output-"
+ "Stream = " + s);
// Ausgabe-Streams kopieren
charArrayOut2.writeTo(charArrayOut1);
s = charArrayOut2.toString();
System.out.println("Inhalt des kopierten Output-"
+ "Stream = " + s);
// CharArrayWriter-Stream in ein char-Array umsetzen, um damit ein
// CharArrayReader-Stream zu erzeugen
array2 = charArrayOut2.toCharArray();
// Array für das Speichern von eingelesenen Daten erzeugen
array3 = new char[11];
// Der zweite Stream beinhaltet nur die Zeichen von Position 0
// bis 4, d.h., dass auch nur diese im nachhinein daraus gelesen
// werden können
CharArrayReader charArrayIn1 =
new CharArrayReader(array2);
CharArrayReader charArrayIn2 =
new CharArrayReader(array2,0,4);
// Einlesen der Daten aus den Eingabe-Streams und diese
// in Strings bzw. char-Werte umsetzen
anzZeichen = charArrayIn1.read(array3);
String s1 = new String(array3);
System.out.println("Inhalt des ersten Input-Stream = "
89
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
+ s1);
System.out.println("Anzahl der aus dem Stream "
+ "gelesenen Zeichen = " + anzZeichen);
while((zeichen = charArrayIn2.read()) != -1)
System.out.println("int- und char-Wert der aus dem"
+ " zweiten Input-Stream eingelesenen Daten: "
+ zeichen + "*" + (char)zeichen);
// Schließen der Streams, weil dadurch Systemressourcen
// freigegeben werden
charArrayOut1.close();
charArrayOut2.close();
charArrayOut3.close();
charArrayIn1.close();
charArrayIn2.close();
}
catch(IOException e) {
System.out.println("Fehler: " + e.getMessage());
}
}
}
Programmausgaben
Inhalt des ersten Output-Stream = 123456
Inhalt des zweiten Output-Stream = Java-Stream
...
int- und char-Werte der aus der Datei eingelesenen
int- und char-Werte der aus der Datei eingelesenen
int- und char-Werte der aus der Datei eingelesenen
int- und char-Werte der aus der Datei eingelesenen
Daten:
Daten:
Daten:
Daten:
74*J
97*a
118*v
97*a
Lösung 1.9
Die Klasse UpperCaseOutputFilter
import java.io.*;
class UpperCaseOutputFilter extends FilterOutputStream {
// Konstruktordefinition
public UpperCaseOutputFilter(OutputStream outStream) {
// Den Konstuktor der Oberklasse aufrufen
super(outStream);
}
// Die write()-Methoden der Oberklasse überschreiben
public void write(int b) throws IOException {
out.write(b-32);
}
90
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
public void write(byte b[]) throws IOException {
for(int i=0; i<b.length; i++)
out.write(b[i]-32);
}
}
Die Klasse ByteOutputFilterfuerFileStreams
public class ByteOutputFilterfuerFileStreams {
public static void main(String[] args) {
byte[] array = new byte[26];
int byteCode, zeichen;
// Das byte-Array initialisieren
for(int i=97; i<123; i++)
array[i-97] = new Byte(""+i);
System.out.println("Ein byte-Array, das in einen FileStream"
+ " geschrieben wird: " + new String(array));
try {
// Einen FilterStream vom Typ der Klasse UpperCaseOutputFilter
// zum Umsetzen von Klein- in Großbuchstaben für einen
// FileOutputStream erzeugen
UpperCaseOutputFilter byteFilterOut1 =
new UpperCaseOutputFilter(
new FileOutputStream("Filter"));
// Das byte-Array schreiben
byteFilterOut1.write(array);
// Die Daten aus der Datei mithilfe eines FileInputStream
// einlesen
FileInputStream byteFileIn =
new FileInputStream("Filter");
zeichen = byteFileIn.read(array);
// und am Bildschirm anzeigen
System.out.println("Aus der Datei wurden " + zeichen
+ " Zeichen eingelesen: " + new String(array));
byteFileIn.close();
}
catch(IOException e) {
System.out.println("Fehler: " + e.getMessage());
}
}
}
Programmausgaben
Ein byte-Array, das in einen FileStream geschrieben wird: abcdefghijklmnopqrstuvwxyz
Aus der Datei wurden 26 Zeichen eingelesen: ABCDEFGHIJKLMNOPQRSTUVWXYZ
91
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
Lösung 1.10
Die Klasse ByteOutputFilterfuerPrintStream
public class ByteOutputFilterfuerPrintStream {
public static void main(String[] args) {
byte[] array = new byte[26];
int byteCode, zeichen;
// Das byte-Array initialisieren
for(int i=97; i<123; i++)
array[i-97] = new Byte(""+i);
System.out.println("Ein byte-Array, das in einen "
+ "PrintStream geschrieben wird: " + new String(array));
try {
// Den von der Klasse FilterOutputStream definierten Null-Filter
// testen
FilterOutputStream byteFilterOut1 =
new FilterOutputStream(System.out);
System.out.println("In den PrintStream ohne Filter "
+ "schreiben: ");
byteFilterOut1.write(array);
byteFilterOut1.write('a');
byteFilterOut1.write('b');
System.out.println();
// Einen FilterStream vom Typ der Klasse UpperCaseOutputFilter
// zum Umsetzen von Klein- in Großbuchstaben für einen
// PrintStream erzeugen
UpperCaseOutputFilter byteFilterOut2 =
new UpperCaseOutputFilter(System.out);
System.out.println("In den PrintStream mit einem Filter "
+ "schreiben: ");
byteFilterOut2.write(array);
byteFilterOut1.write('a');
byteFilterOut1.write('b');
// Die Streams schließen
byteFilterOut1.close();
byteFilterOut2.close();
}
catch(IOException e) {
System.out.println("Fehler: " + e.getMessage());
}
}
}
92
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
Programmausgaben
Ein byte-Array, das in einen PrintStream geschrieben wird: abcdefghijklmnopqrstuvwxyz
In den PrintStream ohne Filter schreiben:
abcdefghijklmnopqrstuvwxyzab
In den PrintStream mit einem Filter schreiben:
ABCDEFGHIJKLMNOPQRSTUVWXYZAB
Lösung 1.11
Die Klasse UpperCaseFilterWriter
import java.io.*;
class UpperCaseFilterWriter extends FilterWriter {
// Konstruktordefinition
public UpperCaseFilterWriter(Writer outStream) {
// Den Konstuktor der Oberklasse aufrufen
super(outStream);
}
// Die write()-Methoden der abstrakten Oberklasse implementieren
public void write(int c) throws IOException {
out.write(Character.toUpperCase((char)c));
}
public void write(char c[], int off, int len)
throws IOException {
// Zwei Alternativen zum Umsetzen von Characters
// for(int i=0; i<c.length; i++)
// out.write(Character.toUpperCase(c[i]));
out.write(String.valueOf(c).toUpperCase());
}
public void write(String s, int off, int len)
throws IOException {
out.write(s.toUpperCase());
}
}
Die Klasse CharOutputFilterfuerPrintWriter
public class CharOutputFilterfuerPrintWriter {
public static void main(String[] args) {
char[] array = new char[26];
int byteCode, zeichen;
// Das char-Array initialisieren
for(int i=97; i<123; i++)
array[i-97] = (char)i;
System.out.println("Ein char-Array, das in einen " +
"PrintStream geschrieben wird: " +
93
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
new String(array));
try {
// Einen FilterStream vom Typ der Klasse UpperCaseWriteFilter zum
// Umsetzen von Klein- in Großbuchstaben für einen PrintStream
// erzeugen
UpperCaseFilterWriter charFilterOut =
new UpperCaseFilterWriter(
new OutputStreamWriter(System.out));
System.out.println("In den PrintStream mit einem Filter "
+ "schreiben: ");
charFilterOut.write(array, 0, 26);
charFilterOut.write(" array ", 0, 5);
charFilterOut.write('a');
charFilterOut.write('b');
charFilterOut.close();
}
catch(IOException e) {
System.out.println("Fehler: " + e.getMessage());
}
}
}
Programmausgaben
Ein char-Array, das in einen PrintStream geschrieben wird: abcdefghijklmnopqrstuvwxyz
In den PrintStream mit einem Filter schreiben:
ABCDEFGHIJKLMNOPQRSTUVWXYZAB ARRAY AB
Lösung 1.12
Die Klasse LowerCaseInputFilter
import java.io.*;
class LowerCaseInputFilter extends FilterInputStream {
// Konstruktordefinition
public LowerCaseInputFilter(InputStream in) {
// Der Konstruktor initialisiert den Konstruktor der Oberklasse
super(in);
}
// Nachfolgend die Implementierung einer read()-Methode von
// FilterInputStream; diese ruft mit in. die read()-Methode der
// Klasse auf, welche im Konstruktor als Parameter übergeben wird;
// in ist ein als protected definiertes Feld der Oberklasse
public int read(byte[] b, int off, int len)
throws IOException {
int zeichen = 0;
zeichen = in.read(b, off, len);
94
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
System.out.println("Zeichen vor der Umsetzung: " +
new String(b));
for(int i=0; i<zeichen; i++) {
int zahl = (new Byte(b[i])).byteValue() +
(new Integer(32)).byteValue();
b[i] = new Byte("" + zahl);
}
return zeichen;
}
}
Die Klasse ByteInputFilterfuerFileStreams
public class ByteInputFilterfuerFileStreams {
public static void main(String[] args) {
byte[] array = new byte[26];
int zeichen;
// Ein zeichenorientierter Stream vom Typ der Klasse FileWriter
// wird mit einer Datei namens "CharFilter" verknüpft
try {
FileOutputStream byteFileOut =
new FileOutputStream("ByteFilter");
// Die binären Werte für die Buchstabenvon A bis Z in die Datei
// schreiben
for(int i=65; i<91; i++)
byteFileOut.write(i);
// Stream schliessen
byteFileOut.close();
// Einen FilterStream vom Typ der Klasse LowerCaseInputFilter
// zum Umsetzen von Groß-in Kleinbuchstaben für einen
// FileInputStream erzeugen
LowerCaseInputFilter byteFilterIn =
new LowerCaseInputFilter(
new FileInputStream("ByteFilter"));
// Den Dateiinhalt einlesen
zeichen = byteFilterIn.read(array, 0, 26);
String s = new String(array);
// und anzeigen
System.out.println("Aus der Datei wurden " + zeichen
+ " Zeichen eingelesen: " + s);
// Streams schliessen
byteFilterIn.close();
}
catch (IOException e) {
e.getMessage();
}
}
}
95
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
Programmausgaben
Zeichen vor der Umsetzung: ABCDEFGHIJKLMNOPQRSTUVWXYZ
Aus der Datei wurden 26 Zeichen eingelesen: abcdefghijklmnopqrstuvwxyz
Lösung 1.13
Die Klasse LowerCaseFilterReader
import java.io.*;
class LowerCaseFilterReader extends FilterReader {
// Konstruktordefinition
public LowerCaseFilterReader(Reader in) {
// Der Konstruktor initialisiert den Konstruktor der Oberklasse
super(in);
}
// Nachfolgend die Implementierung einer read()-Methode von
// FilterReader; diese ruft mit in. die read()-Methode der Klasse
// auf, welche im Konstruktor als Parameter übergeben wird;
// in ist ein als protected definiertes Feld der Oberklasse
public int read(char[] c, int off, int len)
throws IOException {
int zeichen = 0;
zeichen = in.read(c, off, len);
System.out.println("Zeichen vor der Umsetzung: " +
new String(c));
for(int i=0; i<zeichen; i++)
c[i] = Character.toLowerCase(c[i]);
return zeichen;
}
}
Die Klasse CharInputFilterfuerFileReader
public class CharInputFilterfuerFileReader {
public static void main(String[] args) {
char[] array = new char[26];
int zeichen;
// Ein zeichenorientierter Stream vom Typ der Klasse FileWriter
// wird mit einer Datei namens "CharFilter" verknüpft
try {
FileWriter charFileOut =
new FileWriter("CharFilter");
// Die Buchstaben von A bis Z in die Datei schreiben
for(int i=65; i<91; i++)
charFileOut.write(i);
// Stream schliessen
charFileOut.close();
96
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
// Einen FilterStream vom Typ der Klasse LowerCaseFilterReader
// zum Umsetzen von Groß-in Kleinbuchstaben für einen
// FileReader-Stream erzeugen
LowerCaseFilterReader charFilterIn =
new LowerCaseFilterReader(
new FileReader("CharFilter"));
// Den Dateiinhalt einlesen
zeichen = charFilterIn.read(array, 0, 26);
String s = new String(array);
System.out.println("Aus der Datei wurden " + zeichen
+ " Zeichen eingelesen: " + s);
// Streams schliessen
charFilterIn.close();
}
catch (IOException e) {
e.getMessage();
}
}
}
Programmausgaben
Zeichen vor der Umsetzung: ABCDEFGHIJKLMNOPQRSTUVWXYZ
Aus der Datei wurden 26 Zeichen eingelesen: abcdefghijklmnopqrstuvwxyz
Lösung 1.14
Die Klasse ByteFileundBufferedStreams
import java.io.*;
public class ByteFileundBufferedStreams {
public static void main(String args[]) {
byte[] array = new byte[100000];
int byteCode, zeichen;
long anfang, ende;
try {
// Ein byteorientierter Stream vom Typ der Klasse FileOutputStream
// wird mit einer Datei namens Binärdatei verknüpft
FileOutputStream byteFileOut =
new FileOutputStream("Binärdatei");
anfang = System.currentTimeMillis();
for(int i=0; i<100000; i++)
byteFileOut.write(i);
ende = System.currentTimeMillis();
System.out.println("Nicht gepuffertes Schreiben: " +
anfang + "*" + "*" + ende + "*" + (ende-anfang));
// Ein byteorientierter Stream vom Typ der Klasse
// BufferedOutputStream wird mit dem FileOutputStream gekettet
97
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
BufferedOutputStream bufferedOut =
new BufferedOutputStream(byteFileOut);
anfang = System.currentTimeMillis();
for(int i=0; i<100000; i++)
bufferedOut.write(i);
ende = System.currentTimeMillis();
System.out.println("Gepuffertes Schreiben: " +
anfang + "*" + "*" + ende + "*" + (ende-anfang));
// Ausgabe-Streams schliessen
bufferedOut.close();
// Ein byteorientierter Stream vom Typ der Klasse FileInputStream
// wird mit der vorher erstellten Datei verknüpft, um die
// Daten daraus einzeln einzulesen und am Bildschirm anzuzeigen
FileInputStream byteFileIn =
new FileInputStream("Binärdatei");
anfang = System.currentTimeMillis();
for(int i=0; i<100000; i++)
zeichen = byteFileIn.read();
ende = System.currentTimeMillis();
System.out.println("Nicht gepuffertes Einlesen: " +
anfang + "*" + "*" + ende + "*" + (ende-anfang));
// Gepufferter Lesevorgang
BufferedInputStream bufferedIn =
new BufferedInputStream(byteFileIn);
anfang = System.currentTimeMillis();
for(int i=0; i<100000; i++)
zeichen = bufferedIn.read();
ende = System.currentTimeMillis();
System.out.println("Gepuffertes Einlesen: " +
anfang + "*" + "*" + ende + "*" + (ende-anfang));
// Eingabe-Streams schliessen
bufferedIn.close();
}
catch(IOException e) {
e.getMessage();
}
}
}
Programmausgaben
Nicht gepuffertes Schreiben: ... *113
Gepuffertes Schreiben: ...*0
Nicht gepuffertes Lesen: ... *125
Gepuffertes Lesen: ...*0
98
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
Lösung 1.15
Die Klasse CharFileundBufferedStreams
import java.io.*;
public class CharFileundBufferedStreams {
public static void main(String args[]) {
char[] array = new char[100000];
int byteCode, zeichen;
long anfang, ende;
try {
// Ein zeichenorientierter Stream vom Typ der Klasse FileWriter
// wird mit einer Datei namens Textdatei verknüpft
FileWriter charFileOut =
new FileWriter("Textdatei");
anfang = System.currentTimeMillis();
for(int i=0; i<100000; i++)
charFileOut.write(i);
ende = System.currentTimeMillis();
System.out.println("Nicht gepuffertes Schreiben: " +
anfang + "*" + "*" + ende + "*" + (ende-anfang));
// Ein zeichenorientierter Stream vom Typ der Klasse
// BufferedOutputStream wird mit dem FileOutputStream verknüpft
BufferedWriter bufferedOut =
new BufferedWriter(charFileOut);
anfang = System.currentTimeMillis();
for(int i=0; i<100000; i++)
bufferedOut.write(i);
ende = System.currentTimeMillis();
System.out.println("Gepuffertes Schreiben: " +
anfang + "*" + "*" + ende + "*" + (ende-anfang));
// Ausgabe-Streams schliessen
bufferedOut.close();
// Ein zeichenorientierter Stream vom Typ der Klasse FileReader
// wird mit der vorher erstellten Datei verknüpft, um die Daten
// daraus einzeln einzulesen
FileReader charFileIn =
new FileReader("Textdatei");
anfang = System.currentTimeMillis();
for(int i=0; i<100000; i++)
zeichen = charFileIn.read();
ende = System.currentTimeMillis();
System.out.println("Nicht gepuffertes Lesen: " +
anfang+"*"+"*"+ende+"*"+(ende-anfang));
// Gepufferter Lesevorgang
BufferedReader bufferedIn =
new BufferedReader(charFileIn);
anfang = System.currentTimeMillis();
99
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
for(int i=0; i<100000; i++)
zeichen = bufferedIn.read();
ende = System.currentTimeMillis();
System.out.println("Gepuffertes Lesen: " +
anfang+"*"+"*"+ende+"*"+(ende-anfang));
bufferedIn.close();
}
catch(IOException e) {
e.getMessage();
}
}
}
Programmausgaben
Nicht gepuffertes Schreiben: ... *78
Gepuffertes Schreiben: ...*16
Nicht gepuffertes Lesen: ... *16
Gepuffertes Lesen: ...*0
Lösung 1.16
Die Klasse SelektierenvonTextZeilen
import java.io.*;
class SelektierenvonTextZeilen extends BufferedReader {
String buchstabe = new String("E ");
// Konstruktordefinition
public SelektierenvonTextZeilen(Reader in) {
// Der Konstruktor reicht den übergebenen Stream weiter an die
// Oberklasse
super(in);
}
// Die Methode readLine() der Oberklasse überschrieben und
// gleichzeitig mit super. Aufrufen, um die Daten zeilenweise zu
// lesen; es werden aber nur die Zeilen zurückgegeben, welche mit
// dem angegebenen String beginnen
public String readLine() throws IOException {
String zeile;
while(((zeile = super.readLine()) != null) &&
(!zeile.substring(0,2).equals(buchstabe)));
return zeile;
}
}
100
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
Die Klasse SelektierenvonTextZeilenTest
public class SelektierenvonTextZeilenTest {
public static void main(String args[]) {
try {
SelektierenvonTextZeilen bufferedIn = new
SelektierenvonTextZeilen(
new FileReader("Meldungsdatei"));
String zeile;
BufferedWriter bufferedOut =
new BufferedWriter((new FileWriter("Fehlerdatei")));
while((zeile = bufferedIn.readLine()) != null) {
bufferedOut.write(zeile, 0, zeile.length());
bufferedOut.newLine();
}
bufferedIn.close();
bufferedOut.close();
}
catch (Exception e) {
e.getMessage();
}
}
}
Programmausgaben
쐽
Anzeige der Meldungsdatei mit: type Meldungsdatei
E Datei nicht gefunden
E Ein-/Ausgabe-Fehler
I dies ist eine Meldungsdatei
E Falscher Dateieintrag
I Datei wurde bearbeitet
쐽
Anzeige der Fehlerdatei mit: type Fehlerdatei
E Datei nicht gefunden
E Ein-/Ausgabe-Fehler
E Falscher Dateieintrag
Lösung 1.17
Die Klasse DataOutputundInputStreams
import java.io.*;
public class DataOutputundInputStreams {
public static void main(String args[]) {
byte[] array = new byte[100];
101
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
byte einbyte = new Byte("10").byteValue();
char einchar = 'A';
int einint = 123;
short einshort = -1234;
long einlong = -1234;
float einfloat = 123.45F;
double eindouble = -123E3;
boolean einboolean = true;
String einString = "Binaere Datenstroeme";
String einUTFString = "Binaere Datenstroeme";
try {
System.out.println(einbyte + " " + einchar + " " + einint
+ " " + einshort + " " + einlong + " " + einfloat +" "
+ eindouble + " "+ einboolean);
System.out.println(einString);
Ein byteorientierter Stream vom Typ der Klasse
FileOutputStream wird mit einer Datei namens
DateimitprimitivenDatentypen verknüpft
FileOutputStream byteFileOut =
new FileOutputStream("DateimitprimitivenDatentypen");
und ein DataOutputStream wird mit einem
Stream vom Typ der Klasse BufferedOutputStream und mit diesem
FileOutputStream gekettet
DataOutputStream dataOut = new DataOutputStream(
new BufferedOutputStream(byteFileOut));
In die Datei schreiben
dataOut.writeByte(einbyte);
dataOut.writeChar(einchar);
dataOut.writeInt(einint);
dataOut.writeShort(einshort);
dataOut.writeLong(einlong);
dataOut.writeFloat(einfloat);
dataOut.writeDouble(eindouble);
dataOut.writeBoolean(einboolean);
Damit die Daten korrekt gelesen werden können, sollten
Strings auf gleiche Art und Weise geschrieben werden, entweder
mit
// dataOut.writeBytes(einString);
// dataOut.writeChars(einString);
oder mit
dataOut.writeUTF(einUTFString);
Ausgabe-Streams schliessen
dataOut.close();
Ein byteorientierter Stream vom Typ der Klasse FileInputStream
wird mit der vorher erstellten Datei verknüpft und
mit einem DataInputStream und einem BufferedInputStream
gekettet, um die geschriebenen primitiven Daten daraus
102
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
// einzulesen und am Bildschirm anzuzeigen
FileInputStream byteFileIn =
new FileInputStream("DateimitprimitivenDatentypen");
DataInputStream dataIn = new DataInputStream(
new BufferedInputStream(byteFileIn));
// Daten aus der Datei lesen
// Entweder alle Daten oder einzelne einlesen, ansonsten muss der
// Stream neu geöffnet werden
/* dataIn.readFully(array);
for(int i=0; i<100; i++)
System.out.println(array[i]);
System.out.println("Alle Daten: " + new String(array));*/
einbyte = dataIn.readByte();
einchar = dataIn.readChar();
einint = dataIn.readInt();
einshort = dataIn.readShort();
einlong = dataIn.readLong();
einfloat = dataIn.readFloat();
eindouble = dataIn.readDouble();
einboolean = dataIn.readBoolean();
System.out.println(einbyte + " " + einchar + " " + einint
+ " " + einshort + " " + einlong + " " + einfloat + " "
+ eindouble + " "+ einboolean);
// Lesen der mit writeChars() gespeicherten Strings
// einString = dataIn.readLine();
// System.out.println(einString);
// Lesen des UTF-String mit der dafür definierten Klassenmethode
System.out.println(
"!!" + DataInputStream.readUTF(dataIn));
// oder der überladenen Instanzmethode
// einUTFString = dataIn.readUTF();
// System.out.println("??" + einUTFString);
// Eingabe-Streams schliessen
dataIn.close();
}
catch(IOException e) {
e.getMessage();
}
}
}
Programmausgaben
10 A 123 -1234 -1234 123.45 -123000.0 true
Binaere Datenstroeme
10 A 123 -1234 -1234 123.45 -123000.0 true
!!Binaere Datenstroeme
103
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
Lösung 1.18
Die Klasse UmlauteSuchen
import java.io.*;
public class UmlauteSuchen {
public static void main(String args[]) {
String[] umlaute = {"ä", "ö", "ü", "ß", "Ä", "Ü", "Ö"};
String[] worte ={"Präfix", "Java", "Strom", "Ströme",
"Übung", "Größe"};
String buchstaben = "äöüßÄÖÜ";
String wort;
int index;
try {
// Ein byteorientierter Stream vom Typ der Klasse FileOutputStream
// wird mit einer Datei namens DateimitUmlauten verbunden
FileOutputStream byteFileOut =
new FileOutputStream("DateimitUmlauten");
// und ein DataOutputStream wird mit einem byteorientierten
// Stream vom Typ der Klasse BufferedOutputStream und mit diesem
// FileOutputStream gekettet
DataOutputStream dataOut = new DataOutputStream(
new BufferedOutputStream(byteFileOut));
// In die Datei schreiben
for(int i=0; i<worte.length; i++)
dataOut.writeUTF(worte[i]);
dataOut.close();
// Einen DataOutputStream für die Ausgabe auf die Konsole
// erzeugen
DataOutputStream byteOut1 =
new DataOutputStream(System.out);
System.out.println("Umlaute auf die Konsole schreiben");
// und alle im String buchstaben enthaltene Umlaute anzeigen
byteOut1.writeUTF(buchstaben);
System.out.println();
// Ein byteorientierter Stream vom Typ der Klasse FileInputStream
// wird mit der vorher erstellten Datei verknüpft
FileInputStream byteFileIn =
new FileInputStream("DateimitUmlauten");
DataInputStream dataIn =
new DataInputStream(
new BufferedInputStream(byteFileIn));
// Die UTFStrings aus der Datei lesen und auf Umlaute untersuchen
while((wort = dataIn.readUTF()) != null) {
for(int i=0; i<umlaute.length; i++) {
if((index = wort.indexOf(umlaute[i])) != -1)
// Alle Strings, die Umlaute enthalten am Bildschirm anzeigen
System.out.println("Im Wort " + wort +
104
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
" ist der Umlaut mit dem Unicode-Code-Point "
+ umlaute[i].codePointAt(0) + " enthalten");
}
}
dataIn.close();
}
catch (Exception e) {
e.getMessage();
}
}
}
Programmausgaben
Umlaute auf die Konsole schreiben
...
Im Wort Pr..fix ist der Umlaut mit dem Unicode-Code-Point 228 enthalten
Im Wort Str..me ist der Umlaut mit dem Unicode-Code-Point 246 enthalten
...
Hinweise zu den Programmausgaben
Mit der Verssion 6 von Java wurde ein neue Klasse Console implementiert, die
eine korrekte Anzeige von Umlauten am Bildschirm ermöglicht.
Lösung 1.19
Die Klasse ZahlenCharacterundStringFormatierung
import java.util.*;
public class ZahlenCharacterundStringFormatierung {
public static void main(String args[]) {
// Deklaration und Initialisierung von primitiven Datentypen
double[] array = new double[10];
byte einbyte = new Byte("10").byteValue();
char einchar = 'A';
int einint = 123;
short einshort = -1234;
long einlong = -1234;
float einfloat = 123.45F;
double eindouble = -123E3;
boolean einboolean = true;
String string1 = "Ausgabe Formatierungen";
String string2 = "Formatter";
for(int i=0; i<4; i++)
array[i] = -123.4567*i;
// Formatter-Instanz erzeugen und daran die Methode format() der
// Klasse aufrufen
105
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
Formatter forMatter = new Formatter();
// Explizite Indizierung
// String-Ausgabe mit dem Format-Spezifizierer %s (%S)
forMatter.format("%s mit %s %n", string1, string2);
// char-Ausgabe mit dem Format-Spezifizierer %C (%c)
forMatter.format("Primitive Datentypen %nchar: %C",
einchar);
// byte- und short-Werte als hexadezimale bzw. oktale Zahl
// mit den Format-Spezifizierer %x (%X) und %o anzeigen
forMatter.format(" byte: %x short: %o", einbyte, einshort);
// Mit dem Format-Spezifizierer %d kann eine beliebige
// Dezimalzahl angezeigt werden
forMatter.format(" int: %d long: %X float: %,+f", einint,
einlong, einfloat);
// %E steht für eine Anzeige von Gleitkommazahlen in
// wissenschaftlicher Schreibweise (mit Exponent)
forMatter.format(" double: %.10E boolean: %b %n", eindouble,
einboolean);
// Relative Indizierung
forMatter.format("Ein double-Array %n %4$+,3g %3$g %2$+,g"
+" %1$g", array[0], array[1], array[2], array[3]);
// Normale Indizierung
forMatter.format("%n %4$+,3g %3$g %<g %<g", array[0],
array[1], array[2], array[3]);
System.out.println(forMatter);
}
}
Programmausgaben
Ausgabeformatierungen mit Formatter
char: A byte: a short: 175456 int: 123 long: FFFFFFFFFFFFFB2E float:
+123,449997 double: -1.2300000000E+05 boolean: true
...
Lösung 1.20
Die Klasse SuchenundZerlegenvonZeichenketten
import java.util.*;
import java.util.regex.*;
public class SuchenundZerlegenvonZeichenketten {
public static void main(String args[]) {
// Deklaration und Initialisierung von primitiven Datentypen
Scanner scanner;
Pattern pa;
Matcher ma;
106
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
//
//
//
//
//
StringBuffer sb = new StringBuffer();
String s = new String();
String charString = "A a B b C c D d E e F f G g";
String intString = "1 2 3 4 5 6 7 8 9";
String doubleString = "1,2 34,5 678,910";
Scanner-Instanzen erzeugen und deren Inhalt zerlegen
scanner = new Scanner(charString);
System.out.println("Die im String enthaltenen Buchstaben "
+ " sind: ");
while(scanner.hasNext())
System.out.print(scanner.next() + " ");
System.out.println();
scanner = new Scanner(intString);
System.out.println("Die im String enthaltenen ganzen "
+ "Zahlen sind: ");
while(scanner.hasNextInt())
System.out.print(scanner.nextInt() + " ");
scanner = new Scanner(doubleString);
System.out.println();
System.out.println("Die im String enthaltenen Gleitpunkt-"
+ "Zahlen sind: ");
while(scanner.hasNextDouble())
System.out.print(scanner.nextDouble() + " ");
System.out.println();
Strings erweitern und zusammenfügen
charString = charString.concat("$");
intString = intString.concat("&");
s = charString + intString;
System.out.println("Zeichen suchen, ersetzen und "
+ "anfuegen:");
Suchen nach Buchstaben, Zahlen oder Sonderzeichen
scanner = new Scanner(s).useDelimiter("\\D+");
while(scanner.hasNextInt())
System.out.print(scanner.nextInt());
System.out.println();
scanner = new Scanner(s).useDelimiter("\\W+");
while(scanner.hasNext())
System.out.print(scanner.next());
System.out.println();
scanner = new Scanner(s).useDelimiter("\\p{javaUpperCase}");
while(scanner.hasNext())
System.out.print(scanner.next());
System.out.println();
Äquivalente Vorgehensweisen für Angabe des Suchmusters
// scanner = new Scanner(s).useDelimiter("\\$|\\&");
oder
107
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
pa = Pattern.compile("\\$|\\&");
scanner = new Scanner(s).useDelimiter(pa);
while(scanner.hasNext())
System.out.print(scanner.next());
System.out.println();
// Alle Buchstaben von A bis M und a bis m mit ! ersetzen
pa = Pattern.compile("[A-M]|[a-m]+");
ma = pa.matcher("PatternMatcherScanner");
String s1 = ma.replaceAll("!");
System.out.print(s1);
System.out.println();
// Den Buchstaben a und e ein ! nachstellen
pa = Pattern.compile("[ae]+");
ma = pa.matcher("PatternMatcherScanner");
while(ma.find())
ma.appendReplacement(sb, ma.group() + "!");
ma.appendTail(sb);
System.out.print(sb);
System.out.println();
}
}
Programmausgaben
Die im String enthaltenen Buchstaben sind:
A a B b C c D d E e F f G g
Die im String enthaltenen ganzen Zahlen sind:
1 2 3 4 5 6 7 8 9
Die im String enthaltenen Gleitkomma-Zahlen sind:
1.2 34.5 678.91
Zeichen suchen, ersetzen und anfügen:
123456789
AaBbCcDdEeFfGg123456789
a b c d e f g$123456789&
A a B b C c D d E e F f G g1 2 3 4 5 6 7 8 9
P!tt!rn!!t!rS!nn!r
Pa!tte!rnMa!tche!rSca!nne!r
Lösung 1.21
Die Klasse ZahlenMatrixmitprintln
import java.io.*;
import java.util.*;
public class ZahlenMatrixmitprintln {
public static void main(String args[]) {
int n = 5, m = 5;
108
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
double[][] matrix = new double[n][m];
// Formatter-Instanz erzeugen und für die Ausgabe die println()// Methode einfach am immer geöffneten OutputStream System.out,
// welcher standardmäßig der Konsole zugeordnet ist, aufrufen
Formatter forMatter = new Formatter();
for(int i=1; i<n; i++) {
for(int j=1; j<m; j++) {
matrix[i][j] = Math.pow(i,j);
forMatter.format(" %1d ** %2d = %3.3E", i, j,
matrix[i][j]);
}
}
// Äquivalente Vorgehensweisen für die Ausgabe mit der vorher
// definierten Formatierung
// PrintStream printStream = new PrintStream(System.out);
// printStream.println(forMatter);
// oder
System.out.println(forMatter);
}
}
Programmausgaben
1 ** 1 = 1.000E+00 1 ** 2 = 1.000E+00 1 ** 3 = 1.000E+00 ...
2 ** 1 = 2.000E+00 2 ** 2 = 4.000E+00 2 ** 3 =8.000E+00 ...
...
Lösung 1.22
Die Klasse ZahlenMatrixmitFormatter
import java.io.*;
import java.util.*;
public class ZahlenMatrixmitFormatter {
public static void main(String args[]) {
int n = 5, m = 5;
double[][] matrix = new double[n][m];
// In einen PrintStream kann auch mit einem Formatter-Objekt
// geschrieben werden
PrintStream printStream = new PrintStream(System.out);
// Formatter-Instanz erzeugen und eine PrintStream-Instanz im
// Konstruktor übergeben; für die Ausgabe muss keine print- oder
// write-Methode aufgerufen werden
Formatter forMatter = new Formatter(printStream);
// Selbstverständlich kann auch die System.out-Instanz übergeben
// werden
// Formatter forMatter = new Formatter(System.out);
for(int i=1; i<n; i++) {
109
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
for(int j=1; j<m; j++) {
matrix[i][j] = Math.pow(i,j);
forMatter.format(" %1d ** %2d = %3.3E", i, j,
matrix[i][j]);
}
}
}
}
Programmausgaben
1 ** 1 = 1.000E+00 1 ** 2 = 1.000E+00 1 ** 3 = 1.000E+00 ...
2 ** 1 = 2.000E+00 2 ** 2 = 4.000E+00 2 ** 3 =8.000E+00 ...
...
Lösung 1.23
Die Klasse ZahlenMatrixmitprintf
import java.io.*;
public class ZahlenMatrixmitprintf {
public static void main(String args[]) {
int n = 5, m = 5;
double[][] matrix = new double[n][m];
// PrintStream-Instanz erzeugen, um daran die Methode printf()
// der Klasse aufzurufen
PrintStream printStream = new PrintStream(System.out);
for(int i=1; i<n; i++) {
for(int j=1; j<m; j++) {
matrix[i][j] = Math.pow(i,j);
printStream.printf(" %1d ** %2d = %3.3E", i, j,
matrix[i][j]);
// Weil System.out eine Instanz der Klasse PrintStream ist, kann
// die printf()-Methode auch direkt an dieser aufgerufen werden
// System.out.printf(" %1d ** %2d = %3.3E", i, j,
// matrix[i][j]);
}
}
}
}
Programmausgaben
1 ** 1 = 1.000E+00 1 ** 2 = 1.000E+00 1 ** 3 = 1.000E+00 ...
2 ** 1 = 2.000E+00 2 ** 2 = 4.000E+00 2 ** 3 =8.000E+00 ...
...
110
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
Lösung 1.24
Die Klasse ZahlenMatrixmitPrintWriter
import java.io.*;
public class ZahlenMatrixmitPrintWriter {
public static void main(String args[]) {
int n = 5, m = 5;
double[][] matrix = new double[n][m];
// Ein PrintWriter-Objekt erzeugen und mit einer
// OutputStreamWriter-Instanz zwecks Konvertierung ketten; in
// diesem Fall kann auf die Brückenklasse verzichtet werden, weil
// die Umwandlung intern im Konstruktor der Klasse PrintWriter
// automatisch vorgenommen wird
// PrintWriter printStream = new PrintWriter(
// new OutputStreamWriter(System.out));
// Die einfachste Art eine PrintWriter-Instanz zu erzeugen
PrintWriter printStream = new PrintWriter(System.out);
// Die printf()-Methode aufrufen
for(int i=1; i<n; i++) {
for(int j=1; j<m; j++) {
matrix[i][j] = Math.pow(i,j);
printStream.printf(" %1d ** %2d = %3.3E", i, j,
matrix[i][j]);
}
}
// Ein PrintWriter-Stream muss im Gegensatz zu einem PrintStream
// geschlossen werden, damit eine Ausgabe erfolgen kann
printStream.close();
}
}
Programmausgaben
1 ** 1 = 1.000E+00 1 ** 2 = 1.000E+00 1 ** 3 = 1.000E+00 ...
2 ** 1 = 2.000E+00 2 ** 2 = 4.000E+00 2 ** 3 =8.000E+00 ...
...
Lösung 1.25
Die Klasse FileWriterundPrintWriter
import java.io.*;
public class FileWriterundPrintWriter {
public static void main(String args[]) {
int zeichen;
111
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
char[] array1 = {'J', 'a', 'v', 'a', '6', '\n'};
char[] array2 = new char[50];
Ein zeichenorientierter Stream der Klasse FileWriter wird mit
der Datei "Printdatei" verknüpft
try {
FileWriter charFileOut = new FileWriter("Printdatei");
Den FileWriter-Stream mit einem PrintWriter-Stream ketten
und mit den Methoden von beiden Streams in die Datei schreiben
PrintWriter printStream1 = new PrintWriter(charFileOut);
charFileOut.write(array1);
printStream1.println(array1);
charFileOut.write("FileWriter\nPrintWriter");
charFileOut.write('\n');
printStream1.println("FileWriter\nPrintWriter");
printStream1.printf("%C, %c, %c, %c, %c, %c", array1[0],
array1[1], array1[2], array1[3], array1[4], array1[4]);
printStream1.println();
Der äußere Stream wird geschloßen
printStream1.close();
Die erzeugte Datei lesen
FileReader charFileIn = new FileReader("Printdatei");
Ein PrintWriter-Objekt erzeugen und mit einer
OutputStreamWriter-Instanz für die Ausgabe zwecks
Konvertierung ketten
PrintWriter printStream2 = new PrintWriter(
new OutputStreamWriter(System.out));
Die Bildschirmausgabe zeigt, dass mit der write()-Methode, im
Unterschied zu println(), der String nicht mit dem ZeilenendeZeichen abgeschlossen wird
System.out.println("Geschriebene und gelesene "
+ "Zeichen: ");
while((zeichen = charFileIn.read(array2))!= -1) {
String s = new String(array2, 0, zeichen);
printStream2.printf("%s", s);
Ohne dass eine weitere PrintWriter-Instanz ereugt wird, kann
die Ausgabe über den Aufruf der printf()-Methode an System.out
erfolgen; mit dem Format-Spezifizierer %S werden
Großbuchstaben geschrieben
System.out.printf("%S", s);
}
printStream2.close();
Datei schliessen
charFileIn.close();
}
catch(FileNotFoundException e) {
System.out.println("Datei nicht gefunden");
112
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
}
catch(IOException e) {
System.out.println(e.getMessage());
}
}
}
Programmausgaben
JAVA6
JAVA6
FILEWRITER
PRINTWRITER
...
J, A, V, A, 6, 6
Java6
...
FileWriter
...
J, a, v, a, 6, 6
Lösung 1.26
Die Klasse PrintWriterundStringWriter
import java.io.*;
import java.util.*;
public class PrintWriterundStringWriter {
public static void main(String args[]) {
int einint = 123;
long einlong = -1234;
float einfloat = 123.45F;
double eindouble = -123E3;
boolean einboolean = true;
String string1 = "Ausgabe Formatierungen";
String string2 = "Java";
char[] array = {'J', 'a', 'v', 'a', '6', '\n'};
try {
// Ein PrintWriter-Objekt erzeugen und mit einer
// OutputStreamWriter-Instanz für formatierte Ausgaben von Daten
// zwecks Konvertierung ketten
PrintWriter printStream = new PrintWriter(
new OutputStreamWriter(System.out));
// Formatter-Instanz erzeugen und daran die Methode format() der
// Klasse aufrufen
Formatter forMatter = new Formatter();
try {
113
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
forMatter.format("double: %.10E boolean: %b %n",
eindouble, einint);
System.out.println(forMatter);
try {
// Falsche Anzahl Argumente angegeben
printStream.printf("Ein double-Array %n %4$+,3g"
+" %3$g %2$+,g" + " %1$g", array[0], array[1]);
}
catch(MissingFormatArgumentException e) {
e.printStackTrace();
traceStack(e);
}
// Falscher Format-Spezifizierer
printStream.printf("%f", string1, string2);
printStream.close();
}
catch(IllegalFormatConversionException e) {
e.printStackTrace();
traceStack(e);
}
// Falsche Charset-Folge "s" für die Konvertierung angegeben
Formatter forMatter1 = new Formatter("Formatierungsdatei",
"s");
forMatter.format(" int: %d long: %X float: %c", einint,
einfloat);
}
catch(FileNotFoundException e) {
e.printStackTrace();
traceStack(e);
}
catch(UnsupportedEncodingException e) {
e.printStackTrace();
traceStack(e);
}
}
// Methode, die aus dem StackTrace Fehler, Dateiname und die
// Zeilennummer, in welcher der Fehler aufgetreten ist, ermittelt
public static void traceStack(Exception e) {
String dateiName = "";
String fehler = "";
int zeilenNr = 0;
int index1 = 0;
int index2 = 0;
int index3 = 0;
// Ein StringWriter-Stream ist ein Ausgabe-Stream in einen String
StringWriter stringStream = new StringWriter();
// Diesen mit einem PrintWriter-Stream umwickeln, um rein zu
114
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
// schreiben
PrintWriter printStream = new PrintWriter(stringStream);
// Den StackTrace in den angelgten Stream ausgeben
e.printStackTrace(printStream);
// dessen Inhalt als String zurückholen und anzeigen
String s = stringStream.toString();
System.out.println("StackTrace = " + s);
// Den Index des 1. Doppelpunktes ermitteln
index1 = s.indexOf(':');
// Das Ende der Fehlermeldung ermitteln
index3 = s.indexOf("at ");
// Der String "at" darf nicht in einem Wort vorkommen (er muss
// dem Zeichen mit dem Unicode-Code-Point 9 folgen)
if((s.codePointAt(index3-1) != 9))
index3 = s.indexOf("at ", index3+1);
// Die Fehlermeldung lesen
fehler = s.substring(index1+1, index3);
fehler = fehler.trim();
// Den Index des 2. Doppelpunktes ermitteln
index1 = s.indexOf(':', index1+1);
// Das Ende der Zeilennummer suchen
index2 = s.indexOf(')', index1);
// und diese lesen
zeilenNr = Integer.parseInt(s.substring(index1+1, index2));
// Den Anfang des Dateinamens ermitteln
index2 = s.lastIndexOf('(', index1);
// und diesen lesen
dateiName = s.substring(index2+1, index1);
// Dateiname und die Nummer der Zeile, in welcher der Fehler
// aufgetreten ist, am Bildschirm anzeigen
System.out.println("Der Fehler: "+ fehler + " ist in "
+ " der Datei: " + dateiName + " in der Zeile: "
+ zeilenNr + " aufgetreten");
}
}
Programmausgaben
double: -1.2300000000E+05 boolean: true
java.util.MissingFormatArgumentException: ...
Der Fehler: Format specifier ‘+,4$3g’ ist in der Datei PrintWriterundStringWriter.java in der Zeile: 28 aufgetreten
...
java.util.IllegalFormatConversionException: ...
Der Feh5t
ler: f != java.lang.String ist in der Datei PrintWriterundStringWriter.java in der Zeile: 36 aufgetreten
...
115
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
java.util.UnsupportedEncodingException: ...
Der Fehler: Format specifier ‘+,4$3g’ ist in der Datei PrintWriterundStringWriter.java in der Zeile: 45 aufgetreten
...
Lösung 1.27
Die Klasse UngepufferteTastaturEingaben1
import java.io.IOException;
public class UngepufferteTastaturEingaben1 {
// Durch den Aufruf der read()-Methode an der System.in-Instanz
// können die Eingaben von der Tastatur gelesen werden
public static void main(String args[]) {
int byteCode;
// Einlesen von einzelnen Zeichen
System.out.println("Beliebige Zeichen ueber die " +
"Tastatur eingeben und Enter druecken: ");
try {
while((byteCode = System.in.read()) != -1)
System.out.println("ASCII-Key-Code und char-Wert des "
+ "von der Tastatur eingelesenen Zeichens: "
+ byteCode + "*" + (char)byteCode);
}
catch(IOException e) {
e.getMessage();
}
}
}
Programmausgaben
Beliebige Zeichen ueber die Tastatur eingeben und Enter druecken:
Wird das Wort »Java« über die Tastatur eingegeben, erfolgen die Ausgaben:
ASCII-Key-Code
ASCII-Key-Code
ASCII-Key-Code
ASCII-Key-Code
ASCII-Key-Code
ASCII-Key-Code
und
und
und
und
und
und
char-Wert
char-Wert
char-Wert
char-Wert
char-Wert
char-Wert
des
des
des
des
des
des
von
von
von
von
von
von
der
der
der
der
der
der
Tastatur
Tastatur
Tastatur
Tastatur
Tastatur
Tastatur
eingelesenen
eingelesenen
eingelesenen
eingelesenen
eingelesenen
eingelesenen
Zeichens: 74*J
Zeichens: 97*a
Zeichens: 118*v
Zeichen: 97*a
Zeichen: 13*
Zeichen: 10*
Hinweise zu den Programmausgaben
Die Zahlen 13 und 10 spezifizieren den Unicode-CodePoint für die Zeichen
‘\n’ und ‘\r’, welche unter Windows für das Zeilenende gesetzt werden.
116
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
Falls die Abfrage in der while-Schleife auf while((byteCode = System.in.read()) != ‘\n’) abgeändert wird, wird die Schleife nach der Betätigung der Enter-Taste verlassen, und die Unicode-Zahl für das Zeichen
‘\n’ wird nicht mehr angezeigt.
Lösung 1.28
Die Klasse StandardEingabeKanalUmlenken
import java.io.*;
public class StandardEingabeKanalUmlenken {
public static void main(String args[]) {
int byteCode;
try {
// Umlenken des Standard-Input-Kanals in die Datei "Eingabedatei"
FileInputStream byteFileIn =
new FileInputStream("Eingabedatei");
System.setIn(byteFileIn);
// Einlesen von einzelnen Zeichen aus der Datei
while((byteCode = System.in.read()) != -1)
// und diese zusammen mit der returnierten int-Zahl am
// Bildschirm anzeigen
System.out.println("int- und char-Wert des aus der "
+ " Datei: Eingabedatei eingelesenen Zeichens: "
+ byteCode + "*" + (char)byteCode);
}
catch(IOException e) {
e.getMessage();
}
}
}
Programmausgaben
Wird das Wort »Java« in die Datei »Eingabedatei« geschrieben, erfolgen die Ausgaben:
intintintint-
und char-Wert des aus der Datei: Eingabedatei eingelesenen Zeichens: 74*J
und char-Wert des aus der Datei: Eingabedatei eingelesenen Zeichens: 97*a
und char-Wert des aus der Datei eingelesenen Zeichens: 118*v
und char-Wert des aus der Datei eingelesenen Zeichens: 97*a
Lösung 1.29
Die Klasse UngepufferteTastaturEingaben2
import java.io.IOException;
public class UngepufferteTastaturEingaben2 {
// Durch den Aufruf der read(byte[] array)-Methode an der
117
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
// System.in-Instanz können die Eingaben von der Tastatur in ein
// byte-Array eingelesen werden
public static void main(String args[]) {
String s;
byte[] array = new byte[10];
int zeichen;
// Einlesen von Tastatureingeben in ein Array
System.out.println("Beliebige Zeichen ueber die " +
"Tastatur eingeben und Enter druecken: ");
try {
// Den Arrayelenmenten werden die von der Tastatur eingelesenen
// Daten zugewiesen und ihre Anzahl im Feld zeichen gespeichert
zeichen = System.in.read(array);
// Das Array in ein String-Objekt umsetzen
s = new String(array, 0, zeichen);
System.out.println("Von der Tastatur eingelesene "
+ "Zeichen: " + s);
}
catch(IOException e) {
e.getMessage();
}
}
}
Programmausgaben
Beliebige Zeichen ueber die Tastatur eingeben und Enter druecken:
Wird das Wort »Java« über die Tastatur eingegeben, erfolgt die Ausgabe:
Von der Tastatur eingelesene Zeichen: Java
Lösung 1.30
Die Klasse GepufferteTastaturEingaben
import java.io.*;
public class GepufferteTastaturEingaben {
public static void main(String args[]) {
// Einlesen von Tastatureingaben mithilfe eines BufferdReader// Streams
String s;
InputStreamReader tastaturIn =
new InputStreamReader(System.in);
BufferedReader bufferedIn = new BufferedReader(tastaturIn);
System.out.println("Beliebige Zeichen ueber die " +
"Tastatur eingeben und Enter druecken: ");
try {
118
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
// Die Methode readline() der Klasse BufferedReader aufrufen
s = bufferedIn.readLine();
System.out.println("Von der Tastatur eingelesene "
+ "Zeichen: " + s);
}
catch(IOException e) {
System.out.println(e.getMessage());
}
}
}
Programmausgaben
Beliebige Zeichen ueber die Tastatur eingeben und Enter druecken:
Wird das Wort »Java« über die Tastatur eingegeben, erfolgt die Ausgabe:
Von der Tastatur eingelesene Zeichen: Java
Lösung 1.31
Die Klasse StandardFehlerKanalUmlenken
import java.io.*;
public class StandardFehlerKanalUmlenken {
public static void main(String args[]) {
byte[] array = new byte[10];
int zeichen;
FileInputStream byteFileIn = null;
try {
// Ist die Ausgabedatei noch nicht vorhanden, wird eine
// FileNotFoundException ausgelöst, die mit System.err
// auf die Konsole geschrieben wird
byteFileIn = new FileInputStream("Ausgabedatei");
}
catch(FileNotFoundException e) {
// Die Ausgabe von Fehlermeldungen an den Fehlerkanal reichen,
// der unter Windows standardmäßig der Konsole zugeordnet ist
e.printStackTrace(System.err);
System.err.println("Es wurde eine FileNotFoundException "
+ "ausgeloest");
}
try {
// Umlenken des Standard-Error-Kanals in eine Datei
System.setErr(new PrintStream("StdErrDatei"));
// An der Instanz, auf welche die FileInputStream-Referenz verweist,
// die read()-Methode aufrufen, um mit einer null-Referenz
// einen weiteren Fehler zu erzwingen
119
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
byteFileIn.read(array);
}
catch(NullPointerException e) {
// Fehlermeldungen in die Datei "StdErrDatei" schreiben
e.printStackTrace(System.err);
System.err.println("Es wurde eine NullPointerException "
+ "ausgeloest");
}
catch(IOException e) {
// Fehlermeldungen in die Datei "StdErrDatei" schreiben
e.printStackTrace(System.err);
System.err.println("Es wurde eine IOException "
+ "ausgeloest");
}
try {
// Ist die Ausgabedatei noch nicht vorhanden wird diese hiermit
// angelegt
FileOutputStream byteFileOut =
new FileOutputStream("Ausgabedatei");
// Einlesen von Tastatureingeben in ein Array
System.out.println("Beliebige Zeichen ueber die " +
"Tastatur eingeben und Enter druecken: ");
// Den Arrayelenmenten werden die von der Tastatur eingelesenen
// Daten zugewiesen und ihre Anzahl im Feld zeichen gespeichert
zeichen = System.in.read(array);
// Ausgabe-Stream vom Typ ByteArrayOutputStream erzeugen
ByteArrayOutputStream byteArrayOut =
new ByteArrayOutputStream();
// und die vom Standard-Eingabe-Kanal gelesenen Zeichen
// reinschreiben
byteArrayOut.write(array);
// Ein ByteArrayOutputStream kann mit seiner Methode toString(),
// für eine Bildschirmanzeige, in einen String konvertiert werden
String s = byteArrayOut.toString();
System.out.printf("ByteArray-Stream in String "
+ "umgesetzt: %s", s);
// Den Inhalt des ByteArrayOutputStream in einen anderen
// Ausgabe-Stream schreiben
byteArrayOut.writeTo(byteFileOut);
byteFileOut.close();
}
catch(IOException e) {
e.printStackTrace(System.err);
System.err.println("Datei nicht vorhanden!");
}
}
}
120
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
Programmausgaben
Beliebige Zeichen ueber die Tastatur eingeben und Enter druecken:
java.io.FileNotFoundException: Ausgabedatei ...
Es wurde eine FileNotFoundException ausgeloest
ByteArray-Stream in String umgesetzt: Java
쐽
Ausgabe der Fehlerdatei mit: type StdErrDatei
java.lang.NullPointerException
at StandardFehlerKanalUmlenken.main...
Es wurde eine NullPointerException ausgeloest
쐽
Ausgabe der Ausgabedatei mit: type Ausgabedatei:
Java
Lösung 1.32
Die Klasse ScannerfuerStringsundStreams
import java.io.*;
import java.util.*;
import java.util.regex.*;
public class ScannerfuerStringsundStreams {
public static void main(String args[]) {
String zeile1 = "1 2 3 4 5";
String zeile2 = "1,2 34,5 678,910 745,11 23,45";
String zeile3 = "A B C D E";
String s;
int i = 1, j = 1, k = 1;
// Ein zeichenorientierter Stream der Klasse FileWriter wird mit
// der Datei "Scannerdatei" verknüpft
try {
FileWriter charFileOut = new FileWriter("Scannerdatei");
// Den FileWriter-Stream mit einem PrintWriter-Stream ketten
// und die vom Programm definierten Strings in die Datei schreiben
PrintWriter printStream = new PrintWriter(charFileOut);
printStream.println(zeile1);
// Zeilenende schreiben
printStream.println();
printStream.println(zeile2);
printStream.println();
printStream.println(zeile3);
printStream.println();
// Der äußere Stream wird geschloßen
printStream.close();
// Die so erzeugte Datei mithilfe einer Scanner-Instanz lesen
121
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
FileReader charFileIn = new FileReader("Scannerdatei");
Scanner scStream = new Scanner(charFileIn);
while(scStream.hasNextLine()) {
// Die gelesene Zeile in einen String speichern
s = scStream.nextLine();
System.out.println(s);
System.out.println();
// Einer zweiten Scanner-Instanz wird im Konstruktor die
// String-Referenz übergeben
Scanner scString = new Scanner(s);
// Die Methoden der Klasse Scanner für das Zerlegen der
// Zeilen aus der Datei aufrufen
while(scString.hasNextInt()){
System.out.print(("Zahl" + i + " = ")
+ scString.nextInt() + " ");
i++;
}
while(scString.hasNextDouble()) {
System.out.print(("Zahl" + j + " = ")
+ scString.nextDouble()+" ");
j++;
}
while(scString.hasNext()) {
System.out.print(("Buchstabe" + k + " = ")
+ scString.next() +" ");
k++;
}
}
// Der äußere Stream wird geschloßen
scStream.close();
}
catch(FileNotFoundException e) {
System.out.println("Datei nicht gefunden");
}
catch(IOException e) {
System.out.println(e.getMessage());
}
}
}
Programmausgaben
1 2 3 4 5
Zahl1 = 1 Zahl2 = 2 Zahl3 = 3 Zahl4 = 4 Zahl5 = 5
1,2 34,5 678,910 745,11 23,45
Zahl1 = 1.2 Zahl2 = 34.5 Zahl3 = 678.910 Zahl4 = 745.11 Zahl5 = 23.45
A B C D E
Buchstabe1 = A Buchstabe2 = B Buchstabe3 = C Buchstabe4 = D Buchstabe5 = E
122
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
Lösung 1.33
Die Klasse GescannteTastaturEingaben
import java.util.*;
public class GescannteTastaturEingaben {
public static void main(String args[]) {
// Einlesen von Tastatureingaben mithilfe der Klasse Scanner
String s;
Scanner tastaturIn =
new Scanner(System.in);
System.out.println("Zeichenfolgen ueber die " +
"Tastatur eingeben und Enter druecken: ");
// Die Methode nextline() der Klasse Scanner aufrufen
while(tastaturIn.hasNextLine()) {
s = tastaturIn.nextLine();
if(s.equals("Ende"))
break;
System.out.println("Von der Tastatur eingelesene "
+ "Zeichen: " + s);
}
}
}
Programmausgaben
Zeichenfolgen ueber die Tastatur eingeben und Enter druecken:
Wird das Wort »Java« über die Tastatur eingegeben, erfolgt die Ausgabe:
Von der Tastatur eingelesene Zeichen: Java
Lösung 1.34
Die Klasse AusgabenmitderKlasseConsole
import java.io.*;
public class AusgabenmitderKlasseConsole {
public static void main(String args[]) {
// Instanz der Klasse Console erzeugen
Console konsole = System.console();
String umlaute = "öäüßÖÜÄ";
// Bildschirmausgabe mithilfe von Consolekonsole.printf("Ausgabe mit der Console-Instanz: %s%n",
umlaute);
123
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
// und System.out-Instanzen
System.out.printf("Ausgabe mit der System.out-Instanz: "
+ "%s%n", umlaute);
}
}
Programmausgaben
Ausgabe mit der Console-Instanz: äöüßÄÖÜ
Ausgabe mit der System.out-Instanz: ...
Lösung 1.35
Die Klasse EingabenmitderKlasseConsole
import java.io.*;
public class EingabenmitderKlasseConsole {
public static void main(String args[]) {
Console konsole = System.console();
String string;
// Eine BufferedReader-Instanz mithilfe der Brückenklasse
// InputStreamReader mit der System.in-Instanz ketten
InputStreamReader tastaturIn =
new InputStreamReader(System.in);
BufferedReader bufferedIn = new BufferedReader(tastaturIn);
konsole.printf("Beliebige Zeichen ueber die " +
"Tastatur eingeben und Enter druecken: ");
// Die Methode readline() der Klasse Console aufrufen
string = konsole.readLine();
konsole.printf("Von der Tastatur eingelesene Zeichen: %s%n",
string);
System.out.printf("Von der Tastatur eingelesene Zeichen: "
+ "%s%n", string);
System.out.printf("Beliebige Zeichen ueber die " +
"Tastatur eingeben und Enter druecken: ");
// Die Methode readline() der Klasse BufferedReader aufrufen
try {
string = bufferedIn.readLine();
}
catch(IOException e) {
System.out.printf("%n%s", e.getMessage());
}
System.out.printf("Von der Tastatur eingelesene Zeichen: "
+ "%s%n", string);
124
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
konsole.printf("Von der Tastatur eingelesene Zeichen: %s%n",
string);
}
}
Programmausgaben
Beliebige Zeichen über die Teastatur eingeben und Enter drücken: äöüßÄÖÜ
Von der Tastatur eingelesene Zeichen: äöüßÄÖÜ
Von der Tastatur eingelesene Zeichen: ...
Beliebige Zeichen über die Teastatur eingeben und Enter drücken: äöüßÄÖÜ
Von der Tastatur eingelesene Zeichen: äö?ßÄÖÜ
Von der Tastatur eingelesene Zeichen: ...
Lösung 1.36
Die Klasse Punkt
import java.io.*;
public class Punkt implements Serializable {
private double x;
private double y;
// Konstruktordefinition
Punkt(double x, double y) {
this.x = x;
this.y = y;
}
// Zugriffsmethoden
public void setX(double x) {
this.x = x;
}
public void setY(double y) {
this.y = y;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
// Instanzmethode für eine Punktanzeige
public void anzeige() {
System.out.println("Punktkoordinaten: ("+ x +","+ y +")");
}
}
125
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
Die Klasse ObjectStreams
import java.io.*;
public class ObjectStreams {
public static void main(String args[]) {
Punkt originalPunkt = new Punkt(1.2, 3.4);
Punkt serialPunkt;
// Ein byteorientierter FileOuputStream wird mit einem
// ObjectOutputStream gekettet
try {
FileOutputStream byteFileOut =
new FileOutputStream("Objektdatei");
ObjectOutputStream objectOut =
new ObjectOutputStream(byteFileOut);
// Objekt serialisieren, d.h. der writeObject()-Methode übergeben
objectOut.writeObject(originalPunkt);
// Den äußeren Stream schließen
objectOut.close();
// FileInputStream mit der vorher erzeugten Datei verknüpfen
// und mit einem ObjectInputStream ketten
FileInputStream bytefileIn =
new FileInputStream("Punktdatei");
ObjectInputStream objectIn =
new ObjectInputStream(bytefileIn);
// Der Rückgabewert der readObject()-Methode wird in ein Objekt
// vom Typ der Klasse Punkt gecastet
serialPunkt = (Punkt)objectIn.readObject();
// Den äußeren Stream schließen
objectIn.close();
// Am originalen und deserialisierten Objekt die Methode anzeige()
// der Klasse Punkt aufrufen
originalPunkt.anzeige();
serialPunkt.anzeige();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
Programmausgaben
Punktkoordinaten: (1.2,3.4)
Punktkoordinaten: (1.2,3.4)
126
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
Lösung 1.37
Die Klasse Kreis
import java.io.*;
public class Kreis implements Serializable {
// Globale Referenzen vom Typ der Klassen Punkt und String
private Punkt p;
private String s;
private double r;
// Dieses Instanzfeld soll nicht mit serialisiert werden
private transient int instanzZaehler;
// Klassenfelder werden nicht serialisiert
private static int klassenZaehler;
// Konstruktordefinition
public Kreis(Punkt p, double r, String s) {
this.p = p;
this.r = r;
this.s = s;
instanzZaehler++;
klassenZaehler++;
}
// Zugriffsmethoden
public void setR(double r) {
this.r = r;
}
public double getR() {
return r;
}
public void setP(Punkt p) {
this.p = p;
}
public Punkt getP() {
return p;
}
public void setS(String s) {
this.s = s;
}
public String getS() {
return s;
}
// Überschreiben der toString()-Methode der Klasse Object
public String toString() {
System.out.println("Instanzzaehler: " + instanzZaehler);
System.out.println("Klassenzaehler: " + klassenZaehler);
// Aufruf der Methode anzeige() der Klasse Punkt
p.anzeige();
127
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
// Die Kreisgleichung als String returnieren
return "Kreisgleichung: (x - " +p.getX()+")**2 + "+
"(y - " +p.getY()+")**2 "+" - "+r*r+" = 0";
}
}
Die Klasse ErweiterteSerialisierung
import java.io.*;
public class ErweiterteSerialisierung {
public static void main(String args[]) {
Kreis originalKreis = new Kreis(new Punkt(1.2, 3.4), 1.5,
"Serialisierung von Instanzen, deren Klassen "
+ "Referenzfelder definieren");
// Ein byteorientierter FileOuputStream wird mit einem
// ObjectOutputStream gekettet
try {
FileOutputStream byteFileOut =
new FileOutputStream("Kreisdatei");
ObjectOutputStream objectOut =
new ObjectOutputStream(byteFileOut);
// Objekt der Klasse Kreis serialisieren, d.h. der writeObject()// Methode übergeben
objectOut.writeObject(originalKreis);
// Den äußeren Stream schließen
objectOut.close();
// FileInputStream mit der vorher geschriebenen Datei verknüpfen
// und mit einem ObjectInputStream ketten
FileInputStream bytefileIn =
new FileInputStream("Kreisdatei");
ObjectInputStream objectIn =
new ObjectInputStream(bytefileIn);
// Der Rückgabewert der readObject()-Methode wird in ein Objekt
// vom Typ der Klasse Kreis gecastet
Kreis serialKreis = (Kreis)objectIn.readObject();
// Den äußeren Stream schließen
objectIn.close();
// Am originalen und deserialisierten Objekt die Methoden
// der Klasse Kreis aufrufen
System.out.println("Werte der Instanzfelder vor der "
+ "Serialisierung:");
System.out.println(originalKreis.getS());
System.out.println(originalKreis.toString());
System.out.println("Werte der Instanzfelder nach der "
+ "Serialisierung:");
System.out.println(serialKreis.getS());
System.out.println(serialKreis.toString());
128
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
}
catch (Exception e) {
e.printStackTrace();
}
}
}
Programmausgaben
Werte der Instanzfelder vor der Serialisierung:
Serialisierung von Instanzen, deren Klassen Referenzfelder definieren
Instanzzaehler: 1
Klassenzaehler: 1
Punktkoordinaten: (1.2,3.4)
Kreisgleichung: (x - 1.2)**2 + (y - 3.4)**2 -2.25 = 0
Werte derInstanzfelder nach der Serialisierung:
Serialisierung von Instanzen, deren Klassen Referenzfelder definieren
Instanzzaehler: 0
Klassenzaehler: 1
Punktkoordinaten: (1.2,3.4)
Kreisgleichung: (x - 1.2)**2 + (y - 3.4)**2 -2.25 = 0
Lösung 1.38
Die Klasse Schule
class Schule {
// Die Enumeration NotenListe
enum NotenListe {
eins(1), zwei(2), drei(3), vier(4), fuenf(5);
private int i;
// Konstruktordefinition
NotenListe(int i) {
this.i = i;
}
// Zugriffsmethoden
public int getNote() {
return i;
}
public void setNote(int i) {
this.i = i;
}
}
// Die Enumeration SchuelerListe
enum SchuelerListe {
Mueller, Mayer, Schmidt
}
129
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
// Die Enumeration FachListe
enum FachListe {
Mathematik, Deutsch, Englisch, Physik, Sport
}
}
Die Klasse SchulemitVersionUID
import java.io.*;
public class SchulemitVersionUID implements Serializable {
public String name;
public String fach;
public int note;
// Zum Testen der Versionsnummer-Vergabe beim Serialisieren
// wurde der Feldtyp abgeändert
public int testVersionUID;
// privat static final long serialVersionUID =
// 1774228193752720304;
// Konstruktordefinition
public SchulemitVersionUID(String name, String fach,
int note) {
this.name = name;
this.fach = fach;
this.note = note;
}
// Prüfen, ob die im Konstruktor übergebenen Werte unter den
// Aufzählungskonstanten der Enumerationen zu finden sind
public void pruefen() {
for(Schule.SchuelerListe schueler: Schule.SchuelerListe.
values()) {
if(schueler.name().equals(name))
System.out.println("Der Schueler: "+ schueler
+ " ist an dieser Schule ");
}
for(Schule.FachListe fach: Schule.FachListe.values()) {
if(fach.name().equals(this.fach))
System.out.println("Das Fach: "+ fach
+ " wird an dieser Schule unterrichtet");
}
for(Schule.NotenListe note: Schule.NotenListe.values()) {
if(note.getNote() == this.note)
System.out.println("Die Note: "+ note
+ " ist zugelassen ");
}
}
}
130
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
Die Klasse SerialisierungTest
import java.io.*;
public class SerialisierungTest {
private static SchulemitVersionUID originalSchule[] = new
SchulemitVersionUID[2];
private static SchulemitVersionUID serialSchule[] = new
SchulemitVersionUID[2];
public static void main(String args[]) {
originalSchule[0] =
new SchulemitVersionUID("Mayer", "Deutsch", 2);
originalSchule[1] =
new SchulemitVersionUID("Mueller", "Mathematik", 6);
// Die Aufzählungskonstanten der Enumerationen anzeigen
System.out.println("Aufzaehlungskonstanten der "
+ "Enumerationen: ");
for(Schule.SchuelerListe schueler:
Schule.SchuelerListe.values())
System.out.print(schueler + " ");
System.out.println();
for(Schule.FachListe fach: Schule.FachListe.values())
System.out.print(fach + " ");
System.out.println();
for(Schule.NotenListe note: Schule.NotenListe.values())
System.out.print(note + " ");
System.out.println();
// writeObject();
readObject();
}
// Objekt serialisieren
public static void writeObject() {
try {
// Ein byteorientierter FileOuputStream wird mit einem
// ObjectOutputStream gekettet
FileOutputStream byteFileOut =
new FileOutputStream("Schuldatei.ser");
ObjectOutputStream objectOut =
new ObjectOutputStream(byteFileOut);
// Objekte serialisieren und am originalen Objekt die Methode
// pruefen() der Klasse SchulemitVersionUID aufrufen
System.out.println("Serialisierte Werte:");
for(int i=0; i<2; i++) {
objectOut.writeObject(originalSchule[i]);
originalSchule[i].pruefen();
}
131
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
// SerialVersionUID der Klasse SchulemitVersionUID anzeigen
System.out.println("SerialVersionUID: " +
ObjectStreamClass.lookup(originalSchule.getClass()).
getSerialVersionUID());
// Den äußeren Stream schließen
objectOut.close();
}
catch(IOException e) {
e.printStackTrace();
}
}
// Objekte deserialisieren
public static void readObject() {
try {
// FileInputStream mit der vorher erzeugten Datei verknüpfen
// und mit einem ObjectInputStream ketten
FileInputStream bytefileIn =
new FileInputStream("Schuldatei.ser");
ObjectInputStream objectIn =
new ObjectInputStream(bytefileIn);
// Der Rückgabewert der readObject()-Methode wird in ein Objekt
// vom Typ der Klasse Schule gecastet
for(int i=0; i<2; i++) {
serialSchule[i] = (SchulemitVersionUID)objectIn.
readObject();
// Am deserialisierten Objekt die Methode pruefen() der Klasse
// SchulemitVersionUID aufrufen
System.out.println("Deserialisierte Werte:");
serialSchule[i].pruefen();
}
// SerialVersionUID der Klasse SchulemitVersionUID anzeigen
System.out.println("SerialVersionUID: " +
ObjectStreamClass.lookup(serialSchule.getClass()).
getSerialVersionUID());
// Den äußeren Stream schließen
objectIn.close();
}
catch(ClassNotFoundException e) {
e.printStackTrace();
}
catch(IOException e) {
e.printStackTrace();
}
}
}
132
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
Programmausgaben
Aufzählungskonstanten der Enumerationen:
Mueller Mayer Schmidt
...
Serialisierte Werte:
Der Schueler: Mayer ist an dieser Schule
Das Fach: Deutsch wird an dieser Schule unterrichtet
Die Note: zwei ist zugelassen
Deserialisierte Werte:
Der Schueler: Mayer ist an dieser Schule
Das Fach: Deutsch wird an dieser Schule unterrichtet
Die Note: zwei ist zugelassen
Ausgaben nach Änderungen eines Feldtyps und Auskommentieren der writeObject()-Methode:
Java.io.InvalidClassException: SchuelermitVersionUID; local class incompatible: stream classdesc serialVersionUID = - 895241860567637238, local
class serialVersionUID =
1774228193752720304 ...
Lösung 1.39
Die Klasse RandomAccessDatei
import java.io.*;
public class RandomAccessDatei {
private static RandomAccessFile raFile;
private static long anzahl;
public static void main(String argFile[]) {
// Im Konstruktor der Klasse File wird der Pfadname der
// Datei als String-Referenz übergeben
// File file = new File("C:/EJ_Uebungsbuch1/
// RandomAccessDatei");
// Anschließend muss die Methode createNewFile() aufgerufen
// werden, welche die Datei erzeugt
// f.createNewFile();
// Instanz der Klasse RandomAccessFile erzeugen
// raFile = new RandomAccessFile(file, "rw");
// Diese Vorgehensweise kann vereinfacht werden; die Datei mit
// dem Namen RandomAccessDatei wird im aktuellen Verzeichnis
// angelegt
try {
raFile = new RandomAccessFile("RandomAccessDatei", "rw");
// Mehrere Zeilen in die Datei schreiben
for(int i=0; i<5; i++) {
raFile.writeBytes("ByteZeile" + i+"\n");
raFile.writeChars("CharZeile" + i+"\n");
133
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
raFile.writeUTF("UTFZeile" + i+"\n");
}
// Die Größe der Datei und die Zeigerposition ermitteln
anzahl = raFile.length();
// und anzeigen
System.out.println("Dateigroesse: " + anzahl +
" und Zeigerposition nach dem Schreiben: " +
raFile.getFilePointer());
// Den Dateizeiger an den Dateianfang positionieren
raFile.seek(0);
lesenDatei();
// Anstelle des Textes "ByteZeile" wird über absolute oder
// relative Zeigerpositionierung der Text "NeueZeile" geschrieben
raFile.seek(0);
System.out.print("Die Dateizeigerposition fuer das "
+ " Schreiben des neuen Textes: ");
System.out.print(raFile.getFilePointer() + " ");
raFile.writeBytes("NeueZeile");
// Absolute Positionierung
/* raFile.seek(45);
raFile.writeBytes("NeueZeile");
raFile.seek(90);
raFile.writeBytes("NeueZeile");
raFile.seek(135);
raFile.writeBytes("NeueZeile");
raFile.seek(180);
raFile.writeBytes("NeueZeile"); */
// oder relative Positionierung
for(int i=0; i<3; i++) {
raFile.skipBytes(36);
System.out.print(raFile.getFilePointer() + " ");
raFile.writeBytes("NeueZeile");
}
System.out.println();
raFile.seek(0);
lesenDatei();
raFile.close();
}
catch(IOException e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
}
public static void lesenDatei() throws IOException {
// Solange das Dateiende nicht erreicht wurde
while(raFile.getFilePointer() < anzahl) {
// die Textzeilen nacheinander lesen
134
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
String zeile1 = raFile.readLine();
String zeile2 = raFile.readLine();
String zeile3 = raFile.readUTF();
// und anzeigen
System.out.println(zeile1+zeile2+zeile3);
}
}
}
Programmausgaben
Dateigroesse: 225 und Zeigerposition nach dem Schreiben: 225
ByteZeile0 CharZeile0 UTFZeile0
ByteZeile1 CharZeile1 UTFZeile1
...
Die Dateizeigerposition fuer das Schreiben des neuen Textes: 0 45 90 135
NeueZeile0 CharZeile0 UTFZeile0
NeueZeile1 CharZeile1 UTFZeile1
...
Lösung 1.40
Die Klasse DateiundVerzeichnisVerwaltungmitJava7
import java.util.*;
import java.io.*;
import java.nio.file.*;
import java.nio.charset.*;
import java.nio.file.attribute.*;
import java.net.*;
public class DateiundVerzeichnisVerwaltungmitJava7 {
public static void main(String argFile[]) {
File[] f = new File[7];
Path[] p = new Path[9];
try {
// Dateiverzeichnisse und Dateien mit den neuen Möglichkeiten
// von Java 7 erzeugen: File-Objekte über die unterschiedlichen
// Konstruktoren erzeugen und daran die static-Methode toPath()
// der Klasse Files aufrufen, um dieses in ein Path-Objekt
// umzusetzen; im Konstruktor der Klasse File wird ein String
// als Verzeichnisname übergeben
f[0] = new File("C:/EJ_Uebungsbuch1/");
p[0] = f[0].toPath();
// Danach die Methode createDirectory() der Klasse Files
// aufrufen, um das Verzeichnis zu erzeugen, falls es noch nicht
// existiert; diese Vorgehensweise ermöglicht, vorhandene Programme
135
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
umzuschreiben, um die neuen Methoden der Klasse Files beim
Setzen und Lesen von Dateiattributen bzw. in Reaktionen,
wenn Fehler beim Bearbeiten von Dateien auftreten, zu nutzen;
die Methode createDirectory() wirft eine
FileAlreadyExistsException, falls ein Verzeichnis bzw. eine
Datei schon existiert
if(Files.notExists(p[0], new LinkOption[]{LinkOption.
NOFOLLOW_LINKS})) {
Files.createDirectory(p[0]);
}
Alternativ kann in neuen Programmen ein Path-Objekt mit der
Methode getPath() ermittelt werden (dabei muss das angegebene
Verzeichnis nicht existieren) und anschließend die Methode
createDirectory() aufgerufen werden
p[1] = FileSystems.getDefault().getPath(
"C:/EJ_Uebungsbuch2/");
if(Files.notExists(p[1], new LinkOption[]{LinkOption.
NOFOLLOW_LINKS})) {
Files.createDirectory(p[1]);
}
Im Konstruktor der Klasse File werden das Verzeichnis und der
Dateiname als String-Referenz übergeben und danach die
Methoden toPath() und createFile() (falls die Datei noch nicht
existiert) aufgerufen; das Dateiverzeichnis muss wie hier schon
vorhanden sein oder wie in den nachfolgenden Beispielen, bevor
die Datei erstellt wird, angelegt werden
f[2] = new File("C:/EJ_Uebungsbuch1/", "Datei1");
p[2] = f[2].toPath();
if(Files.notExists(p[2], new LinkOption[]{LinkOption.
NOFOLLOW_LINKS})) {
Files.createFile(p[2]);
}
Alternativ kann in neuen Programmen ein Path-Objekt mit der
Methode getPath() ermittelt werden (dabei muss das angegebene
Verzeichnis nicht existieren) und anschließend die Methoden
createDirectory() und createFile() aufgerufen werden;
existiert das Directory schon, wird eine
FileAlreadyExistsException geworfen
p[3] = FileSystems.getDefault().getPath(
"C:/EJ_Uebungsbuch3/");
if(Files.notExists(p[3], new LinkOption[]{LinkOption.
NOFOLLOW_LINKS})) {
Files.createDirectory(p[3]);
}
Bei diesem Methodenaufruf muss das Directory existieren;
existiert die Datei, wird eine FileAlreadyExistsException
geworfen
if(Files.notExists(FileSystems.getDefault().getPath(
136
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
"C:/EJ_Uebungsbuch3/Datei2"),
new LinkOption[]{LinkOption.NOFOLLOW_LINKS})) {
Files.createFile(FileSystems.getDefault().getPath(
"C:/EJ_Uebungsbuch3/Datei2"));
}
// Im Konstruktor der Klasse File wird ein abstrakter Pfadname
// als File-Referenz übergeben
f[3] = new File("C:/EJ_Uebungsbuch4/NIO2");
f[4] = new File(f[3], "Datei3");
p[4] = f[3].toPath();
// Erst nachdem das Verzeichnis angelegt wurde, kann auch die
// Datei erstellt werden; existiert das Directory schon,
// wird eine FileAlreadyExistsException geworfen
if(Files.notExists(FileSystems.getDefault().getPath(
"C:/EJ_Uebungsbuch4/NIO2/"), new LinkOption[]
{LinkOption.NOFOLLOW_LINKS})) {
Files.createDirectory(p[4]);
}
// Existiert die Datei, wird ebenfalls eine
// FileAlreadyExistsException geworfen
if(Files.notExists(FileSystems.getDefault().getPath(
"C:/EJ_Uebungsbuch4/NIO2/Datei3"), new LinkOption[]
{LinkOption.NOFOLLOW_LINKS})) {
Files.createFile(f[4].toPath());
}
// Absolute Pfadnamen können auch in Teile zerlegt angegeben
// werden
p[5] = Paths.get("C:","EJ_Uebungsbuch4","NIO2",
"Datei4");
// Existiert die Datei, wird eine FileAlreadyExistsException
// geworfen
if(Files.notExists(FileSystems.getDefault().getPath(
"C:/EJ_Uebungsbuch4/NIO2/Datei4"), new LinkOption[]
{LinkOption.NOFOLLOW_LINKS})) {
Files.createFile(p[5]);
}
// Der Dateiname kann auch relativ zum aktuellen Verzeichnis
// angegeben werden
p[6] = FileSystems.getDefault().getPath("Binärdatei");
if(Files.notExists(FileSystems.getDefault().getPath(
"Binärdatei"), new LinkOption[]
{LinkOption.NOFOLLOW_LINKS})) {
Files.createFile(p[6]);
}
// Außerdem können Shortcuts benutzt werden: Wird in das
// übergeordnete Verzeichnis von NIO2 verzweigt, wird da eine
// Datei1 gefunden; diese Path-Art kann benutzt werden, um
// redundante Fälle auszuschließen, indem die Methode
137
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
// Path.normalize() aufgerufen wird, die jedes redundante Element
// inklusive denen, die in "." oder "verzeichnisname/.."
// vorkommen, löscht
p[7] = Paths.get("C:/EJ_Uebungsbuch1/NIO2/../Datei1").
normalize();
// Oder der Pfadnamen kann mit einer URI erzeugt werden
p[8] = Paths.get(URI.create(
"file:///C:/EJ_Uebungsbuch1/Datei5"));
if(Files.notExists(FileSystems.getDefault().getPath(
"C:/EJ_Uebungsbuch1/Datei5"), new LinkOption[]
{LinkOption.NOFOLLOW_LINKS})) {
Files.createFile(p[8]);
}
// Weil verschiedene Dateisysteme unterschiedliche Sichten auf
// die Art und Weise, wie Dateiattribute ausfindig gemacht
// werden, haben können, gruppiert das NIO.2 die Attribute in
// Views; der Zugriff auf ein View kann für die Ermittlung von
// allen Attributen ("in bulk") mit der Methode readAttributes()
// erfolgen und mit der Methode getAttribute() für einzelne
// Attribute; mit setAttribute() können einzelne Attribute
// gesetzt werden; das NIO.2 definiert 6 Views
FileSystem fileSystem = FileSystems.getDefault();
Set<String> views = fileSystem.
supportedFileAttributeViews();
System.out.println("Die NIO.2-Views fuer Windows 7: ");
for(String view: views) {
System.out.print(view + " ");
}
System.out.println("\nBasic-Attribute");
// Die Attribute aus dem BasicFileAttributeView
for(int i=0; i<7; i++) {
BasicFileAttributes bfa = Files.readAttributes(p[i],
BasicFileAttributes.class);
System.out.println(p[i] + " Size: "
+ bfa.size());
System.out.print(" CreationTime: " +
bfa.creationTime());
System.out.print(" LastAccessTime: "
+ bfa.lastAccessTime());
System.out.print(" LastModifiedTime: "
+ bfa.lastModifiedTime());
System.out.print(" isDirectory: "
+ bfa.isDirectory());
System.out.print(" isRegularFile: " +
bfa.isRegularFile());
System.out.print(" isSymbolicLink: "
+ bfa.isSymbolicLink());
System.out.print(" isOther: " + bfa.isOther());
138
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
//
//
//
//
//
//
//
//
//
System.out.println();
}
Lesen eines einzelnen Attributs
System.out.println("Das size-Attribut von " + p[6] +
" " + Files.getAttribute(p[6], "basic:size",
LinkOption.NOFOLLOW_LINKS));
Die Attribute aus dem DosFileAttributeView
System.out.println("\nDOS-Attribute");
for(int i=0; i<9; i++) {
DosFileAttributes dfa = Files.readAttributes(p[i],
DosFileAttributes.class);
System.out.println(p[i] + " isReadOnly: "
+ dfa.isReadOnly());
System.out.print(" isHidden: " + dfa.isHidden());
System.out.print(" isArchive: " + dfa.isArchive());
System.out.print(" isSystem: " + dfa.isSystem());
System.out.println();
}
Der Benutzer kann auch eigene Attribute für Verzeichnisse
und Dateien setzen (und ggf. wieder löschen)
System.out.println("\nUser-Attribute");
for(int i=0; i<9; i++) {
UserDefinedFileAttributeView udfav = Files.
getFileAttributeView(p[i],
UserDefinedFileAttributeView.class);
udfav.write("Benutzereintrag" + (i+1), Charset.
defaultCharset().encode(
"Dies ist ein Benutzereintrag"));
for(String name: udfav.list()) {
System.out.println(p[i] + "*" + name);
}
}
}
catch(NullPointerException | IOException e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
Dateien umbenennen und löschen; die Methoden renameTo()
und delete() der Klasse File werfen keine Exceptions
boolean b = f[4].renameTo(new File(
"C:/EJ_Uebungsbuch2/DateiNeu"));
System.out.println(f[4].getName() + " " + b);
b = f[4].delete();
System.out.println(f[4].getName() + " " + b);
Die Methode delete() der Klasse Files wirft eine IOException,
die es ermöglicht, in Programmen auf Fehler zu reagieren; hier
wird die Ausnahme DirectoryNotEmptyException geworfen, weil
139
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
// ein Dateiverzeichnis, in dem sich Dateien befinden, nicht
// gelöscht werden kann
try {
Files.delete(f[3].toPath());
}
catch(IOException e) {
// System.out.println(e.getMessage());
e.printStackTrace();
}
}
}
Programmausgaben
140
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
Lösung 1.41
Die Klasse SynchroneFileChannels
import java.io.*;
import java.nio.file.*;
import java.nio.channels.*;
import java.nio.charset.*;
import java.nio.ByteBuffer;
public class SynchroneFileChannels {
// Synchrones Schreiben und Lesen in/aus Dateien mit Channels
public static void main(String args[]) {
// Zum Erzeugen eines FilChannels alternativ eine Path- bzw.
// File-Referenz auf den Pfadnamen ermitteln
Path path = Paths.get("C:/EJ_Uebungsbuch3", "Datei2");
// File file = new File("C:/EJ_Uebungsbuch3", "Datei2");
// Zwei verschiedene ByteBuffer-Instanzen für das Schreiben und
// Lesen benutzen, um die Werte im Nachhinein anzuzeigen
ByteBuffer byteBuffer1 = ByteBuffer.allocate(26);
ByteBuffer byteBuffer2 = ByteBuffer.allocate(26);
// Ein byte-Array mit den Großbuchstaben des Alphabets
// initialisieren
byte[] array = new byte[26];
int byteCode;
141
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
for(int i=65; i<91; i++)
array[i-65] = new Byte(""+i);
// Das Array in den ersten ByteBuffer einhüllen
byteBuffer1 = ByteBuffer.wrap(array,0,26);
// Einen synchronen FileChannel für das Schreiben in die
// Datei bereitstellen
try {
// Mit Java 1.4
// FileChannel fileChannel =
// new FileOutputStream(file).getChannel();
// Alternativ die open()-Methode von Java 7 aufrufen
FileChannel fileChannel = FileChannel.open(path,
StandardOpenOption.WRITE);
// Den Inhalt des Puffers auf den Channel schreiben
int anzahl = fileChannel.write(byteBuffer1);
System.out.println("Es wurden " + anzahl + " Bytes in "
+ "die Datei geschrieben");
// FileChannel schließen
fileChannel.close();
// Die FileChannel-Instanz zum Lesen eröffnen
// Mit Java 1.4
// fileChannel = new FileInputStream(file).
// getChannel();
// Alternativ die open()-Methode von Java 7 aufrufen
fileChannel = FileChannel.open(path,
StandardOpenOption.READ);
anzahl = fileChannel.read(byteBuffer2);
System.out.println("Es wurden " + anzahl + " Bytes aus "
+ "der Datei gelesen");
// FileChannel schließen
fileChannel.close();
}
catch(IOException e) {
e.printStackTrace();
}
byteBuffer1.flip();
byteBuffer2.flip();
// Für die Anzeige den Pufferinhalt dekodieren
System.out.println("Schreibpuffer: " + Charset.forName(
System.getProperty("file.encoding")).decode(byteBuffer1));
System.out.println("Lesepuffer: " + Charset.forName(
System.getProperty("file.encoding")).decode(byteBuffer2));
byteBuffer1.clear();
byteBuffer2.clear();
}
}
142
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
1.15
Lösungen
Die Klasse AsynchroneFileChannels
import java.io.*;
import java.nio.file.*;
import java.nio.channels.*;
import java.nio.charset.*;
import java.nio.ByteBuffer;
import java.util.concurrent.*;
public class AsynchroneFileChannels {
// Asynchrones Schreiben und Lesen in/aus Dateien mit Channels
public static void main(String args[]) {
// Den Pfadnamen der Datei ermitteln
Path path = Paths.get("C:/EJ_Uebungsbuch2", "Datei5");
// Zwei verschiedene ByteBuffer-Instanzen für das Schreiben und
// Lesen benutzen, um die Werte kontrollieren zu können
ByteBuffer byteBuffer1 = ByteBuffer.allocate(26);
ByteBuffer byteBuffer2 = ByteBuffer.allocate(52);
// Ein byte-Array mit den Großbuchstaben des Alphabets
// initialisieren
byte[] array = new byte[26];
int byteCode;
for(int i=65; i<91; i++)
array[i-65] = new Byte(""+i);
// Das Array in den ersten ByteBuffer einhüllen
byteBuffer1 = ByteBuffer.wrap(array,0,26);
// Eine AsynchronousFileChannel-Instanz für das Schreiben und
// Lesen in/aus eine/einer Datei bereitstellen; Channels können
// im Gegensatz zu Streams gleichzeitig zum Lesen und Schreiben
// eröffnet werden; wird der try-Block aus Java 7 benutzt,
// braucht ein Channel nicht explizit geschlossen zu werden, wenn
// die entsprechende Klasse das AutoCloseable-Interface
// implementiert
try(AsynchronousFileChannel asynchronousFileChannel =
AsynchronousFileChannel.open(path,
StandardOpenOption.WRITE, StandardOpenOption.READ)){
// Den Inhalt des Puffers ab der angegebenen Position auf den
// Channel schreiben
Future<Integer> result = asynchronousFileChannel.
write(byteBuffer1, 26);
// Die Methode isDone() des Future-Interface gibt true zurück,
// wenn ein Task komplett abgearbeitet wurde
while(!result.isDone()) {
System.out.println("Auf das Ende von Write warten");
}
// Die write()-Methode liefert in einer Future<Integer>-Instanz
// die Anzahl der geschriebenen Bytes, die mit ihrer get()// Methode ermittelt werden kann
System.out.println("Anzahl der geschriebenen Bytes: "
143
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Kapitel 1
Dateien und Streams
+ result.get());
Die Bytesequenz aus dem Channel ab der angegebenen Position
lesen und in den Puffer übertragen; die read()-Methode liefert
in einer Future<Integer>-Instanz die Anzahl der gelesenen
Bytes,
result = asynchronousFileChannel.read(byteBuffer2, 0);
while(!result.isDone()) {
System.out.println("Auf das Ende von Read warten");
}
// die mit ihrer get()-Methode ermittelt werden kann
System.out.println("Anzahl der gelesenen Bytes: "
+ result.get());
}
catch(IOException | InterruptedException
| ExecutionException e) {
System.out.println("Fehler " + e.getMessage());
}
// Die von der abstrakten Oberklasse Buffer geerbte flip()// Methode setzt das Limit (das heißt den Index des ersten
// Elements, das nicht mehr gelesen werden soll) auf die aktuelle
// Position und danach die Position (das heißt den Index des
// Elements, das als Nächstes gelesen werden soll) auf 0
byteBuffer1.flip();
byteBuffer2.flip();
// Für die Anzeige den Pufferinhalt dekodieren
System.out.println("Schreibpuffer: " + Charset.forName(
System.getProperty("file.encoding")).decode(byteBuffer1));
System.out.println("Lesepuffer: " + Charset.forName(
System.getProperty("file.encoding")).decode(byteBuffer2));
// Die Methode clear() setzt das Limit gleich der Puffergröße
// und die Position auf 0
byteBuffer1.clear();
byteBuffer2.clear();
}
}
//
//
//
//
Programmausgaben
Es wurden 26 Bytes in die Datei geschrieben
Schreibpuffer: ABCDEFGHIJKLMNOPQRSTUVWXYZ
Lesepuffer: ABCDEFGHIJKLMNOPQRSTUVWXYZ
bzw.
Auf das Ende von Write warten
Anzahl der geschriebenen Bytes: 26
Auf das Ende von Read warten
Anzahl der gelesenen Bytes: 52
Schreibpuffer: ABCDEFGHIJKLMNOPQRSTUVWXYZ
Lesepuffer: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
144
© des Titels »Java 7« (ISBN 978-3-8266-9203-1) 2012 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg.
Nähere Informationen unter: http://www.mitp.de/9203
Herunterladen