Christian Ullenboom
Java 7 – Mehr als eine Insel Das Handbuch zu den Java SE-Bibliotheken
Liebe Leserin, lieber Leser, Sie halten die erste Auflage der Insel zu den Java SE-Bibliotheken in den Händen! Ja, es ist ein ganz neues Buch, das es hoffentlich so weit bringen wird wie unsere bewährte Insel zu den Java-Grundlagen, die Sie vielleicht schon kennen. Java wächst und gedeiht, und da wir nicht alles zu Java und seinen Bibliotheken in einem Buch zusammenfassen können, weil dies buchbinderische Grenzen sprengen würde, haben wir uns entschlossen, mit den Ausgaben zu Java 7 zwei umfangreiche und in sich vollständige Werke anzubieten: Ein Einsteiger- und Lehrwerk für Studenten und Auszubildende, die umfassendes Grundlagenwissen brauchen. Und eine Insel für den erfahrenen Java-Programmierer, der konkrete Fragen aus der Praxis hat und sich schnell und gründlich in verschiedene Spezialgebiete einarbeiten muss. Herausgekommen sind 2700 Seiten Java-Wissen, fundiert und reich an Details, passgenau auf ihre jeweiligen Bedürfnisse zugeschnitten. Ohne dass Sie Abstriche machen müssen! Die Inseln sind Kultbücher. Und diese Bücher wollen sorgfältig gepflegt und aktualisiert werden. Dass der Inhalt up-to-date ist, versteht sich von selbst. Die Präsentation, sprich: das Layout, ist es jetzt auch. Frischer, aufgeräumter und lesefreundlicher. Sie haben bestimmt ihre Freude daran. Wir hoffen, dass keine Java-Fragen offen bleiben! Sollten Sie dennoch kleine Fehler oder Ungenauigkeiten finden, so haben Sie jederzeit die Möglichkeit, sich an Christian Ullenboom (
[email protected]) oder an mich zu wenden. Wir freuen uns über eine freundliche Rückmeldung!
Judith Stevens-Lemoine Lektorat Galileo Computing
[email protected] www.galileocomputing.de Galileo Press · Rheinwerkallee 4 · 53227 Bonn
Auf einen Blick 1
Neues in Java 7 ..................................................................................................
41
2
Threads und nebenläufige Programmierung ..............................................
65
3
Datenstrukturen und Algorithmen ...............................................................
175
4
Raum und Zeit ...................................................................................................
305
5
Dateien, Verzeichnisse und Dateizugriffe ...................................................
357
6
Datenströme ......................................................................................................
427
7
Die eXtensible Markup Language (XML) ......................................................
531
8
Dateiformate .....................................................................................................
617
9
Grafische Oberflächen mit Swing .................................................................
645
10
Grafikprogrammierung ...................................................................................
865
11
Netzwerkprogrammierung .............................................................................
955
12
Verteilte Programmierung mit RMI .............................................................. 1035
13
RESTful und SOAP Web-Services .................................................................... 1057
14
JavaServer Pages und Servlets ....................................................................... 1079
15
Applets ................................................................................................................ 1123
16
Datenbankmanagement mit JDBC ................................................................ 1139
17
Technologien für die Infrastruktur ................................................................ 1207
18
Reflection und Annotationen ......................................................................... 1227
19
Dynamische Übersetzung und Skriptsprachen .......................................... 1289
20
Logging und Monitoring .................................................................................. 1315
21
Java Native Interface (JNI) ............................................................................... 1337
22
Sicherheitskonzepte ......................................................................................... 1355
23
Dienstprogramme für die Java-Umgebung ................................................. 1381
Der Name Galileo Press geht auf den italienischen Mathematiker und Philosophen Galileo Galilei (1564– 1642) zurück. Er gilt als Gründungsfigur der neuzeitlichen Wissenschaft und wurde berühmt als Verfechter des modernen, heliozentrischen Weltbilds. Legendär ist sein Ausspruch Eppur si muove (Und sie bewegt sich doch). Das Emblem von Galileo Press ist der Jupiter, umkreist von den vier Galileischen Monden. Galilei entdeckte die nach ihm benannten Monde 1610. Lektorat Judith Stevens-Lemoine, Anne Scheibe Korrektorat Friederike Daenecke, Zülpich Cover Barbara Thoben, Köln Titelbild Karte: @Christian Nitz – fotolia.com, Palme: @pati – fotolia.com Typografie und Layout Vera Brauner Herstellung Norbert Englert Satz SatzPro, Krefeld Druck und Bindung Bercker Graphischer Betrieb, Kevelaer
Gerne stehen wir Ihnen mit Rat und Tat zur Seite:
[email protected] bei Fragen und Anmerkungen zum Inhalt des Buches
[email protected] für versandkostenfreie Bestellungen und Reklamationen
[email protected] für Rezensions- und Schulungsexemplare
Dieses Buch wurde gesetzt aus der TheAntiqua (9,5/14 pt) in FrameMaker.
Bibliografische Information der Deutschen Nationalbibliothek Die Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über http://dnb.d-nb.de abrufbar.
ISBN 978-3-8362-1507-7 © Galileo Press, Bonn 2012 1. Auflage 2012
Das vorliegende Werk ist in all seinen Teilen urheberrechtlich geschützt. Alle Rechte vorbehalten, insbesondere das Recht der Übersetzung, des Vortrags, der Reproduktion, der Vervielfältigung auf fotomechanischem oder anderen Wegen und der Speicherung in elektronischen Medien. Ungeachtet der Sorgfalt, die auf die Erstellung von Text, Abbildungen und Programmen verwendet wurde, können weder Verlag noch Autor, Herausgeber oder Übersetzer für mögliche Fehler und deren Folgen eine juristische Verantwortung oder irgendeine Haftung übernehmen. Die in diesem Werk wiedergegebenen Gebrauchsnamen, Handelsnamen, Warenbezeichnungen usw. können auch ohne besondere Kennzeichnung Marken sein und als solche den gesetzlichen Bestimmungen unterliegen.
Inhalt
Inhalt Vorwort ......................................................................................................................................................
35
1
Neues in Java 7
1.1
Sprachänderungen ........................................................................................................................
41
1.1.1
Zahlen im Binärsystem schreiben ..........................................................................
41
1.1.2
Unterstriche bei Ganzzahlen ....................................................................................
41
1.1.3
switch (String) ................................................................................................................
42
1.1.4
Zusammenfassen gleicher catch-Blöcke mit dem multi-catch ......................
43
1.1.5
Präzisiertes rethrows ..................................................................................................
47
1.1.6
Automatisches Ressourcen-Management (try mit Ressourcen) ...................
51
1.2 1.3
2 2.1
2.2
1.1.7
try mit Ressourcen .......................................................................................................
51
1.1.8
Ausnahmen vom close() bleiben bestehen ..........................................................
53
1.1.9
Die Schnittstelle AutoCloseable ...............................................................................
53
1.1.10
Mehrere Ressourcen nutzen .....................................................................................
55
1.1.11
Unterdrückte Ausnahmen * ......................................................................................
56
JVM-Änderungen ...........................................................................................................................
59
1.2.1
invokedynamic .............................................................................................................
60
Neue Pakete und Klassen ............................................................................................................
63
1.3.1
63
Weitere Änderungen ...................................................................................................
Threads und nebenläufige Programmierung Threads erzeugen ..........................................................................................................................
65
2.1.1
Threads über die Schnittstelle Runnable implementieren .............................
65
2.1.2
Thread mit Runnable starten ...................................................................................
66
2.1.3
Die Klasse Thread erweitern .....................................................................................
68
Thread-Eigenschaften und -Zustände .....................................................................................
71
2.2.1
71
Der Name eines Threads ............................................................................................
2.2.2
Wer bin ich? ....................................................................................................................
72
2.2.3
Die Zustände eines Threads * ....................................................................................
72
5
Inhalt
2.3
2.2.4
Schläfer gesucht ...........................................................................................................
2.2.5
Mit yield() auf Rechenzeit verzichten ...................................................................
75
2.2.6
Der Thread als Dämon ................................................................................................
76
2.2.7
Das Ende eines Threads .............................................................................................
78
2.2.8
Einen Thread höflich mit Interrupt beenden .....................................................
79
2.2.9
UncaughtExceptionHandler für unbehandelte Ausnahmen ........................
81
2.2.10
Der stop() von außen und die Rettung mit ThreadDeath * .............................
82
2.2.11
Ein Rendezvous mit join() * ......................................................................................
84
2.2.12
Arbeit niederlegen und wieder aufnehmen * .....................................................
87
2.2.13
Priorität * ........................................................................................................................
87
Der Ausführer (Executor) kommt .............................................................................................
88
2.3.1
Die Schnittstelle Executor .........................................................................................
89
2.3.2
Die Thread-Pools ..........................................................................................................
91
2.3.3
Threads mit Rückgabe über Callable .....................................................................
92
2.3.4
Mehrere Callable abarbeiten ....................................................................................
96
2.3.5
ScheduledExecutorService für wiederholende Ausgaben und Zeitsteuerungen nutzen ............................................................................................
2.4
2.5
6
73
97
Synchronisation über kritische Abschnitte ...........................................................................
98
2.4.1
Gemeinsam genutzte Daten ....................................................................................
98
2.4.2
Probleme beim gemeinsamen Zugriff und kritische Abschnitte .................
98
2.4.3
Punkte parallel initialisieren ...................................................................................
100
2.4.4
i++ sieht atomar aus, ist es aber nicht * ................................................................
102
2.4.5
Kritische Abschnitte schützen .................................................................................
103
2.4.6
Kritische Abschnitte mit ReentrantLock schützen ...........................................
106
2.4.7
Synchronisieren mit synchronized .......................................................................
113
2.4.8
Synchronized-Methoden der Klasse StringBuffer * ..........................................
114
2.4.9
Mit synchronized synchronisierte Blöcke ...........................................................
115
2.4.10
Dann machen wir doch gleich alles synchronisiert! ........................................
117
2.4.11
Lock-Freigabe im Fall von Exceptions ...................................................................
118
2.4.12
Deadlocks .......................................................................................................................
119
2.4.13
Mit synchronized nachträglich synchronisieren * ............................................
121
2.4.14
Monitore sind reentrant – gut für die Geschwindigkeit * ...............................
122
2.4.15
Synchronisierte Methodenaufrufe zusammenfassen * ..................................
123
Synchronisation über Warten und Benachrichtigen ..........................................................
124
2.5.1
Die Schnittstelle Condition ......................................................................................
125
2.5.2
It’s Disco-Time * ...........................................................................................................
129
Inhalt
2.6
2.7
2.5.3
Warten mit wait() und Aufwecken mit notify() * ................................................
135
2.5.4
Falls der Lock fehlt: IllegalMonitorStateException * .........................................
136
Datensynchronisation durch besondere Concurrency-Klassen * .....................................
138
2.6.1
Semaphor ........................................................................................................................
138
2.6.2
Barrier und Austausch ................................................................................................
143
2.6.3
Stop and go mit Exchanger .......................................................................................
145
Atomare Operationen und frische Werte mit volatile * .....................................................
145
2.7.1
Der Modifizierer volatile bei Objekt-/Klassenvariablen ..................................
146
2.7.2
Das Paket java.util.concurrent.atomic ...................................................................
148
Teile und herrsche mit Fork und Join * ....................................................................................
149
2.8.1
Algorithmendesign per »teile und herrsche« .....................................................
149
2.8.2
Paralleles Lösen von D&C-Algorithmen ...............................................................
151
2.8.3
Fork und Join .................................................................................................................
152
Mit dem Thread verbundene Variablen * ...............................................................................
156
2.9.1
ThreadLocal ....................................................................................................................
156
2.9.2
InheritableThreadLocal ..............................................................................................
159
2.9.3
ThreadLocalRandom als Zufallszahlengenerator ...............................................
160
2.9.4
ThreadLocal bei der Performance-Optimierung ................................................
161
2.10 Threads in einer Thread-Gruppe * ............................................................................................
162
2.8
2.9
2.10.1
Aktive Threads in der Umgebung ...........................................................................
162
2.10.2
Etwas über die aktuelle Thread-Gruppe herausfinden .....................................
163
2.10.3
Threads in einer Thread-Gruppe anlegen .............................................................
166
2.10.4
Methoden von Thread und ThreadGroup im Vergleich ..................................
169
2.11 Zeitgesteuerte Abläufe ................................................................................................................
170
2.11.1
Die Typen Timer und TimerTask .............................................................................
171
2.11.2
Job-Scheduler Quartz ..................................................................................................
172
2.12 Einen Abbruch der virtuellen Maschine erkennen ...............................................................
173
2.13 Zum Weiterlesen ...........................................................................................................................
174
3
Datenstrukturen und Algorithmen
3.1
Datenstrukturen und die Collection-API ................................................................................
175
3.1.1
Designprinzip mit Schnittstellen, abstrakten und konkreten Klassen .......
176
3.1.2
Die Basis-Schnittstellen Collection und Map ......................................................
176
3.1.3
Die Utility-Klassen Collections und Arrays ..........................................................
177
7
Inhalt
3.2
3.3
3.4 3.5
3.6
8
3.1.4
Das erste Programm mit Container-Klassen ......................................................
177
3.1.5
Die Schnittstelle Collection und Kernkonzepte .................................................
178
3.1.6
Schnittstellen, die Collection erweitern, und Map ............................................
182
3.1.7
Konkrete Container-Klassen ....................................................................................
185
3.1.8
Generische Datentypen in der Collection-API ....................................................
187
3.1.9
Die Schnittstelle Iterable und das erweiterte for ...............................................
187
Listen ................................................................................................................................................
188
3.2.1
Erstes Listen-Beispiel ..................................................................................................
189
3.2.2
Auswahlkriterium ArrayList oder LinkedList .....................................................
190
3.2.3
Die Schnittstelle List ...................................................................................................
191
3.2.4
ArrayList .........................................................................................................................
197
3.2.5
LinkedList .......................................................................................................................
200
3.2.6
Der Feld-Adapter Arrays.asList() .............................................................................
201
3.2.7
ListIterator * ...................................................................................................................
202
3.2.8
toArray() von Collection verstehen – die Gefahr einer Falle erkennen ......
204
3.2.9
Primitive Elemente in Datenstrukturen verwalten ..........................................
207
Mengen (Sets) ................................................................................................................................
208
3.3.1
Ein erstes Mengen-Beispiel .......................................................................................
209
3.3.2
Methoden der Schnittstelle Set ...............................................................................
212
3.3.3
HashSet ...........................................................................................................................
213
3.3.4
TreeSet – die sortierte Menge ..................................................................................
214
3.3.5
Die Schnittstellen NavigableSet und SortedSet .................................................
216
3.3.6
LinkedHashSet ..............................................................................................................
218
Stack (Kellerspeicher, Stapel) ....................................................................................................
218
3.4.1
Die Methoden von Stack ...........................................................................................
219
Queues (Schlangen) und Deques ..............................................................................................
220
3.5.1
221
Queue-Klassen ..............................................................................................................
3.5.2
Deque-Klassen ..............................................................................................................
222
3.5.3
Blockierende Queues und Prioritätswarteschlangen .......................................
223
3.5.4
PriorityQueue ...............................................................................................................
223
Assoziative Speicher ....................................................................................................................
228
3.6.1
Die Klassen HashMap und TreeMap ......................................................................
228
3.6.2
Einfügen und Abfragen der Datenstruktur .........................................................
231
3.6.3
Über die Bedeutung von equals() und hashCode() ............................................
234
3.6.4
Eigene Objekte hashen ...............................................................................................
235
3.6.5
IdentityHashMap .........................................................................................................
237
3.6.6
Das Problem von veränderten Elementen ..........................................................
237
Inhalt
3.7
3.8
3.9
3.6.7
Aufzählungen und Ansichten des Assoziativspeichers ...................................
237
3.6.8
Die Arbeitsweise einer Hash-Tabelle * ...................................................................
241
Die Properties-Klasse ...................................................................................................................
243
3.7.1
Properties setzen und lesen ......................................................................................
243
3.7.2
Properties verketten ....................................................................................................
244
3.7.3
Hierarchische Eigenschaften ....................................................................................
245
3.7.4
Eigenschaften auf der Konsole ausgeben * ..........................................................
245
3.7.5
Properties laden und speichern ...............................................................................
246
3.7.6
Klassenbeziehungen: Properties und Hashtable * .............................................
248
Mit einem Iterator durch die Daten wandern .......................................................................
249
3.8.1
Die Schnittstelle Iterator ............................................................................................
249
3.8.2
Der Iterator kann (eventuell auch) löschen .........................................................
251
3.8.3
Einen Zufallszahleniterator schreiben ..................................................................
252
3.8.4
Iteratoren von Sammlungen, das erweiterte for und Iterable .......................
254
3.8.5
Fail-Fast-Iterator und die ConcurrentModificationException .......................
258
3.8.6
Die Schnittstelle Enumerator * ................................................................................
260
Algorithmen in Collections .........................................................................................................
261
3.9.1
Die Bedeutung von Ordnung mit Comparator und Comparable .................
263
3.9.2
Sortieren ..........................................................................................................................
264
3.9.3
Den größten und kleinsten Wert einer Collection finden ...............................
267
3.9.4
Nicht-änderbare Datenstrukturen, immutable oder nur-lesen? ...................
270
3.9.5
Null Object Pattern und leere Sammlungen/Iteratoren zurückgeben ........
274
3.9.6
Mit der Halbierungssuche nach Elementen fahnden .......................................
278
3.9.7
Ersetzen, Kopieren, Füllen, Umdrehen, Rotieren * ............................................
279
3.9.8
Listen durchwürfeln * .................................................................................................
281
3.9.9
Häufigkeit eines Elements * ......................................................................................
282
3.9.10
Singletons * .....................................................................................................................
282
3.9.11
nCopies() * .......................................................................................................................
283
3.10 Spezielle thread-sichere Datenstrukturen .............................................................................
284
3.10.1
Wait-free-Algorithmen ...............................................................................................
3.10.2
Nebenläufiger Assoziativspeicher und die Schnittstelle
284
ConcurrentMap .............................................................................................................
284
3.10.3
ConcurrentLinkedQueue ...........................................................................................
285
3.10.4
CopyOnWriteArrayList und CopyOnWriteArraySet ..........................................
285
3.10.5
Wrapper zur Synchronisation ..................................................................................
286
3.10.6
Blockierende Warteschlangen ..................................................................................
287
9
Inhalt
3.10.7
ArrayBlockingQueue und LinkedBlockingQueue .............................................
287
3.10.8
PriorityBlockingQueue ..............................................................................................
290
3.11 Google Guava (aka Google Collections Library) ..................................................................
294
3.11.1
Beispiel Multi-Set und Multi-Map ..........................................................................
294
3.11.2
Datenstrukturen aus Guava .....................................................................................
295
3.11.3
Utility-Klassen von Guava ........................................................................................
297
3.11.4
Prädikate .........................................................................................................................
298
3.11.5
Transformationen .......................................................................................................
299
3.12 Die Klasse BitSet für Bitmengen * ............................................................................................
299
3.12.1
Ein BitSet anlegen, füllen und erfragen ................................................................
300
3.12.2
Mengenorientierte Operationen ............................................................................
301
3.12.3
Methodenübersicht ....................................................................................................
302
3.12.4
Primzahlen in einem BitSet verwalten .................................................................
304
3.13 Zum Weiterlesen ..........................................................................................................................
304
4
Raum und Zeit
4.1
Weltzeit * ........................................................................................................................................
305
4.2
Wichtige Datum-Klassen im Überblick ..................................................................................
306
4.2.1
Der 1.1.1970 ...................................................................................................................
307
4.2.2
System.currentTimeMillis() .....................................................................................
307
4.2.3
Einfache Zeitumrechnungen durch TimeUnit ...................................................
307
4.3 4.4
4.5
4.6
10
Sprachen der Länder ....................................................................................................................
308
4.3.1
309
Sprachen und Regionen über Locale-Objekte .....................................................
Internationalisierung und Lokalisierung ...............................................................................
312
4.4.1
ResourceBundle-Objekte und Ressource-Dateien .............................................
313
4.4.2
Ressource-Dateien zur Lokalisierung ....................................................................
314
4.4.3
Die Klasse ResourceBundle ......................................................................................
314
4.4.4
Ladestrategie für ResourceBundle-Objekte .........................................................
316
Die Klasse Date ..............................................................................................................................
317
4.5.1
Objekte erzeugen und Methoden nutzen ............................................................
317
4.5.2
Date-Objekte sind nicht immutable ......................................................................
319
Calendar und GregorianCalendar ............................................................................................
320
4.6.1
Die abstrakte Klasse Calendar ..................................................................................
320
4.6.2
Der gregorianische Kalender ...................................................................................
322
Inhalt
4.6.3
4.7
4.8
Calendar nach Date und Millisekunden fragen ..................................................
325
4.6.4
Ostertage * ......................................................................................................................
326
4.6.5
Abfragen und Setzen von Datumselementen über Feldbezeichner ............
328
4.6.6
Wie viele Tage hat der Monat, oder wie viele Monate hat ein Jahr? * ..........
333
4.6.7
Wann beginnt die Woche und wann die erste Woche im Jahr? * ..................
335
Zeitzonen in Java * ........................................................................................................................
336
4.7.1
337
Zeitzonen durch die Klasse TimeZone repräsentieren .....................................
4.7.2
SimpleTimeZone ..........................................................................................................
337
4.7.3
Methoden von TimeZone ..........................................................................................
339
Zeitdauern und der XML-Datentyp Duration * .....................................................................
340
4.8.1
DatatypeFactory als Fabrik ........................................................................................
341
4.8.2
Die Duration-Klasse und ihre Methoden ..............................................................
342
Formatieren und Parsen von Datumsangaben .....................................................................
345
4.9.1
Ausgaben mit printf() ..................................................................................................
345
4.9.2
Mit DateFormat und SimpleDateFormat formatieren .....................................
346
4.9.3
Parsen von Datumswerten ........................................................................................
352
4.10 Zum Weiterlesen ...........................................................................................................................
355
4.9
5
Dateien, Verzeichnisse und Dateizugriffe
5.1
Datei und Verzeichnis ..................................................................................................................
357
5.1.1
Dateien und Verzeichnisse mit der Klasse File ...................................................
358
5.1.2
Verzeichnis oder Datei? Existiert es? .....................................................................
361
5.1.3
Verzeichnis- und Dateieigenschaften/-attribute ...............................................
362
5.1.4
Umbenennen und Verzeichnisse anlegen ...........................................................
365
5.1.5
Verzeichnisse auflisten und Dateien filtern ........................................................
365
5.1.6
Dateien berühren, neue Dateien anlegen, temporäre Dateien ......................
369
5.1.7
Dateien und Verzeichnisse löschen ........................................................................
370
5.1.8
Verzeichnisse nach Dateien iterativ durchsuchen * ..........................................
372
5.1.9
Wurzelverzeichnis, Laufwerksnamen, Plattenspeicher * ................................
373
5.1.10
URL-, URI- und Path-Objekte aus einem File-Objekt ableiten * ......................
376
5.1.11
Mit Locking Dateien sperren * ..................................................................................
376
5.1.12
Sicherheitsprüfung * ...................................................................................................
377
5.1.13
Zugriff auf SMB-Server mit jCIFS * ..........................................................................
378
11
Inhalt
5.2
5.3
5.4
Dateien mit wahlfreiem Zugriff ...............................................................................................
378
5.2.1
Ein RandomAccessFile zum Lesen und Schreiben öffnen ..............................
379
5.2.2
Aus dem RandomAccessFile lesen .........................................................................
380
5.2.3
Schreiben mit RandomAccessFile ..........................................................................
383
5.2.4
Die Länge des RandomAccessFile ...........................................................................
384
5.2.5
Hin und her in der Datei ............................................................................................
384
Dateisysteme unter NIO.2 ..........................................................................................................
385
5.3.1
FileSystem und Path ...................................................................................................
386
5.3.2
Die Utility-Klasse Files ...............................................................................................
392
5.3.3
Dateien kopieren und verschieben ........................................................................
395
5.3.4
Dateiattribute * .............................................................................................................
397
5.3.5
Neue Dateien, Verzeichnisse, symbolische Verknüpfungen anlegen und löschen ...................................................................................................................
406
5.3.6
MIME-Typen testen * ..................................................................................................
408
5.3.7
Verzeichnislistings (DirectoryStream) und Filter * ...........................................
410
5.3.8
Rekursive Abläufe des Verzeichnisbaums (FileVisitor) * ................................
412
5.3.9
Dateisysteme und Dateisystemattribute * ..........................................................
416
5.3.10
Verzeichnisse im Dateisystem überwachen * .....................................................
420
Wahlfreier Zugriff mit SeekableByteChannel und ByteBuffer * .....................................
422
5.4.1
422
SeekableByteChannel .................................................................................................
5.4.2
ByteBuffer ......................................................................................................................
423
5.4.3
Beispiel mit Path + SeekableByteChannel + ByteBuffer ..................................
423
5.4.4
FileChannel ....................................................................................................................
424
5.5
Zum Weiterlesen ..........................................................................................................................
426
6
Datenströme
6.1
6.2
12
Stream-Klassen und Reader/Writer am Beispiel von Dateien .........................................
427
6.1.1
Mit dem FileWriter Texte in Dateien schreiben .................................................
428
6.1.2
Zeichen mit der Klasse FileReader lesen ..............................................................
430
6.1.3
Kopieren mit FileOutputStream und FileInputStream ...................................
431
6.1.4
Das FileDescriptor-Objekt * ......................................................................................
435
6.1.5
Datenströme über Files mit NIO.2 beziehen .......................................................
436
Basisklassen für die Ein-/Ausgabe ...........................................................................................
438
6.2.1
Die abstrakten Basisklassen .....................................................................................
438
6.2.2
Übersicht über Ein-/Ausgabeklassen ....................................................................
439
Inhalt
6.3
6.4
6.5
6.6
6.7
6.2.3
Die abstrakte Basisklasse OutputStream ..............................................................
441
6.2.4
Die Schnittstellen Closeable, AutoCloseable und Flushable ...........................
443
6.2.5
Ein Datenschlucker * ...................................................................................................
444
6.2.6
Die abstrakte Basisklasse InputStream .................................................................
445
6.2.7
Ressourcen aus dem Klassenpfad und aus Jar-Archiven laden .....................
446
6.2.8
Ströme mit SequenceInputStream zusammensetzen * ...................................
447
6.2.9
Die abstrakte Basisklasse Writer ..............................................................................
449
6.2.10
Die Schnittstelle Appendable * .................................................................................
451
6.2.11
Die abstrakte Basisklasse Reader .............................................................................
451
Formatierte Textausgaben .........................................................................................................
454
6.3.1
Die Klassen PrintWriter und PrintStream ............................................................
455
6.3.2
System.out, System.err und System.in ..................................................................
460
Schreiben und Lesen aus Strings und Byte-Feldern .............................................................
462
6.4.1
Mit dem StringWriter ein String-Objekt füllen ...................................................
463
6.4.2
CharArrayWriter ...........................................................................................................
464
6.4.3
StringReader und CharArrayReader .......................................................................
465
6.4.4
Mit ByteArrayOutputStream in ein Byte-Feld schreiben ................................
467
6.4.5
Mit ByteArrayInputStream aus einem Byte-Feld lesen ....................................
467
Datenströme filtern und verketten .........................................................................................
468
6.5.1
Streams als Filter verketten (verschachteln) .......................................................
469
6.5.2
Gepufferte Ausgaben mit BufferedWriter und BufferedOutputStream .....
469
6.5.3
Gepufferte Eingaben mit BufferedReader/BufferedInputStream ................
471
6.5.4
LineNumberReader zählt automatisch Zeilen mit * ..........................................
474
6.5.5
Daten mit der Klasse PushbackReader zurücklegen * .......................................
474
6.5.6
DataOutputStream/DataInputStream * ................................................................
478
6.5.7
Basisklassen für Filter * ...............................................................................................
478
6.5.8
Die Basisklasse FilterWriter * ....................................................................................
479
6.5.9
Ein LowerCaseWriter * ................................................................................................
480
6.5.10
Eingaben mit der Klasse FilterReader filtern * ....................................................
482
6.5.11
Anwendungen für FilterReader und FilterWriter * ............................................
483
Vermittler zwischen Byte-Streams und Unicode-Strömen ...............................................
491
6.6.1
Datenkonvertierung durch den OutputStreamWriter .....................................
491
6.6.2
Automatische Konvertierungen mit dem InputStreamReader ....................
492
Kommunikation zwischen Threads mit Pipes * ....................................................................
493
6.7.1
PipedOutputStream und PipedInputStream .......................................................
494
6.7.2
PipedWriter und PipedReader ..................................................................................
496
13
Inhalt
6.8
6.9
Prüfsummen ...................................................................................................................................
498
6.8.1
Die Schnittstelle Checksum ......................................................................................
498
6.8.2
Die Klasse CRC32 ..........................................................................................................
499
6.8.3
Die Adler32-Klasse .......................................................................................................
501
Persistente Objekte und Serialisierung ..................................................................................
502
6.9.1
Objekte mit der Standard-Serialisierung speichern und lesen .....................
503
6.9.2
Zwei einfache Anwendungen der Serialisierung * ............................................
506
6.9.3
Die Schnittstelle Serializable ....................................................................................
507
6.9.4
Nicht serialisierbare Attribute aussparen ............................................................
509
6.9.5
Das Abspeichern selbst in die Hand nehmen .....................................................
511
6.9.6
Tiefe Objektkopien * ...................................................................................................
515
6.9.7
Versionenverwaltung und die SUID ......................................................................
517
6.9.8
Wie die ArrayList serialisiert * .................................................................................
519
6.9.9
Probleme mit der Serialisierung .............................................................................
520
6.10 Alternative Datenaustauschformate ......................................................................................
522
6.10.1
Serialisieren in XML-Dateien ...................................................................................
6.10.2
XML-Serialisierung von JavaBeans mit JavaBeans Persistence * ..................
522
6.10.3
Die Open-Source-Bibliothek XStream * ................................................................
525
6.10.4
Binäre Serialisierung mit Google Protocol Buffers * ........................................
526
6.11 Tokenizer * ......................................................................................................................................
526
6.11.1
522
StreamTokenizer ..........................................................................................................
526
6.12 Zum Weiterlesen ..........................................................................................................................
529
7
Die eXtensible Markup Language (XML)
7.1
Auszeichnungssprachen .............................................................................................................
531
7.1.1
Die Standard Generalized Markup Language (SGML) ......................................
532
7.1.2
Extensible Markup Language (XML) ......................................................................
532
Eigenschaften von XML-Dokumenten ....................................................................................
533
7.2.1
Elemente und Attribute .............................................................................................
533
7.2.2
Beschreibungssprache für den Aufbau von XML-Dokumenten ...................
535
7.2.3
Schema – eine Alternative zu DTD .........................................................................
539
7.2.4
Namensraum (Namespace) ......................................................................................
542
7.2.5
XML-Applikationen * ..................................................................................................
543
7.2
14
Inhalt
7.3
7.4
7.5
7.6
7.7
Die Java-APIs für XML ..................................................................................................................
544
7.3.1
Das Document Object Model (DOM) ......................................................................
545
7.3.2
Simple API for XML Parsing (SAX) ...........................................................................
545
7.3.3
Pull-API StAX .................................................................................................................
545
7.3.4
Java Document Object Model (JDOM) ....................................................................
545
7.3.5
JAXP als Java-Schnittstelle zu XML .........................................................................
546
7.3.6
DOM-Bäume einlesen mit JAXP * ............................................................................
547
Java Architecture for XML Binding (JAXB) ..............................................................................
548
7.4.1
Bean für JAXB aufbauen .............................................................................................
548
7.4.2
JAXBContext und die Marshaller ............................................................................
549
7.4.3
Ganze Objektgraphen schreiben und lesen .........................................................
550
7.4.4
Validierung .....................................................................................................................
553
7.4.5
Weitere JAXB-Annotationen * ..................................................................................
557
7.4.6
Beans aus XML-Schema-Datei generieren ............................................................
565
Serielle Verarbeitung mit StAX .................................................................................................
570
7.5.1
Unterschiede der Verarbeitungsmodelle .............................................................
571
7.5.2
XML-Dateien mit dem Cursor-Verfahren lesen ..................................................
572
7.5.3
XML-Dateien mit dem Iterator-Verfahren verarbeiten * .................................
576
7.5.4
Mit Filtern arbeiten * ...................................................................................................
578
7.5.5
XML-Dokumente schreiben ......................................................................................
579
Serielle Verarbeitung von XML mit SAX * ..............................................................................
582
7.6.1
583
Schnittstellen von SAX ...............................................................................................
7.6.2
SAX-Parser erzeugen ...................................................................................................
584
7.6.3
Operationen der Schnittstelle ContentHandler .................................................
584
7.6.4
ErrorHandler und EntityResolver ...........................................................................
587
XML-Dateien mit JDOM verarbeiten .......................................................................................
588
7.7.1
JDOM beziehen ..............................................................................................................
588
7.7.2
Paketübersicht * ............................................................................................................
588
7.7.3
Die Document-Klasse ..................................................................................................
590
7.7.4
Eingaben aus der Datei lesen ....................................................................................
591
7.7.5
Das Dokument im XML-Format ausgeben ...........................................................
592
7.7.6
Der Dokumenttyp * ......................................................................................................
593
7.7.7
Elemente .........................................................................................................................
594
7.7.8
Zugriff auf Elementinhalte ........................................................................................
597
7.7.9
Liste mit Unterelementen erzeugen * ....................................................................
599
7.7.10
Neue Elemente einfügen und ändern ....................................................................
600
15
Inhalt
7.8
7.9
7.7.11
Attributinhalte lesen und ändern ..........................................................................
603
7.7.12
XPath ...............................................................................................................................
606
Transformationen mit XSLT * ....................................................................................................
610
7.8.1
Templates und XPath als Kernelemente von XSLT ...........................................
610
7.8.2
Umwandlung von XML-Dateien mit JDOM und JAXP .....................................
613
XML-Schema-Validierung * ........................................................................................................
614
7.9.1
614
SchemaFactory und Schema ....................................................................................
7.9.2
Validator .........................................................................................................................
614
7.9.3
Validierung unterschiedlicher Datenquellen durchführen ...........................
615
7.10 Zum Weiterlesen ..........................................................................................................................
615
8 8.1
8.2
8.3
Dateiformate Einfache Dateiformate für strukturierte Daten ...................................................................
618
8.1.1
618
Property-Dateien .........................................................................................................
8.1.2
CSV-Dateien ...................................................................................................................
618
8.1.3
JSON-Serialisierung mit Jackson .............................................................................
619
Dokumentenformate ...................................................................................................................
620
8.2.1
(X)HTML ..........................................................................................................................
620
8.2.2
PDF-Dokumente ...........................................................................................................
622
8.2.3
Microsoft Office-Dokumente ...................................................................................
622
8.2.4
OASIS Open Document Format ...............................................................................
624
Datenkompression * ....................................................................................................................
624
8.3.1
Java-Unterstützung beim Komprimieren ............................................................
625
8.3.2
Daten packen und entpacken ..................................................................................
626
8.3.3
Datenströme komprimieren ....................................................................................
627
8.3.4
Zip-Archive ....................................................................................................................
632
8.3.5
Jar-Archive .....................................................................................................................
640
8.4
Bild-Formate ..................................................................................................................................
640
8.5
Audio-Dateien ...............................................................................................................................
640
16
8.5.1
Die Arbeit mit AudioClip ...........................................................................................
640
8.5.2
MIDI-Dateien abspielen .............................................................................................
641
Inhalt
9
Grafische Oberflächen mit Swing
9.1
Fenster zur Welt ............................................................................................................................
645
9.1.1
Swing-Fenster mit javax.swing.JFrame darstellen .............................................
645
9.1.2
Fenster schließbar machen – setDefaultCloseOperation() ..............................
647
9.1.3
Sichtbarkeit des Fensters ...........................................................................................
648
9.1.4
Größe und Position des Fensters verändern .......................................................
648
9.1.5
Fenster- und Dialog-Dekoration, Transparenz * .................................................
649
9.1.6
Die Klasse Toolkit * ......................................................................................................
650
9.1.7
Dynamisches Layout während einer Größenänderung * ................................
651
9.1.8
Zum Vergleich: AWT-Fenster darstellen * .............................................................
651
9.2 9.3
9.4
9.5
9.6
Beschriftungen (JLabel) ...............................................................................................................
653
9.2.1
656
Mehrzeiliger Text, HTML in der Darstellung .......................................................
Icon und ImageIcon für Bilder auf Swing-Komponenten ..................................................
657
9.3.1
Die Klasse ImageIcon ..................................................................................................
657
9.3.2
Die Schnittstelle Icon und eigene Icons zeichnen * ...........................................
659
Es tut sich was – Ereignisse beim AWT ...................................................................................
661
9.4.1
Die Ereignisquellen und Horcher (Listener) von Swing ...................................
662
9.4.2
Listener implementieren ...........................................................................................
663
9.4.3
Listener bei dem Ereignisauslöser anmelden/abmelden ................................
666
9.4.4
Adapterklassen nutzen ...............................................................................................
668
9.4.5
Innere Mitgliedsklassen und innere anonyme Klassen ...................................
670
9.4.6
Aufrufen der Listener im AWT-Event-Thread .....................................................
672
9.4.7
Ereignisse, etwas genauer betrachtet * ..................................................................
673
Schaltflächen ..................................................................................................................................
676
9.5.1
Normale Schaltflächen (JButton) .............................................................................
676
9.5.2
Der aufmerksame ActionListener ...........................................................................
678
9.5.3
Schaltflächen-Ereignisse vom Typ ActionEvent .................................................
679
9.5.4
Basisklasse AbstractButton .......................................................................................
680
9.5.5
Wechselknopf (JToggleButton) ................................................................................
682
Textkomponenten ........................................................................................................................
683
9.6.1
Text in einer Eingabezeile .........................................................................................
684
9.6.2
Die Oberklasse der Text-Komponenten (JTextComponent) ..........................
685
9.6.3
Geschützte Eingaben (JPasswordField) ..................................................................
686
9.6.4
Validierende Eingabefelder (JFormattedTextField) ...........................................
687
9.6.5
Einfache mehrzeilige Textfelder (JTextArea) .......................................................
688
9.6.6
Editor-Klasse (JEditorPane) * .....................................................................................
692
17
Inhalt
9.7
Swing Action * ...............................................................................................................................
9.8
JComponent und Component als Basis aller Komponenten ............................................
697
9.8.1
Hinzufügen von Komponenten ..............................................................................
697
9.8.2
Tooltips (Kurzhinweise) .............................................................................................
698
9.8.3
Rahmen (Border) * .......................................................................................................
699
9.8.4
Fokus und Navigation * ..............................................................................................
701
9.8.5
Ereignisse jeder Komponente * ...............................................................................
702
9.8.6
Die Größe und Position einer Komponente * .....................................................
706
9.8.7
Komponenten-Ereignisse * .......................................................................................
707
9.8.8
UI-Delegate – der wahre Zeichner * ........................................................................
708
9.8.9
Undurchsichtige (opake) Komponente * ..............................................................
711
9.8.10
Properties und Listener für Änderungen * ..........................................................
711
Container ........................................................................................................................................
711
9.9
694
9.9.1
Standardcontainer (JPanel) .......................................................................................
712
9.9.2
Bereich mit automatischen Rollbalken (JScrollPane) .......................................
713
9.9.3
Reiter (JTabbedPane) ...................................................................................................
714
9.9.4
Teilungskomponente (JSplitPane) ..........................................................................
715
9.10 Alles Auslegungssache: die Layoutmanager ........................................................................
716
9.10.1
Übersicht über Layoutmanager ..............................................................................
717
9.10.2
Zuweisen eines Layoutmanagers ...........................................................................
717
9.10.3
Im Fluss mit FlowLayout ...........................................................................................
718
9.10.4
BoxLayout ......................................................................................................................
720
9.10.5
Mit BorderLayout in alle Himmelsrichtungen ...................................................
721
9.10.6
Rasteranordnung mit GridLayout ..........................................................................
724
9.10.7
Der GridBagLayoutmanager * ..................................................................................
725
9.10.8
Null-Layout * .................................................................................................................
731
9.10.9
Weitere Layoutmanager ............................................................................................
732
9.11 Rollbalken und Schieberegler ...................................................................................................
733
9.11.1
Schieberegler (JSlider) ................................................................................................
733
9.11.2
Rollbalken (JScrollBar) * .............................................................................................
734
9.12 Kontrollfelder, Optionsfelder, Kontrollfeldgruppen ..........................................................
739
9.12.1
Kontrollfelder (JCheckBox) .......................................................................................
740
9.12.2
ItemSelectable, ItemListener und das ItemEvent .............................................
741
9.12.3
Sich gegenseitig ausschließende Optionen (JRadioButton) ...........................
743
9.13 Fortschritte bei Operationen überwachen * .........................................................................
745
18
9.13.1
Fortschrittsbalken (JProgressBar) ...........................................................................
745
9.13.2
Dialog mit Fortschrittsanzeige (ProgressMonitor) ...........................................
748
Inhalt
9.14 Menüs und Symbolleisten ..........................................................................................................
748
9.14.1
Die Menüleisten und die Einträge ..........................................................................
748
9.14.2
Menüeinträge definieren ...........................................................................................
750
9.14.3
Einträge durch Action-Objekte beschreiben ........................................................
751
9.14.4
Mit der Tastatur: Mnemonics und Shortcut ........................................................
752
9.14.5
Der Tastatur-Shortcut (Accelerator) .......................................................................
753
9.14.6
Tastenkürzel (Mnemonics) ........................................................................................
755
9.14.7
Symbolleisten alias Toolbars ....................................................................................
756
9.14.8
Popup-Menüs ................................................................................................................
759
9.14.9
System-Tray nutzen * ..................................................................................................
764
9.15 Das Model-View-Controller-Konzept ......................................................................................
765
9.16 Auswahlmenüs, Listen und Spinner .........................................................................................
767
9.16.1
Listen (JList) ....................................................................................................................
767
9.16.2
Auswahlmenü (JComboBox) .....................................................................................
775
9.16.3
Drehfeld (JSpinner) * ....................................................................................................
787
9.16.4
Datumsauswahl * ..........................................................................................................
789
9.17 Tabellen (JTable) ............................................................................................................................
789
9.17.1
Ein eigenes Tabellen-Model ......................................................................................
791
9.17.2
Basisklasse für eigene Modelle (AbstractTableModel) .....................................
792
9.17.3
Ein vorgefertigtes Standard-Modell (DefaultTableModel) ..............................
796
9.17.4
Ein eigener Renderer für Tabellen ..........................................................................
798
9.17.5
Zell-Editoren ..................................................................................................................
801
9.17.6
Größe und Umrandung der Zellen * .......................................................................
803
9.17.7
Spalteninformationen * .............................................................................................
803
9.17.8
Tabellenkopf von Swing-Tabellen * ........................................................................
804
9.17.9
Selektionen einer Tabelle * ........................................................................................
805
9.17.10 Automatisches Sortieren und Filtern mit RowSorter * ....................................
806
9.18 Bäume (JTree) .................................................................................................................................
809
9.18.1
JTree und sein TreeModel und TreeNode .............................................................
809
9.18.2
Selektionen bemerken ................................................................................................
811
9.18.3
Das TreeModel von JTree * .........................................................................................
811
9.19 JRootPane und JDesktopPane * .................................................................................................
815
9.19.1
Wurzelkomponente der Top-Level-Komponenten (JRootPane) ...................
815
9.19.2
JDesktopPane und die Kinder von JInternalFrame ...........................................
815
9.19.3
JLayeredPane .................................................................................................................
817
9.20 Dialoge und Window-Objekte ................................................................................................... 9.20.1
JWindow und JDialog ..................................................................................................
818 818
19
Inhalt
9.20.2
Modal oder nicht-modal? ..........................................................................................
819
9.20.3
Standarddialoge mit JOptionPane .........................................................................
820
9.20.4
Der Dateiauswahldialog ............................................................................................
823
9.20.5
Der Farbauswahldialog JColorChooser * ...............................................................
827
9.21 Flexibles Java-Look-and-Feel ....................................................................................................
830
9.21.1
Look and Feel global setzen ......................................................................................
830
9.21.2
UIManager .....................................................................................................................
830
9.21.3
Die Windows-Optik mit JGoodies Looks verbessern * .....................................
833
9.22 Swing-Komponenten neu erstellen oder verändern * .......................................................
833
9.22.1
Überlagerungen mit dem Swing-Komponenten-Dekorator JLayer ............
834
9.23 Die Zwischenablage (Clipboard) ...............................................................................................
836
9.23.1
Clipboard-Objekte .......................................................................................................
836
9.23.2
Mit Transferable auf den Inhalt zugreifen ...........................................................
837
9.23.3
DataFlavor ist das Format der Daten in der Zwischenablage ........................
838
9.23.4
Einfügungen in der Zwischenablage erkennen ..................................................
840
9.23.5
Drag & Drop ...................................................................................................................
841
9.24 Undo durchführen * .....................................................................................................................
842
9.25 AWT, Swing und die Threads .....................................................................................................
844
9.25.1
Ereignisschlange (EventQueue) und AWT-Event-Thread ................................
844
9.25.2
Swing ist nicht thread-sicher ...................................................................................
845
9.25.3
invokeLater() und invokeAndWait() ......................................................................
848
9.25.4
SwingWorker .................................................................................................................
851
9.25.5
Eigene Ereignisse in die Queue setzen * ...............................................................
853
9.25.6
Auf alle Ereignisse hören * ........................................................................................
854
9.26 Barrierefreiheit mit der Java Accessibility API .....................................................................
854
9.27 Zeitliches Ausführen mit dem javax.swing.Timer ..............................................................
855
9.28 Die Zusatzkomponentenbibliothek SwingX .........................................................................
856
9.28.1
Im Angebot: Erweiterte und neue Swing-Komponenten ...............................
857
9.28.2
Überblick über erweiterte Standard-Swing-Klassen .........................................
857
9.28.3
Neue Swing-Klassen ....................................................................................................
858
9.28.4
Weitere SwingX-Klassen ............................................................................................
859
9.28.5
SwingX-Installation ....................................................................................................
860
9.29 Alternativen zu programmierten Oberflächen, AWT und Swing * .................................
860
9.29.1
Deklarative Beschreibungen der Oberfläche: Swing JavaBuilder, Swixml
860
9.29.2
SWT (Standard Widget Toolkit) ...............................................................................
862
9.30 Zum Weiterlesen ..........................................................................................................................
864
20
Inhalt
10 Grafikprogrammierung 10.1 Grundlegendes zum Zeichnen .................................................................................................
865
10.1.1
Die paint()-Methode für das AWT-Frame ............................................................
865
10.1.2
Zeichnen von Inhalten auf ein JFrame ................................................................
867
10.1.3
Auffordern zum Neuzeichnen mit repaint() ......................................................
869
10.1.4
Java 2D-API ...................................................................................................................
869
10.2 Einfache Zeichenmethoden ......................................................................................................
870
10.2.1
Linien .............................................................................................................................
870
10.2.2
Rechtecke ......................................................................................................................
871
10.2.3
Ovale und Kreisbögen ...............................................................................................
872
10.2.4
Polygone und Polylines ............................................................................................
874
10.3 Zeichenketten schreiben und Fonts .......................................................................................
876
10.3.1
Zeichenfolgen schreiben ..........................................................................................
877
10.3.2
Die Font-Klasse ...........................................................................................................
878
10.3.3
Einen neuen Font aus einem gegebenen Font ableiten .................................
880
10.3.4
Zeichensätze des Systems ermitteln * .................................................................
880
10.3.5
Neue TrueType-Fonts in Java nutzen ...................................................................
881
10.3.6
Font-Metadaten durch FontMetrics * ..................................................................
882
10.4 Geometrische Objekte ...............................................................................................................
886
10.4.1
Die Schnittstelle Shape .............................................................................................
888
10.4.2
Kreisförmiges ..............................................................................................................
889
10.4.3
Kurviges * ......................................................................................................................
890
10.4.4
Area und die konstruktive Flächengeometrie * ................................................
890
10.4.5
Pfade * ............................................................................................................................
891
10.4.6
Punkt in einer Form, Schnitt von Linien, Abstand Punkt/Linie * ...............
895
10.5 Das Innere und Äußere einer Form ........................................................................................
896
10.5.1
Farben und die Paint-Schnittstelle .......................................................................
896
10.5.2
Farben mit der Klasse Color ....................................................................................
897
10.5.3
Die Farben des Systems über SystemColor * .....................................................
903
10.5.4
Composite und Xor * .................................................................................................
907
10.5.5
Dicke und Art der Linien von Formen bestimmen über Stroke * ...............
908
10.6 Bilder ...............................................................................................................................................
913
10.6.1
Eine Übersicht über die Bilder-Bibliotheken .....................................................
914
10.6.2
Bilder mit ImageIO lesen .........................................................................................
915
10.6.3
Ein Bild zeichnen ........................................................................................................
917
10.6.4
Programm-Icon/Fenster-Icon setzen ...................................................................
921
21
Inhalt
10.6.5
Splash-Screen * ...........................................................................................................
922
10.6.6
Bilder im Speicher erzeugen * ................................................................................
922
10.6.7
Pixel für Pixel auslesen und schreiben * ............................................................
925
10.6.8
Bilder skalieren * ........................................................................................................
927
10.6.9
Schreiben mit ImageIO ............................................................................................
929
10.6.10 Asynchrones Laden mit getImage() und dem MediaTracker * ....................
934
10.7 Weitere Eigenschaften von Graphics * .................................................................................
935
10.7.1
Eine Kopie von Graphics erstellen .......................................................................
935
10.7.2
Koordinatensystem verschieben .........................................................................
936
10.7.3
Beschnitt (Clipping) ..................................................................................................
937
10.7.4
Zeichenhinweise durch RenderingHints ...........................................................
941
10.7.5
Transformationen mit einem AffineTransform-Objekt ...............................
941
10.8 Drucken * .......................................................................................................................................
944
10.8.1
Drucken der Inhalte ..................................................................................................
944
10.8.2
Bekannte Drucker ......................................................................................................
946
10.9 Benutzerinteraktionen automatisieren, Robot und Screenshots * ..............................
947
10.9.1
Der Roboter .................................................................................................................
947
10.9.2
Automatisch in die Tasten hauen ........................................................................
948
10.9.3
Automatisierte Mausoperationen .......................................................................
949
10.9.4
Methoden zur Zeitsteuerung .................................................................................
949
10.9.5
Bildschirmabzüge (Screenshots) ...........................................................................
950
10.9.6
Funktionsweise und Beschränkungen ................................................................
952
10.9.7
MouseInfo und PointerInfo ...................................................................................
952
10.10 Zum Weiterlesen ........................................................................................................................
953
11 Netzwerkprogrammierung 11.1 Grundlegende Begriffe .............................................................................................................
955
11.2 URI und URL ..................................................................................................................................
957
11.2.1
Die Klasse URI .............................................................................................................
957
11.2.2
Die Klasse URL ............................................................................................................
958
11.2.3
Informationen über eine URL * .............................................................................
961
11.2.4
Der Zugriff auf die Daten über die Klasse URL .................................................
963
11.3 Die Klasse URLConnection * .....................................................................................................
965
22
11.3.1
Methoden und Anwendung von URLConnection ...........................................
965
11.3.2
Protokoll- und Content-Handler ..........................................................................
967
Inhalt
11.3.3
Im Detail: vom URL zur URLConnection ............................................................
968
11.3.4
Der Protokoll-Handler für Jar-Dateien ................................................................
969
11.3.5
Basic Authentication und Proxy-Authentifizierung .......................................
971
11.4 Mit GET und POST Daten übergeben * ..................................................................................
973
11.4.1
Kodieren der Parameter für Serverprogramme ...............................................
974
11.4.2
Eine Suchmaschine mit GET-Request ansprechen ..........................................
975
11.4.3
POST-Request absenden ...........................................................................................
976
11.5 Host- und IP-Adressen ...............................................................................................................
977
11.5.1
Lebt der Rechner? .......................................................................................................
980
11.5.2
IP-Adresse des lokalen Hosts ..................................................................................
980
11.5.3
Das Netz ist klasse ......................................................................................................
981
11.5.4
NetworkInterface .......................................................................................................
981
11.6 Mit dem Socket zum Server ......................................................................................................
983
11.6.1
Das Netzwerk ist der Computer .............................................................................
983
11.6.2
Sockets ...........................................................................................................................
983
11.6.3
Eine Verbindung zum Server aufbauen ..............................................................
984
11.6.4
Server unter Spannung: die Ströme .....................................................................
985
11.6.5
Die Verbindung wieder abbauen ...........................................................................
986
11.6.6
Informationen über den Socket * ..........................................................................
986
11.6.7
Reine Verbindungsdaten über SocketAddress * ...............................................
988
11.7 Client-Server-Kommunikation ................................................................................................
989
11.7.1
Warten auf Verbindungen .......................................................................................
990
11.7.2
Ein Multiplikationsserver ........................................................................................
991
11.7.3
Blockierendes Lesen ..................................................................................................
995
11.7.4
Von außen erreichbar sein * ...................................................................................
996
11.8 Apache HttpComponents und Commons Net * ..................................................................
997
11.8.1
HttpComponents .......................................................................................................
997
11.8.2
Apache Commons Net ..............................................................................................
998
11.9 Arbeitsweise eines Webservers * ............................................................................................
998
11.9.1
Das Hypertext Transfer Protocol (HTTP) ............................................................
998
11.9.2
Anfragen an den Server ............................................................................................
999
11.9.3
Die Antworten vom Server ...................................................................................... 1002
11.9.4
Webserver mit com.sun.net.httpserver.HttpServer ........................................ 1006
11.10 Verbindungen durch einen Proxy-Server * .......................................................................... 1007 11.10.1 System-Properties ...................................................................................................... 1008 11.10.2 Verbindungen durch die Proxy-API ..................................................................... 1008
23
Inhalt
11.11 Datagram-Sockets * ...................................................................................................................
1010
11.11.1 Die Klasse DatagramSocket ....................................................................................
1012
11.11.2 Datagramme und die Klasse DatagramPacket .................................................
1013
11.11.3 Auf ein hereinkommendes Paket warten ..........................................................
1014
11.11.4 Ein Paket zum Senden vorbereiten ......................................................................
1015
11.11.5 Methoden der Klasse DatagramPacket ...............................................................
1016
11.11.6 Das Paket senden .......................................................................................................
1017
11.12 E-Mail * ..........................................................................................................................................
1019
11.12.1 Wie eine E-Mail um die Welt geht ........................................................................
1019
11.12.2 Das Simple Mail Transfer Protocol und RFC 822 .............................................
1019
11.12.3 POP (Post Office Protocol) .......................................................................................
1020
11.12.4 Die JavaMail API .........................................................................................................
1020
11.12.5 E-Mails mittels POP3 abrufen ................................................................................
1023
11.12.6 Multipart-Nachrichten verarbeiten .....................................................................
1025
11.12.7 E-Mails versenden .....................................................................................................
1027
11.12.8 Ereignisse und Suchen .............................................................................................
1030
11.13 Tiefer liegende Netzwerkeigenschaften * ...........................................................................
1031
11.13.1 Internet Control Message Protocol (ICMP) ........................................................
1031
11.13.2 MAC-Adresse ...............................................................................................................
1032
11.14 Zum Weiterlesen ........................................................................................................................
1033
12 Verteilte Programmierung mit RMI 12.1 Entfernte Objekte und Methoden .........................................................................................
1035
12.1.1
Stellvertreter helfen bei entfernten Methodenaufrufen ..............................
1035
12.1.2
Standards für entfernte Objekte ...........................................................................
1037
12.2 Java Remote Method Invocation ...........................................................................................
1038
12.2.1
Zusammenspiel von Server, Registry und Client ............................................
1038
12.2.2
Wie die Stellvertreter die Daten übertragen .....................................................
1038
12.2.3
Probleme mit entfernten Methoden ...................................................................
1039
12.2.4
Nutzen von RMI bei Middleware-Lösungen ......................................................
1040
12.2.5
Zentrale Klassen und Schnittstellen ....................................................................
1041
12.2.6
Entfernte und lokale Objekte im Vergleich .......................................................
1042
12.3 Auf der Serverseite .....................................................................................................................
24
1042
12.3.1
Entfernte Schnittstelle deklarieren ......................................................................
1042
12.3.2
Remote-Objekt-Implementierung .......................................................................
1043
Inhalt
12.3.3
Stellvertreterobjekte ................................................................................................. 1044
12.3.4
Der Namensdienst (Registry) .................................................................................. 1045
12.3.5
Remote-Objekt-Implementierung exportieren und beim Namensdienst anmelden ........................................................................................ 1047
12.3.6
Einfaches Logging ...................................................................................................... 1049
12.3.7
Aufräumen mit dem DGC * ..................................................................................... 1050
12.4 Auf der Clientseite ...................................................................................................................... 1051 12.5 Entfernte Objekte übergeben und laden ............................................................................. 1052 12.5.1
Klassen vom RMI-Klassenlader nachladen ........................................................ 1052
12.6 Weitere Eigenschaften von RMI .............................................................................................. 1053 12.6.1
RMI und CORBA .......................................................................................................... 1053
12.6.2
RMI über HTTP getunnelt ........................................................................................ 1054
12.6.3
Automatische Remote-Objekt-Aktivierung ....................................................... 1054
12.7 Java Message Service (JMS) ...................................................................................................... 1055 12.8 Zum Weiterlesen ......................................................................................................................... 1055
13 RESTful und SOAP Web-Services 13.1 Web-Services ................................................................................................................................ 1057 13.2 RESTful Web-Services ................................................................................................................. 1059 13.2.1
Aus Prinzip REST .........................................................................................................
13.2.2
Jersey .............................................................................................................................. 1060
1059
13.2.3
JAX-RS-Annotationen für den ersten REST-Service ......................................... 1061
13.2.4
Test-Server starten ..................................................................................................... 1062
13.2.5
REST-Services konsumieren .................................................................................... 1063
13.2.6
Content-Hander, Marshaller und verschiedene MIME-Typen ..................... 1064
13.2.7
REST-Parameter .......................................................................................................... 1067
13.2.8
REST-Services mit Parametern über die Jersey-Client-API aufrufen .......... 1069
13.2.9
PUT-Anforderungen und das Senden von Daten ............................................. 1070
13.2.10 PUT/POST/DELETE-Sendungen mit der Jersey-Client-API absetzen .......... 1071
13.3 Daily Soap und das SOAP-Protokoll ....................................................................................... 1071 13.3.1
Die technische Realisierung .................................................................................... 1072
13.3.2
Web-Service-APIs und Implementierungen ...................................................... 1072
13.3.3
@WebService in Java 6 ............................................................................................. 1073
13.3.4
Einen Web-Service definieren ................................................................................ 1074
25
Inhalt
13.3.5
Web-Services veröffentlichen ................................................................................
1075
13.3.6
Einen JAX-WS-Client implementieren ................................................................
1076
13.4 Zum Weiterlesen ........................................................................................................................
1078
14 JavaServer Pages und Servlets 14.1 Dynamisch generierte Webseiten .........................................................................................
1079
14.1.1
Was sind Servlets? .....................................................................................................
1080
14.1.2
Was sind JavaServer Pages? ....................................................................................
1081
14.2 Servlets und JSPs mit Tomcat entwickeln ...........................................................................
1082
14.2.1
Servlet-Container .......................................................................................................
1082
14.2.2
Entwicklung der Servlet/JSP-Spezifikationen ...................................................
1083
14.2.3
Webserver mit Servlet-Funktionalität ................................................................
1083
14.2.4
Tomcat installieren ...................................................................................................
1084
14.2.5
Ablageort für eigene JSPs ........................................................................................
1086
14.2.6
Webapplikationen .....................................................................................................
1087
14.2.7
Zuordnung von Webapplikationen zu physikalischen Verzeichnissen ...
1088
14.2.8
Web-Projekt mit Eclipse IDE for Java EE Developers ......................................
1088
14.3 Statisches und Dynamisches ...................................................................................................
1090
14.3.1
Statischer Template-Code .......................................................................................
1090
14.3.2
Dynamische Inhalte ..................................................................................................
1090
14.3.3
Kommentare ...............................................................................................................
1091
14.4 Die Expression Language (EL) ..................................................................................................
1091
14.4.1
Operatoren der EL ......................................................................................................
1092
14.4.2
Literale ..........................................................................................................................
1092
14.4.3
Implizite EL-Objekte .................................................................................................
1093
14.5 Formulardaten .............................................................................................................................
1094
14.5.1
Einen Parameter auslesen ......................................................................................
1094
14.5.2
HTML-Formulare .......................................................................................................
1095
14.6 Auf Beans zurückgreifen .......................................................................................................... 14.6.1
26
Beans in JSPs anlegen ...............................................................................................
1096 1097
14.6.2
Properties einer Bean im EL-Ausdruck erfragen ..............................................
1097
14.6.3
Properties mit setzen ............................................................
1097
14.6.4
Bean-Klasse zum Testen von E-Mail-Adressen .................................................
1098
14.6.5
Parameterwerte in Bean übertragen ...................................................................
1100
Inhalt
14.7 JSP-Tag-Libraries .......................................................................................................................... 1101 14.7.1
Standard Tag Library (JSTL) ..................................................................................... 1101
14.8 Skripting-Elemente in JSPs ....................................................................................................... 1105 14.8.1
Scriptlets ....................................................................................................................... 1106
14.8.2
JSP-Ausdrücke ............................................................................................................. 1106
14.8.3
JSP-Deklarationen ...................................................................................................... 1107
14.8.4
Quoting .......................................................................................................................... 1107
14.8.5
Entsprechende XML-Tags ........................................................................................ 1108
14.8.6
Implizite Objekte für Scriptlets und JSP-Ausdrücke ........................................ 1108
14.9 Sitzungsverfolgung (Session Tracking) ................................................................................. 1109 14.9.1
Lösungen für die Sitzungsverfolgung .................................................................. 1109
14.9.2
Sitzungen in JSPs ........................................................................................................ 1110
14.9.3
Auf Session-Dateien zurückgreifen ...................................................................... 1111
14.10 Servlets ........................................................................................................................................... 1111 14.10.1 Servlets compilieren ................................................................................................. 1115 14.10.2 Servlet-Mapping ......................................................................................................... 1116 14.10.3 Der Lebenszyklus eines Servlets ............................................................................ 1117 14.10.4 Mehrere Anfragen beim Servlet und die Thread-Sicherheit ......................... 1118 14.10.5 Servlets und Sessions ................................................................................................ 1118 14.10.6 Weiterleiten und Einbinden von Servlet-Inhalten .......................................... 1119
14.11 Zum Weiterlesen ......................................................................................................................... 1120
15 Applets 15.1 Applets in der Wiege von Java ................................................................................................ 1123 15.1.1
Applets heute ............................................................................................................... 1123
15.1.2
(J)Applet und Applikationen ................................................................................... 1124
15.1.3
Das erste Hallo-Applet .............................................................................................. 1125
15.2 Die Applet-API .............................................................................................................................. 1127 15.2.1
Die Zyklen eines Applets .......................................................................................... 1127
15.2.2
Parameter an das Applet übergeben .................................................................... 1127
15.2.3
Wie das Applet den Browser-Inhalt ändern kann * .......................................... 1129
15.2.4
Den Ursprung des Applets erfragen ..................................................................... 1130
15.2.5
Datenaustausch zwischen Applets * ..................................................................... 1131
15.2.6
Was ein Applet alles darf * ....................................................................................... 1134
27
Inhalt
15.2.7
Ist Java im Browser aktiviert? * ..............................................................................
1135
15.2.8
Applet unter Firefox (Netscape) oder Microsoft Internet Explorer? * .......
1135
15.3 Webstart .......................................................................................................................................
1137
15.4 Zum Weiterlesen ........................................................................................................................
1137
16 Datenbankmanagement mit JDBC 16.1 Relationale Datenbanken ........................................................................................................ 16.1.1
1139
Das relationale Modell .............................................................................................
1139
16.2 Datenbanken und Tools ............................................................................................................
1140
16.2.1
HSQLDB .........................................................................................................................
1141
16.2.2
Weitere Datenbanken * ............................................................................................
1142
16.2.3
Eclipse-Plugins zum Durchschauen von Datenbanken .................................
1145
16.3 JDBC und Datenbanktreiber ....................................................................................................
1148
16.3.1
Treibertypen * .............................................................................................................
1149
16.3.2
JDBC-Versionen * .......................................................................................................
1151
16.4 Eine Beispielabfrage ..................................................................................................................
1152
16.4.1
Schritte zur Datenbankabfrage .............................................................................
16.4.2
Ein Client für die HSQLDB-Datenbank ................................................................
1153
16.4.3
Datenbankbrowser und eine Beispielabfrage unter NetBeans ...................
1155
16.5 Mit Java an eine Datenbank andocken ................................................................................ 16.5.1
Der Treiber-Manager * ..............................................................................................
1159 1160
16.5.2
Den Treiber laden ......................................................................................................
1160
16.5.3
Eine Aufzählung aller Treiber * ..............................................................................
1162
16.5.4
Log-Informationen * .................................................................................................
1162
16.5.5
Verbindung zur Datenbank auf- und abbauen ................................................
1163
16.6 Datenbankabfragen ...................................................................................................................
28
1152
1167
16.6.1
Abfragen über das Statement-Objekt ..................................................................
1168
16.6.2
Ergebnisse einer Abfrage in ResultSet ................................................................
1169
16.6.3
Java und SQL-Datentypen .......................................................................................
1170
16.6.4
Date, Time und Timestamp ....................................................................................
1173
16.6.5
Unicode in der Spalte korrekt auslesen ..............................................................
1175
16.6.6
Eine SQL-NULL und wasNull() bei ResultSet ......................................................
1175
16.6.7
Wie viele Zeilen hat ein ResultSet? * ....................................................................
1176
Inhalt
16.7 Elemente einer Datenbank hinzufügen und aktualisieren ............................................. 1176 16.7.1
Batch-Updates ............................................................................................................. 1177
16.7.2
Die Ausnahmen bei JDBC, SQLException und Unterklassen ........................ 1179
16.8 ResultSet und RowSet * ............................................................................................................. 1182 16.8.1
Die Schnittstelle RowSet .......................................................................................... 1182
16.8.2
Implementierungen von RowSet .......................................................................... 1182
16.8.3
Der Typ CachedRowSet ............................................................................................. 1183
16.8.4
Der Typ WebRowSet .................................................................................................. 1184
16.9 Vorbereitete Anweisungen (Prepared Statements) .......................................................... 1187 16.9.1
PreparedStatement-Objekte vorbereiten ........................................................... 1187
16.9.2
Werte für die Platzhalter eines PreparedStatement ........................................ 1189
16.10 Transaktionen .............................................................................................................................. 1190 16.11 Metadaten * .................................................................................................................................. 1190 16.11.1 Metadaten über die Tabelle ..................................................................................... 1190 16.11.2 Informationen über die Datenbank ..................................................................... 1194
16.12 Vorbereitete Datenbankverbindungen ................................................................................ 1195 16.12.1 DataSource ................................................................................................................... 1195 16.12.2 Gepoolte Verbindungen ........................................................................................... 1198
16.13 JPA-Beispiel mit der NetBeans-IDE ......................................................................................... 1199 16.13.1 Entity-Beans generieren ........................................................................................... 1199 16.13.2 Die Quellen im Überblick ......................................................................................... 1201 16.13.3 Persistence Unit .......................................................................................................... 1203 16.13.4 Ein JPA-Beispielprogramm ...................................................................................... 1204
16.14 Zum Weiterlesen ......................................................................................................................... 1205
17 Technologien für die Infrastruktur 17.1 Property-Validierung durch Bean Validation ...................................................................... 1207 17.1.1
Technische Abhängigkeiten und POJOs .............................................................. 1218
17.2 Wie eine Implementierung an die richtige Stelle kommt ............................................... 1220 17.2.1
Arbeiten mit dem ServiceLoader ........................................................................... 1221
17.2.2
Die Utility-Klasse Lookup als ServiceLoader-Fassade ..................................... 1223
17.2.3
Contexts and Dependency Injection (CDI) aus dem JSR-299 ........................ 1224
17.3 Zum Weiterlesen ......................................................................................................................... 1226
29
Inhalt
18 Reflection und Annotationen 18.1 Metadaten .................................................................................................................................... 18.1.1
1227
Metadaten durch JavaDoc-Tags .............................................................................
1227
18.2 Metadaten der Klassen mit dem Class-Objekt ...................................................................
1228
18.2.1
An ein Class-Objekt kommen ................................................................................
1228
18.2.2
Was das Class-Objekt beschreibt * ........................................................................
1231
18.2.3
Der Name der Klasse .................................................................................................
1233
18.2.4
instanceof mit Class-Objekten * ............................................................................
1235
18.2.5
Oberklassen finden * .................................................................................................
1236
18.2.6
Implementierte Interfaces einer Klasse oder eines Interfaces * .................
1237
18.2.7
Modifizierer und die Klasse Modifier * ...............................................................
1238
18.2.8
Die Arbeit auf dem Feld * .........................................................................................
1240
18.3 Attribute, Methoden und Konstruktoren ............................................................................
1241
18.3.1
Reflections – Gespür für die Attribute einer Klasse ........................................
18.3.2
Methoden einer Klasse erfragen ...........................................................................
1246
18.3.3
Properties einer Bean erfragen .............................................................................
1250
18.3.4
Konstruktoren einer Klasse ....................................................................................
1250
18.3.5
Annotationen ..............................................................................................................
1253
18.4 Objekte erzeugen und manipulieren .................................................................................... 18.4.1
1253
Objekte erzeugen .......................................................................................................
1253
18.4.2
Die Belegung der Variablen erfragen ..................................................................
1255
18.4.3
Eine generische eigene toString()-Methode * ...................................................
1257
18.4.4
Variablen setzen .........................................................................................................
1260
18.4.5
Bean-Zustände kopieren * .......................................................................................
1262
18.4.6
Private Attribute ändern .........................................................................................
1262
18.4.7
Methoden aufrufen ...................................................................................................
1263
18.4.8
Statische Methoden aufrufen ................................................................................
1265
18.4.9
Dynamische Methodenaufrufe bei festen Methoden beschleunigen * ....
1266
18.5 Eigene Annotationstypen * ......................................................................................................
30
1243
1268
18.5.1
Annotationen zum Laden von Ressourcen .......................................................
1268
18.5.2
Neue Annotationen deklarieren ...........................................................................
1269
18.5.3
Annotationen mit genau einem Attribut ..........................................................
1270
18.5.4
Element-Werte-Paare (Attribute) hinzufügen ..................................................
1271
18.5.5
Annotationsattribute vom Typ einer Aufzählung ..........................................
1272
Inhalt
18.5.6
Felder von Annotationsattributen ....................................................................... 1273
18.5.7
Vorbelegte Attribute ................................................................................................. 1274
18.5.8
Annotieren von Annotationstypen ...................................................................... 1276
18.5.9
Deklarationen für unsere Ressourcen-Annotationen .................................... 1280
18.5.10 Annotierte Elemente auslesen ............................................................................... 1282 18.5.11 Auf die Annotationsattribute zugreifen ............................................................. 1283 18.5.12 Komplettbeispiel zum Initialisieren von Ressourcen .................................... 1284 18.5.13 Mögliche Nachteile von Annotationen ............................................................... 1287
18.6 Zum Weiterlesen ......................................................................................................................... 1288
19 Dynamische Übersetzung und Skriptsprachen 19.1 Codegenerierung ......................................................................................................................... 1290 19.1.1
Generierung von Java-Quellcode .......................................................................... 1291
19.1.2
Codetransformationen ............................................................................................. 1294
19.1.3
Erstellen von Java-Bytecode .................................................................................... 1294
19.2 Programme mit der Compiler API übersetzen .................................................................... 1295 19.2.1
Java Compiler API ....................................................................................................... 1295
19.2.2
Fehler-Diagnose .......................................................................................................... 1298
19.2.3
Eine im String angegebene Compiliationseinheit übersetzen .................... 1300
19.2.4
Wenn Quelle und Ziel der Speicher sind ............................................................. 1302
19.3 Ausführen von Skripten ............................................................................................................ 1306 19.3.1
JavaScript-Programme ausführen ........................................................................ 1306
19.3.2
Groovy ........................................................................................................................... 1308
19.4 Zum Weiterlesen ......................................................................................................................... 1314
20 Logging und Monitoring 20.1 Logging mit Java .......................................................................................................................... 1315 20.1.1
Logging-APIs ................................................................................................................ 1315
20.1.2
Logging mit java.util.logging .................................................................................. 1316
20.1.3
Logging mit log4j * ..................................................................................................... 1320
20.1.4
Die Simple Logging Facade ...................................................................................... 1323
20.2 Systemzustände überwachen .................................................................................................. 1324
31
Inhalt
20.3 MBean-Typen, MBean-Server und weitere Begriffe ......................................................... 20.3.1
1325
MXBeans des Systems ..............................................................................................
1325
20.4 Geschwätzige Programme und JConsole .............................................................................
1328
20.4.1
JConsole ........................................................................................................................
1328
20.5 Der MBeanServer ........................................................................................................................
1330
20.6 Eine eigene Standard-MBean ..................................................................................................
1331
20.6.1
Management-Schnittstelle .....................................................................................
1332
20.6.2
Implementierung der Managed-Ressource .......................................................
1332
20.6.3
Anmeldung beim Server .........................................................................................
1333
20.6.4
Eine eigene Bean in JConsole einbringen ..........................................................
1333
20.7 Zum Weiterlesen ........................................................................................................................
1336
21 Java Native Interface (JNI) 21.1 Java Native Interface und Invocation-API ...........................................................................
1337
21.2 Eine C-Funktion in ein Java-Programm einbinden ............................................................
1338
21.2.1
Den Java-Code schreiben .........................................................................................
1338
21.3 Dynamische Bibliotheken erzeugen .....................................................................................
1340
21.3.1
Die Header-Datei erzeugen .....................................................................................
1340
21.3.2
Implementierung der Funktion in C ...................................................................
1342
21.3.3
Die C-Programme übersetzen und die dynamische Bibliothek überzeugen ..................................................................................................................
1343
21.4 Nativ die Stringlänge ermitteln .............................................................................................
1345
21.5 Erweiterte JNI-Eigenschaften ..................................................................................................
1347
21.5.1
Klassendefinitionen .................................................................................................
1347
21.5.2
Zugriff auf Attribute .................................................................................................
1348
21.5.3
Methoden aufrufen ...................................................................................................
1350
21.5.4
Threads und Synchronisation ...............................................................................
1352
21.6 Einfache Anbindung von existierenden Bibliotheken .....................................................
1352
21.6.1
Generieren von JNI-Wrappern aus C++-Klassen und C-Headern ................
1352
21.6.2
COM-Schnittstellen anzapfen ................................................................................
1353
21.7 Invocation-API .............................................................................................................................
1353
21.8 Zum Weiterlesen ........................................................................................................................
1354
32
Inhalt
22 Sicherheitskonzepte 22.1 Zentrale Elemente der Java-Sicherheit ................................................................................. 1355 22.1.1
Security-API der Java SE ............................................................................................ 1355
22.1.2
Cryptographic Service Providers ........................................................................... 1356
22.2 Der Sandkasten (Sandbox) ....................................................................................................... 1357 22.3 Sicherheitsmanager (Security Manager) .............................................................................. 1358 22.3.1
Der Sicherheitsmanager bei Applets .................................................................... 1359
22.3.2
Sicherheitsmanager aktivieren .............................................................................. 1361
22.3.3
Rechte durch Policy-Dateien vergeben ............................................................... 1363
22.3.4
Erstellen von Rechtedateien mit dem grafischen Policy-Tool ..................... 1365
22.3.5
Kritik an den Policies ................................................................................................ 1366
22.4 Signierung ..................................................................................................................................... 1368 22.4.1
Warum signieren? ...................................................................................................... 1368
22.4.2
Digitale Ausweise und die Zertifizierungsstelle ............................................... 1368
22.4.3
Mit keytool Schlüssel erzeugen ............................................................................. 1369
22.4.4
Signieren mit jarsigner ............................................................................................. 1370
22.5 Digitale Unterschriften * ........................................................................................................... 1371 22.5.1
Die MDx-Reihe ............................................................................................................ 1371
22.5.2
Secure Hash Algorithm (SHA) ................................................................................ 1372
22.5.3
Mit der Security-API einen Fingerabdruck berechnen ................................... 1372
22.5.4
Die Klasse MessageDigest ........................................................................................ 1373
22.6 Verschlüsseln von Daten(-strömen) * .................................................................................... 1375 22.6.1
Den Schlüssel, bitte .................................................................................................... 1375
22.6.2
Verschlüsseln mit Cipher ......................................................................................... 1377
22.6.3
Verschlüsseln von Datenströmen ......................................................................... 1377
22.7 Zum Weiterlesen ......................................................................................................................... 1379
23 Dienstprogramme für die Java-Umgebung 23.1 Programme des JDK .................................................................................................................... 1381 23.2 Monitoringprogramme vom JDK ............................................................................................ 1381 23.2.1
jps .................................................................................................................................... 1381
23.2.2
jstat ................................................................................................................................. 1382
23.2.3
jmap ................................................................................................................................ 1382
33
Inhalt
23.2.4
jstack ..............................................................................................................................
1383
23.2.5
VisualVM ......................................................................................................................
1385
23.3 Programmieren mit der Tools-API .........................................................................................
1390
23.3.1
Eigene Doclets .............................................................................................................
1390
23.4 Ant ..................................................................................................................................................
1393
23.4.1
Bezug und Installation von Ant ............................................................................
1393
23.4.2
Das Build-Skript build.xml .....................................................................................
1394
23.4.3
Build den Build ...........................................................................................................
1394
23.4.4
Properties .....................................................................................................................
1395
23.4.5
Externe und vordefinierte Properties .................................................................
1396
23.4.6
Weitere Ant-Tasks ......................................................................................................
1397
23.5 Disassembler, Decompiler und Obfuscator ......................................................................... 23.5.1
1398
Der Diassembler javap .............................................................................................
1399
23.5.2
Decompiler ..................................................................................................................
1404
23.5.3
Obfuscatoren ...............................................................................................................
1406
23.6 Weitere Dienstprogramme ......................................................................................................
1408
23.6.1
Sourcecode Beautifier ..............................................................................................
1408
23.6.2
Java-Programme als Systemdienst ausführen .................................................
1409
23.7 Zum Weiterlesen ........................................................................................................................
1410
Index ............................................................................................................................................................
1411
34
Vorwort »Alles, was einen Anfang hat, hat auch ein Ende, und meistens hat das, was ein Ende hat, auch eine Fortsetzung.« – Sprichwort
Willkommen im zweiten Teil der Insel. Während der erste Band in die Grundlagen von Java einführte und sich dabei insbesondere auf die objektorientierten Konzepte und die Syntax der Sprache Java konzentrierte, kümmert sich der zweite Band um die Bibliotheken aus der Java SE.
Organisation der Kapitel Den Einsteig ins Buch bildet Kapitel 1, »Neues in Java 7«, mit einer kompakten Darstellung der Neuerungen. Die Änderungen in den Bibliotheken fließen dabei in die jeweiligen Kapitel ein; so konzentriert sich das kurze Kapitel eher auf die neuen Spracheigenschaften. Kapitel 2 beschäftigt sich mit »Threads und nebenläufiger Programmierung«. Das Kapitel
umfasst auch die Koordination mehrerer kooperierender oder konkurrierender Threads. Seit Java 5 ist ein neues java.util.concurrent-Paket hinzugekommen, das die Programmierung nebenläufiger Programme gegenüber Java 1.0 sehr vereinfacht – dieses neue Paket bildet die Grundlage des Kapitels. Kapitel 3 beschäftigt sich mit den »Datenstrukturen und Algorithmen«, die die Standard-
bibliothek anbietet. Die wichtigsten Klassen wie Listen, Mengen, Stapel, Bitmengen und Assoziativspeicher werden vorgestellt, und dann werden unterschiedliche Aufgaben mit den jeweils passenden Datenstrukturen gelöst. Als Algorithmen kommen beispielsweise vorgefertigte Sortierverfahren zum Einsatz. Das Kapitel diskutiert ebenfalls für Nebenläufigkeit optimierte Datenstrukturen. Zeitzonen und unterschiedliche Ausgabeformate für Datumswerte sind Thema in Kapitel 4, »Raum und Zeit«. Darunter fallen auch Datumsberechnungen auf der Grundlage des gregorianischen Kalenders. Interessant sind Java-Möglichkeiten zur Internationalisierung, was sich durchweg durch alle APIs zieht, die in irgendeiner Weise Nachrichten zum Benutzer geben.
35
Vorwort
Das Kapitel 5, »Dateien, Verzeichnisse und Dateizugriffe«, setzt den Fokus auf Dateiverarbeitung. Zuerst zeigen wir, wie sich Attribute von Dateien und Verzeichnissen auslesen lassen, und dann, wie sich wahlfreier Zugriff auf eine Datei realisieren lässt. Das in Java 7 eingeführte NIO.2-Paket nimmt dabei einen großen Raum ein, da es sehr leistungsfähig ist. Nahtlos folgt in Kapitel 6, »Datenströme«, die Verarbeitung von Daten aus beliebigen Quellen und Senken. Während sich Kapitel 5 nur auf Dateien beschränkt, geht es in diesem Kapitel um allgemeinere Ein-/Ausgabe-Konzepte, die auch bei Datenströmen aus Netzwerken, Datenbanken oder Schnittstellen vorkommen. Die Datenströme können dabei durch Filter geschickt werden. Von Letzteren stellen wir einige vor, die zum Beispiel die Zeilennummer zählen, einen Datenstrom puffern oder ihn komprimieren. Eine elegante Möglichkeit ist das Serialisieren von Objekten. Dabei wird der Zustand eines Objekts ausgelesen und so in einen Datenstrom geschrieben, dass sich das Objekt später wiederherstellen lässt. Eine eigene Speicherroutine kann somit entfallen. Ein neues Thema spannt Kapitel 7 mit »Die eXtensible Markup Language (XML)« auf. Java als plattformunabhängige Programmiersprache und XML als dokumentenunabhängige Beschreibungssprache sind ein ideales Paar, und die Kombination dieser beiden Technologien ist der Renner der letzten Jahre. Das Kapitel beginnt auf der höchsten Abstraktionsebene, beschreibt, wie XML-Daten auf Objekte übertragen werden, und geht dann weiter zur elementaren Verarbeitung der XML-Dokumente, wie sie zum Beispiel als Objekt-Baum aufgebaut und modifiziert werden. Heutzutage ist die Speicherung in XML populär, und auch Office-Dokumente werden in diesem Stil gespeichert. Doch keiner würde auf die Idee kommen, die Dokumente im XML-Format auszulesen und dort die Absätze und Zeichen herauszuziehen. Das Gleiche gilt für Grafikdateien: Es ist zwar eine Byte-Datei, doch die wird nicht als Byte für Byte von uns eingelesen, sondern es gibt immer eine API, die sich darum kümmert, Zeichen oder Bytes in ein für uns komfortables Format zu bringen. Ein paar populäre Dateiformate stellt Kapitel Kapitel 8, »Dateiformate«, vor. Zu den beschriebenen Formaten zählen nicht nur Dokumentenformate, wie für Microsoft Office, sondern auch Formate für die Kompression. Kapitel 9, »Grafische Oberflächen mit Swing«, stellt die Swing-Komponenten zur Interaktion
vor, wie zum Beispiel Schaltflächen. Es geht auf die Behandlung von Ereignissen ein, die aus Benutzeraktionen resultieren, und beschreibt Container, die andere Komponenten aufnehmen und layouten. Das anschließende Kapitel 10 deckt die zweite Aufgabe der grafischen Oberflächen ab, indem es auf die »Grafikprogrammierung« eingeht. Das AWT (Abstract Window Toolkit) ist die JavaMöglichkeit, grafische Oberflächen zu gestalten. Dabei gliedert es sich in zwei große Teile: zum einen in die direkte Ausgabe von Grafik-Primitiven wie Linien, und zum anderen in
36
Vorwort
Komponenten für grafische Oberflächen. Das Kapitel behandelt die Themen Fenster, Zeichenketten und Zeichensätze, Farben und Bilder. Kapitel 11, »Netzwerkprogrammierung«, stellt vor, welche Informationen eine URL hat und wie mit dieser URL Daten von Webservern bezogen werden können. Bei Webservern werden wir CGI-Programme ansprechen, um an gewünschte Inhalte zu kommen. Mithilfe von Sockets wollen wir eine eigene Client-Server-Kommunikation aufbauen. Außer auf die gesicherte Verbindung TCP gehen wir auch auf ungesicherte UDP-Verbindungen ein.
»Verteilte Programmierung mit RMI« in Kapitel 12 zeigt auf, wie ein Java-Programm einfach Objekte und Methoden nutzen kann, die auf einem anderen Rechner gespeichert beziehungsweise ausgeführt werden. Dabei wird der Aufruf einer Methode über das Netzwerk übertragen, und für das aufrufende Programm sieht es so aus, als ob es sich um einen normalen Methodenaufruf für ein lokales Objekt handelt. Kapitel 13, »RESTful und SOAP Web-Services«, stellt mit REST einen neuen Trend in der Kom-
munikation zwischen Browser und Web-Server vor und mit SOAP einen Klassiker für den entfernten Methodenaufruf zwischen Systemen. SOAP ist Teil der Java SE, und für REST wird Jersey vorgestellt, die Referenzimplementierung der JAX-RS-API. Mit Kapitel 14, »JavaServer Pages und Servlets«, geht es dann in die Welt der dynamischen Webseiten. Java ist zurzeit auf der Serverseite sehr populär, und dort besonders beim sogenannten Enterprise-Computing. Mit JavaServer Pages ist es besonders einfach, dynamische Webinhalte zu formulieren und auf die vom Client mitgeschickten Informationen sehr einfach zuzugreifen. JSPs verfügen zudem über die gesamten Java-Möglichkeiten, insbesondere die Fähigkeit zur Datenbankanbindung. Grafische Oberflächen und Netzwerke verbinden das Kapitel 15 zum Thema »Applets«. Applets sind Java-Programme innerhalb eines Webbrowsers. Applets machten Java vor über 10 Jahren auf der Welt bekannt, und die ersten Java-Bücher erklärten alles im Zusammenhang mit Applets. Heutzutage spielen Applets keine große Rolle mehr – auch durch die Verbreitung von Flash – und sind schon fast zu einer Nischentechnologie geworden. »Datenbankmanagement mit JDBC« ist das Thema von Kapitel 16. Als freie, quelloffene Beispieldatenbank wird HSQLDB vorgestellt, da sie sehr leicht zu installieren und zu betreiben ist und praktischerweise Beispieldaten mitbringt. Die Java-Beispiele bauen eine Verbindung zu HSQLDB auf, setzen SQL-Anweisungen ab, holen die Ergebnisse herein und visualisieren sie. Viele der vorgestellen Technologien sind sehr speziell, doch Kapitel 17, »Technologien für die Infrastruktur«, geht auf den Aufbau großer Anwendungen ein und stellt Basistechnologien vor, die ein gutes Fundament für eigene Anwendungen bilden und in jeder Architektur zu finden sein dürften, ob es nun grafische Anwendungen sind oder reine Serveranwendungen.
37
Vorwort
Mit Kapitel 18 widmen wir uns einer Java-typischen Technik: »Reflection und Annotationen«. Java-Klassen liegen selbst wieder als Meta-Objekte, als Exemplare der speziellen Klasse Class, vor. Diese Class-Objekte geben Auskunft über die verfügbaren und definierten Variablen, Methoden und Konstruktoren. So lassen sich beispielsweise dynamisch bestimmte Methoden aufrufen oder die Werte von dynamisch ausgewählten Objektvariablen abfragen. Die Annotationen sind eine bedeutende Neuerung in Java 5. Am Anfang war die JVM fest mit der Programmiersprache Java verbunden, doch das änderte sich im Laufe der Jahre. Heute gibt es unterschiedliche Programmiersprachen, die auf einer JVM laufen – von ganz entwickelten Skriptsprachen bis zu portierten. Kapitel 19, »Dynamische Übersetzung und Skriptsprachen«, stellt Skriptsprachen vor und auch die Möglichkeit, wie eigene Klassen mit der Compiler-API übersetzt werden. Wie Java-Programme zu Testzwecken überwacht werden können, zeigt Kapitel 20, »Logging und Monitoring«. Mit der JMX-API lassen sich MBeans an einem MBean-Server anmelden, und das Dienstprogramm jconsole ermöglicht den Zugriff und die Steuerung der Komponenten. Java kann nicht alles plattformunabhängig lösen. An einer bestimmen Stelle muss plattformabhängiger Programmcode eingebaut werden. Um von Java auf Nicht-Java-Code wie C(++) zuzugreifen, definiert Oracle das in Kapitel 21 vorgestellte »Java Native Interface (JNI)«. Kapitel 22 zeigt kurz »Sicherheitskonzepte«, etwa das Sandkastenprinzip, und Sicherheitsmanager auf, zeigt aber auch, wie von Daten eine Prüfsumme gebildet werden kann und wie Daten mit DES oder RSA verschlüsselt werden.
In Kapitel 23, »Dienstprogramme für die Java-Umgebung«, geht es um die zum JDK gehörigen Programme und um einige Extratools, die für Ihre Arbeit nützlich sind. Im Mittelpunkt stehen Compiler, Interpreter und die Handhabung von Jar-Archiven. Dieses Archivformat ist vergleichbar mit den bekannten Zip-Archiven und fasst mehrere Dateien zusammen. Mit den eingebetteten Dokumentationskommentaren in Java kann aus einer Quellcodedatei ganz einfach eine komplette HTML-Dokumentation der Klassen, Schnittstellen, Vererbungsbeziehungen und Eigenschaften inklusive Verlinkung erstellt werden. Unter den Programmen, die zu keiner Standardinstallation gehören, sind etwa Tools, die Java-Programme in C-Programme übersetzen, sie neu einrücken und formatieren und Bytecode wieder in lesbaren Java-Quellcode umwandeln.
Vorwort zur 1. Auflage Es ist unverkennbar, dass dieses Buch seinen Ursprung in »Java ist auch eine Insel« hat. Es enthält alle fortgeschrittenen Java-Konzepte der vorangegangenen Insel. Bei dieser Aufspaltung ist es natürlich nicht geblieben, denn sonst wäre das Buch ja auch nur 700 Seiten dick und nicht 1400. Es gab also Änderungen und Neuerungen gegenüber diesen Kapiteln in der alten Insel.
38
Vorwort
Zu den Neuerungen: Natürlich ist das Buch auf der Höhe der Zeit und beschreibt die aktuellen Java 7-Features. Das erste Kapitel fasst die bescheidenen Sprachänderungen zusammen. Aufseiten der Bibliothek gibt es zwei größere Zuwächse: NIO.2 und das kleinere Fork-And-JoinFramework. Swing bietet eine neue Klasse namens JLayer und bekommt kleine Updates, etwa durch eine generische Typvariable etwa bei ListModel oder JComboBox. Ein weiteres neues Kapitel behandelt dynamische Übersetzung mit dem Java-Compiler zur Laufzeit und Skriptsprachen, wobei es einige Beispiele zur beliebten JVM-Sprache Groovy gibt. Und da Web-Services nicht mehr nur durch SOAP realisiert werden, gibt es ein Unterkapitel zum REST-Prinzip und JAW-RS. Im Swing-Kapitel ist eine bildgeführte Anleitung zu finden, wie sich mit NetBeans einfach grafische Oberflächen entwickeln lassen. Am Ende des Swing-Kapitels befindet sich ein Ausblick auf die freie Komponentenbibliothek SwingX, mit der Entwickler die GUIs aufhübschen können. Das Datenbankkapitel hat einen Ausblick auf JPA bekommen, wobei die Beispiele hier auch wieder mit NetBeans realisiert werden. Zu den Aktualisierungen: Auch ohne die Anpassungen aus Java 7 gibt es Verbesserungen und Aktualisierungen. Im Tooling-Bereich ist der Decompiler Jad durch JD ersetzt worden. Gleichzeitig gibt das gleiche Kapitel eine Einführung in Java-Bytecode und das Tool javap. Weiterhin im Bereich Datenbanktools ist eine kleine Änderung nötig geworden, da der Datenbankbrowser in die Eclipse Data Tools Platform (DTP) gewandert ist und sich auch die grafische Oberfläche veränderte. Im Bereich Java ME ist das Plugin zu Eclipse gewandert und trägt jetzt den neuen Namen Eclipse Mobile Tools for Java (MTJ).
Christian Ullenboom
Sonsbeck, im Oktober 2011
39
1
Kapitel 1 Neues in Java 7 »Jede Lösung eines Problems ist ein neues Problem.« – Johann Wolfgang von Goethe (1749–1832)
1.1
Sprachänderungen
Die folgenden Abschnitte stellen Sprachänderungen aus Java 7 vor.
1.1.1
Zahlen im Binärsystem schreiben
Die Literale für Ganzzahlen lassen sich in vier unterschiedlichen Stellenwertsystemen angeben. Das natürlichste ist das Dezimalsystem (auch Zehnersystem genannt), bei dem die Literale aus den Ziffern »0« bis »9« bestehen. Zusätzlich existieren Oktal- und Hexadezimalsysteme, und seit Java 7 die Binärschreibweise. Bis auf Dezimalzahlen beginnen die Zahlen in anderen Formaten mit einem besonderen Präfix. Präfix
Stellenwertsystem
Basis
Darstellung von 1
0b oder 0B
Binär
2
0b1 oder 0B1
0
Oktal
8
01
Kein
Dezimal
10
1
0x oder 0X
Hexadezimal
16
0x1 oder 0X1
Tabelle 1.1: Die Stellenwertsysteme und ihre Schreibweise
1.1.2 Unterstriche bei Ganzzahlen Um eine Anzahl von Millisekunden in Tage zu konvertieren, muss einfach eine Division vorgenommen werden. Um Millisekunden in Sekunden umzurechnen, brauchen wir eine Division durch 1000, von Sekunden auf Minuten eine Division durch 60, von Minuten auf Stun-
41
1
Neues in Java 7
den eine Division durch 60, und die Stunden auf Tage bringt die letzte Division durch 24. Schreiben wir das auf: long millis = 10 * 24*60*60*1000L; long days = millis / 86400000L; System.out.println( days );
// 10
Eine Sache fällt bei der Zahl 86400000 auf: Besonders gut lesbar ist sie nicht. Die eine Lösung ist, es erst gar nicht zu so einer Zahl kommen zu lassen und sie wie in der ersten Zeile durch eine Reihe von Multiplikationen aufzubauen – mehr Laufzeit kostet das nicht, da dieser konstante Ausdruck zur Übersetzungszeit feststeht. Die zweite Variante ist eine neue Schreibweise, die Java 7 einführt: Unterstriche in Zahlen. Anstatt ein numerisches Literal als 86400000 zu schreiben, ist in Java 7 auch Folgendes erlaubt: long millis = 10 * 86_400_000L; long days = millis / 86_400_000L; System.out.println( days );
// 10
Die Unterstriche machen die 1000er-Blöcke gut sichtbar. Hilfreich ist die Schreibweise auch bei Literalen in Binär- und Hexdarstellung, da die Unterstriche hier ebenfalls Blöcke absetzen können.1
Beispiel Unterstriche verdeutlichen Blöcke bei Binär- und Hexadezimalzahlen. int i = 0b01101001_01001101_11100101_01011110; long l = 0x7fff_ffff_ffff_ffffL;
Der Unterstrich darf in jedem Literal stehen, zwei aufeinanderfolgende Unterstriche sind aber nicht erlaubt.
1.1.3 switch (String) Seit Java 7 sind switch-Anweisungen auf String-Objekten möglich. String input = javax.swing.JOptionPane.showInputDialog( "Eingabe" ); switch ( input.toLowerCase() ) 1
42
Bei Umrechnungen zwischen Stunden, Minuten und so weiter hilft auch die Klasse TimeUnit mit einigen statischen toXXX()-Methoden.
1.1
Sprachänderungen
{
1
case "kekse": System.out.println( "Ich mag Keeeekse" ); break; case "kuchen": System.out.println( "Ich mag Kuchen" ); break; case "scholokade": case "lakritze": System.out.println( "Hm. lecker" ); break; default: System.out.printf( "Kann man %s essen?", input ); }
Obwohl Zeichenkettenvergleiche nun möglich sind, fallen Überprüfungen auf reguläre Ausdrücke leider heraus, die insbesondere Skriptsprachen anbieten. Wie auch beim switch mit Ganzzahlen können die Zeichenketten beim String-case-Zweig aus finalen (also nicht änderbaren) Variablen stammen. Ist etwa String KEKSE = "kekse"; vordefiniert, ist case KEKSE: erlaubt.
1.1.4 Zusammenfassen gleicher catch-Blöcke mit dem multi-catch Greift ein Programm auf Teile zurück, die scheitern können, so ergeben sich in komplexeren Abläufen schnell Situationen, in denen unterschiedliche Ausnahmen auftreten können. Entwickler sollten versuchen, den Programmcode in einem try-Block durchzuschreiben, und dann in catch-Blöcken auf alle möglichen Fehler zu reagieren, die den Block vom korrekten Durchlaufen abgehalten haben. Oftmals kommt es zu dem Phänomen, dass die aufgerufenen Programmteile unterschiedliche Ausnahmetypen auslösen, aber die Behandlung der Fehler gleich aussieht. Um Quellcodeduplizierung zu vermeiden, sollte der Programmcode zusammengefasst werden. Nehmen wir an, die Behandlung der Ausnahmen SQLException und IOException soll gleich sein. Wir haben schon gesehen, dass ein catch(Exception e) keine gute Lösung ist und nie im Programmcode vorkommen sollte, denn dann würden auch andere Ausnahmen mitgefangen. Zum Glück gibt es in Java 7 eine elegante Lösung.
43
1
Neues in Java 7
Multi-catch Java 7 führt eine neue Schreibweise für catch-Anweisungen ein, um mehrere Ausnahmen auf einmal aufzufangen; sie heißt multi-catch. In der abgewandelten Variante von catch steht dann nicht mehr nur eine Ausnahme, sondern eine Sammlung von Ausnahmen, die ein »|« trennt. Der Schrägstrich ist schon als Oder-Operator bekannt und wurde daher auch hier eingesetzt, denn die Ausnahmen sind ja auch als eine Oder-Verknüpfung zu verstehen. Die allgemeine Syntax ist: try { ... } catch ( E1 | E2 | ... | En exception )
Die Variable exception ist implizit final. Um das multi-catch zu demonstrieren, nehmen wir ein Programm an, das eine Farbtabelle einliest. Die Datei besteht aus mehreren Zeilen, wobei in jeder Zeile die erste Zahl einen Index repräsentiert und die zweite Zahl den hexadezimalen RGB-Farbwert. Listing 1.1: basiscolors.txt 0 000000 1 ff0000 8 00ff00 9 ffff00
Eine eigene Methode readColorTable() soll die Datei einlesen und ein int-Feld der Größe 256 als Rückgabe liefern, wobei an den in der Datei angegebeen Positionen jeweils die Farbwerte eingetragen sind. Nicht belegte Positionen bleiben 0. Gibt es einen Ladefehler, soll die Rückgabe null sein und die Methode eine Meldung auf dem Fehlerausgabekanal ausgeben. Das Einlesen soll die Scanner-Klasse übernehmen. Bei der Verarbeitung der Daten und der Füllung des Feldes sind diverse Fehler denkbar: Þ IOException: Die Datei ist nicht vorhanden, oder während des Einlesens kommt es zu Pro-
blemen. Þ InputMismatchException: Der Index oder die Hexadezimalzahl sind keine Zahlen (einmal zur
Basis 10, und dann zur Basis 16). Den Fehlertyp löst der Scanner aus. Þ ArrayIndexOutOfBoundsException: Der Index liegt nicht im Bereich von 0 bis 255.
Während der erste Fehler beim Dateisystem zu suchen ist, sind die zwei unteren Fehler – unabhängig davon, dass sie ungeprüfte Ausnahmen sind – auf ein fehlerhaftes Format zurück-
44
1.1
Sprachänderungen
zuführen. Die Behandlung soll immer gleich aussehen und kann daher gut in einem multicatch zusammengefasst werden. Daraus folgt: Listing 1.2: ReadColorTable.java import java.io.*; import java.util.Scanner; public class ReadColorTable { private static int[] readColorTable( String filename ) { Scanner input; int[] colors = new int[ 256 ]; try { input = new Scanner( new File(filename) ); while ( input.hasNextLine() ) { int index = input.nextInt(); int rgb
= input.nextInt( 16 );
colors[ index ] = rgb; } return colors; } catch ( IOException e ) { System.err.printf( "Dateioperationen fehlgeschlagen%n%s%n", e ); } catch ( InputMismatchException | ArrayIndexOutOfBoundsException e ) { System.err.printf( "Datenformat falsch%n%s%n", e ); } finally { input.close(); } return null; }
45
1
1
Neues in Java 7
public static void main( String[] args ) { readColorTable( "basiscolors.txt" ); } }
Multi-catch-Blöcke sind also eine Abkürzung, und der Bytecode sieht genauso aus wie mehrere gesetzte catch-Blöcke, also wie: catch ( InputMismatchException e ) { System.err.printf( "Datenformat falsch%n%s%n", e ); } catch ( ArrayIndexOutOfBoundsException e ) { System.err.printf( "Datenformat falsch%n%s%n", e ); }
Multi-catch-Blöcke sind nur eine Abkürzung, daher teilen sie auch die Eigenschaften der normalen catch-Blöcke. Der Compiler führt die gleichen Prüfungen wie bisher durch, also ob etwa die genannten Ausnahmen im try-Block überhaupt ausgelöst werden können. Nur das, was in der durch »|« getrennten Liste aufgezählt ist, wird behandelt; unser Programm fängt zum Beispiel nicht generisch alle RuntimeExceptions ab. Und genauso dürfen die in catch oder multicatch genannten Ausnahmen nicht in einem anderen (multi)-catch auftauchen. Neben den Standard-Tests kommen neue Überprüfungen hinzu, ob etwa die exakt gleiche Exception zweimal in der Liste ist oder ob es Widersprüche durch Mengenbeziehungen gibt.
Hinweis Der folgende multi-catch ist falsch: try { new RandomAccessFile("", ""); } catch ( FileNotFoundException | IOException | Exception e ) { }
Der javac-Compiler meldet einen Fehler der Art »Alternatives in a multi-catch statement cannot be related by subclassing« und bricht ab.
46
1.1
Sprachänderungen
1
Hinweis (Forts.) Mengenprüfungen führt der Compiler auch ohne multi-catch durch, und Folgendes ist ebenfalls falsch: try { new RandomAccessFile("", ""); } catch ( Exception e ) { } catch ( IOException e ) { } catch ( FileNotFoundException e ) { }
Während allerdings eine Umsortierung der Zeilen die Fehler korrigiert, spielt die Reihenfolge bei multi-catch keine Rolle.
1.1.5 Präzisiertes rethrows Die Notwendigkeit, Ausnahmen über einen Basistyp zu fangen, ist mit dem Einzug vom multi-catch gesunken. Doch für gewisse Programmteile ist es immer noch praktisch, alle Fehler eines gewissen Typs aufzufangen. Wir können auch so weit in der Ausnahmehierarchie nach oben laufen, um alle Fehler aufzufangen – dann haben wir es mit einem try { ... } catch(Throwable t){ ... } zu tun. Ein multi-catch ist für geprüfte Ausnahmen besonders gut, aber bei ungeprüften Ausnahmen ist eben nicht immer klar, was als Fehler denn so ausgelöst wird, und ein catch(Throwable t) hat den Vorteil, dass es alles wegfischt.
Problemstellung Werden Ausnahmen über einen Basistyp gefangen und wird diese Ausnahme mit throw weitergeleitet, dann ist es naheliegend, dass der aufgefangene Typ genau der Typ ist, der auch bei throws in der Methodensignatur stehen muss. Stellen wir uns vor, ein Programmblock nimmt einen Screenshot und speichert ihn in einer Datei. Kommt es beim Abspeichern zu einem Fehler, soll das, was vielleicht schon in die Datei geschrieben wurde, gelöscht werden; die Regel ist also: Entweder steht der Screenshot komplett in der Datei oder es gibt gar keine Datei. Die Methode kann so aussehen, wobei die Ausnahmen an den Aufrufer weitergegeben werden sollen: public static void saveScreenshot( String filename ) throws AWTException, IOException { try { Rectangle r = new Rectangle( Toolkit.getDefaultToolkit().getScreenSize() );
47
1
Neues in Java 7
BufferedImage screenshot = new Robot().createScreenCapture( r ); ImageIO.write( screenshot, "png", new File( filename ) ); } catch ( AWTException e ) { throw e; } catch ( IOException e ) { new File( filename ).delete(); throw e; } }
Mit den beiden catch-Blöcken sind wir genau auf die Ausnahmen eingegangen, die createScreenCapture() und write() auslösen. Das ist richtig, aber löschen wir wirklich immer die Da-
teireste, wenn es Probleme beim Schreiben gibt? Richtig ist, dass wir immer dann die Datei löschen, wenn es zu einer IOException kommt. Aber was passiert, wenn die Implementierung eine RuntimeException auslöst? Dann wird die Datei nicht gelöscht, aber das ist gefragt! Das scheint einfach gefixt, denn statt catch ( IOException e ) { new File( filename ).delete(); throw e; }
schreiben wir: catch ( Throwable e ) { new File( filename ).delete(); throw e; }
Doch können wir das Problem so lösen? Der Typ Throwable passt doch gar nicht mehr mit dem deklarierten Typ IOException in der Methodensignatur zusammen: public static void saveScreenshot( String filename ) throws AWTException /*1*/, IOException /*2*/ {
48
1.1
Sprachänderungen
...
1
catch ( AWTException /*1*/ e ) { throw e; } catch ( Throwable /*?*/ e ) { new File( filename ).delete(); throw e; } }
Die erste catch-Klausel fängt AWTException und leitet es weiter. Damit wird saveScreenshot() zum möglichen Auslöser von AWTException und die Ausnahme muss mit throws an die Signatur. Wenn nun ein catch-Block jedes Throwable auffängt und diesen Throwable-Fehler weiterleitet, ist zu erwarten, dass an der Signatur auch Throwable stehen muss und IOException nicht reicht. Das war auch bis Java 6 so, aber in Java 7 kam eine Anpassung.
Neu seit Java 7: eine präzisere Typprüfung In Java 7 hat der Compiler eine kleine Veränderung erfahren, von einer unpräziseren zu einer präziseren Typanalyse: Immer dann, wenn in einem catch-Block ein throw stattfindet, ermittelt der Compiler die im try-Block tatsächlich aufgetretenen geprüften Exception-Typen und schenkt dem im catch genannten Typ für das rethrow im Prinzip keine Beachtung. Statt dem gefangenen Typ wird der Compiler den durch die Codeanalyse gefundenen Typ beim rethrow melden. Der Compiler erlaubt nur dann das präzise rethrow, wenn die catch-Variable nicht verändert wird. Zwar ist eine Veränderung einer nicht-finalen catch-Variablen wie auch unter Java 1.0 erlaubt, doch wenn die Variable belegt wird, schaltet der Compiler von der präzisen in die unpräzise Erkennung zurück. Führen wir etwa die folgende Zuweisung ein, so funktioniert das Ganze schon nicht mehr: catch ( Throwable e ) { new File( filename ).delete(); e = new IllegalStateException(); throw e; }
49
1
Neues in Java 7
Die Zuweisung führt zu dem Compilerhinweis, dass jetzt auch Throwable mit in die throwsKlausel muss.
Stilfrage Die catch-Variable kann für die präzisere Typprüfung den Modifizierer final tragen, muss das aber nicht tun. Immer dann, wenn es keine Veränderung an der Variablen gibt, wird der Compiler sie als final betrachten und eine präzisere Typprüfung durchführen – daher nennt sich das auch effektiv final. Die Java Language Specification rät vom final-Modifizierer aus Stilgründen ab. Ab Java 7 ist es das Standardverhalten, und daher ist es Quatsch, überall ein final dazuzuschreiben, um die präzisere Typprüfung zu dokumentieren.
Migrationsdetail Da der Compiler nun mehr Typwissen hat, stellt sich die Frage, ob alter Programmode mit dem neuen präziseren Verhalten vielleicht ungültig werden könnte. Theoretisch ist das möglich, aber die Sprachdesigner haben in über 9 Millionen Zeilen Code2 von unterschiedlichen Projekten keine Probleme gefunden. Prinzipiell könnte der Compiler jetzt unerreichbaren Code finden, der vorher versteckt blieb. Ein kleines Beispiel, was vor Java 7 compiliert, aber ab Java 7 nicht mehr: try { throw new FileNotFoundException(); } catch ( IOException e ) { try { throw e;
// e ist für den Compiler vom Typ FileNotFoundException
} catch ( MalformedURLException f ) { } }
Die Variable e in catch (IOException e) ist effektiv final, und der Compiler führt die präzisere Typerkennung durch. Er findet heraus, dass der wahre rethrow-Typ nicht IOException, sondern FileNotFoundException ist. Wenn dieser Typ dann mit throw e weitergeleitet wird, kann ihn catch(MalformedURLException) nicht auffangen.
2
50
Die Zahl stammt aus der FOSDEM 2011-Präsentation »Project Coin: Language Evolution in the Open«.
1.1
Sprachänderungen
1
Migrationsdetail (Forts.) Unter Java 6 war das etwas anders, denn hier wusste der Compiler nur, dass e irgendeine IOException ist, und es hätte ja durchaus die IOException-Unterklasse MalformedURLException sein können. (Warum MalformedURLException aber eine Unterklasse von IOException ist, steht auf einem ganz anderen Blatt.)
1.1.6 Automatisches Ressourcen-Management (try mit Ressourcen) Java hat eine automatische Garbage-Collection (GC), sodass nicht mehr referenzierte Objekte erkannt und ihr Speicher automatisch freigegeben wird. Nun bezieht sich die GC aber ausschließlich auf Speicher, doch es gibt viele weitere Ressourcen: Þ Dateisystem-Ressourcen von Dateien Þ Netzwerkressourcen wie Socket-Verbindungen Þ Datenbankverbindungen Þ nativ gebundene Ressourcen vom Grafiksubsystem Þ Synchronisationsobjekte
Auch hier gilt es, nach getaner Arbeit aufzuräumen und Ressourcen freizugeben, etwa Dateien und Datenbankverbindungen zu schließen. Mit dem try-catch-finally-Konstrukt haben wir gesehen, wie Ressourcen freizugeben sind. Doch es lässt sich auch ablesen, dass relativ viel Quellcode geschrieben werden muss und der try-catch-finally drei Unfeinheiten hat: 1. Sollte eine Variable in finally zugänglich sein, muss sie außerhalb des try-Blocks deklariert werden, was ihr eine höhere Sichtbarkeit als nötig gibt. 2. Das Schließen der Ressourcen bringt oft ein zusätzliches try-catch mit sich. 3. Eine im finally ausgelöste Ausnahme (etwa beim close()) überdeckt die im try-Block ausgelöste Ausnahme.
1.1.7 try mit Ressourcen Um das Schließen von Ressourcen zu vereinfachen, wurde in Java 7 eine besondere Form der try-Anweisung eingeführt, die try mit Ressourcen genannt werden. Mit ihm lassen sich Ressource-Typen, die die Schnittstelle java.lang.AutoCloseable implementieren, automatisch schließen. Ein-/Ausgabeklassen wie Scanner, InputStream und Writer, implementieren diese Schnittstelle und können direkt verwendet werden. Weil try mit Ressourcen dem Automatic Resource Management dient, heißt der spezielle try-Block auch ARM-Block.
51
1
Neues in Java 7
Gehen wir in die Praxis: Aus einer Datei soll mit einem Scanner die erste Zeile gelesen und ausgegeben werden. Nach dem Lesen soll der Scanner geschlossen werden. Die linke Seite der folgenden Tabelle nutzt die spezielle Syntax, die rechte Seite ist im Prinzip die Übersetzung, allerdings noch etwas vereinfacht, wie wir später genauer sehen werden. try mit Ressourcen
Vereinfachte ausgeschriebene Implementierung
InputStream in =
InputStream in =
ClassLoader.getSystemResourceAsStream(
ClassLoader.getSystemResourceAsStream( "EastOfJava.txt" );
"EastOfJava.txt" ); try ( Scanner res =
{ final Scanner res =
new Scanner( in ) )
new Scanner( file );
{ try
System.out.println(
{
res.nextLine() );
System.out.println(
}
res.nextLine() ); } finally { res.close(); } } Tabelle 1.2: Die main()-Methode von TryWithResources1 und ihre prinzipielle Umsetzung
Üblicherweise folgt nach dem Schlüsselwort try ein Block, doch try-mit-Ressourcen nutzt eine eigene spezielle Syntax. 1. Nach dem try folgt statt dem direkten {}-Block eine Anweisung in runden Klammern, und dann erst der {}-Block, also try (...) {...} statt try {...}. 2. In den runden Klammern findet man eine lokale Variablendeklaration (die Variable ist automatisch final) mit einer Zuweisung. Die Ressourcenvariable muss vom Typ AutoCloseable sein. Rechts vom Gleichheitszeichen steht ein Ausdruck, etwa ein Konstruktor- oder Methodenaufruf. Der Typ des Ausdrucks muss natürlich auch intanceof AutoCloseable sein. Die in dem try deklarierte lokale AutoCloseable-Variable ist nur in dem Block gültig und wird automatisch freigegeben, gleichgültig ob der ARM-Block korrekt durchlaufen wurde oder ob es bei der Abarbeitung zu einem Fehler kam. Der Compiler fügt alle nötigen Prüfungen ein.
52
1.1
Sprachänderungen
1.1.8 Ausnahmen vom close() bleiben bestehen
1
Unser Scanner-Beispiel hat eine Besonderheit, denn keine der Methoden löst eine geprüfte Ausnahme aus – weder getSystemResourceAsStream(), new Scanner(InputStream), nextLine() noch das close(), was try-mit-Ressourcen automatisch aufruft. Anders ist es, wenn die Ressource ein InputStream ist, denn dort deklariert die close()-Methode eine IOException. Die muss daher auch behandelt werden, wie es das folgende Beispiel zeigt: Listing 1.3: TryWithResourcesReadsLine, readFirstLine() static String readFirstLine( File file ) { try ( BufferedReader br = new BufferedReader(new FileReader(file) ) ) { return br.readLine(); } catch ( IOException e ) { e.printStackTrace(); return null; } }
Wenn try-mit-Ressourcen verwendet wird, bleibt die Ausnahme bestehen; es zaubert die Ausnahmen beim catch also nicht weg.
Hinweis Löst close() eine geprüfte Ausnahme aus, und wird diese nicht behandelt, so kommt es zum Compilerfehler. Die close()-Methode vom BufferedReader löst zum Beispiel eine IOException aus, sodass sich die folgende Methode nicht übersetzen lässt: void no() { try ( Reader r = new BufferedReader(null) ) { }
// 1 Compilerfehler
}
Der Ausdruck new BufferedReader(null) benötigt keine Behandlung, denn der Konstruktor löst keine Ausnahme aus. Einzig der nicht behandelte Fehler von close() führt zu »exception thrown from implicit call to close() on resource variable 'r'«.
1.1.9 Die Schnittstelle AutoCloseable Die ARM-Anweisung schließt Ressourcen vom Typ AutoCloseable. Daher wird es Zeit, sich diese Schnittstelle etwas genauer anzuschauen:
53
1
Neues in Java 7
package java.lang; public interface AutoCloseable { void close() throws Exception; }
Anders als das übliche close() ist die Ausnahme deutlich allgemeiner mit Exception angegeben; die Ein-/Ausgabe-Klassen lösen beim Misslingen immer eine IOException aus, aber jede Klasse hat eigene Ausnahmetypen: Typ
Signatur
java.io.Scanner
close() // ohne Ausnahme
javax.sound.sampled.Line
close() // ohne Ausnahme
java.io.FileInputStream
close() throws IOException
java.sql.Connection
close() throws SQLException
Tabelle 1.3: Einige Typen, die AutoCloseable implementieren
Eine Unterklasse darf die Ausnahme ja auch weglassen, das machen Klassen wie der Scanner, der keine Ausnahme weiterleitet, sondern sie intern schluckt – wenn es Ausnahmen gab, liefert sie die Scanner-Methode ioException().
AutoCloseable und Closeable Auf den ersten Blick einleuchtend wäre es, die schon existierende Schnittstelle Closeable als Typ zu nutzen. Doch das hätte Nachteile: Die close()-Methode ist mit einem throws IOException deklariert, was bei einer allgemeinen automatischen Ressourcen-Freigabe unpassend ist, wenn etwa ein Grafikobjekt bei der Freigabe eine IOException auslöst. Vielmehr ist der Weg anders herum: Closeable erweitert AutoCloseable, denn das Schließen von Ein-/Ausgabe-Ressourcen ist eine besondere Art, allgemeine Ressourcen zu schließen. package java.io; import java.io.IOException; public interface Closeable extends AutoCloseable { void close() throws IOException; }
54
1.1
Sprachänderungen
Wer ist AutoCloseable? Da alle Klassen, die Closeable implementieren, auch automatisch vom Typ AutoCloseable sind, kommen schon einige Typen zusammen. Im Wesentlichen sind es aber Klassen aus dem java.io-Paket, wie Channel-, Reader-, Writer-Implementierungen, FileLock, XMLDecoder und noch ein paar Exoten wie URLClassLoader, ImageOutputStream. Auch Typen aus dem java.sql-Paket gehören zu den Nutznießern. Klassen aus dem Bereich Threading, wo etwa ein Lock wieder freigeben werden könnte, oder Grafik-Anwendungen, bei denen der Grafik-Kontext wieder freigegeben werden muss, gehören nicht dazu.
1.1.10
Mehrere Ressourcen nutzen
Unsere beiden Beispiele zeigten die Nutzung eines Ressource-Typs. Es sind aber auch mehrere Typen möglich, die dann mit einem Semikolon getrennt werden: try ( InputStream in = new FileInputStream(src); OutputStream out = new FileOutputStream(dest) ) { ... }
Hinweis Die Trennung erledigt ein Semikolon, und jedes Segment kann einen unterschiedlichen Typ deklarieren, etwa InputStream/OutputStream. Die Ressourcen-Typen müssen also nicht gleich sein, und auch wenn sie es sind, muss der Typ immer neu geschrieben werden, also etwa: try ( InputStream in1 = ...; InputStream in2 = ... )
Es ist ungültig, Folgendes zu schreiben: try ( InputStream in1 = ..., in2 = ... )
// 1
Compilerfehler
Wenn es beim Anlegen in der Kette zu einem Fehler kommt, wird nur das geschlossen, was auch aufgemacht wurde. Wenn es also bei der ersten Initialisierung von in1 schon zu einer Ausnahme kommt, wird die Belegung von in2 erst gar nicht begonnen und daher auch nicht geschlossen. (Intern setzt der Compiler das als geschachtelte try-catch-finally-Blöcke um.)
Beispiel Am Schluss der Ressourcensammlung kann – muss aber nicht – ein Semikolon stehen, so wie auch bei Feldinitialisierungen zum Schluss ein Komma stehen kann: int[] array = { 1, 2, }; //
^ Komma optional
55
1
1
Neues in Java 7
Beispiel (Forts.) try ( InputStream in = new FileInputStream(src); ) { ... } //
^ Semikolon optional
try ( InputStream in = new FileInputStream(src); OutputStream out = new FileOutputStream(dest); ) { ... } //
^ Semikolon optional
Ob das stilvoll ist, muss jeder selbst entscheiden; in der Insel steht kein unnützes Zeichen.
1.1.11
Unterdrückte Ausnahmen *
Aufmerksame Leser haben bestimmt schon ein Detail wahrgenommen: Im Text steht »vereinfachte ausgeschriebene Implementierung«, was vermuten lässt, dass es ganz so einfach doch nicht ist. Das stimmt, denn es können zwei Ausnahmen auftauchen, die einiges an Sonderbehandlung benötigen: Þ Ausnahme im try-Block. An sich unproblematisch. Þ Ausnahme beim close(). Auch an sich unproblematisch. Aber es gibt mehrere close()-Auf-
rufe, wenn nicht nur eine Ressource verwendet wurde. Ungünstig. Þ Die Steigerung: Ausnahme im try-Block und dann auch noch Ausnahme(n) beim close().
Das ist ein echtes Problem! Eine Ausnahme alleine ist kein Problem, aber zwei Ausnahmen auf einmal bilden ein großes Problem, da ein Programmblock nur genau eine Ausnahme melden kann und nicht eine Sequenz von Ausnahmen. Daher sind verschiedene Fragen zu klären, falls der try-Block und close() beide eine Ausnahme auslösen: Þ Welche Ausnahme ist wichtiger? Die Ausnahme im try-Block oder die vom close()? Þ Wenn es zu zwei Ausnahmen kommt: Soll die von close() vielleicht immer verdeckt wer-
den und immer nur die vom try-Block zum Anwender kommen? Þ Wenn beide Ausnahmen wichtig sind, wie sollen sie gemeldet werden?
Wie haben sich die Java-Ingenieure entschieden? Eine Ausnahme bei close() darf bei einem gleichzeitigen Auftreten einer Exception im try-Block auf keinen Fall verschwinden.3 Wie also beide Ausnahmen melden? Hier gibt es einen Trick: Da die Ausnahme im try-Block wichtiger ist, ist sie die »Haupt-Ausnahme«, und die close()-Ausnahme kommt Huckepack als ExtraInformation mit oben drauf.
3
56
In einem frühen Prototyp war dies tatsächlich der Fall – die Ausnahme wurde komplett geschluckt.
1.1
Sprachänderungen
Dieses Verhalten soll das nächste Beispiel zeigen. Um die Ausnahmen besser steuern zu können, soll eine eigene AutoCloseable-Implementierung eine Ausnahme in close() auslösen. Listing 1.4: SuppressedClosed.java public class SuppressedClosed { public static void main( String[] args ) { class NotCloseable implements AutoCloseable { @Override public void close() { throw new UnsupportedOperationException( "close() mag ich nicht" ); } } try ( NotCloseable res = new NotCloseable() ) { throw new NullPointerException(); } } }
Das Programm löst also im close() und im try-Block eine Ausnahme aus. Das Resultat ist: Exception in thread "main" java.lang.NullPointerException at SuppressedClosed.main(SuppressedClosed.java:14) Suppressed: java.lang.UnsupportedOperationException: close() mag ich nicht at SuppressedClosed$1NotCloseable.close(SuppressedClosed.java:9) at SuppressedClosed.main(SuppressedClosed.java:15)
Die interessante Zeile beginnt mit »Suppressed:«, denn dort ist die close()-Ausnahme referenziert. An den Aufrufer kommt die spannende Ausnahme vom misslungenen try-Block aber nicht direkt von close(), sondern verpackt in der Hauptausnahme und muss extra erfragt werden. Zum Vergleich: Kommentieren wir throw new NullPointerException() aus, gibt es nur noch die close()-Ausnahme und es folgt auf der Konsole:
57
1
1
Neues in Java 7
Exception in thread "main" java.lang.UnsupportedOperationException: close() mag ich nicht at SuppressedClosed$1NotCloseable.close(SuppressedClosed.java:9) at SuppressedClosed.main(SuppressedClosed.java:15)
Die Ausnahme ist also nicht irgendwo anders untergebracht, sondern die »Hauptausnahme«. Eine Steigerung ist, dass es mehr als eine Ausnahme beim Schließen geben kann. Simulieren wir auch dies wieder an einem Beispiel, indem wir unser Beispiel um eine Zeile ergänzen: try ( NotCloseable res1 = new NotCloseable(); NotCloseable res2 = new NotCloseable() ) { throw new NullPointerException(); }
Aufgerufen führt dies zu: Exception in thread "main" java.lang.NullPointerException at SuppressedClosed.main(SuppressedClosed.java:15) Suppressed: java.lang.UnsupportedOperationException: close() mag ich nicht at SuppressedClosed$1NotCloseable.close(SuppressedClosed.java:9) at SuppressedClosed.main(SuppressedClosed.java:16) Suppressed: java.lang.UnsupportedOperationException: close() mag ich nicht at SuppressedClosed$1NotCloseable.close(SuppressedClosed.java:9) at SuppressedClosed.main(SuppressedClosed.java:16)
Jede unterdrückte close()-Ausnahme taucht auf.
Umsetzung Im Kapitel über finally wurde das Verhalten vorgestellt, dass eine Ausnahme im finally eine Ausnahme im try-Block unterdrückt. Der Compiler setzt bei der Umsetzung vom try-mit-Ressourcen das close() in einen finally-Block. Ausnahmen im finally-Block sollen eine mögliche Hauptausnahme aber nicht schlucken. Daher fängt die Umsetzung vom Compiler jede mögliche Ausnahme im try-Block ab sowie die close()-Ausnahme und hängt diese SchließAusnahme, falls vorhanden, an die Hauptausnahme.
Spezielle Methoden in Throwable * Damit eine normale Exception die unterdrückten close()-Ausnahmen Huckepack nehmen kann, sind in der Basisklasse Throwable seit Java 7 zwei Methoden hinzugekommen:
58
1.2
JVM-Änderungen
1
final class java.lang.Throwable Þ final Throwable[] getSuppressed()
Liefert alle unterdrückten Ausnahmen. Die printStackTrace()-Methode zeigt alle unterdrückten Ausnahmen und greift auf getSuppressed() zurück. Für Anwender wird es selten Anwendungsfälle für diese Methode geben. Þ final void addSuppressed(Throwable exception)
Fügt eine neue unterdrückte Ausnahme hinzu. In der Regel ruft der finally-Block vom trymit-Ressourcen die Methode auf, doch wir können auch selbst die Methode nutzen, wenn wir mehr als eine Ausnahme melden wollen. Die Java-Bibliothek selbst nutzt das bisher nur an sehr wenigen Stellen. Neben den beiden Methoden gibt es einen protected-Konstruktor, der bestimmt, ob es überhaupt unterdrückte Ausnahmen geben soll oder ob sie nicht vielleicht komplett geschluckt werden. Wenn, dann zeigt sie auch printStackTrace() nicht mehr an.
Blick über den Tellerrand In C++ gibt es Dekonstruktoren, die beliebige Anweisungen ausführen, wenn ein Objekt freigegeben wird. Hier lässt sich auch das Schließen von Ressourcen realisieren. C# nutzt statt try das spezielle Schlüsselwort using, mit Typen, die die Schnittstelle IDisposable implementieren, mit einer Methode Dispose() statt close(). (In Java sollte die Schnittstelle ursprünglich auch Disposable statt nun AutoCloseable heißen.) In Python 2.5 wurde ein context management protocol mit dem Schlüsselwort with realisiert, sodass Python automatisch bei Betreten eines Blockes __enter__() aufruft und beim Verlassen die Methode __exit__(). Das ist insofern interessant, als dass hier zwei Methoden zur Verfügung stehen. Bei Java ist es nur close() beim Verlassen des Blockes, aber es gibt keine Methode zum Betreten eines Blockes; so etwas muss beim Anlegen der Ressource erledigt werden.
1.2 JVM-Änderungen Wenn es um Java geht, müssen wir immer unterscheiden, ob es um die Sprache selbst geht, um die Bibliotheken, um die JVM oder um die Implementierung von Oracle, die das JDK darstellt. Eine wichtige Änderung am Bytecode, und damit an der JVM selbst, ist ein neuer Bytecode zum Beschleunigen von Methoden, deren Signatur nicht bekannt ist.
59
1
Neues in Java 7
In der Implementierung von Oracle gibt es zwei große Änderungen an der JVM selbst: Þ einen neuen Garbage-Collector G1 (für Garbage Frist) Þ komprimierte 64-Bit-Objekt-Pointer, sodass sie in 32 Bit Platz finden.
Bei Java-Programmen mit DTrace (wie Solaris) lassen sich die Zustände erfragen und die Abarbeitung überwachen.
1.2.1 invokedynamic Seit Java vor über 10 Jahren auf dem Markt erschien, hat sich vieles geändert (und auch vieles ist gleich geblieben). Auffällig ist eine starke Zunahme von Skriptsprachen – spielten sie vor 10 Jahren kaum eine Rolle, sind sie heute unübersehbar. Möglicherweise ist ein Grund dafür, dass vor 10–20 Jahren compilierte Sprache einfach nicht die nötige Performance brachten, aber heute auch durch leistungsfähige Maschinen und intelligente Ausführung die Leistung einfach da ist.
Skriptsprachen auf der JVM Eine unübersehbare Tatsache ist, dass viele Skriptsprachen heute in einer virtuellen Maschine laufen. Besondere Rolle nehmen die Java-Plattform (mit der JVM) und die .NET-Plattform mit CLR (einer virtuellen Maschine für .NET) ein. Zwei Trends zeigen sich: Zum einen werden existierende Skriptsprachen auf die JVM/CLR übertragen und zum anderen Sprachen explizit für die virtuellen Maschinen entworfen. Skriptsprache
Für JVM
Für CLR
Python
Jython
IronPython
Ruby
JRuby
IronRuby
Lua
Jill, Kahlua
LuaCLR
JavaScript
Rhino
IronJS
PHP
Quercus
Tcl
Jacl Groovy JavaFX Boo
Tabelle 1.4: Skriptsprachen auf der JVM (Java) und CLR (.NET)
60
1.2
JVM-Änderungen
Umsetzung der Skriptsprachen auf der JVM Eine Skriptsprache, die explizit für eine VM entworfen wurde, berücksichtigt natürlich Einschränkungen. Existierende Programmiersprachen sind eine ganz andere Herausforderung, da sie Sprach- und Laufzeitkonstrukte bieten können, die auf der JVM vielleicht nicht unterstützt werden. Das ist ein Problem, und drei Strategien zur Lösung bieten sich an: Þ Ignorieren der Eigenschaften: Python ermöglicht Mehrfachvererbung, Java nicht. Daher
kann das Verhalten von Python unter Java nicht perfekt nachgebildet werden, obwohl Jython sein Bestes gibt. Weiterhin kann Python Interrupts auffangen, aber so etwas gibt es unter Java nicht, also auch nicht in Jython. (Das ist aber eher eine Bibliotheks- und weniger eine Spracheigenschaft.) Þ Nachbilden der Eigenschaften: In Sprachen wie JavaScript, Python oder Ruby gibt es das so-
genannte Duck Typing. Zur Laufzeit wird erst das Vorhandensein von Methoden geprüft, und die Typen müssen nicht im Quellcode stehen. In JavaScript ist zum Beispiel function add(x, y){return x + y;} oder function isEmpty(s){return s == null || s.length() == 0;} erlaubt, und erst später beim Aufruf stellt sich heraus, was x und y überhaupt ist und ob ein Plus-Operator/length()-Methode definiert ist. Deklarationen und Aufrufe dieser Art sind nur mit vielen Tricks auf der JVM umzusetzen. Þ Ändern der JVM zur Unterstützung der Eigenschaften: Die Spracheigenschaften zu ignorie-
ren ist natürlich keine schöne Sache. Glücklicherweise kommt das selten vor, denn die JVM macht vieles mit. Ruby erlaubt etwa Methodennamen wie ==, , +, und 1.+2 ist gültig. Die Bezeichner sind in Java zwar nicht möglich, aber die JVM hat grundsätzlich mit den Bezeichnen keine Probleme. Es ist interessant zu sehen, wie unterschiedlich Java als Sprache und die JVM sind, denn vieles in Java gibt es in der JVM gar nicht. Enums zum Beispiel sind nur einfache Klassen. Oder dass eine Klasse ohne einen vom Entwickler explizit geschriebenen Konstruktor automatisch einen Standardkonstruktor bekommt, ist nur etwas, was der Compiler generiert. Oder ein super() als ersten Aufruf im Konstruktor – alles das sind Compilereigenschaften, und von Generics ganz zu schweigen. Wenn die Besonderheiten einer Skriptsprache über Tricks und intelligentes Übersetzen in Bytecode realisiert werden können, stellt sich die Frage, ob an der JVM überhaupt Änderungen nötig sind. Wie bei vielen Diskussionen kommt dann ein Argument auf, das Änderungen oft rechtfertigt: Performance. Wenn eine Änderung in der JVM die Abarbeitung bestimmter Konstrukte massiv beschleunigt, ist das ein Grund für die Änderung. Und somit sind wir beim neuen Bytecode invokedynamic angelangt.
Umsetzung von Duck Typing Reine Java-Programme werden den Bytecode invokedynamic nie benötigen, er ist einzig und allein in die JVM eingeführt worden, um Methodenaufrufe der Skriptsprachen auf der JVM zu
61
1
1
Neues in Java 7
beschleunigen. Die bisherigen Bytecodes von Methodenaufrufen trugen alle nötigen Typinformationen, so wie es bei Java üblich ist. Nehmen wir: System.out.prinln( 1 );
Der Compiler macht aus dem Programm ein invokevirtual, wobei genau bei dem Aufruf steht, dass ein println() gemeint ist, was ein int annimmt. Die Typinformationen im Bytecode sind: Þ Der Empfänger ist PrintStream, und seine Methode ist println. Þ Der Parametertyp ist ein int. Þ Die »Rückgabe« ist void.
Exakt diese Typen müssen vom Compiler in Bytecode gegossen werden, sonst kann die JVM den Aufruf nicht durchführen. Skriptsprachen hingegen sind da lascher. Nehmen wir noch einmal unser Beispiel aus JavaScript: function isEmpty(s) { return s == null || s.length() == 0; }
Die Typen wären zwar zur Laufzeit bekannt, aber eben nicht zur Compilezeit, in der der Bytecode erstellt würde. Ein Aufruf der Methode wird schwierig, denn es fehlen ganz offensichtlich der Rückgabe- und der Parametertyp. Natürlich könnten sie über einen Trick zu Object ergänzt werden. Spielen wir kurz die Konsequenzen durch: Object isEmpty( Object s )
Das sieht gut aus. Aber jetzt wird es haarig bei length(), das keine Methode von Object ist. Das Problem kann ein Compiler auf zwei Arten lösen. Zunächst kann der Aufruf an das unbekannte Objekt, das ja zur Laufzeit üblicherweise etwas anderes als String ist, über Reflection gelöst werden. Da Reflection-Aufrufe aber nicht so performant sind wie richtige Methodenaufrufe, ist diese Lösung nicht so effizient. Eine zweite Lösung besteht in der Einführung einer Schnittstelle, die genau die fehlenden Operationen deklariert: interface I { int length(); } I isEmpty( I s ) { return s == null || s.length() == 0; }
Ja, das geht – irgendwie. Das ist performanter als Reflection, aber auf diese Weise kommen eine riesige Anzahl neuer Schnittstellen bzw. Klassen in die JVM, deren Behandlung recht speicherintensiv ist. Außerdem können die Deklarationen von Methoden in Skriptsprachen auch außerhalb von Klassen vorkommen, sodass für Java ein Fake-Methoden-Empfänger erzeugt werden müsste.
62
1.3
Neue Pakete und Klassen
invokedynamic und Hilfsklassen Um die sonderbare Methodendeklaration in Java performant umzusetzen, hat der JSR-292, »Supporting Dynamically Typed Languages on the Java Platform«, den neuen Bytecode invokedynamic zusammen mit einigen Hilfsklassen im Paket java.lang.invoke definiert. Der neue Bytecode muss von einer Java 7-JVM unterstützt werden und wird von dynamischen Skriptsprachen generiert, um die Aufrufe schnell von der JVM ausführen zu lassen. Normale Entwickler werden den neuen Bytecode kaum brauchen, und im Quellcode ist er eh nicht sichtbar. Weitere Details und ein Programmbeispiel finden Leser unter http://java.sun.com/developer/ technicalArticles/DynTypeLang/. Sehr tiefe Detailinformationen liefert http://blogs.oracle. com/jrose/resource/pres/200910-VMIL.pdf.
1.3 Neue Pakete und Klassen Auf einige Neuerungen geht das Buch noch gezielter ein, etwa auf NIO.2 in Kapitel 5, »Dateien, Verzeichnisse und Dateizugriffe«, oder auf die API für nebenläufige Programmierung, das Fork-und-Join-Framework in Kapitel 2, »Threads und nebenläufige Programmierung«. Da die APIs alle bibliotheksbezogen sind und dieses Buch die Java-Bibliothek in den Mittelpunkt rückt, sind die Änderungen zu Java 7 dort vermerkt – mal ist es hier eine Methode, mal da; große Überraschungen gibt es nicht. An Typen im Paket java.lang gibt es ebenfalls kleine Änderungen. Java 7 unterstützt Unicode 6, was bedeutet, dass Java 7 Konstanten für die neuen Zeichenbereiche einführt und Methoden wie isLetter() aktualisiert hat, um auch dieses neue Zeichen korrekt zu berücksichtigen. Die Klasse Character hat für die Unicode-Blöcke Konstanten bekommen. Über den Unicode 6Standard gibt http://unicode.org/versions/Unicode6.0.0/ Auskunft. Für »normale« Entwickler ist das wenig relevant. Für die üblichen Unicode-Kodierungen gibt es in StandardCharsets Konstanten.
1.3.1 Weitere Änderungen Im GIU-Bereich, das heißt bei AWT und Swing, haben sich Kleinigkeiten geändert. Fenster können auch nicht eckig und transparent sein – das ging zwar auch schon seit einer späteren Java 6-Version, aber nur inoffiziell – das heißt, mit keiner Klasse im »erlaubten« Paket. Zwar war ein Datumsauswahldialog für Java 7 in Planung, aber er verschiebt sich wohl noch bis Java 8.
63
1
1
Neues in Java 7
Neuerungen in Kürze: Þ Alle Wrapper-Klassen bieten eine statische compare()-Methode, die zwei primitive Werte
vergleicht. Die Methode ist eine Alternative zur compareTo()-Methode, die alle WrapperKlassen bieten, die Comparable implementieren. Þ Der ProcessBuilder ist eine Klasse in Java, um externe Prozesse aufzubauen. Neu sind
Methoden, um die Datenströme umzuleiten. Þ Klassenlader lassen sich mit close() wieder schließen. Þ kleine Änderungen an den Kalender-Klassen Þ Unterstützung für SCTP (Stream Control Transmission Protocol) und SDP (Sockets Direct
Protocol). Das Tutorial gibt unter http://download.oracle.com/javase/tutorial/sdp/sockets/ eine Einführung in die sehr spezielle Technologie. Þ neue Klassen für asynchrone Datei- und Socket-Operationen Þ Neu in Java 7 ist JDBC 4.1, was nicht nur auf AutoCloseable eingeht, sondern auch Standard-
Implementierungen für sogenannte Row-Sets mitbringt. Weiterhin können die JDBC-Klassen Pseudo-Spalten nutzen, und bei einer Connection lässt sich das Datenbank-Schema erfragen und setzen.
64
Kapitel 2
2
Threads und nebenläufige Programmierung »Es ist nicht zu wenig Zeit, die wir haben, sondern es ist zu viel Zeit, die wir nicht nutzen.« – Lucius Annaeus Seneca (ca. 4 v. Chr.– 65 n. Chr.), römischer Philosoph und Staatsmann
2.1 Threads erzeugen Die folgenden Abschnitte verdeutlichen, wie der nebenläufige Programmcode in einen Runnable verpackt und dem Thread zur Ausführung vorgelegt wird.
2.1.1 Threads über die Schnittstelle Runnable implementieren Damit der Thread weiß, was er ausführen soll, müssen wir ihm Anweisungsfolgen geben. Diese werden in einem Befehlsobjekt vom Typ Runnable verpackt und dem Thread übergeben. Wird der Thread gestartet, arbeitet er die Programmzeilen aus dem Befehlsobjekt parallel zum restlichen Programmcode ab. Die Schnittstelle Runnable ist schmal und schreibt nur eine run()-Methode vor. interface java.lang.Runnable Þ void run()
Implementierende Klassen realisieren die Operation und setzen dort den parallel auszuführenden Programmcode ein.
Abbildung 2.1: UML-Diagramm der einfachen Schnittstelle Runnable
65
2
Threads und nebenläufige Programmierung
Wir wollen zwei Threads angeben, wobei einer zwanzigmal das aktuelle Datum und die Uhrzeit ausgibt und der andere einfach eine Zahl: Listing 2.1: com/tutego/insel/thread/DateCommand.java package com.tutego.insel.thread; public class DateCommand implements Runnable { @Override public void run() { for ( int i = 0; i < 20; i++ ) System.out.println( new java.util.Date() ); } } Listing 2.2: com/tutego/insel/thread/CounterCommand.java package com.tutego.insel.thread; class CounterCommand implements Runnable { @Override public void run() { for ( int i = 0; i < 20; i++ ) System.out.println( i ); } }
Unser parallel auszuführender Programmcode in run() besteht aus einer Schleife, die in einem Fall ein aktuelles Date-Objekt ausgibt und im anderen Fall einen Schleifenzähler.
2.1.2 Thread mit Runnable starten Nun reicht es nicht aus, einfach die run()-Methode einer Klasse direkt aufzurufen, denn dann wäre nichts nebenläufig, sondern wir würden einfach eine Methode sequenziell ausführen. Damit der Programmcode parallel zur Applikation läuft, müssen wir ein Thread-Objekt mit dem Runnable verbinden und dann den Thread explizit starten. Dazu übergeben wir dem Konstruktor der Klasse Thread eine Referenz auf das Runnable-Objekt und rufen start() auf. Nach-
66
2.1
Threads erzeugen
dem start() für den Thread eine Ablaufumgebung geschaffen hat, ruft es intern selbstständig die Methode run() genau einmal auf. Läuft der Thread schon, so löst ein zweiter Aufruf der start()-Methode eine IllegalThreadStateException aus: Listing 2.3: com/tutego/insel/thread/FirstThread.java, main() Thread t1 = new Thread( new DateCommand() ); t1.start(); Thread t2 = new Thread( new CounterCommand() ); t2.start();
Beim Starten des Programms erfolgt eine Ausgabe auf dem Bildschirm, die in etwa so aussehen kann: Tue Aug 21 16:59:58 CEST 2007 0 1 2 3 4 5 6 7 8 9 Tue Aug 21 16:59:58 CEST 2007 10 ...
Deutlich ist die Verzahnung der beiden Threads zu erkennen. Was allerdings auf den ersten Blick etwas merkwürdig wirkt, ist die erste Zeile des Datum-Threads und viele weitere Zeilen des Zähl-Threads. Dies hat jedoch nichts zu bedeuten und zeigt deutlich den Nichtdeterminismus1 bei Threads. Interpretiert werden kann dies jedoch durch die unterschiedlichen Laufzeiten, die für die Datums- und Zeitausgabe nötig sind.
1
Nicht vorhersehbar, bedeutet hier: Wann der Scheduler den Kontextwechsel vornimmt, ist unbekannt.
67
2
2
Threads und nebenläufige Programmierung
class java.lang.Thread implements Runnable Þ Thread(Runnable target)
Erzeugt einen neuen Thread mit einem Runnable, das den parallel auszuführenden Programmcode vorgibt. Þ void start()
Ein neuer Thread – neben dem die Methode aufrufenden Thread – wird gestartet. Der neue Thread führt die run()-Methode nebenläufig aus. Jeder Thread kann nur einmal gestartet werden.
Hinweis Wenn ein Thread im Konstruktor einer Runnable-Implementierung gestartet wird, sollte die Arbeitsweise bei der Vererbung beachtet werden. Nehmen wir an, eine Klasse leitet von einer anderen Klasse ab, die im Konstruktor einen Thread startet. Bildet die Applikation ein Exemplar der Unterklasse, so werden bei der Bildung des Objekts immer erst die Konstruktoren der Oberklasse aufgerufen. Dies hat zur Folge, dass der Thread schon läuft, auch wenn das Objekt noch nicht ganz gebaut ist. Die Erzeugung ist erst abgeschlossen, wenn nach dem Aufruf der Konstruktoren der Oberklassen der eigene Konstruktor vollständig abgearbeitet wurde.
2.1.3 Die Klasse Thread erweitern Da die Klasse Thread selbst die Schnittstelle Runnable implementiert und die run()-Methode mit leerem Programmcode bereitstellt, können wir auch Thread erweitern, wenn wir eigene parallele Aktivitäten programmieren wollen: Listing 2.4: com/tutego/insel/thread/DateThread.java, DateThread public class DateThread extends Thread { @Override public void run() { for ( int i = 0; i < 20; i++ ) System.out.println( new Date() ); } }
Dann müssen wir kein Runnable-Exemplar mehr in den Konstruktor einfügen, denn wenn unsere Klasse eine Unterklasse von Thread ist, reicht ein Aufruf der geerbten Methode start().
68
2.1
Threads erzeugen
2
Abbildung 2.2: UML-Diagramm der Klasse Thread, die Runnable implementiert
Danach arbeitet das Programm direkt weiter, führt also kurze Zeit später die nächste Anweisung hinter start() aus:
69
2
Threads und nebenläufige Programmierung
Listing 2.5: com/tutego/insel/thread/DateThreadUser, main() Thread t = new DateThread(); t.start(); new DateThread().start();
// (*)
Die (*)-Zeile zeigt, dass das Starten sehr kompakt auch ohne Zwischenspeicherung der Objektreferenz möglich ist. class java.lang.Thread implements Runnable Þ void run()
Diese Methode in Thread hat einen leeren Rumpf. Unterklassen überschreiben run(), sodass sie den parallel auszuführenden Programmcode enthält.
Überschreiben von start() und Selbststarter Die Methode start() kann von uns auch überschrieben werden, was aber nur selten sinnvoll beziehungsweise nötig ist. Wir müssen dann darauf achten, super.start() aufzurufen, damit der Thread wirklich startet. Damit wir als Thread-Benutzer nicht erst die start()-Methode aufrufen müssen, kann ein Thread sich auch selbst starten. Der Konstruktor ruft dazu einfach die eigene start()Methode auf: class DateThread extends Thread { DateThread() { start(); } // ... der Rest bleibt ... }
run( ) wurde statt start( ) aufgerufen: Ja, wo laufen sie denn? Ein Programmierfehler, der Anfängern schnell unterläuft, ist folgender: Statt start() rufen sie aus Versehen run() auf dem Thread auf. Was geschieht? Fast genau das Gleiche wie bei start(), nur mit dem Unterschied, dass die Objektmethode run() nicht parallel zum übrigen Programm abgearbeitet wird. Der aktuelle Thread bearbeitet die run()-Methode sequenziell, bis
70
2.2
Thread-Eigenschaften und -Zustände
sie zu Ende ist und die Anweisungen nach dem Aufruf an die Reihe kommen. Der Fehler fällt nicht immer direkt auf, denn die Aktionen in run() finden ja statt – nur eben nicht nebenläufig.
Erweitern von Thread oder Implementieren von Runnable? Die beste Idee wäre, Runnable-Objekte zu bauen, die dann dem Thread übergeben werden. Befehlsobjekte dieser Art sind recht flexibel, da die einfachen Runnable-Objekte leicht übergeben und sogar von Threads aus einem Thread-Pool ausgeführt werden können. Ein Nachteil der Thread-Erweiterung ist, dass die Einfachvererbung störend sein kann; erbt eine Klasse von Thread, ist die Erweiterung schon »aufgebraucht«. Doch, egal ob eine Klasse Runnable implementiert oder Thread erweitert, eines bleibt: eine neue Klasse.
2.2 Thread-Eigenschaften und -Zustände Ein Thread hat eine ganze Reihe von Zuständen, wie einen Namen und eine Priorität, die sich erfragen und setzen lassen. Nicht jede Eigenschaft ist nach dem Start änderbar, doch welche das sind, zeigen die folgenden Abschnitte.
2.2.1 Der Name eines Threads Ein Thread hat eine ganze Menge Eigenschaften – wie einen Zustand, eine Priorität und auch einen Namen. Dieser kann mit setName() gesetzt und mit getName() erfragt werden. class java.lang.Thread implements Runnable Þ Thread(String name)
Erzeugt ein neues Thread-Objekt und setzt den Namen. Sinnvoll bei Unterklassen, die den Konstruktor über super(name) aufrufen. Þ Thread(Runnable target, String name)
Erzeugt ein neues Thread-Objekt mit einem Runnable und setzt den Namen. Þ final String getName()
Liefert den Namen des Threads. Der Name wird im Konstruktor angegeben oder mit setName() zugewiesen. Standardmäßig ist der Name »Thread-x«, wobei x eine eindeutige Num-
mer ist. Þ final void setName(String name)
Ändert den Namen des Threads.
71
2
2
Threads und nebenläufige Programmierung
2.2.2 Wer bin ich? Eine Erweiterung der Klasse Thread hat den Vorteil, dass geerbte Methoden wie getName() sofort genutzt werden können. Wenn wir Runnable implementieren, genießen wir diesen Vorteil nicht. Die Klasse Thread liefert mit der statischen Methode currentThread() die Objektreferenz für das Thread-Exemplar, das diese Anweisung gerade ausführt. Auf diese Weise lassen sich nichtstatische Thread-Methoden wie getName() verwenden.
Beispiel Gib die aktuelle Priorität des laufenden Threads und den Namen aus: System.out.println( Thread.currentThread().getPriority() ); // z. B. 5 System.out.println( Thread.currentThread().getName() );
// z. B. main
Falls es in einer Schleife wiederholten Zugriff auf Thread.currentThread() gibt, sollte das Ergebnis zwischengespeichert werden, denn der Aufruf ist nicht ganz billig. class java.lang.Thread implements Runnable Þ static Thread currentThread()
Liefert den Thread, der das laufende Programmstück ausführt.
2.2.3 Die Zustände eines Threads * Bei einem Thread-Exemplar können wir einige Zustände feststellen: 1. Nicht erzeugt: Der Lebenslauf eines Thread-Objekts beginnt mit new, doch befindet er sich damit noch nicht im Zustand ausführend. 2. Laufend (vom Scheduler berücksichtigt) und nicht laufend (vom Scheduler nicht berücksichtigt): Durch start() gelangt der Thread in den Zustand »ausführbar« beziehungsweise »laufend«. Der Zustand kann sich ändern, wenn ein anderer Thread zur Ausführung gelangt und dann dem aktuellen Thread den Prozessor entzieht. Der vormals laufende Thread kommt in den Zustand nicht laufend, bis der Scheduler ihm wieder Rechenzeit zuordnet. 3. Wartend: Dieser Zustand wird mittels spezieller Synchronisationstechniken oder Ein-/Ausgabefunktionen erreicht – der Thread verweilt in einem Wartezustand. 4. Beendet: Nachdem die Aktivität des Thread-Objekts beendet wurde, kann es nicht mehr aktiviert werden und ist tot, also beendet.
72
2.2
Thread-Eigenschaften und -Zustände
Zustand über Thread.State In welchem Zustand ein Thread gerade ist, zeigt die Methode getState(). Sie liefert ein Objekt vom Typ der Aufzählung Thread.State (die einzige Aufzählung im Paket java.lang), die Folgendes deklariert: Zustand
Erläuterung
NEW
neuer Thread, noch nicht gestartet
RUNNABLE
Läuft in der JVM.
BLOCKED
Wartet auf einen MonitorLock, wenn er etwa einen synchronized-Block betreten möchte.
WAITING
Wartet etwa auf ein notify().
TIMED_WAITING
Wartet etwa in einem sleep().
TERMINATED
Ausführung ist beendet.
Tabelle 2.1: Zustände eines Threads
Zudem lässt sich die Methode isAlive() verwenden, die erfragt, ob der Thread gestartet wurde, aber noch nicht tot ist.
2.2.4 Schläfer gesucht Manchmal ist es notwendig, einen Thread eine bestimmte Zeit lang anzuhalten. Dazu lassen sich Methoden zweier Klassen nutzen: Þ die überladene statische Methode Thread.sleep(): Etwas erstaunlich ist sicherlich, dass sie
keine Objektmethode von einem Thread-Objekt ist, sondern eine statische Methode. Ein Grund wäre, dass dadurch verhindert wird, externe Threads zu beeinflussen. Es ist nicht möglich, einen fremden Thread, über dessen Referenz wir verfügen, einfach einige Sekunden lang schlafen zu legen und ihn so von der Ausführung abzuhalten. Þ die Objektmethode sleep() auf einem TimeUnit-Objekt: Auch sie bezieht sich immer auf
den ausführenden Thread. Der Vorteil gegenüber sleep() ist, dass hier die Zeiteinheiten besser sichtbar sind.
73
2
2
Threads und nebenläufige Programmierung
Beispiel Der ausführende Thread soll zwei Sekunden lang schlafen. Einmal mit Thread.sleep(): try { Thread.sleep( 2000 ); } catch ( InterruptedException e ) { }
Dann mit TimeUnit: try { TimeUnit.SECONDS.sleep( 2 ); } catch ( InterruptedException e ) { }
Der Schlaf kann durch eine InterruptedException unterbrochen werden, etwa durch interrupt(). Die Ausnahme muss behandelt werden, da sie keine RuntimeException ist. Praktisch wird das Erweitern der Klasse Thread bei inneren anonymen Klassen. Die folgende Anweisung gibt nach zwei Sekunden Schlafzeit eine Meldung auf dem Bildschirm aus: Listing 2.6: com/tutego/insel/thread/SleepInInnerClass.java, main() new Thread() { @Override public void run() { try { Thread.sleep( 2000 ); System.out.println( "Zeit ist um." ); } catch ( InterruptedException e ) { e.printStackTrace(); } } }.start();
Da new Thread(){...} ein Exemplar der anonymen Unterklasse ergibt, lässt die auf dem Ausdruck aufgerufene Objektmethode start() den Thread gleich loslaufen. Aufgaben dieser Art lösen auch die Timer gut. class java.lang.Thread implements Runnable Þ static void sleep(long millis) throws InterruptedException
Der aktuell ausgeführte Thread wird mindestens millis Millisekunden schlafen gelegt. Unterbricht ein anderer Thread den schlafenden, wird vorzeitig eine InterruptedException ausgelöst.
74
2.2
Thread-Eigenschaften und -Zustände
Þ static void sleep(long millis, int nanos) throws InterruptedException
Der aktuell ausgeführte Thread wird mindestens millis Millisekunden und zusätzlich nanos Nanosekunden schlafen gelegt. Im Gegensatz zu sleep(long) wird bei einer negativen Millisekundenanzahl eine IllegalArgumentException ausgelöst; auch wird diese Exception ausgelöst, wenn die Nanosekundenanzahl nicht zwischen 0 und 999.999 liegt. enum java.util.concurrent.TimeUnit extends Enum implements Serializable, Comparable Þ NANOSECONDS, MICROSECONDS, MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS
Aufzählungselemente von TimeUnit. Þ void sleep(long timeout) throws InterruptedException
Führt ein Thread.sleep() für die Zeiteinheit aus. Eine überladene Methode Thread.sleep(long, TimeUnit) wäre nett, gibt es aber nicht.
Abbildung 2.3: UML-Diagramme für Timer und TimerTask
2.2.5 Mit yield() auf Rechenzeit verzichten Neben sleep() gibt es eine weitere Methode, um kooperative Threads zu programmieren: die Methode yield(). Sie funktioniert etwas anders als sleep(), da hier nicht nach Ablauf der genannten Millisekunden zum Thread zurückgekehrt wird, sondern yield() den Thread bezüglich seiner Priorität wieder in die Thread-Warteschlange des Systems einordnet. Einfach ausgedrückt, sagt yield() der Thread-Verwaltung: »Ich setze diese Runde aus und mache weiter, wenn ich das nächste Mal dran bin.«
75
2
2
Threads und nebenläufige Programmierung
class java.lang.Thread implements Runnable Þ static void yield()
Der laufende Thread gibt freiwillig seine Rechenzeit ab. Die Methode ist für Implementierungen der JVM nicht verbindlich.
2.2.6 Der Thread als Dämon Ein Server reagiert oft in einer Endlosschleife auf eingehende Aufträge vom Netzwerk und führt die gewünschte Aufgabe aus. In unseren bisherigen Programmen haben wir oft Endlosschleifen eingesetzt, sodass ein gestarteter Thread nie beendet wird. Wenn also run() wie in den vorangehenden Beispielen nie abbricht (Informatiker sprechen hier von terminiert), läuft der Thread immer weiter, auch wenn die Hauptapplikation beendet ist. Dies ist nicht immer beabsichtigt, da vielleicht Server-Funktionalität nach dem Beenden der Applikation nicht mehr gefragt ist. Dann sollte auch der endlos laufende Thread beendet werden. Um dies auszudrücken, erhält ein im Hintergrund arbeitender Thread eine spezielle Kennung: Der Thread wird als Dämon2 gekennzeichnet. Standardmäßig ist ein aufgebauter Thread kein Dämon.
Ein Dämon ist wie ein Heinzelmännchen im Hintergrund mit einer Aufgabe beschäftigt. Wenn das Hauptprogramm beendet ist und die Laufzeitumgebung erkennt, dass kein normaler Thread mehr läuft, sondern nur Dämonen, dann ist das Ende der Dämonen eingeläutet, und die JVM kommt zum Ende. Denn Dämonen-Threads sind Zulieferer: Gibt es keine Klienten mehr, werden auch sie nicht mehr gebraucht. Das ist wie bei den Göttern der Scheibenwelt: Glaubt keiner an sie, hören sie auf zu existieren. Wir müssen uns also um das Ende des Dämons nicht kümmern. Gleichzeitig heißt das aber auch, dass ein Dämonen-Thread vorsichtig mit Ein-/Ausgabeoperationen sein muss, denn er kann jederzeit – auch etwa während einer Schreiboperation auf die Festplatte – abgebrochen werden, was zu beschädigten Daten führen kann.
2
76
Das griechische dalx (engl. daemon) bezeichnet allerlei Wesen zwischen Gott und Teufel. Eine gute Einleitung gibt http://de.wikipedia.org/wiki/D%C3 %A4mon.
2.2
Thread-Eigenschaften und -Zustände
Hinweis
2
Der Garbage-Collector (GC) ist ein gutes Beispiel für einen Dämon. Nur, wenn es andere Threads gibt, muss der Speicher aufgeräumt werden. Gibt es keine anderen Threads mehr, kann auch die JVM mit beendet werden, was auch die Dämonen-Threads beendet.
Wie ein Thread in Java zum Dämon wird Einen Thread in Java als Dämon zu kennzeichnen, heißt, die Methode setDaemon() mit dem Argument true aufzurufen. Die Methode ist nur vor dem Starten des Threads erlaubt. Danach kann der Status nicht wieder vom Dämon in den normalen Benutzer-Thread umgesetzt werden. Die Auswirkungen von setDaemon(true) können wir am folgenden Programm ablesen: Listing 2.7: com/tutego/insel/thread/DaemonThread.java package com.tutego.insel.thread; class DaemonThread extends Thread { DaemonThread() { setDaemon( true ); } @Override public void run() { while ( true ) System.out.println( "Lauf, Thread, lauf" ); } public static void main( String[] args ) { new DaemonThread().start(); } }
In diesem Programm wird der Thread gestartet, und danach ist die Anwendung sofort beendet. Vor dem Ende kann der neue Thread aber schon einige Zeilen auf der Konsole ausgeben.
77
2
Threads und nebenläufige Programmierung
Klammern wir die Anweisung mit setDaemon(true) aus, läuft das Programm ewig, da die Laufzeitumgebung auf das natürliche Ende der Thread-Aktivität wartet. class java.lang.Thread implements Runnable Þ final void setDaemon(boolean on)
Markiert den Thread als Dämon oder normalen Thread. Die Methode muss aufgerufen werden, bevor der Thread gestartet wurde, andernfalls folgt eine IllegalThreadStateException. Mit anderen Worten: Nachträglich kann ein existierender Thread nicht mehr zu einem Dämon gemacht werden, und ihm kann auch nicht die Dämonenhaftigkeit genommen werden, so er sie hat. Þ final boolean isDaemon()
Testet, ob der Thread ein Dämon-Thread ist.
AWT und Dämonen * Obwohl Dämonen für Hintergrundaufgaben eine gute Einrichtung sind, ist der AWT-Thread kein Dämon. Unterschiedliche AWT-Threads sind normale Benutzer-Threads; dazu gehören AWT-Input, AWT-Motif oder Screen_Updater. Dies bedeutet, dass bei einmaliger Nutzung einer AWT-Methode ein spezieller Nicht-Dämon-Thread gestartet wird, sodass die Applikation nicht automatisch beendet wird, wenn das Hauptprogramm endet. Daher muss die Applikation in vielen Fällen hart mit System.exit() beendet werden.
2.2.7 Das Ende eines Threads Es gibt Threads, die dauernd laufen, weil sie zum Beispiel Serverfunktionen implementieren. Andere Threads führen einmalig eine Operation aus und sind danach beendet. Allgemein ist ein Thread beendet, wenn eine der folgenden Bedingungen zutrifft: Þ Die run()-Methode wurde ohne Fehler beendet. Wenn wir eine Endlosschleife programmie-
ren, würde diese potenziell einen nie endenden Thread bilden. Þ In der run()-Methode tritt eine RuntimeException auf, die die Methode beendet. Das beendet
weder die anderen Threads noch die JVM als Ganzes. Þ Der Thread wurde von außen abgebrochen. Dazu dient die prinzipbedingt problematische
Methode stop(), von deren Verwendung abgeraten wird und die auch veraltet ist. Þ Die virtuelle Maschine wird beendet und nimmt alle Threads mit ins Grab.
78
2.2
Thread-Eigenschaften und -Zustände
Wenn der Thread einen Fehler melden soll Da ein Thread nebenläufig arbeitet, kann die run()-Methode synchron schlecht Exceptions melden oder einen Rückgabewert liefern. Wer sollte auch an welcher Stelle darauf hören? Eine Lösung für das Problem ist ein Listener, der sich beim Thread anmeldet und darüber informiert wird, ob der Thread seine Arbeit machen konnte oder nicht. Eine andere Lösung gibt Callable, mit dem ein spezieller Fehlercode zurückgegeben oder eine Exception angezeigt werden kann. Speziell für ungeprüfte Ausnahmen kann ein UncaughtExceptionHandler weiterhelfen.
2.2.8 Einen Thread höflich mit Interrupt beenden Der Thread ist in der Regel zu Ende, wenn die run()-Methode ordentlich bis zum Ende ausgeführt wurde. Enthält eine run()-Methode jedoch eine Endlosschleife – wie etwa bei einem Server, der auf eingehende Anfragen wartet –, so muss der Thread von außen zur Kapitulation gezwungen werden. Die naheliegende Möglichkeit, mit der Thread-Methode stop() einen Thread abzuwürgen, wollen wir in Abschnitt 2.2.10, »Der stop() von außen und die Rettung mit ThreadDeath«, diskutieren. Wenn wir den Thread schon nicht von außen beenden wollen, können wir ihn immerhin bitten, seine Arbeit aufzugeben. Periodisch müsste er dann nur überprüfen, ob jemand von außen den Abbruchswunsch geäußert hat.
Die Methoden interrupt() und isInterrupted() Die Methode interrupt() setzt von außen in einem Thread-Objekt ein internes Flag, das dann in der run()-Methode durch isInterrupted() periodisch abgefragt werden kann. Das folgende Programm soll jede halbe Sekunde eine Meldung auf dem Bildschirm ausgeben. Nach zwei Sekunden wird der Unterbrechungswunsch mit interrupt() gemeldet. Auf dieses Signal achtet die sonst unendlich laufende Schleife und bricht ab: Listing 2.8: com/tutego/insel/thread/ThreadusInterruptus.java, main() Thread t = new Thread() { @Override public void run() { System.out.println( "Es gibt ein Leben vor dem Tod. " ); while ( ! isInterrupted() )
79
2
2
Threads und nebenläufige Programmierung
{ System.out.println( "Und er läuft und er läuft und er läuft" ); try { Thread.sleep( 500 ); } catch ( InterruptedException e ) { interrupt(); System.out.println( "Unterbrechung in sleep()" ); } } System.out.println( "Das Ende" ); } }; t.start(); Thread.sleep( 2000 ); t.interrupt();
Die Ausgabe zeigt hübsch die Ablaufsequenz: Es gibt ein Leben vor dem Tod. Und er läuft und er läuft und er läuft Und er läuft und er läuft und er läuft Und er läuft und er läuft und er läuft Und er läuft und er läuft und er läuft Unterbrechung in sleep() Das Ende
Die run()-Methode im Thread ist so implementiert, dass die Schleife genau dann verlassen wird, wenn isInterrupted() den Wert true ergibt, also von außen die interrupt()-Methode für dieses Thread-Exemplar aufgerufen wurde. Genau dies geschieht in der main()-Methode. Auf den ersten Blick ist das Programm leicht verständlich, doch vermutlich erzeugt das interrupt() im catch-Block die Aufmerksamkeit. Stünde diese Zeile dort nicht, würde das Programm aller Wahrscheinlichkeit nach nicht funktionieren. Das Geheimnis ist folgendes: Wenn die Ausgabe nur jede halbe Sekunde stattfindet, befindet sich der Thread fast die gesamte Zeit über in der Schlafmethode sleep(). Also wird vermutlich der interrupt() den
80
2.2
Thread-Eigenschaften und -Zustände
Thread gerade beim Schlafen stören. Genau dann wird sleep() durch InterruptedException unterbrochen, und der catch-Behandler fängt die Ausnahme ein. Jetzt passiert aber etwas Unerwartetes: Durch die Unterbrechung wird das interne Flag zurückgesetzt, sodass isInterrupted() meint, die Unterbrechung habe gar nicht stattgefunden. Daher muss interrupt() erneut aufgerufen werden, da das Abbruch-Flag neu gesetzt werden muss und isInterrupted() das Ende bestimmen kann. Wenn wir mit der Objektmethode isInterrupted() arbeiten, müssen wir beachten, dass neben sleep() auch die Methoden join() und wait() durch die InterruptedException das Flag löschen.
Hinweis Die Methoden sleep(), wait() und join() lösen alle eine InterruptedException aus, wenn sie durch die Methode interrupt() unterbrochen werden. Das heißt, interrupt() beendet diese Methoden mit der Ausnahme.
Zusammenfassung: interrupted(), isInterrupted() und interrupt() Die Methodennamen sind verwirrend gewählt, sodass wir die Aufgaben noch einmal zusammenfassen wollen: Die Objektmethode interrupt() setzt in einem (anderen) Thread-Objekt ein Flag, dass es einen Antrag gab, den Thread zu beenden. Sie beendet aber den Thread nicht, obwohl es der Methodenname nahelegt. Dieses Flag lässt sich mit der Objektmethode isInterrupted() abfragen. In der Regel wird dies innerhalb einer Schleife geschehen, die darüber bestimmt, ob die Aktivität des Threads fortgesetzt werden soll. Die statische Methode interrupted() ist zwar auch eine Anfragemethode und testet das entsprechende Flag des aktuell laufenden Threads, wie Thread.currentThread().isInterrupted(), aber zusätzlich löscht es den Interrupt-Status auch, was isInterrupted() nicht tut. Zwei aufeinanderfolgende Aufrufe von interrupted() führen daher zu einem false, es sei denn, in der Zwischenzeit erfolgt eine weitere Unterbrechung.
2.2.9 UncaughtExceptionHandler für unbehandelte Ausnahmen Einer der Gründe für das Ende eines Threads ist eine unbehandelte Ausnahme, etwa von einer nicht aufgefangenen RuntimeException. Um in diesem Fall einen kontrollierten Abgang zu ermöglichen, lässt sich an den Thread ein UncaughtExceptionHandler hängen, der immer dann benachrichtigt wird, wenn der Thread wegen einer nicht behandelten Ausnahme endet. UncaughtExceptionHandler ist eine in Thread deklarierte innere Schnittstelle, die eine Operation void uncaughtException(Thread t, Throwable e) vorschreibt. Eine Implementierung der Schnittstelle lässt sich entweder einem individuellen Thread oder allen Threads anhängen, sodass im
81
2
2
Threads und nebenläufige Programmierung
Fall des Abbruchs durch unbehandelte Ausnahmen die JVM die Methode uncaughtException() aufruft. Auf diese Weise kann die Applikation im letzten Atemzug noch den Fehler loggen, den die JVM über das Throwable e übergibt. class java.lang.Thread implements Runnable Þ void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
Setzt den UncaughtExceptionHandler für den Thread. Þ Thread.UncaughtExceptionHandler getUncaughtExceptionHandler()
Liefert den aktuellen UncaughtExceptionHandler. Þ Static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
Setzt den UncaughtExceptionHandler für alle Threads. Þ static Thread.UncaughtExceptionHandler getDefaultUncaughtExceptionHandler()
Liefert den zugewiesenen UncaughtExceptionHandler aller Threads. Ein mit setUncaughtExceptionHandler() lokal gesetzter UncaughtExceptionHandler überschreibt den Eintrag für den setDefaultUncaughtExceptionHandler(). Zwischen dem mit dem Thread assoziierten Handler und dem globalen gibt es noch einen Handler-Typ für Thread-Gruppen, der jedoch seltener verwendet wird.
2.2.10
Der stop() von außen und die Rettung mit ThreadDeath *
Wenn ein Thread nicht auf interrupt() hört, aber aus irgendwelchen Gründen dringend beendet werden muss, müssen wir wohl oder übel die veraltete Methode stop() einsetzen.
Abbildung 2.4: Dass die Methode stop() veraltet ist, zeigen in Eclipse eine unterschlängelte Linie und ein Symbol am linken Rand an. Steht der Cursor auf der problematischen Zeile, weist eine Fehlermeldung ebenfalls auf das Problem hin.
deprecated gibt uns schon einen guten Hinweis darauf, stop() besser nicht zu benutzen (leider gibt es hier, im Gegensatz zu den meisten anderen veralteten Methoden, keinen einfachen, empfohlenen Ersatz). Überschreiben können wir stop() auch nicht, da es final ist. Wenn wir einen Thread von außen beenden, geben wir ihm keine Chance mehr, seinen Zustand
82
2.2
Thread-Eigenschaften und -Zustände
konsistent zu verlassen. Zudem kann die Unterbrechung an beliebiger Stelle erfolgen, sodass angeforderte Ressourcen frei in der Luft hängen können. class java.lang.Thread implements Runnable Þ final void stop()
Wurde der Thread gar nicht gestartet oder ist er bereits abgearbeitet beziehungsweise beendet, kehrt die Methode sofort zurück. Andernfalls wird über checkAccess() geprüft, ob wir überhaupt das Recht haben, den Thread abzuwürgen. Dann wird der Thread beendet, egal was er zuvor unternommen hat; jetzt kann er nur noch sein Testament in Form eines ThreadDeathObjekts als Exception anzeigen.
Das ThreadDeath-Objekt So unmöglich ist das Reagieren auf ein stop() auch nicht. Immer dann, wenn ein Thread mit stop() zum Ende kommen soll, löst die JVM eine ThreadDeath-Ausnahme aus, die letztendlich den Thread beendet. ThreadDeath ist eine Unterklasse von Error, das wiederum von Throwable abgeleitet ist, sodass ThreadDeath mit einem try-Block abgefangen werden kann. Die Java-Entwickler haben ThreadDeath nicht zu einer Unterklasse von Exception gemacht, weil sie nicht wollten, dass ThreadDeath bei einer allgemeinen Exception-Behandlung über catch(Exception e) abgefangen wird.3 Wenn wir ThreadDeath auffangen, können wir noch auf den Tod reagieren und Aufräumarbeiten erlauben. Wir sollten aber nicht vergessen, anschließend das aufgefangene ThreadDeathObjekt wieder auszulösen, weil der Thread sonst nicht beendet wird: Listing 2.9: com/tutego/insel/thread/ThreadStopRecovery.java, main() Thread t = new Thread() { @Override public void run() { try { while ( true ) System.out.println( "I Like To Move It." ); } catch ( ThreadDeath td )
3
Dass wir die Klasse überhaupt nutzen können, ist einem Fehler von Sun zuzuschreiben; die Klasse sollte eigentlich nicht sichtbar sein.
83
2
2
Threads und nebenläufige Programmierung
{ System.out.println( "Das Leben ist nicht totzukriegen." ); throw td; } } }; t.start(); try { Thread.sleep( 1 ); } catch ( Exception e ) { } t.stop(); ThreadDeath bietet eine extravagante Möglichkeit, um das aktuell laufende Programm zu be-
enden: throw new ThreadDeath(). Die Anweisung System.exit() ist aber weniger Aufsehen erregend.
2.2.11
Ein Rendezvous mit join() *
Wollen wir Aufgaben auf mehrere Threads verteilen, kommt der Zeitpunkt, an dem die Ergebnisse eingesammelt werden. Die Resultate können allerdings erst dann zusammengebracht werden, wenn alle Threads mit ihrer Ausführung fertig sind. Da sie sich zu einem bestimmten Zeitpunkt treffen, heißt das auch Rendezvous. Zum Warten gibt es mehrere Strategien. Zunächst lässt sich mit Callable arbeiten, um dann mit get() synchron auf das Ende zu warten. Arbeiten wir mit Runnable, so kann ein Thread keine direkten Ergebnisse wie eine Methode nach außen geben, weil die run()-Methode den Ergebnistyp void hat. Da ein nebenläufiger Thread zudem asynchron arbeitet, wissen wir nicht einmal, wann wir das Ergebnis erwarten können. Die Übertragung von Werten ist kein Problem. Hier können Klassenvariablen und auch Objektvariablen helfen, denn über sie können wir kommunizieren. Jetzt fehlt nur noch, dass wir auf das Ende der Aktivität eines Threads warten können. Das funktioniert mit der Methode join(). In unserem folgenden Beispiel legt ein Thread t in der Variable result ein Ergebnis ab. Wir können die Auswirkungen von join() sehen, wenn wir die auskommentierte Zeile hineinnehmen: Listing 2.10: com/tutego/insel/thread/JoinTheThread.java package com.tutego.insel.thread; class JoinTheThread
84
2.2
Thread-Eigenschaften und -Zustände
{ static class JoinerThread extends Thread
2
{ public int result; @Override public void run() { result = 1; } } public static void main( String[] args ) throws InterruptedException { JoinerThread t = new JoinerThread(); t.start(); //
t.join(); System.out.println( t.result ); }
}
Ohne den Aufruf von join() wird als Ergebnis 0 ausgegeben, denn das Starten des Threads kostet etwas Zeit. In dieser Zeit geben wir aber die automatisch auf 0 initialisierte Klassenvariable aus. Nehmen wir join() hinein, wird die run()-Methode zu Ende ausgeführt, und der Thread setzt die Variable result auf 1. Das sehen wir dann auf dem Bildschirm. class java.lang.Thread implements Runnable Þ final void join() throws InterruptedException
Der aktuell ausgeführte Thread wartet auf den Thread, für den die Methode aufgerufen wird, bis dieser beendet ist. Þ final void join(long millis) throws InterruptedException
Wie join(), doch wartet diese Variante höchstens millis Millisekunden. Wurde der Thread bis dahin nicht vollständig beendet, fährt das Programm fort. Auf diese Weise kann versucht werden, innerhalb einer bestimmten Zeitspanne auf den Thread zu warten, sonst aber weiterzumachen. Ist millis gleich 0, so hat dies die gleiche Wirkung wie join(). Þ final void join (long millis, int nanos) throws InterruptedException
Wie join(long), jedoch mit potenziell genauerer Angabe der maximalen Wartezeit.
85
2
Threads und nebenläufige Programmierung
Nach einem thread.join(long) ist mitunter die thread.isAlive()-Methode nützlich, denn sie sagt aus, ob thread noch aktiv arbeitet oder beendet ist. In TimeUnit gibt es mit timedJoin() eine Hilfsmethode, um mit der Dauer schöner zu arbeiten. class java.lang.TimeUnit implements Runnable Þ voidtimedJoin(Thread thread, long timeout) throws InterruptedException
Berechnet aus der TimeUnit und dem timeout Millisekunden (ms) und Nanosekunden (ns) und führt ein join(ms, ns) auf dem thread aus.
Warten auf den Langsamsten Große Probleme lassen sich in mehrere Teile zerlegen, und jedes Teilproblem kann dann von einem Thread gelöst werden. Dies ist insbesondere bei Mehrprozessorsystemen eine lohnenswerte Investition. Zum Schluss müssen wir nur noch darauf warten, dass die Threads zum Ende gekommen sind, und das Ergebnis einsammeln. Dazu eignet sich join() gut.
Beispiel Zwei Threads arbeiten an einem Problem. Anschließend wird gewartet, bis beide ihre Aufgabe erledigt haben. Dann könnte etwa ein anderer Thread die von a und b verwendeten Ressourcen wieder nutzen: Thread a = new AThread(); Thread b = new BThread(); a.start(); b.start(); a.join(); b.join();
Es ist unerheblich, wessen join() wir zuerst aufrufen, da wir ohnehin auf den langsamsten Thread warten müssen. Wenn ein Thread schon beendet ist, kehrt join() sofort zurück. Eine andere Lösung für zusammenlaufende Threads besteht darin, diese in einer ThreadGruppe zusammenzufassen. Dann können sie zusammen behandelt werden, sodass nur das Ende der Thread-Gruppe beobachtet wird.
86
2.2
2.2.12
Thread-Eigenschaften und -Zustände
Arbeit niederlegen und wieder aufnehmen *
Wollen wir erreichen, dass ein Thread für eine bestimmte Zeit die Arbeit niederlegt und ein anderer den schlafenden Thread wieder aufwecken kann, müssten wir das selbst implementieren. Zwar gibt es mit suspend() und resume() zwei Methoden, doch diese Start-Stopp-Technik ist nicht erwünscht, da sie ähnlich problematisch ist wie stop(). class java.lang.Thread implements Runnable Þ final void suspend()
Lebt der Thread, wird er so lange eingefroren (schlafen gelegt), bis resume() aufgerufen wird. Þ final void resume()
Weckt einen durch suspend() lahmgelegten Thread wieder auf, der dann wieder arbeiten kann.
2.2.13
Priorität *
Jeder Thread verfügt über eine Priorität, die aussagt, wie viel Rechenzeit ein Thread relativ zu anderen Threads erhält. Die Priorität ist eine Zahl zwischen Thread.MIN_PRIORITY (1) und Thread.MAX_PRIORITY (10). Durch den Wert kann der Scheduler erkennen, welchem Thread er den Vorzug geben soll, wenn mehrere Threads auf Rechenzeit warten. Bei seiner Initialisierung bekommt jeder Thread die Priorität des erzeugenden Threads. Normalerweise ist es die Priorität Thread.NORM_PRIORITY (5). Das Betriebssystem (oder die JVM) nimmt die Threads immer entsprechend der Priorität aus der Warteschlange heraus (daher Prioritätswarteschlange). Ein Thread mit der Priorität N wird vor allen Threads mit der Wichtigkeit kleiner N, aber hinter denen der Priorität größer gleich N gesetzt. Ruft nun ein kooperativer Thread mit der Priorität N die Methode yield() auf, bekommt ein Thread mit der Priorität Java(TM) 2 Runtime Environment, Standard Edition Sun Microsystems Inc. http://java.sun.com/ ; ... windows pentium i486 i386
Die Methode loadFromXML() liest aus einem InputStream und löst im Fall eines fehlerhaften Dateiformats eine InvalidPropertiesFormatException aus. Beim Speichern kann so ein Fehler natürlich nicht auftreten. Und genauso, wie bei store() ein OutputStream mit einem Kommentar gespeichert wird, macht das auch storeToXML(). Die Methode ist mit einem zusätzlichen Parameter überladen, der eine XML-Kodierung erlaubt. Ist der Wert nicht gesetzt, so ist die Standardkodierung UTF-8.
247
3
Datenstrukturen und Algorithmen
class java.util.Properties extends Hashtable Þ void store(OutputStream out, String header)
Speichert die Properties-Liste mithilfe des Ausgabestroms ab. Am Kopf der Datei wird eine Kennung geschrieben, die im zweiten Argument steht. Die Kennung darf null sein. Þ void load(InputStream inStream)
Lädt eine Properties-Liste aus einem Eingabestrom. Þ void storeToXML(OutputStream os, String comment, String encoding) throws IOException
Speichert die Properties im XML-Format. comment kann null sein, wenn ein Kommentar erwünschst ist. encoding steht für die Zeichenkodierung, etwa »Latin-1« oder »UTF-8«. Þ void loadFromXML(InputStream in) throws IOException, InvalidPropertiesFormatException
Liest Properites im XML-Format von einem Eingabestrom ein.
3.7.6 Klassenbeziehungen: Properties und Hashtable * Die Properties-Klasse ist eine Erweiterung von Hashtable, weil die Speicherung der Einstellungsdaten in dieser Datenstruktur erfolgt. Ein Properties-Objekt erweitert die Hash-Tabelle um die Möglichkeit, die Schlüssel-Werte-Paare in einem festgelegten Format aus einer Textdatei beziehungsweise einem Datenstrom zu laden und wieder zu speichern. Doch gerade weil Hashtable erweitert wird, sind auch alle Methoden der Klasse Hashtable auf ein PropertiesObjekt anwendbar. Das ergibt nicht für alle Methoden Sinn und ist auch nicht in jedem Fall problemlos. Dass Properties eine Unterklasse von Hashtable ist, ist ähnlich fragwürdig wie die Vererbungsbeziehung von Stack als Unterklasse von Vector. So ist ein Augenmerk auf die put()-Methode zu legen. Sie gibt es in der Klasse Properties nicht, denn put() wird von Hashtable geerbt. Wir sollten sie auch nicht verwenden, da es über sie möglich ist, Objekte einzufügen, die nicht vom Typ String sind. Das gleiche Argument könnte für get() gelten, doch sprechen zwei Dinge dagegen: zum einen, dass wir beim get() aus einem Hashtable-Objekt immer ein Object-Objekt bekommen und daher meistens eine Typanpassung benötigen; und zum anderen durchsucht diese Methode lediglich den Inhalt des angesprochenen PropertiesExemplars. getProperties() arbeitet da etwas anders. Nicht nur ist der Rückgabewert automatisch ein String, sondern getProperties() durchsucht auch übergeordnete Properties-Objekte mit, die zum Beispiel Standardwerte speichern.
248
3.8
Mit einem Iterator durch die Daten wandern
3.8 Mit einem Iterator durch die Daten wandern Wenn wir mit einer ArrayList oder LinkedList arbeiten, so haben wir zumindest eine gemeinsame Schnittstelle List, um an die Daten zu kommen. Doch was vereinigt eine Menge (Set) und eine Liste, sodass sich die Elemente der Sammlungen mit gleichem Programmcode erfragen lassen? Listen geben als Sequenz den Elementen zwar Positionen, aber in einer Menge hat kein Element eine Position. Hier bieten sich Iteratoren beziehungsweise Enumeratoren an, die unabhängig von der Datenstruktur alle Elemente auslesen – wir sagen dann, dass sie »über die Datenstruktur iterieren«. Und nicht nur eine Datenstruktur kann Daten liefern; eine Dateioperation könnte genauso gut Datengeber für alle Zeilen sein. In Java gibt es für Iteratoren zum einen die Schnittstelle java.util.Iterator und zum anderen den älteren java.util.Enumeration. Der Enumerator ist nicht mehr aktuell, daher konzentrieren wir uns zunächst auf den Iterator.
Abbildung 3.10: Iterator und Enumeration
3.8.1 Die Schnittstelle Iterator Ein Iterator ist ein Datengeber, der über eine Methode verfügen muss, um das nächste Element zu liefern. Dann muss es eine zweite Methode geben, die Auskunft darüber gibt, ob der Datengeber noch weitere Elemente zur Verfügung stellt. Zwei Operationen der Schnittstelle Iterator sind daher:
Iterator
Hast du mehr?
Gib mir das Nächste!
hasNext()
next()
Tabelle 3.4: Zwei zentrale Methoden des Iterators
Die Methode hasNext() ermittelt, ob es überhaupt ein nächstes Element gibt, und wenn ja, ob next() das nächste Element erfragen darf. Bei jedem Aufruf von next() erhalten wir ein weiteres Element der Datenstruktur. So kann der Iterator einen Datengeber (in der Regel eine Datenstruktur) Element für Element ablaufen. Übergehen wir ein false von hasNext() und fragen trotzdem mit next() nach dem nächsten Element, bestraft uns eine NoSuchElementException.
249
3
3
Datenstrukturen und Algorithmen
Prinzipiell könnte die Methode, die das nächste Element liefert, auch per Definition null zurückgeben und so anzeigen, dass es keine weiteren Elemente mehr gibt. Allerdings kann null dann kein gültiger Iterator-Wert sein, und das wäre ungünstig. interface java.util.Iterator Þ boolean hasNext()
Liefert true, falls die Iteration weitere Elemente bietet. Þ E next()
Liefert das nächste Element in der Aufzählung oder NoSuchElementException, wenn keine weiteren Elemente mehr vorhanden sind. Þ void remove()
Die Schnittstelle Iterator erweitert selbst keine weitere Schnittstelle.11 Die Deklaration ist generisch, da das, was der Iterator liefert, immer von einem bekannten Typ ist.
Beispiel Die Aufzählung erfolgt meistens über einen Zweizeiler. Da jede Collection eine Methode iterator() besitzt, lassen sich alle Elemente wie folgt auf dem Bildschirm ausgeben: Collection set = new TreeSet(); Collections.addAll( set, "Horst", "Schlämmer", "Hape" , "Kerkeling" ); for ( Iterator iter = set.iterator(); iter.hasNext(); ) System.out.println( iter.next() );
Das erweiterte for macht das Ablaufen aber noch einfacher, und der gleiche Iterator steckt dahinter.
Beim Iterator geht es immer nur vorwärts Im Gegensatz zum Index eines Felds können wir beim Iterator ein Objekt nicht noch einmal auslesen (next() geht automatisch zum nächsten Element), nicht vorspringen beziehungsweise hin und her springen. Ein Iterator gleicht anschaulich einem Datenstrom; wollten wir ein Element zweimal besuchen, zum Beispiel eine Datenstruktur von rechts nach links noch einmal durchwandern, dann müssen wir wieder ein neues Iterator-Objekt erzeugen oder uns die Elemente zwischendurch merken. Nur bei Listen und sortierten Datenstrukturen ist die Reihenfolge der Elemente vorhersehbar.
11
Konkrete Enumeratoren (und Iteratoren) können nicht automatisch serialisiert werden; die realisierenden Klassen müssen hierzu die Schnittstelle Serializable implementieren.
250
3.8
Mit einem Iterator durch die Daten wandern
Hinweis In Java steht der Iterator nicht auf einem Element, sondern zwischen Elementen.
3 3.8.2 Der Iterator kann (eventuell auch) löschen Die Schnittstelle Iterator bietet prinzipiell die Möglichkeit, das zuletzt aufgezählte Element aus dem zugrunde liegenden Container mit remove() zu entfernen. Vor dem Aufruf muss also next() das zu löschende Element als Ergebnis geliefert haben. Eine Enumeration kann die aufgezählte Datenstruktur grundsätzlich nicht verändern.
Beispiel Ein LinkedHashSet ist eine auf dem HashSet basierende Datenstruktur, die sich aber zusätzlich die Einfügereihenfolge merkt. Ein Programm soll die ältesten Einträge löschen und nur noch die neusten zwei Elemente behalten: LinkedHashSet set = new LinkedHashSet(); set.addAll( Arrays.asList( 3, 2, 1, 6, 5, 4 ) ); System.out.println( set );
// [3, 2, 1, 6, 5, 4]
for ( Iterator iter = set.iterator(); iter.hasNext(); ) { iter.next(); if ( set.size() > 2 ) iter.remove(); } System.out.println( set );
// [5, 4]
interface java.util.Iterator Þ boolean hasNext() Þ E next() Þ void remove()
Entfernt das Element, das der Iterator zuletzt bei next() geliefert hat. Kann ein Iterator keine Elemente löschen, so löst er eine UnsupportedOperationException aus. In der Dokumentation ist die Methode remove() als optional gekennzeichnet. Das heißt, dass ein konkreter Iterator kein remove() können muss – auch eine UnsupportedOperationException ist möglich. Das ist etwa dann der Fall, wenn ein Iterator von einer unveränderbaren Datenstruktur kommt.
251
3
Datenstrukturen und Algorithmen
Hinweis Warum es die Methode remove() im Iterator gibt, ist eine interessante Frage. Die Erklärung dafür: Der Iterator kennt die Stelle, an der sich die Daten befinden (eine Art Cursor). Darum können die Daten dort auch effizient und direkt gelöscht werden. Das erklärt jedoch nicht unbedingt, warum es keine Einfüge-Methode gibt. Ein allgemeiner Grund mag sein, dass bei vielen Container-Typen das Einfügen an einer bestimmten Stelle keinen Sinn ergibt, etwa bei einem sortierten NavigableSet oder NavigableMap. Dort ist die Einfügeposition durch die Sortierung vorgegeben oder belanglos (beziehungsweise bei HashSet durch die interne Realisierung bestimmt), also kein Fall für einen Iterator. Dazu wirft das Einfügen weitere Fragen auf: vor oder nach dem zuletzt per next() gelieferten Element? Soll das neue Element mit aufgezählt werden oder nicht? Soll es auch dann nicht aufgezählt werden, wenn es in der Sortierung erst später an die Reihe käme? Eine Löschen-Methode ist problemloser und universell anwendbar.
3.8.3 Einen Zufallszahleniterator schreiben Zur Übung wollen wir einen Iterator schreiben, der Zufallszahlen liefert. Der Konstruktor soll dazu die maximale Zufallszahl entgegennehmen (exklusiv), die Methode next() liefert anschließend immer die Zufallszahl und hasNext() immer true. Da auch remove() von der Schnittstelle Iterator vorgeschrieben ist, müssen wir die Methode implementieren, lösen jedoch eine Ausnahme aus, da es nichts zu löschen gibt. Listing 3.23: com/tutego/insel/util/RandomIterator.java package com.tutego.insel.util; import java.util.*; /** * Iterator for pseudorandom numbers. */ public class RandomIterator implements Iterator { private final Random random = new Random(); private final int bound; /** * Initializes this iterator with a maximum value (exclusive) for * pseudorandom numbers.
252
3.8
Mit einem Iterator durch die Daten wandern
* @param bound Maximum (exclusive) pseudorandom */ public RandomIterator( int bound )
3
{ this.bound = bound; } /** * Always true. * @return {@code true}. */ @Override public boolean hasNext() { return true; } /** * Returns a pseudorandom, uniformly distributed {@code Integer} value * between 0 (inclusive) and the specified value (exclusive). * @return Next pseudorandom. */ @Override public Integer next() { return random.nextInt( bound ); } /** * Not supported. * @throws UnsupportedOperationException */ @Override public void remove() { throw new UnsupportedOperationException(); } }
253
3
Datenstrukturen und Algorithmen
Ein Beispiel: Listing 3.24: com/tutego/insel/util/RandomIteratorDemo.java, main() Iterator random = new RandomIterator( 6 ); int dice1 = random.next(); int dice2 = random.next(); System.out.println( dice1 ); System.out.println( dice2 );
Als Übung kann jeder den Iterator so verändern, dass auch ein Startwert definiert werden kann (jetzt ist er 0).
3.8.4 Iteratoren von Sammlungen, das erweiterte for und Iterable Jede Collection wie ArrayList oder HashSet liefert mit iterator() einen Iterator. interface java.util.Collection extends Iterable Þ Iterator iterator()
Liefert den Iterator der Datenstruktur. Collection erweitert eine Schnittstelle Iterable, die unter Java 5 eingeführt wurde; sie schreibt
die Methode vor, die einen Iterator liefert. interface java.util.Iterable Þ Iterator iterator()
Liefert den Iterator für eine Sammlung.
Der typisierte Iterator Von einer typisierten Collection liefert iterator() ebenfalls einen typisierten Iterator. Das heißt, die Datenstruktur überträgt den generischen Typ auf den Iterator. Nehmen wir eine mit String typisierte Sammlung an: Collection c = new LinkedList();
Ein Aufruf von c.iterator() liefert nun Iterator, und beim Durchlaufen über den Iterator kann die explizite Typanpassung beim next() entfallen:
254
3.8
Mit einem Iterator durch die Daten wandern
for ( Iterator i = c.iterator(); i.hasNext(); ) { String s = i.next();
3
... }
Iterator und erweitertes for Stößt der Compiler auf ein erweitertes for, und erkennt er rechts vom Doppelpunkt den Typ Iterable, so erzeugt er Bytecode für eine Schleife, die den Iterator und seine bekannten Methoden hasNext() und next() nutzt. Nehmen wir eine statische Methode totalStringLength(List) an, die ermitteln soll, wie viele Zeichen alle Strings zusammen besitzen. Aus static int totalStringLength( List strings ) { int result = 0; for ( String s : strings ) result += s.length(); return result; }
erzeugt der Compiler selbstständig: static int totalStringLength( List strings ) { int result = 0; for ( Iterator iter = strings.iterator(); iter.hasNext(); ) result += iter.next().length(); return result; }
Da die erweiterte Schleife das Ablaufen einer Datenstruktur vereinfacht, wird ein explizit ausprogrammierter Iterator selten benötigt. Doch der Iterator kann ein Element über die remove()-Methode des Iterators löschen, was über das erweiterte for nicht möglich ist.
255
3
Datenstrukturen und Algorithmen
Einen eigenen Iterator und Iterable-Implementierung Die Konzepte Iterator und Iterable müssen sauber getrennt werden: ein Iterator ist das Objekt, das durch eine Datensammlung läuft, während ein Iterable das Objekt ist, das einen anderen Iterator liefert. Eine Klasse kann beide Schnittstellen implementieren, doch oft kommt das nicht vor. Für unser nächstes Beispiel ergibt das aber Sinn – es nutzt einem Iterator, der durch eine Sammlung läuft und immer dann, wenn er an das Ende kommt, wieder von vorne beginnt. Zunächst der Einsatz: Listing 3.25: com/tutego/insel/util/RotatingIteratorDemo.java, main int i = 0; for ( String s : new RotatingIterator( "Bohnen", "Eintopf" ) ) { System.out.println( "Toll, heute gibt es " + s ); if ( i++ == 7 ) break; }
Zur Implementierung: Listing 3.26: com/tutego/insel/util/RotatingIterator.java package com.tutego.insel.util; import java.util.Arrays; import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.NoSuchElementException; /** * An {@link Iterator} that goes over a given {@link Collection} * but rolls back to the start when it reaches the end. * If the underling {@link Collection} contains no elements the method * {@link #hasNext()} will return {@code false}. Removing elements is * supported if the Iterator of the underlying Collection * supports {@link Iterator#remove()}. Iterating over the underling * Collection and modifying it at the same time will probably result in a * {@link ConcurrentModificationException}. */
256
3.8
Mit einem Iterator durch die Daten wandern
public class RotatingIterator implements Iterator, Iterable { private final Collection