Beruflich Dokumente
Kultur Dokumente
<<
Guido Krger
Handbuch der
JavaProgrammierung
3. Auflage
HTML-Ausgabe 3.0 19982002 Guido Krger
Addison-Wesley, 2002, ISBN 3-82731949-8
Homepage: www.javabuch.de
Autor:
www.gkrueger.com
hjp3@gkrueger.com
Titel
<<
<
>
>>
API
Inhaltsverzeichnis
0
1
Vorwort
Tabellen
1 Was ist Java?
2 4 Datentypen
7 OOP I:
3
Grundlagen
11 Strings
15 Collections II
4
19 Byte-Streams
23 Grafikausgabe
27 Fenster
5
31 GUI-Dialoge
35 Swing:
6 Grundlagen
Versionen
Listings
2
Schnelleinstieg
5 Ausdrcke
8 OOP II:
Vererbung, ...
12 Exceptions
16 UtilityKlassen I
20 RandomAccess-I/O
24 Textausgabe
28 EventHandling
32 AWTDialogelemente
6 Anweisungen
9 OOP III: Interfaces 10 OOP IV: Diverses
13 Progr.14 Collections I
Strukturierung
17 Utility-Klassen II 18 CharacterStreams
21
22 Multithreading
Dateien/Verzeichnisse
25 Farben
26 Drucken
29 Low-Level-Events 30 Mens
33 Eigene
Dialogelemente
34
Bitmaps/Animationen
36 Swing:
37 Swing: Kompon. I 38 Swing: Kompon.
Container/Mens
II
40 Applets II
7 39 Applets I
41 Serialisierung
42 JDBC
46 RMI
8 45
Netzwerkprogramm.
49 Performance9 Tuning
Inhaltsverzeichnis
Abbildungen
Icons
3 Wie geht es weiter?
50
Hilfsprogramme
43 Reflection
44 Beans
47
48 Sound
Sicherheit/Kryptogr.
<
>
>>
Suchen
Hier steht das SuchApplet
Anleitung
<
>
>>
<
>
>>
<
>
>>
<
>
>>
Vorwort
Das Handbuch der Java-Programmierung ist der Nachfolger von Go To Java 2
und seiner im Sptsommer 2000 erschienenen zweiten Auflage. Somit verlt das
Buch die GoTo-Reihe, in die es, vor allem auf Grund des erneut gestiegenen
Umfangs, nicht mehr recht hineinpate. Dennoch ist das Handbuch der JavaProgrammierung nicht mehr und nicht weniger als die konsequente Fortfhrung
seiner erfolgreichen Vorgnger, die bis auf "Java 1.1 lernen" und "Symantec Visual
Cafe" zurckgefhrt werden knnen. Um diese Kontinuitt deutlich zu machen, haben
wir die Numerierung der Auflagen beibehalten und der aktuellen Auflage die
Nummer 3.0 zugeordnet.
Untersttzt durch die groe Zahl an Leserzuschriften und die Diskussion mit
Kollegen, Freunden und anderen Java-Enthusiasten wurde das Buch mit vielen
Erweiterungen und Ergnzungen versehen. Alle fr Java-Einsteiger und
Fortgeschrittene wichtigen Themen werden ausfhrlich behandelt. Der bewhrte
Aufbau wurde beibehalten, und das Buch kann sowohl als Lehr- wie auch als
Nachschlagewerk eingesetzt werden.
Das Handbuch der Java-Programmierung besteht aus 50 Kapiteln, die alle
wesentlichen Aspekte der Programmiersprache Java und seiner umfangreichen
Klassenbibliothek erlutern. Mit ber 170 Abbildungen, 80 Tabellen, 440
Beispielprogrammen und 600 Querverweisen ist es fr die Verwendung im Unterricht
und zum Selbststudium bestens geeignet.
Gegenber der zweiten Auflage wurden folgende Abschnitte neu aufgenommen:
Zudem wurde das Buch an die Version 1.4 des JDK angepat und viele kleine Fehler
aus der vorigen Auflage korrigiert. Die Linkliste wurde ebenso angepat wie die JavaHistorie, die Beschreibung der Standardpakete oder die Syntax der JDK-Werkzeuge.
Fr Anfnger wurden die Kapitel 2 und 3 erneut erweitert, es gibt detaillierte
Erklrungen zum Aufruf von Java-Programmen unter Windows, und Abschnitt 2.3.7
beschreibt hufig gemachte Fehler und wie man sie umgeht. Weitere Verbesserungen
gab es bei den Abschnitten ber die Swing-Komponenten, die jetzt auch die neuen 1.4Komponenten JFormattedTextField und JSpinner sowie animierte
Fortschrittsanzeigen umfassen und auf das neue Focus-Subsystem eingehen. Der
JDBC-Teil wurde um Hinweise zur Konfiguration von MS Access, InstantDB und
HSQLDB erweitert, und der ExperimentalWebServer beherrscht jetzt das
HEAD-Kommando und eignet sich fr den Betrieb von Java WebStart. Nicht zuletzt
gab es Verbesserungen bei der HTML-Ausgabe, die jetzt noch umfangreicher und
leichter zu bedienen ist.
Gegenber der ersten Auflage wurden in der zweiten die einleitenden Kapitel
vereinfacht, um Anfngern den Einstieg zu erleichtern. Zudem wurden
weiterfhrende Themen aufgenommen, um auch dem fortgeschrittenen Leser
gengend Lesestoff zu bieten.
Die wichtigsten nderungen der zweiten Auflage waren:
Die ersten Schritte werden ausfhrlicher erklrt, und das fr viele Anfnger zu
schwierige Kapitel 2 wurde entfernt
Es gibt eine Einfhrung in die objektorientierte Programmierung sowie ein
Kapitel ber objektorientiertes Design und Design-Patterns
Bei vielen Themenkomplexen werden in einer Einleitung zunchst die
wichtigsten fachlichen Grundlagen vermittelt, bevor auf die Umsetzung in
Java eingegangen wird (Objektorientierte Programmierung,
Netzwerkprogrammierung, SQL, Kryptographie etc.)
Viele neue Kapitel oder Abschnitte sind hinzugekommen, beispielsweise
Java Beans (Kapitel 44)
Sicherheit und Kryptographie (Kapitel 47)
Remote Methode Invocation (Kapitel 46)
Byte-Streams (Kapitel 19)
Beliebig genaue Arithmetik (Abschnitt 17.3)
Internationalisierung und Ressourcen-Management (Abschnitt 17.4)
Drucken und Zugriff auf serielle und parallele Schnittstelle (Kapitel 26)
Fortgeschrittene Applet-Programmierung (Kapitel 40)
Zudem gibt es eine komplette Einfhrung in die Swing-Programmierung, die
folgende Themen behandelt:
Grundlagen und Abgrenzung zwischen Swing und AWT (Kapitel 35)
Swing-Container und Mens (Kapitel 36)
Alle elementaren Swing-Komponenten (Kapitel 37)
Komplexe Komponenten und Panels, Tables und Trees (Kapitel 38)
Ebenso wie zu den Vorgngerversionen wurde auch zu diesem Buch eine HTMLAusgabe erstellt. Sie ist auf der beigefgten CD-ROM enthalten und stellt das
komplette Buch im Hypertext-Format dar. Mit ihren Navigationshilfen und der
groen Zahl an Querverweisen (es sind inkl. Index ber 20000) ist sie ausgezeichnet
als Referenz verwendbar. Daneben enthlt die CD-ROM die Beispiele aus dem Buch
sowie das Java Development Kit und weitere ntzliche Werkzeuge und Hilfsmittel.
Wie bisher kann die HTML-Ausgabe frei aus dem Internet geladen werden. Studenten
und Leser mit eingeschrnkten finanziellen Mglichkeiten knnen so Java lernen,
ohne das Buch kaufen zu mssen, oder knnen es vor dem Kauf eingehend studieren.
Auch Universitten und vergleichbare Einrichtungen werden die Mglichkeit zur
Installation einer gespiegelten Version erhalten, um das Buch effizient in der JavaAusbildung nutzen zu knnen.
Um weitere Informationen zum Handbuch der Java-Programmierung zu erhalten,
knnen Sie seine Homepage unter http://www.javabuch.de besuchen. Dort knnen Sie
auch die freie HTML-Ausgabe herunterladen, und Sie finden eine Liste aller
bekannten Fehler und Ungenauigkeiten. Teilen Sie mir Ihre Kritik und Anregungen
mit, oder schreiben Sie mir, wenn Sie Fehler gefunden haben. Natrlich knnen Sie
auch schreiben, da Ihnen das Buch gefallen hat! Verstndnisfragen zu einzelnen
Aspekten der Java-Programmierung kann ich aus Zeitgrnden leider nicht mehr
beantworten. Sie sind besser in einer der zahlreichen Java-Newsgroups aufgehoben
(siehe Abschnitt 3.2.2).
Wie die Vorversionen wurde auch die dritte Auflage vollstndig in SGML
geschrieben (der Mutter von HTML und XML). Alle Werkzeuge zum Erstellen der
verschiedenen Ausgaben wurden vom Autor selbst entwickelt und sind in Java
geschrieben. Die ohne zustzlichen Aufwand erstellbare HTML-Ausgabe kann bei
Fehlern oder Erweiterungen sehr schnell aktualisiert und unabhngig von den
Nachdruckzyklen der Papierversion gepflegt werden. Alle nderungen werden
versioniert, der Erstdruck des Buchs entspricht der HTML-Ausgabe 3.0.
Ich wnsche allen Lesern, da ihnen dieses Buch beim Erlernen und Anwenden von
Java ein unentbehrlicher Helfer sein wird und da sie nach seiner Lektre ber
umfassende Kenntnisse in der Java-Programmierung verfgen mgen.
Mein Dank gilt allen, die bei der Entstehung mitgewirkt haben. Besonders mchte ich
Kollegen und Bekannten danken, die sich der Mhe unterzogen haben, einzelne
Kapitel zu lesen, und mit ihren Hinweisen und Anregungen zu seiner jetzigen Form
beigetragen haben. Hier sind vor allem Ilona Brinkmeier, Holger Jdicke, Boris
Gruschko, Thomas Backens, Goetz Perry, Stefan Stark, Andi Mller, Jacques Nietsch
und Carsten Leutzinger zu nennen. Den vielen Lesern der Vorversionen, die Fehler
gefunden oder Anregungen gegeben haben, mchte ich ebenso danken wie Christiane
Auf und Christina Gibbs, die das Buch als Lektorinnen bei Addison-Wesley betreut
haben.
Wie immer geht ein besonderer Dank an Sabine, Jana und Jasmin, ohne deren
Untersttzung und Geduld auch dieses Buch nicht mglich gewesen wre.
Guido Krger, Februar 2002
<
>
>>
45.2 Standard-Port-Nummern
45.3 Liste wichtiger RFCs
47.1 Wichtige Permission-Klassen
49.1 Geschwindigkeit von Methodenaufrufen
49.2 Parameter von hprof
50.1 Optionen von javac
50.2 Optionen des Java-Interpreters
50.3 Optionen von appletviewer
50.4 Kommandos von jdb
50.5 Markierungen in Dokumentationskommentaren
50.6 Einige Optionen von javadoc
50.7 Kommandos von jar
50.8 Optionen von javap
50.9 Optionen von serialver
50.10 Optionen von jarsigner
50.11 Optionen von rmic
<
>
>>
<
>
>>
Tip
Warnung
<
>
>>
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 1
Was ist Java?
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 2
Schnelleinstieg
2 Schnelleinstieg
2.1 Installation des JDK
2.1.1 Hardware-Voraussetzungen
2.1.2 Installation
Installation des JDK
Installation der Dokumentation
Installation der Quelltexte
Deinstallation
2.2 Erste Gehversuche
2.2.1 Quelltext erstellen, bersetzen
und ausfhren
1. Vorbereitung
2. Erstellen des Quelltextes
3. bersetzen des Quelltextes
4. Ausfhren des erzeugten
Programms
5. Beenden des Programms
2.2.2 Die Beispielprogramme
Starten der Beispielprogramme
Zustzlich erforderliche Dateien
2.3 Tips fr eigene Experimente
2.3.1 Der Entwicklungszyklus in
Kurzform
2.3.2 Einfache Ausgaben
2.3.3 Einfache Eingaben
2.3.4 Formatierung der Quelltexte
2.3.5 Namenskonventionen
2.3.6 Aufruf von Java-Programmen
unter Windows
Erstellen einer Batchdatei
Erstellen einer Verknpfung auf
dem Desktop
Verwenden von javaw anstelle
von java
2.3.7 Troubleshooting
Was ist eine DOS-Box und wie
startet man sie?
java luft, javac aber nicht
Was sind eigentlich
Umgebungsvariablen?
Der Compiler javac arbeitet
nicht
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 3
Wie geht es weiter?
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 4
Datentypen
4 Datentypen
4.1 Lexikalische Elemente eines JavaProgramms
4.1.1 Eingabezeichen
4.1.2 Kommentare
4.1.3 Bezeichner
4.1.4 Weitere Unterschiede zu C
4.2 Primitive Datentypen
4.2.1 Der logische Typ
Literale
4.2.2 Der Zeichentyp
Literale
4.2.3 Die integralen Typen
Literale
4.2.4 Die Fliekommazahlen
Literale
4.3 Variablen
4.3.1 Grundeigenschaften
4.3.2 Deklaration von Variablen
4.3.3 Lebensdauer/Sichtbarkeit
4.4 Arrays
4.4.1 Deklaration und
Initialisierung
4.4.2 Zugriff auf ArrayElemente
4.4.3 Mehrdimensionale Arrays
4.5 Referenztypen
4.5.1 Beschreibung
4.5.2 Speichermanagement
4.6 Typkonvertierungen
4.6.1 Standardkonvertierungen
4.6.2 Vorzeichenlose Bytes
4.7 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 5
Ausdrcke
5 Ausdrcke
5.1 Eigenschaften von Ausdrcken
5.2 Arithmetische Operatoren
5.3 Relationale Operatoren
5.4 Logische Operatoren
5.5 Bitweise Operatoren
5.6 Zuweisungsoperatoren
5.7 Sonstige Operatoren
5.7.1 Weitere Operatoren fr primitive
Typen
Fragezeichenoperator
Type-Cast-Operator
5.7.2 Operatoren fr Objekte
String-Verkettung
Referenzgleichheit und ungleichheit
Der instanceof-Operator
Der new-Operator
Member-Zugriff
Methodenaufruf
Zugriff auf Array-Elemente
5.7.3 Welche Operatoren es nicht gibt
5.8 Operator-Vorrangregeln
5.9 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 6
Anweisungen
6 Anweisungen
6.1 Elementare Anweisungen
6.1.1 Die leere Anweisung
Syntax
Bedeutung
6.1.2 Der Block
Syntax
Bedeutung
6.1.3 Variablendeklarationen
Syntax
Bedeutung
6.1.4 Ausdrucksanweisungen
Syntax
Bedeutung
6.2 Verzweigungen
6.2.1 Die if-Anweisung
Syntax
Bedeutung
Dangling else
Bedingtes Kompilieren
6.2.2 Die switch-Anweisung
Syntax
Bedeutung
6.3 Schleifen
6.3.1 Die while-Schleife
Syntax
Bedeutung
6.3.2 Die do-Schleife
Syntax
Bedeutung
6.3.3 Die for-Schleife
Syntax
Bedeutung
break und continue
6.4 Sonstige Anweisungen
6.4.1 Die assert-Anweisung
Syntax
Bedeutung
An- und Abschalten der
Assertions
Anwendungen
Nebeneffekte
Kompatibilitt
6.5 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 7
OOP I: Grundlagen
7 OOP I: Grundlagen
7.1 Konzepte objektorientierter
Programmiersprachen
7.1.1 Einfhrung
7.1.2 Abstraktion
7.1.3 Kapselung
7.1.4 Wiederverwendung
7.1.5 Beziehungen
Generalisierung und Spezialisierung
Aggregation und Komposition
Verwendungs- und
Aufrufbeziehungen
7.1.6 Polymorphismus
7.1.7 Fazit
7.2 Klassen und Objekte in Java
7.2.1 Klassen
7.2.2 Objekte
7.3 Methoden
7.3.1 Definition
7.3.2 Aufruf
7.3.3 Parameter
7.3.4 Rckgabewert
7.3.5 berladen von Methoden
Die Signatur einer Methode
7.3.6 Konstruktoren
Default-Konstruktoren
Verkettung von Konstruktoren
Initialisierungsreihenfolge
7.3.7 Destruktoren
7.4 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Kapitel 8
OOP II: Vererbung, Polymorphismus und statische Elemente
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 9
OOP III: Interfaces
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 10
OOP IV: Verschiedenes
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 11
Strings
11 Strings
11.1 Grundlegende Eigenschaften
11.2 Methoden der Klasse String
11.2.1 Konstruktoren
11.2.2 Zeichenextraktion
11.2.3 Die Lnge der
Zeichenkette
11.2.4 Vergleichen von
Zeichenketten
11.2.5 Suchen in Zeichenketten
11.2.6 Ersetzen von
Zeichenketten
11.2.7 Konvertierungsfunktionen
11.3 Weitere Eigenschaften
11.3.1 Die Klasse String ist final
11.3.2 Was ist ein String fr den
Compiler?
String-Literale
String-Verkettung und Zuweisung
11.3.3 String-Objekte sind nicht
dynamisch
11.4 Die Klasse StringBuffer
11.4.1 Konstruktoren
11.4.2 Einfgen von Elementen
11.4.3 Lschen von Elementen
11.4.4 Verndern von Elementen
11.4.5 Lngeninformationen
11.4.6 Konvertierung in einen
String
11.5 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 12
Exceptions
12 Exceptions
12.1 Grundlagen und Begriffe
12.2 Behandlung von Exceptions
12.2.1 Die try-catch-Anweisung
12.2.2 Das Fehlerobjekt
12.2.3 Die Fehlerklassen von Java
12.2.4 Fortfahren nach Fehlern
12.2.5 Mehr als eine catch-Klausel
12.2.6 Die finally-Klausel
12.3 Weitergabe von Exceptions
12.3.1 Die catch-or-throw-Regel
12.3.2 Weitergabe einer Exception
Die Klasse
RuntimeException
12.3.3 Auslsen von Ausnahmen
12.4 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 13
Strukturierung von Java-Programmen
Archivs
Ausfhrbare jar-Archive
13.4.2 Einbinden von
Ressourcen-Dateien
13.5 Java Web Start
13.5.1 Einleitung und
Funktionsweise
13.5.2 Erstellen einer WebStartApplikation
Erzeugen der jar-Dateien
Anlegen der
Deskriptordatei
Die Anwendung auf dem
Web-Server installieren
Starten der Anwendung
13.5.3 Das jnlp-API
Services
Ein einfaches Beispiel
13.6 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 14
Collections I
14 Collections I
14.1 Grundlagen und Konzepte
14.2 Die Klasse Vector
14.2.1 Einfgen von
Elementen
14.2.2 Zugriff auf
Elemente
14.2.3 Der Vektor als
Iterator
14.3 Die Klasse Stack
14.4 Die Klasse Hashtable
14.4.1 Einfgen von
Elementen
14.4.2 Zugriff auf
Elemente
14.4.3 Hashtable als
Iterator
14.4.4 Die Klasse
Properties
14.5 Die Klasse BitSet
14.5.1 Elementweise
Operationen
14.5.2 Mengenorientierte
Operationen
14.6 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 15
Collections II
15 Collections II
15.1 Grundlagen und Konzepte
15.2 Die Collection des Typs List
15.2.1 Abstrakte
Eigenschaften
15.2.2 Implementierungen
15.3 Iteratoren
15.3.1 Das Interface
Iterator
15.3.2 Das Interface
ListIterator
15.4 Eine eigene Queue-Klasse
15.4.1 Anforderungen
15.4.2 Implementierung
15.5 Die Collection des Typs Set
15.5.1 Abstrakte
Eigenschaften
15.5.2 Implementierungen
15.6 Die Collection des Typs
Map
15.6.1 Abstrakte
Eigenschaften
15.6.2 Implementierungen
15.7 Sortierte Collections
15.7.1 Comparable und
Comparator
15.7.2 SortedSet und
TreeSet
15.7.3 SortedMap und
TreeMap
15.8 Die Klasse Collections
15.8.1 Sortieren und
Suchen
15.8.2 Synchronisieren
von Collections
15.8.3 Erzeugen
unvernderlicher
Collections
15.9 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 16
Utility-Klassen I
16 Utility-Klassen I
16.1 Die Klasse Random
16.1.1 Initialisierung des
Zufallszahlengenerators
16.1.2 Erzeugen von
Zufallszahlen
Gleichverteilte
Zufallszahlen
Normalverteilte
Zufallszahlen
16.2 Die Klassen Date, Calendar und
GregorianCalendar
16.2.1 Konstruktoren
16.2.2 Abfragen und Setzen von
Datumsbestandteilen
16.2.3 Vergleiche und Datums/Zeitarithmetik
16.2.4 Umwandlung zwischen
Date und Calendar
16.3 Die Klasse System
16.3.1 System-Properties
16.3.2 in, err und out
16.3.3 exit
16.3.4 gc
16.3.5 currentTimeMillis
16.3.6 arraycopy
16.4 Die Klasse RunTime
16.4.1 Grundlagen
16.4.2 Interaktion mit dem
externen Programm
16.5 Die Klasse Arrays
16.6 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 17
Utility-Klassen II
17 Utility-Klassen II
17.1 Die Klasse StringTokenizer
17.1.1 Anlegen eines StringTokenizers
17.1.2 Zugriff auf Tokens
17.2 Die Klasse Math
17.2.1 Winkelfunktionen
17.2.2 Minimum und Maximum
17.2.3 Arithmetik
17.2.4 Runden und Abschneiden
17.3 Die Klassen BigInteger und BigDecimal
17.3.1 Die Klasse BigInteger
17.3.2 Die Klasse BigDecimal
17.4 Internationalisierung und Lokalisierung
17.4.1 Die Klasse Locale
17.4.2 Zahlen formatieren
17.4.3 Datum und Uhrzeit formatieren
17.4.4 Laden von Ressourcen
Die Methode getBundle
Eigene Ressourcenklassen
erstellen
Die Verkettung von
ResourceBundles
Die Klasse
PropertyResourceBundle
17.5 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 18
Character-Streams
18 Character-Streams
18.1 Allgemeine Konzepte
18.2 Ausgabe-Streams
18.2.1 Die abstrakte Klasse Writer
18.2.2 Auswahl des Ausgabegertes
OutputStreamWriter und
FileWriter
StringWriter und
CharArrayWriter
18.2.3 Schachteln von AusgabeStreams
BufferedWriter
PrintWriter
FilterWriter
18.3 Eingabe-Streams
18.3.1 Die abstrakte Klasse Reader
18.3.2 Auswahl des Eingabegertes
InputStreamReader und
FileReader
StringReader und
CharArrayReader
18.3.3 Schachteln von EingabeStreams
BufferedReader
LineNumberReader
FilterReader und
PushbackReader
18.4 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 19
Byte-Streams
19 Byte-Streams
19.1 Architektur und Vergleich mit CharacterStreams
19.2 Ausgabe-Streams
19.2.1 Die Basisklasse OutputStream
19.2.2 Aus OutputStream direkt
abgeleitete Klassen
FileOutputStream
ByteArrayOutputStream
ObjectOutputStream
PipedOutputStream
19.2.3 Aus FilterOutputStream
abgeleitete Klassen
BufferedOutputStream
PrintStream
DataOutput und
DataOutputStream
Komprimieren von Dateien
Berechnung von Prfsummen
19.3 Eingabe-Streams
19.3.1 Die Basisklasse InputStream
19.3.2 Aus InputStream direkt abgeleitete
Klassen
FileInputStream
ByteArrayInputStream
ObjectInputStream
SequenceInputStream
PipedInputStream
19.3.3 Aus FilterInputStream abgeleitete
Klassen
BufferedInputStream
PushbackInputStream
DataInputStream und DataInput
CheckedInputStream
Entpacken von Dateien
19.4 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 20
Random-Access-I/O
20 Random-Access-I/O
20.1 Grundlegende
Operationen
20.2 Navigation in der Datei
20.2.1 Positionierung
des Dateizeigers
20.2.2 Die Lnge der
Datei
20.3 Lesezugriffe
20.4 Schreibzugriffe
20.5 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 21
Datei- und Verzeichnis-Handling
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 22
Multithreading
22 Multithreading
22.1 Grundlagen und Begriffe
22.2 Die Klasse Thread
22.2.1 Erzeugen eines neuen
Threads
22.2.2 Abbrechen eines Threads
22.2.3 Anhalten eines Threads
22.2.4 Weitere Methoden
sleep
isAlive
join
22.3 Das Interface Runnable
22.3.1 Implementieren von
Runnable
22.3.2 Multithreading durch
Wrapper-Klassen
22.4 Synchronisation
22.4.1 Synchronisationsprobleme
22.4.2 Monitore
Anwendung von
synchronized auf einen
Block von Anweisungen
Anwendung von
synchronized auf eine
Methode
22.4.3 wait und notify
22.4.4 PipedInputStream und
PipedOutputStream
22.5 Verwalten von Threads
22.5.1 Prioritt und Name
22.5.2 Thread-Gruppen
22.6 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 23
Grafikausgabe
23 Grafikausgabe
23.1 Das Abstract Windowing
Toolkit
23.1.1 Grundlegende
Eigenschaften
23.1.2 Von AWT nach
Swing
nderungen mit dem
JDK 1.1
Swing
23.2 Grundlagen der Grafikausgabe
23.2.1 Anlegen eines
Fensters
23.2.2 Die Methode paint
23.2.3 Das grafische
Koordinatensystem
23.2.4 Schlieen eines
Fensters
23.3 Elementare Grafikroutinen
23.3.1 Linie
23.3.2 Rechteck
23.3.3 Polygon
23.3.4 Kreis
23.3.5 Kreisbogen
23.4 Weiterfhrende Funktionen
23.4.1 Linien- oder
Fllmodus
23.4.2 Kopieren und Lschen
von Flchen
23.4.3 Die Clipping-Region
23.5 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 24
Textausgabe
24 Textausgabe
24.1 Ausgabefunktionen
24.2 Unterschiedliche Schriftarten
24.2.1 Font-Objekte
24.2.2 Standardschriftarten
24.3 Eigenschaften von
Schriftarten
24.3.1 Font-Informationen
24.3.2 Font-Metriken
24.4 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 25
Farben
25 Farben
25.1 Das JavaFarbmodell
25.2 Erzeugen von
Farben
25.3 Verwenden von
Farben
25.4 Systemfarben
25.5 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 26
Drucken
26 Drucken
26.1 Einleitung
26.2 Drucken mit dem JDK 1.1
26.2.1 Grundlagen
26.2.2 Seitenweise Ausgabe
26.2.3 Plazierung des Codes zur
Druckausgabe
26.3 Drucken seit dem JDK 1.2
26.3.1 berblick
26.3.2 Zusammenspiel der Klassen
Die Klasse PrinterJob
Die Klassen Paper und
PageFormat
Die Konfigurationsdialoge
Das Interface Printable
Das Interface Pageable und die
Klasse Book
26.3.3 Ausdrucken einer Textdatei
Die Hilfsklasse FilePrintHelper
Die Klasse SimpleFilePrinter
26.4 Zugriff auf serielle und parallele
Schnittstellen
26.4.1 Das Java Communications API
Installation
Prinzipielle Arbeitsweise
26.4.2 Ein einfaches Beispielprogramm
26.5 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
DOC
API
Kapitel 27
Fenster
Titel
<<
27 Fenster
27.1 Die verschiedenen
Fensterklassen
27.2 Aufrufen und Schlieen
eines Fensters
27.3 Visuelle Eigenschaften
27.4 Anzeigezustand
27.5 Fensterelemente
27.5.1 Der Fenstertitel
27.5.2 Das Icon des
Fensters
27.5.3 Der Mauscursor
27.5.4 Die Vorder- und
Hintergrundfarbe
27.5.5 Der StandardFont
27.6 Zusammenfassung
>
>>
DOC
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 28
Event-Handling
28 Event-Handling
28.1 Das Event-Handling im JDK 1.1
28.1.1 Grundlagen
28.1.2 Ereignistypen
28.1.3 Ereignisempfnger
28.1.4 Ereignisquellen
28.1.5 Adapterklassen
28.1.6 Zusammenfassung
Focus-Ereignisse
Key-Ereignisse
Mouse-Ereignisse
MouseMotionEreignisse
Component-Ereignisse
Container-Ereignisse
Window-Ereignisse
Action-Ereignisse
Adjustment-Ereignisse
Item-Ereignisse
Text-Ereignisse
28.2 Entwurfsmuster fr den
Nachrichtenverkehr
28.2.1 Variante 1:
Implementierung eines
EventListener-Interfaces
28.2.2 Variante 2: Lokale und
anonyme Klassen
Lokale Klassen
Anonyme Klassen
28.2.3 Variante 3: Trennung
von GUI- und
Anwendungscode
28.2.4 Variante 4: berlagern
der Event-Handler in den
Komponenten
28.2.5 Ausblick
28.3 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 29
Low-Level-Events
29 Low-Level-Events
29.1 Window-Events
29.2 ComponentEvents
29.3 Mouse-Events
29.4 MouseMotionEvents
29.5 Focus-Events
29.6 Key-Events
29.7 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 30
Mens
30 Mens
30.1 Grundlagen
30.2 Menleiste
30.3 Mens
30.4 Meneintrge
30.4.1 Einfache
Meneintrge
30.4.2 CheckboxMenuItem
30.4.3 Beschleunigertasten
30.4.4 Untermens
30.5 Action-Events
30.6 Kontextmens
30.7 Datenaustausch mit der
Zwischenablage
30.7.1 berblick
30.7.2 Kommunikation mit
der Zwischenablage
30.8 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 31
GUI-Dialoge
31 GUI-Dialoge
31.1 Erstellen eines Dialogs
31.1.1 Anlegen eines
Dialogfensters
31.1.2 Zuordnen eines
Layoutmanagers
31.1.3 Einfgen von
Dialogelementen
31.1.4 Anzeigen des
Dialogfensters
31.2 Die Layoutmanager
31.2.1 FlowLayout
31.2.2 GridLayout
Die Gre der
Komponenten
31.2.3 BorderLayout
31.2.4 GridBagLayout
31.2.5 NULL-Layout
31.2.6 Schachteln von
Layoutmanagern
31.3 Modale Dialoge
31.4 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 32
AWT-Dialogelemente
32 AWT-Dialogelemente
32.1 Rahmenprogramm
32.2 Label
32.3 Button
32.4 Checkbox
32.5 CheckboxGroup
32.6 TextField
32.7 TextArea
32.8 Choice
32.9 List
32.10 Scrollbar
32.11 ScrollPane
32.12
Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 33
Eigene Dialogelemente
33 Eigene Dialogelemente
33.1 Die Klasse Canvas
33.2 Entwicklung einer 7-SegmentAnzeige
33.2.1 Anforderungen
33.2.2 Bildschirmanzeige
33.2.3 Ereignisbehandlung
33.3 Einbinden der Komponente
33.4 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 34
Bitmaps und Animationen
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 35
Swing: Grundlagen
35 Swing: Grundlagen
35.1 Eigenschaften und Architektur von
Swing
35.1.1 Einleitung
35.1.2 Eigenschaften von Swing
Leichtgewichtige
Komponenten
Pluggable Look-and-Feel
Das Model-ViewController-Prinzip
Aber...
35.1.3 Wie geht es weiter?
35.2 Ein einfhrendes Beispiel
35.2.1 Das Beispielprogramm
35.2.2 Beschreibung des
Beispielprogramms
Die import-Anweisungen
JFrame
JPanel
JLabel
JTextField
Tooltips und
Umrandungen
JList und JScrollPane
JButton
Umschalten des Look-andFeel
35.3 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 36
Swing: Container und Mens
Registrieren von
Tastaturkommandos
36.3.2 JPanel und JLayeredPane
JPanel
JLayeredPane
36.4 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 37
Swing: Komponenten I
37 Swing: Komponenten I
37.1 Label und Textfelder
37.1.1 JLabel
37.1.2 JTextField
37.1.3 JPasswordField
37.1.4 JTextArea
37.1.5 JSpinner
37.2 Buttons
37.2.1 JButton
37.2.2 JCheckBox
37.2.3 JRadioButton
37.3 Listen und Comboboxen
37.3.1 JList
Selektieren von
Elementen
Den Listeninhalt
dynamisch verndern
Beispiel
37.3.2 JComboBox
37.4 Quasi-analoge Komponenten
37.4.1 JScrollBar
37.4.2 JSlider
37.4.3 JProgressBar
37.5 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 38
Swing: Komponenten II
38 Swing: Komponenten II
38.1 Spezielle Panels
38.1.1 JScrollPane
38.1.2 JSplitPane
38.1.3 JTabbedPane
38.2 JTable
38.2.1 Erzeugen von Tabellen
38.2.2 Konfiguration der Tabelle
38.2.3 Selektieren von Elementen
Selektionsmodi
Abfragen der Selektion
Verndern der Selektion
38.2.4 Zugriff auf den Inhalt der
Tabelle
Die Daten in der Tabelle
Editieren von
Tabellenelementen
38.2.5 Das Tabellenmodell
Beispiel
38.2.6 Das Spaltenmodell
38.2.7 Rendering der Zellen
38.2.8 Reaktion auf Ereignisse
38.3 JTree
38.3.1 Erzeugen eines Baums
38.3.2 Selektieren von Knoten
Konfiguration der
Selektionsmglichkeit
Abfragen der Selektion
Verndern der Selektion
38.3.3 ffnen und Schlieen der
Knoten
38.3.4 Verndern der Baumstruktur
38.4 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 39
Applets I
39 Applets I
39.1 Die Architektur eines Applets
39.1.1 Grundlagen
39.1.2 Die Klasse java.awt.Applet
39.1.3 Initialisierung und
Endebehandlung
Instanzierung des Applets
Initialisierung des Applets
Starten des Applets
Stoppen des Applets
Zerstren des Applets
39.1.4 Weitere Methoden der Klasse
Applet
Methoden zum
Nachrichtentransfer
showStatus
getParameterInfo
getAppletInfo
39.2 Einbinden eines Applets
39.2.1 Das APPLET-Tag
39.2.2 Die Parameter des AppletTags
39.2.3 Parameterbergabe an
Applets
39.3 Die Ausgabe von Sound
39.3.1 Soundausgabe in Applets
39.3.2 Soundausgabe in
Applikationen
39.4 Animation in Applets
39.5 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 40
Applets II
40 Applets II
40.1 Verweise auf andere Seiten
40.1.1 Die Klasse URL
40.1.2 Der Applet-Kontext
40.1.3 Die Methode
showDocument
40.2 Kommunikation zwischen
Applets
40.3 Umwandlung einer Applikation
in ein Applet
40.3.1 Die BeispielApplikation
40.3.2 Variante 1: Das
Programm als Popup-Fenster
40.3.3 Variante 2: Erstellen
eines gleichwertigen Applets
40.4 Das Java-Plugin
40.4.1 Funktionsweise
40.4.2 Verwendung des Plugins
Installation
Konvertierung der
HTML-Seiten
40.5 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt Suchen
<
>
Index
>>
DOC
API
Kapitel 41
Serialisierung
Titel
<<
41 Serialisierung
41.1 Grundlagen
41.1.1 Begriffsbestimmung
41.1.2 Schreiben von
Objekten
41.1.3 Lesen von Objekten
41.2 Weitere Aspekte der
Serialisierung
41.2.1 Versionierung
41.2.2 Nicht-serialisierte
Membervariablen
41.2.3 Objektreferenzen
41.2.4 Serialisieren von
Collections
41.3 Anwendungen
41.3.1 Ein einfacher
Objektspeicher
41.3.2 Kopieren von
Objekten
41.4 Zusammenfassung
Inhalt Suchen
<
>
Index
DOC
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 42
Datenbankzugriffe mit JDBC
42.5 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
DOC
API
Kapitel 43
Reflection
Titel
<<
43 Reflection
43.1 Einleitung
43.2 Die Klassen Object und
Class
43.2.1 Die Klasse
Object
43.2.2 Die Klasse
Class
43.3 Methoden- und
Konstruktorenaufrufe
43.3.1 Parameterlose
Methoden
43.3.2 Parametrisierte
Methoden
43.3.3 Parametrisierte
Konstruktoren
43.4 Zugriff auf
Membervariablen
43.5 Arrays
43.5.1 Erzeugen von
Arrays
43.5.2 Zugriff auf
Array-Elemente
43.6 Zusammenfassung
>
>>
DOC
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 44
Beans
44 Beans
44.1 Grundlagen und Begriffe
44.2 Entwurf einer einfachen Bean
44.2.1 Grundstzliche Architektur
44.2.2 Grafische Darstellung
44.2.3 Eigenschaften
Objekte als Eigenschaften
Indizierte Eigenschaften
44.2.4 Implementierung
44.2.5 Verwendung der Bean
44.3 Die Beanbox
44.3.1 Beschreibung und Installation
Installation des BDK
Die Aufrufscripte
44.3.2 Grundlagen der Bedienung
Bedienelemente
Einfgen und Modifizieren von
Beans
Events
44.3.3 Integration eigener Beans
Entwickeln und bersetzen der
Bean
Erzeugen einer Manifest-Datei
Erzeugen des jar-Archivs
Verwenden der eigenen Bean
44.3.4 Serialisierte Beans
Serialisieren von Beans mit der
Beanbox
Einbinden serialisierter Beans
44.4 Bean-Ereignisse
44.4.1 Bound Properties
44.4.2 Constrained Properties
44.4.3 Anwendungsbeispiel
44.5 Panel-Beans
44.6 BeanInfo-Klassen und Property-Editoren
44.6.1 BeanInfo-Klassen
getIcon
getPropertyDescriptors
getMethodDescriptors
Die Klasse LightBulbBeanInfo
44.6.2 Property-Editoren
LightBulbLightOnEditor1
LightBulbLightOnEditor2
LightBulbLightOnEditor3
44.7 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 45
Netzwerkprogrammierung
45 Netzwerkprogrammierung
45.1 Grundlagen der
Netzwerkprogrammierung
45.1.1 Was ist ein
Netzwerk?
45.1.2 Protokolle
45.1.3 Adressierung von
Daten
IP-Adressen
Domain-Namen
45.1.4 Ports und
Applikationen
45.1.5 Request for
Comments
45.1.6 Firewalls und
Proxys
45.2 Client-Sockets
45.2.1 Adressierung
45.2.2 Aufbau einer
einfachen SocketVerbindung
45.2.3 Lesen und
Schreiben von Daten
45.2.4 Zugriff auf einen
Web-Server
45.3 Server-Sockets
45.3.1 Die Klasse
ServerSocket
45.3.2 Verbindungen zu
mehreren Clients
45.3.3 Entwicklung eines
einfachen Web-Servers
45.4 Daten mit Hilfe der Klasse
URL lesen
45.5 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 46
Remote Method Invocation
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 47
Sicherheit und Kryptographie
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 48
Sound
48 Sound
48.1 Grundlagen und Konzepte
48.2 Gesampelter Sound
48.2.1 Was ist Sampling?
48.2.2 Das Sampling-API
Zugriff auf AudioDateien
Audio-Gerte beschaffen
Die Steuerelemente eines
Audio-Gerts
Der Clip
48.2.3 Abspielen einer SampleDatei
48.3 Midi
48.3.1 Was ist Midi?
48.3.2 Grundlegende Klassen
des Midi-APIs
48.3.3 Alle meine Entchen Erster Versuch
48.3.4 Alle meine Entchen mit
dem Sequenzer
48.3.5 Zugriff auf Midi-Dateien
Lesen und Abspielen
einer Midi-Datei
Abspeichern einer
Sequenz in einer MidiDatei
48.4 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Kapitel 49
Performance-Tuning
49 Performance-Tuning
49.1 Einleitung
49.2 Tuning-Tips
49.2.1 String und StringBuffer
String-Verkettung
Einfgen und Lschen in
Strings
Die Methode toString der
Klasse StringBuffer
Die Unvernderlichkeit von
String-Objekten
Durchlaufen von
Zeichenketten
49.2.2 Methodenaufrufe
49.2.3 Vektoren und Listen
49.2.4 Dateizugriffe
Schreiben von Streams
Lesen von Streams
RandomAccess-Dateien
49.2.5 Speicher-Optimierung
49.3 Einsatz eines Profilers
49.3.1 Grundlagen
49.3.2 Eine Beispielsitzung mit dem
Profiler
Erzeugen der ProfilingInformationen
Das CPU-Profile
Das Heap-Profile
Die optimierte Version des
Programms
49.3.3 Ausblick
49.4 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Kapitel 50
Hilfsprogramme des JDK
Index
>>
DOC
API
Skeletons
50.12.1 Aufruf
50.12.2 Beschreibung
50.12.3 Optionen
50.13 rmiregistry - Der RMINamensservice
50.13.1 Aufruf
50.13.2 Beschreibung
50.14 Zusammenfassung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
50.14 Zusammenfassung
50.14 Zusammenfassung
<<
<
>
>>
API
-0-author
-c
-classpath
-client
-cp
-D
-d
-da
-debug
-depend
-deprecation
-disableassertions
-ea
-enableassertions
-g
-help
-J
-jar
-keepgenerated
-keypass
-keystore
-l
-nowarn
-O
-package
-private
-prof
-protected
-public
-s
[50.5.5]
[50.7.3]
[2.3.7.5] [13.4.1.1] [50.1.3] [50.2.3] [50.5.5]
[50.7.3] [50.12.3]
[50.2.2]
[2.3.7.5] [13.4.1.1] [50.2.3]
[50.2.3]
[13.4.1.2] [50.5.5] [50.12.3]
[6.4.1.3]
[50.3.3]
[50.1.3]
[50.1.3]
[6.4.1.3] [50.2.3]
[6.4.1.3]
[6.4.1.3] [50.2.3]
[50.1.3] [50.4.3] [50.12.3]
[50.2.3]
[49.3.2.4]
[13.4.1.3]
[50.12.3]
[50.11.3]
[50.11.3]
[50.7.3]
[50.1.3]
[50.1.3]
[50.5.5] [50.7.3]
[50.5.5] [50.7.3]
[49.3.2.4] [50.2.3]
[50.5.5] [50.7.3]
[50.5.5] [50.7.3]
[50.7.3]
[50.2.2]
[50.8.3]
[6.4.1.3]
[50.5.5]
[50.11.3]
[50.11.3]
[50.1.3]
[50.12.3]
[50.12.3]
[50.12.3]
[50.1.3] [50.2.3] [50.5.5]
[50.2.3]
[50.11.3]
[50.2.3] [50.5.5]
[50.2.2]
[49.3.2.1] [49.3.2.4] [50.2.2]
[50.2.2]
[49.2.5] [50.2.3]
[50.2.3]
[49.3.2.4]
[49.3.2.1] [49.3.2.4]
[13.2.4.1] [13.3.1] [13.3.2.1] [50.1.2] [20.3
(Format)]
.class-Notation fr Klassenobjekte [43.3.2]
.inc-Dateien
[23.3]
.java-Dateien
[2.2.1.2] [2.2.2.1] [13.2.4.1] [50.1.2]
.java.policy
[47.3.4.1]
.keystore
[47.1.6.1]
.TYPE-Objekte
[43.3.1]
[4.1.2]
/*
[4.1.2] [50.5.3]
/**
[4.1.2]
//
100 % Pure Java Initiative
[1.1]
2D-API
[24.1] [35.1.3]
7-Segment-Anzeige
[33.2]
[39.2.1]
<APPLET>
-server
-show
-source 1.4
-sourcepath
-storepass
-storetype
-target
-v1.1
-v1.2
-vcompat
-verbose
-verbosegc
-verify
-version
-Xclassic
-Xint
-Xmixed
-Xms
-Xmx
-Xprof
-Xrunhprof
.class-Dateien
[4.1.2] [50.5.3]
[50.1.3] [50.5.3]
[4.1.2] [50.5.3]
[4.1.2] [50.5.3]
[4.1.2] [50.5.3]
[4.1.2] [50.5.3]
[50.5.3]
[4.1.2] [50.5.3]
[49.3.2.3]
[49.3.2.3]
[40.1.3]
[40.1.3]
[40.1.3]
[40.1.3]
@author
@deprecated
@exception
@param
@return
@see
@since
@version
[C
[I
_blank
_parent
_self
_top
<
>
>>
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
0 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
-AAbgeleitete Klasse
Abhngigkeiten zwischen den Quelldateien
Ableiten einer Klasse
Ableitungsbume
abs
absbottom
absmiddle
abstract
Abstract Windowing Toolkit
AbstractButton
AbstractList
AbstractMap
AbstractSet
AbstractSpinnerModel
AbstractTableModel
Abstrakte Factory
Abstrakte Klassen und Polymorphismus
Abstraktion
Accelerator-Taste
accept
Accessibility-API
acos
action
Action-Ereignisse
Action-Events
ActionEvent
ActionListener
actionPerformed
ACTION_EVENT_MASK
activeCaption
activeCaptionBorder
activeCaptionText
activeCount
Adapterklasse
[7.1.5.1]
[50.1.2]
[8.1.1]
[7.1.5.1]
[16.1.2.1] [17.2.4] [17.3.1]
[39.2.2]
[39.2.2]
[8.4.1] [9.1.2]
[1.2.3] [23.1.1]
[35.2.2.8] [37.2.1]
[15.1] [15.2.2]
[15.6.2]
[15.5.2]
[37.1.5]
[38.2.5.1]
[10.3.4.3]
[8.4]
[7.1.2]
[36.2.2.3]
[21.4.1] [45.3.1]
[35.1.3]
[17.2.1]
[28.1.1]
[28.1.6.8]
[30.5]
[28.1.2] [28.1.6.8] [30.5]
[32.3]
[28.1.6.8] [30.2] [30.5]
[32.3]
[28.1.6.8] [30.5] [32.3]
[28.2.4]
[25.4]
[25.4]
[25.4]
[22.5.2]
[28.1.1] [28.1.5]
javax.swing
java.util
java.util
java.util
javax.swing
javax.swing.table
java.awt.event
java.awt.event
add
addActionListener
addAdjustmentListener
addAll
addChangeListener
addColumnSelectionInterval
addComponentListener
addContainerListener
addElement
addFocusListener
addImage
addItem
addItemListener
Addition
addJMenuBar
addKeyListener
addListSelectionListener
addMouseListener
addMouseMotionListener
addPoint
addPropertyChangeListener
addRowSelectionInterval
addSelectionInterval
addSelectionPath
addSelectionPaths
addSeparator
addTab
addTableModelListener
addTextListener
addTreeSelectionListener
addVetoableChangeListener
addWindowListener
Adjustable
Adjustment-Ereignisse
AdjustmentEvent
AdjustmentListener
adjustmentValueChanged
ADJUSTMENT_EVENT_MASK
Adler32
after
Aggregation
aiff-Format
ALAW
Alexander, Christopher
Aliasing
ALIGN
ALL
AllPermission
ALT
ALTER TABLE
AM_PM
anchor
and
AND
andNot
Andreessen, Marc
Anhalten eines Threads
Animation
Anlegen eines Fensters
Anonyme Klassen
Anonyme lokale Klasse
ant
Anweisungen
Anweisungsobjekt
Anwendungsprotokolle
ANY
Apache-Server
API
[38.2.5]
[28.1.6.11] [32.6]
[38.3.2.2]
[44.4.2]
[23.2.4] [23.3] [28.1.6.7]
[29.1]
[32.10] [32.11]
[28.1.6.9] [32.10]
[28.1.2] [28.1.6.9] [32.10]
[28.1.6.9] [32.10]
[28.1.6.9] [32.10]
[28.2.4]
[19.3.3.4]
[16.2.3]
[7.1.5.2]
[1.2.3] [39.3.1] [48.2.2]
[48.2.3]
[10.3]
[49.2.1.4]
[39.2.2]
[42.4.7.3]
[47.3.4.2]
[39.2.2]
[42.4.7.1]
[16.2.2]
[31.2.4]
[14.5.2]
[42.4.7.3]
[14.5.2]
[1.1]
[22.2.3]
[34.2] [39.4 (in Applets)]
[23.2.1]
[28.2.2.2]
[15.4.2]
[13.3.2.2]
[6] [13.1.1]
[42.2.2]
[45.1.2]
[42.4.7.3]
[45.2.4]
[3.2.1]
java.awt
java.awt.event
java.awt.event
java.util.zip
java.security
java.applet
java.applet
java.lang
java.lang.reflect
java.lang
java.util
java.util
java.lang
java.lang
[17.2.1]
atan
ATM
[45.1.2]
Attribute
[7.1.3]
au-Dateien
[1.2.3]
au-Format
[39.3.1] [48.2.2]
[39.3.1]
AudioClip
[48.2.2]
AudioFileFormat
[48.2.2]
AudioFormat
[48.2.2.1]
AudioInputStream
[48.2.2]
AudioSystem
Aufruf einer Methode
[7.3.2]
Ausdrucksanweisungen
[6.1.4]
Ausdrcke
[1.2.1]
Ausfhrungszeitpunkt
[44.1]
Ausgabe von Sound
[39.3]
Ausgabe-Streams
[18.2]
Ausgaben, einfache
[2.3.2]
Auslsen einer Ausnahme
[12.1] [12.3.3]
Ausnahmen
[12]
Auswertungsreihenfolge
[5.1]
Auto-Commit-Modus
[42.4.3]
Automatisches Speichermanagement
[1.2.1]
[38.2.2]
AUTO_RESIZE_ALL_COLUMNS
[38.2.2]
AUTO_RESIZE_LAST_COLUMN
[38.2.2]
AUTO_RESIZE_NEXT_COLUMN
[38.2.2]
AUTO_RESIZE_OFF
AUTO_RESIZE_SUBSEQUENT_COLUMNS[38.2.2]
[19.3.1]
available
AWT
[1.2.3] [23.1.1]
[28.1.2] [28.2.4]
AWTEvent
[47.3.4.2]
AWTPermission
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
java.applet
javax.sound.sampled
javax.sound.sampled
javax.sound.sampled
javax.sound.sampled
java.awt
java.awt
-BBaratz, Alan
baseline
BasicService
Basisklasse
BDK
Bean Development Kit
Bean-Customizer
Beanbox
BeanInfo
BeanInfo-Klasse
Beans
Bedingtes Kompilieren
before
Behandeln einer Ausnahme
Beschleunigertasten
BETWEEN
BevelBorder
Bezeichner
BigDecimal
BigInteger
Bildschirmflackern reduzieren
binarySearch
bind
Bindungsregeln
binre Suche
Bitmap laden und anzeigen
BitmapComponent
BitSet
Bitweise Operatoren
black
Blobs
[1.1]
[39.2.2]
[13.5.3.1]
[7.1.5.1]
[44.3.1]
[44.3.1]
[44.6.2]
[44.3]
[44.6.1]
[44.6.1]
[44.1]
[6.2.1.4]
[16.2.3]
[12.1]
[30.4.3] [36.2.2.3]
[42.4.7.3]
[36.3.1.1]
[4.1.3]
[17.3]
[17.3]
[34.2.4]
[15.8.1] [16.5]
[46.2.4.2]
[5.1]
[15.8.1]
[34.1.1]
[34.1.2]
[14.5]
[5.5]
[25.2]
[42.1]
javax.jnlp
java.beans
javax.swing.border
java.math
java.math
java.util
Block
BLOCK_DECREMENT
BLOCK_INCREMENT
blue
bmp-Format
BOLD
Book
boolean
[6.1.2] [13.1.2]
[32.10]
[32.10]
[25.2]
[34.1.1]
[24.2.1]
[26.3.2.5]
[4.2] [4.2.1] [4.6.1]
[6.2.1.2] [10.2.1]
[10.2.1]
Boolean
[43.3.1]
Boolean.TYPE
[48.2.2.3]
BooleanControl
[10.2.1.2]
booleanValue
Bootstrap Classes
[13.2.2.2]
Bootstrap Loader
[44.2.2]
[35.2.2.6]
Border
[35.2.2.6] [36.1.2]
BorderFactory
[31.2.3]
BorderLayout
Borland
[1.1]
[31.2.4]
BOTH
[39.2.2]
bottom
[36.2.3.2]
BOTTOM
Bound Properties
[44.4.1]
BPM
[48.3.4]
[1.2.1] [12.2.6] [6.2.2.2
break
(in switch-Anweisung)]
[6.3.3.3 (in Schleifen)]
Breite eines Zeichens
[24.3.2]
BufferedInputStream [19.3.3.1] [49.2.4.2]
BufferedOutputStream [19.2.3.1] [49.2.4.1]
[2.3.3] [18.3.2]
BufferedReader
[18.3.3.1]
[18.2.2] [18.2.3]
BufferedWriter
[49.2.4.1]
[36.3.1.4]
BUFFERED_OPTION
Bug Parade
[1.3.1.5] [3.2.2.2]
[32.3]
Button
java.awt.print
java.lang
javax.sound.sampled
javax.swing.border
javax.swing
java.awt
java.io
java.io
java.io
java.io
java.awt
[36.2.3.3] [37.2.3]
ButtonGroup
[37.2.3]
ButtonModel
[10.2.1]
Byte
[4.2] [4.2.3] [10.2.1]
byte
Byte-Streams
[18.1]
[43.3.1]
Byte.TYPE
ByteArrayInputStream [19.3.2.2] [41.3.2]
ByteArrayOutputStream[19.2.2.2] [41.3.2]
Bytecode
[13.3.1]
Bytecode-Verifier
[47.2.1]
ByteToCharConverter [18.3.2.1] [49.2.4.2]
<
>
>>
java.io
java.io
javax.swing
javax.swing
java.lang
Titel
<<
0 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
-CCA
Cafe
CAFEBABE
Calendar
call by reference
call by value
Call-Level-Interface
Callback-Methoden
cancel
CancelButton
canRead
Canvas
canWrite
capacity
Card API
CardLayout
CaretListener
case
catch
catch-or-throw-Regel
ceil
CENTER
Certificate
Certification Authority
CGI-Script
ChangeEvent
ChangeListener
[47.1.7]
[1.1]
[20.3]
[16.2] [16.2.4]
java.util
[7.3.3] [10.2.2]
[7.3.3]
[42.1.1]
[13.1.7]
[26.3.2.1]
[37.2.1]
[13.5.3.2] [21.3]
[32.11] [33.1] [34.1.2]java.awt
[13.5.3.2] [21.3]
[11.4.5]
[1.1]
[31.1.2]
java.awt
[37.1.2]
javax.swing.event
[6.2.2.2]
[12.2.1]
[12.3.1]
[17.2.4]
[31.1.3] [31.2.1
(FlowLayout)] [31.2.4
(GridBagConstraints)]
[32.2 (Label)]
[36.2.3.2
(SwingConstants)]
[47.1.6.2]
java.security.cert
[47.1.7]
[40.1.1]
[37.4.2] [38.1.3]
javax.swing.event
[37.4.2] [38.1.3]
javax.swing.event
char
Character
Character-Streams
Character.TYPE
CharArrayReader
CharArrayWriter
charAt
CharToByteConverter
charValue
charWidth
CHAR_UNDEFINED
Checkbox
CheckboxGroup
CheckboxMenuItem
CheckedInputStream
CheckedOutputStream
checkRead
Checksum
Choice
class
Class
ClassCastException
classes.zip
Classloader
ClassNotFoundException
CLASSPATH
clear
clearRect
clearSelection
Client-Hotspot
Client-Server-Beziehung
java.lang
java.io
java.io
java.awt
java.awt
java.awt
java.util.zip
java.util.zip
java.util.zip
java.awt
java.lang
java.lang
java.lang
[48.2.2]
javax.sound.sampled
[30.7.1]
java.awt.datatransfer
[30.7.2]
java.awt.datatransfer
[13.5.3.1]
javax.jnlp
[23.4.3]
[23.4.3]
[4.5.1] [7.3.3] [8.1.2]
[9.4.2] [41.3.2]
[41.3.2]
Cloneable
java.lang
java.lang
CloneNotSupportedException[9.4.2]
[18.2.1 (Writer)]
close
[18.3.1 (Reader)]
[19.2.1
(OutputStream)] [20.1
(RandomAccessFile)]
[42.2.1 (Connection)]
[48.2.2.4 (Line)]
[48.3.3 (MidiDevice)]
Cluster
[42.3.8]
[39.2.1]
CODE
[39.2.2]
CODEBASE
Coding Conventions
[2.3.5]
[38.3.3]
collapsePath
[15.1]
Collection
java.util
Collection-API
[1.1] [1.2.4] [15]
[15.8]
Collections
java.util
[25.2]
java.awt
Color
[38.2.8]
columnAtPoint
com.sun.image.codec.jpeg [13.2.2]
Comboboxen
[32.8]
[42.4.3]
commit
[26.4.1.2]
CommPort
javax.comm
[26.4.1.2]
javax.comm
CommPortIdentifier
Community Source Licence
[1.1]
[3.2.2.1]
comp.lang.java
Comparable
[9.2]
[9.2] [15.7.1]
Comparable
java.lang
[15.7.1] [16.5]
java.util
Comparator
[15.7.1]
compare
[9.2] [11.2.4] [15.7.1]
compareTo
Clip
Clipboard
ClipboardOwner
ClipboardService
Clipping-Region
clipRect
clone
Compiler
Component
Component-Ereignisse
Component-Events
ComponentAdapter
componentAdded
ComponentEvent
[50.1]
[27.1]
[28.1.6.5]
[29.2]
[28.1.5]
[28.1.6.6]
[28.1.2] [28.1.6.5]
[29.2]
[28.1.6.5] [29.2]
componentHidden
[28.1.6.5] [29.2]
ComponentListener
[28.1.6.5] [29.2]
componentMoved
[28.1.6.6]
componentRemoved
[28.1.6.5] [29.2]
componentResized
[28.1.6.5] [29.2]
componentShown
[36.3.1.3]
ComponentUI
[28.2.4] [33.2.3]
COMPONENT_EVENT_MASK
[33.2.3]
COMPONENT_SHOWN
Composite-Pattern
[10.3.7]
[36.3.1.1]
CompoundBorder
[48.2.2.3]
CompoundControl
[22.4.4]
connect
[42.2.1]
Connection
Constrained Properties
[44.4.2]
Constraints-Objekt
[31.1.3]
[43.3.3]
Constructor
[27.1] [31.1.2]
Container
Container-Ereignisse
[28.1.6.6]
[28.1.5]
ContainerAdapter
[28.1.6.6]
ContainerEvent
[28.1.6.6]
ContainerListener
[14.4.2] [15.1]
contains
[15.1]
containsAll
[14.4.2] [15.6.1]
containsKey
[15.6.1]
containsValue
Content Handler Factories
[45.4]
ContentPane
[35.2.2.2] [36.1.1.1]
CONTIGUOUS_TREE_SELECTION [38.3.2.1]
[1.2.1] [6.3.3.3]
continue
[12.2.6]
java.awt
java.awt.event
java.awt.event
java.awt.event
javax.swing.plaf
javax.swing.border
javax.sound.sampled
java.sql
java.lang.reflect
java.awt
java.awt.event
java.awt.event
java.awt.event
[25.4]
[48.2.2] [48.2.2.3]
[48.2.2.3]
[25.4]
[25.4]
[25.4]
[25.4]
[25.4]
[39.3.1]
[23.4.2]
[1.2.4] [13.2.1.2]
[46.1.2]
[17.2.1]
[24.2.1]
[24.2.1]
[19.3.3.4]
[42.4.7.1]
[42.4.7.1]
[36.1.2]
[36.1.2]
[27.5.3]
[36.3.1.1]
[36.1.2]
[13.4.2] [34.1.1]
[34.2.4.3]
[36.3.1.1]
[21.5.2]
[42.2.2]
[21.5.1]
[36.3.1.2]
[48.3.4]
[27.5.3]
[16.3.5]
[27.5.3]
[25.2]
[47.1.2.1]
control
Control
Control.Type
controlDkShadow
controlHighlight
controlLtHighlight
controlShadow
controlText
CoolEdit
copyArea
CORBA
cos
Courier
Courier New
CRC32
CREATE INDEX
CREATE TABLE
createBevelBorder
createCompoundBorder
createCustomCursor
createEmptyBorder
createEtchedBorder
createImage
createLineBorder
createNewFile
createStatement
createTempFile
createToolTip
createTrack
CROSSHAIR_CURSOR
currentTimeMillis
Cursor
cyan
Csarische Verschlsselung
Titel
<<
>
>>
java.awt
javax.sound.sampled
Titel
<<
Inhalt Suchen
<
>
Index
>>
DOC
API
0 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
-DDangling else
darkGray
Data Definition Language
DatabaseMetaData
DataFlavor
DataFlavors
DatagramPacket
DatagramSocket
DataInput
DataInputStream
DataLine
DataLine.Info
DataOutput
DataOutputStream
Date
DateFormat
Datei- und Verzeichnis-Handling
Dateiformate
[6.2.1.3]
[25.2]
[42.2.4]
[42.3.3]
[30.7.2]
[30.7.1]
[45.1.2]
[45.1.2]
[19.3.3.3]
[19.2.3.3] [19.3.3.3]
[48.2.2]
[48.2.3]
[19.2.3.3]
[19.2.3.3]
[16.2] [16.2.4] [16.2.4
(java.sql)]
[17.4.3]
[21]
[1.2.3 (aiff)] [1.2.3 (wav)]
[2.2.1.2 (java)] [13.3.2.1
(class)] [34.1.1 (gif)]
[34.1.1 (jpeg)] [39.3.1
(au)] [39.3.1 (aiff)] [39.3.1
(wav)] [39.3.1 (Midi)]
[50.6 (jar)]
[42]
[4.2] [5.1]
[4] [4.2 (Primitive)] [4.5
(Referenz-)]
[16.2.3]
[16.2]
[45.2.2]
[16.2.2]
[16.2.2]
[16.2.2]
[16.2.2]
[48.2.2.3]
[35.1.2.1] [36.3.1.4]
java.sql
java.awt.datatransfer
java.net
java.net
java.io
java.io
javax.sound.sampled
java.io
java.io
java.util
java.text
Debugger
DebugGraphics
DecimalFormat
deep copy
default
DEFAULT
Default-Implementierung
Default-Konstruktor
Default-Paket
DefaultButton
DefaultDesktopManager
DefaultListModel
DefaultMutableTreeNode
defaultPage
defaultReadObject
DefaultTableCellRenderer
DefaultTableColumnModel
DefaultTreeModel
DefaultTreeSelectionModel
DEFAULT_CURSOR
Definite Assignment
DeflaterOutputStream
Deklaration von Variablen
Dekrement-Operator
Delegate
Delegate-Pattern
Delegation
Delegation Based Event Handling
Delegator
delete
DELETE FROM
deleteCharAt
deleteOnExit
deleteShortCut
delItem
Denial-of-Service-Attacken
[50.4]
[36.3.1.4]
javax.swing
[17.4.2]
java.text
[9.4.2] [41.3.2]
[6.2.2.2]
[17.4.3]
[9.5.1]
[7.3.6.1] [8.1.4.2]
[13.2.4.2]
[37.2.1]
[36.1.6.1]
javax.swing
[37.3.1.2]
javax.swing
[38.3.1]
javax.swing.tree
[26.3.2.2]
[44.2.4]
[38.2.7]
javax.swing.table
[38.2.6]
javax.swing.table
[38.3.1] [38.3.4]
javax.swing.tree
[38.3.2.1]
javax.swing.tree
[27.5.3]
[4.2] [5.1]
[19.2.3.4]
java.util.zip
[4.3.2] [6.1.3]
[4.3.1]
[10.3.6]
[10.3.6] [15.8.2]
[9.5.2] [10.3.6]
[10.3.9] [23.1.2.1] [28.1.1]
[10.3.6]
[11.4.3 (StringBuffer)]
[20.1 (File)] [21.4.2 (File)]
[49.2.1.2 (StringBuffer)]
[42.2.4] [42.4.7.2]
[11.4.3]
[21.5.2]
[30.4.3]
[32.9]
[13.5.3.1]
dispose
DISPOSE_ON_CLOSE
divide
Division
DNS
DNS-Konfiguration
do
Doclets
doClick
Document
DocumentListener
Dokumentation des JDK
Dokumentationsgenerator
Dokumentationskommentare
Domain Name System
Doppelpufferung
Double
double
Double.TYPE
doubleValue
DownloadService
DO_NOTHING_ON_CLOSE
Dr. Dobb's Journal
Drag-and-Drop API
drawArc
drawBytes
drawChars
drawImage
drawLine
drawOval
drawPolygon
drawPolyline
drawRect
drawRoundRect
drawString
DriverManager
DROP INDEX
DROP TABLE
Drop-Down-Listboxen
Drucken
DSA
DST_OFFSET
Duff's Device
[26.2.2] [27.2]
[36.1.6.2]
[17.3.1]
[5.2]
[45.1.3.2]
[50.4.3]
[1.2.1] [6.3.2] [6.3.3.3]
[50.5.5]
[37.2.1]
[37.1.2]
[37.1.2]
[2.1.2.2] [3.2.1]
[50.5]
[4.1.2] [50.5.3]
[45.1.3.2]
[34.2.4.3]
[4.2.4.1] [10.2.1]
[4.2] [4.2.4] [10.2.1]
[43.3.1] [43.3.2]
[10.2.1.2]
[13.5.3.1]
[36.1.6.2]
[3.2.2.4]
[1.1] [35.1.3]
[23.3.5]
[24.1]
[24.1]
[34.1.1] [34.2.4.3]
[23.3.1]
[23.3.4]
[23.3.3]
[23.3.3]
[23.3.2]
[23.3.2]
[24.1]
[42.2.1]
[42.4.7.1]
[42.4.7.1]
[32.8]
[26]
[47.1.5]
[16.2.2]
[6.2.2.2]
javax.swing.text
javax.swing.event
java.lang
javax.jnlp
java.sql
Dynamische Datenstrukturen
Dynamische Methodensuche
Dynamisches Binden
Dynamisches Laden von Klassen
Dmon
Titel
<<
Inhalt Suchen
<
>
[4.2] [13.1.4]
[8.1.3.1]
[8.1.3.1]
[43.2.2]
[22.2.1]
Index
DOC
>>
API
-EEAST
ECHO-Service
editCellAt
Eiffel
Eigenzertifikat
Einerkomplement
Einfache Vererbung
Eingabe-Streams
Eingaben, einfache
Einschrnkende
Konvertierungen
Einweg-Hashfunktion
Einzeilige Kommentare
elementAt
elements
else
EMBED
Embedded SQL
empty
EmptyBorder
EmptyStackException
enableEvents
end
Ende des Programms
endsWith
Entry
entrySet
Entschlsseln
[31.1.3] [31.2.4
(GridBagConstraints)]
[36.2.3.2
(SwingConstants)]
[45.2.3]
[38.2.4.2]
[6.4.1.4]
[47.1.6.1]
[5.5]
[8.1]
[18.3]
[2.3.3]
[4.6.1]
[47.1.3]
[4.1.2]
[14.2.2]
[14.2.3] [14.4.3]
[2.3.4] [6.2.1.3]
[40.4.1]
[42.1.1]
[14.3]
[36.3.1.1]
javax.swing.border
[14.3]
java.util
[28.2.4] [30.6] [33.2.3]
[26.2.2]
[22.2.1]
[11.2.4]
[15.6.1]
[15.6.1]
[47.1.1]
Entwicklungszyklus
Entwurfsmuster
EnumControl
enumerate
Enumeration
equals
equalsIgnoreCase
ERA
Ereignisempfnger
Ereignisquellen
Ereignistypen
Ergebnistyp eines Ausdrucks
err
erreichbare Anweisungen
Error
ERROR_MESSAGE
Ersetzen von Zeichenketten
Erste Gehversuche
Erstellen eigener Pakete
Erweiternde Konvertierungen
Escape-Kommandos
Escape-Sequenzen
ESQL
EtchedBorder
Ethernet
Event
Event Sources
EventListener
EventListener
EventObject
Exception
Exceptions
[2.3.1] [13.3]
[10.3]
[48.2.2.3]
[22.5.2]
[1.2.4] [14.2.3]
[15.3.1]
[4.5.1] [8.1.2] [5.7.2.2
(String)] [11.2.4
(String)] [14.4
(Schlsselvergleich)]
[11.2.4]
[16.2.2]
[28.1.1] [28.1.3]
[28.1.1] [28.1.4]
[28.1.2]
[5.2]
[16.3.2]
[6.2.1.4]
[12.2.3]
[36.1.4.1]
[11.2.6]
[2.2]
[13.2.4]
[4.6.1]
[42.4.2]
[4.2.2.1]
[42.1.1]
[36.3.1.1]
[45.1.2]
[28.1.2]
[28.1.1]
[28.1.1]
[28.1.1] [28.1.3]
[28.1.2] [44.4.1]
[12.2.3]
[1.2.1] [12]
javax.sound.sampled
java.util
java.lang
javax.swing.border
java.awt
java.util
java.util
java.lang
exec
executeQuery
executeUpdate
EXISTS
exists
exit
exitValue
EXKLUSIV-ODER-Operator
exp
expandCapacity
expandPath
ExperimentalWebServer
Exponentialfunktion
extends
Extension Framework
Externalizable
[16.4.1]
[42.2.2] [42.2.3]
[42.2.2] [42.2.4]
[42.4.7.3]
[21.3]
[16.3.3]
[16.4.2]
[5.4]
[17.2.3]
[49.3.2.2]
[38.3.3]
[45.3.3]
[17.2.3]
[8.1.1]
[1.1]
[41.2.1]
<
>
>>
java.io
-FFactory
[10.3.4.1 (-Methode)]
[10.3.4.2 (-Klasse)]
Factory-Pattern
[10.3.4]
[4.1.3] [4.2] [4.2.1.1]
false
FDDI
[45.1.2]
Fehlerobjekt
[12.2.2]
Fensterklassen
[27.1]
Fenstertitel
[27.5.1]
[43.4]
Field
[17.4.3]
FieldPosition
[18.2.2.1] [21.1]
File
[16.3.1] [18.1]
file.separator
[13.5.3.2]
FileContents
[18.2.2.1]
FileDescriptor
[27.1]
FileDialog
[19.3.2.1] [41.1.3]
FileInputStream
[21.4.1]
FilenameFilter
FileNotFoundException[18.3.2.1] [19.3.2.1]
[20.1]
[13.5.3.1] [13.5.3.2]
FileOpenService
[19.2.2.1] [41.1.2]
FileOutputStream
[49.2.4.1]
[47.3.4.2]
FilePermission
[18.3.2]
FileReader
[13.5.3.1]
FileSaveService
[18.2.2] [18.2.2.1]
FileWriter
[16.5] [31.2.4]
fill
[23.4.1]
fillArc
[23.4.1]
fillOval
[23.4.1]
fillPolygon
[23.4.1]
fillRect
java.lang.reflect
java.text
java.io
javax.jnlp
java.io
java.awt
java.io
java.io
java.io
javax.jnlp
java.io
java.io
java.io
javax.jnlp
java.io
fillRoundRect
FilterInputStream
FilterOutputStream
FilterReader
FilterWriter
final
finalize
finally
Fingerprint
firePropertyChange
Firewall
first
First Person, Inc.
firstElement
FLASH_OPTION
Fliekommazahlen
float
Float
Float.TYPE
FloatControl
FloatControl.TYPE
floatValue
floor
FlowLayout
flush
[23.4.1]
[19.3.3]
java.io
[19.2.3]
java.io
[18.3.2] [18.3.3.3]
java.io
[18.2.2] [18.2.3]
java.io
[18.2.3.3]
[8.1.1] [8.1.3.1] [8.2.2.6]
[8.3.2] [49.2.2]
[7.3.7]
[12.2.6]
[47.1.3.3]
[44.4.1]
[45.1.6]
[15.7.2]
[1.1]
[14.2.2]
[36.3.1.4]
[4.2.4]
[4.2] [4.2.4] [10.2.1]
[4.2.4.1] [10.2.1]
java.lang
[43.3.1]
[48.2.2.3]
javax.sound.sampled
[48.2.2.3]
[10.2.1.2]
[17.2.4]
[31.2.1]
java.awt
[18.2.1 (Writer)]
[18.2.3.1
(BufferedWriter)]
[19.2.1 (OutputStream)]
[19.2.3.1
(BufferedOutputStream)]
[36.3.1.9]
[28.1.6.1]
[29.5]
[28.1.5]
java.awt.event
[28.1.6.1]
java.awt.event
[28.1.6.1] [29.5]
[28.1.6.1]
java.awt.event
[28.1.6.1] [29.5]
[33.2.3]
[33.2.3]
[33.2.3]
[33.2.3]
[24.2.1]
java.awt
[24.3.1]
[24.3.2]
[24.2.1]
[24.3.2]
java.awt
[1.2.1] [6.3.3.2] [6.3.3.3]
[6.3.3]
[17.4.2] [17.4.3]
[17.4.2 (Zahlen-)]
[17.4.3 (Datum-/Uhrzeit)]
[43.2.2] [43.3.2]
[5.7.1.1]
[23.2.1] [24.3.1] [27.1] java.awt
[31.1.1]
[1.2.1]
[17.4.3]
[29.6]
[9.4.3 (mit Interfaces)]
[43.3.2 (mit Reflection)]
[23.4.1]
focusGained
FocusListener
focusLost
FOCUS_EVENT_MASK
FOCUS_GAINED
FOCUS_LOST
Fokus-Subsystem
Font
Font-Informationen
Font-Metriken
font.properties
FontMetrics
for
for-Schleife
format
Formatierung
forName
Fragezeichenoperator
Frame
friend
FULL
Funktionstasten
Funktionszeiger
Fllmodus
<
>
>>
Titel
<<
>
>>
API
0 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
-GGamelan
Gamma, Erich
Garbage Collector
gc
gcd
Gebundene Eigenschaften
Generizitt
Gesampelter Sound
Geschachtelte Schleifen
get
GET
getAbsolutePath
getActionCommand
getAddress
getAdjustable
getAdjustmentType
getAnchorSelectionIndex
getApplet
getAppletContext
getAppletInfo
getApplets
getAscent
getAsText
getAudioClip
getAudioFileFormat
getAudioInputStream
[1.1]
[10.3]
[4.5.2] [16.3.4]
[16.3.4] [49.2.5]
[17.3.1]
[44.4.1]
[9.2]
[48.2]
[6.3.3.3]
[14.4.2 (Hashtable)]
[14.5.1 (BitSet)]
[15.6.1 (Map)] [16.2.2
(Calendar)] [43.4
(Field)] [43.5.2
(Array)]
[45.2.4] [45.3.3]
[21.2]
[30.5] [32.3] [32.6]
[45.2.1]
[32.10]
[32.10]
[37.3.1.1]
[40.1.2] [40.2]
[40.1.2]
[39.1.4.4]
[40.1.2] [40.2]
[24.3.2]
[44.6.2.1]
[39.3.1]
[48.2.2.1]
[48.2.2.1]
getAvailableFontFamilyNames
getBestCursorSize
getBlockIncrement
getBlue
getBoolean
getBounds
getBuffer
getBundle
getByName
getByte
getBytes
getCaretPosition
getCertificate
getChar
getChars
getCheckboxGroup
getChecksum
getChildAt
getChildCount
getClass
getClickCount
getClip
getClipBounds
getCodeBase
getColor
getColumnClass
getColumnCount
getColumnModel
getColumnName
getColumns
getColumnType
getComponent
getComponentAt
getComponentCount
getComponents
getConnection
[24.2.2]
[27.5.3]
[32.10] [37.4.1]
[25.2]
[42.2.3] [43.5.2]
[23.4.3] [27.3]
[18.2.2.2]
[17.4.4.1]
[45.2.1]
[42.2.3] [43.5.2]
[42.2.3]
[32.6] [37.1.2]
[47.1.6.2]
[43.5.2]
[49.2.1.5]
[32.5]
[19.2.3.5] [19.3.3.4]
[38.3.1]
[38.3.1]
[22.4.2.1] [43.2.2]
[29.3]
[23.4.3]
[23.4.3]
[39.3.1]
[25.3]
[38.2.5]
[38.2.4.1] [38.2.5]
[42.4.1]
[38.2.8]
[38.2.5] [42.4.1]
[32.6]
[42.4.1]
[29.2] [31.1.3]
[38.1.3]
[31.1.3]
[31.1.3]
[42.2.1]
getConstructor
getConstructors
getContent
getContentPane
getContents
getControl
getControls
getCopies
getCountry
getCurrent
getCustomEditor
getDate
getDateInstance
getDateTimeInstance
getDeclaredField
getDeclaredFields
getDeclaredMethods
getDefault
getDefaultRenderer
getDefaultToolkit
getDescent
getDocument
getDocumentBase
getDouble
getEchoCharacter
getEditingColumn
getEditingRow
getElements
getEnabled
getErrorCode
getErrorStream
getFamily
getField
getFields
getFilePointer
getFloat
[43.3.3]
[43.3.3]
[45.4]
[35.2.2.2] [36.1.1.1]
[30.7.2]
[48.2.2.3]
[48.2.2.3]
[26.3.2.1]
[17.4.1]
[32.5]
[44.6.2.3]
[42.2.3]
[17.4.3]
[17.4.3]
[43.4]
[43.4]
[43.3.1]
[16.2.2] [17.4.1]
[38.2.7]
[24.2.2]
[24.3.2]
[37.1.2]
[39.3.1]
[42.2.3] [43.4]
[43.5.2]
[32.7]
[38.2.4.2]
[38.2.4.2]
[37.2.3]
[36.2.2.3]
[42.2.5]
[16.4.2]
[24.3.1]
[43.4]
[43.4]
[20.2.1]
[42.2.3] [43.5.2]
getFont
getFontList
getFontMetrics
getGlassPane
getGraphics
getGreen
getHAdjustable
getHeight
getHorizontalAlignment
getHorizontalTextPosition
getHostAddress
getHostName
getIcon
getID
getImage
getImageableHeight
getImageableWidth
getImageableX
getImageableY
getInputStream
getInsets
getInstance
getInt
getItem
getItemCount
getItemSelectable
getJobName
getKey
getKeyChar
[24.2.1]
[24.2.2]
[24.3.2]
[36.1.1.1]
[26.2.1] [34.2.4.3]
[25.2]
[32.11]
[24.3.2] [26.3.2.2]
[34.1.2]
[37.1.1] [37.2.1]
[37.1.1] [37.2.1]
[45.2.1]
[45.2.1]
[44.6.1.1]
[28.1.2] [29.1]
[34.1.1]
[26.3.2.2]
[26.3.2.2]
[26.3.2.2]
[26.3.2.2]
[13.5.3.2
(FileContents)] [16.4.2
(Process)] [26.4.1.2
(CommPort)] [45.2.2
(Socket)]
[23.2.3] [24.1]
[47.1.4]
[42.2.3 (ResultSet)]
[43.4 (Field)] [43.5.2
(Array)]
[30.3] [32.8]
[30.3] [32.8]
[32.4] [32.8]
[26.3.2.1]
[15.6.1]
[28.1.2] [29.6]
[33.2.3]
getKeyCode
getKeys
getKeyStroke
getLabel
getLanguage
getLastPathComponent
getLayeredPane
getLeading
getLeadSelectionIndex
getLeadSelectionPath
getLeadSelectionRow
getLength
getLine
getLineCount
getLineEndOffset
getLineNumber
getLineOfOffset
getLineStartOffset
getLineWrap
getLocalGraphicsEnvironment
getLocalHost
getLocation
getLong
getMajorTickSpacing
getMaximum
getMaximumCursorColors
getMaximumSize
getMaxReceivers
getMenu
getMessage
getMetaData
getMethod
getMethodDescriptors
getMethods
getMicrosecondPosition
getMidiDevice
[28.2.1] [29.6]
[17.4.4.2]
[36.2.2.3]
[30.4.1] [32.3] [32.4]
[17.4.1]
[38.3.2.2]
[36.1.1.1]
[24.3.2]
[37.3.1.1]
[38.3.2.2]
[38.3.2.2]
[13.5.3.2] [43.5.2]
[48.2.2.2]
[37.1.4]
[37.1.4]
[18.3.3.2]
[37.1.4]
[37.1.4]
[37.1.4]
[24.2.2]
[45.2.1]
[27.3] [31.3]
[42.2.3] [43.5.2]
[37.4.2]
[32.10] [37.4.1]
[48.2.2.3]
[27.5.3]
[33.2.2] [36.3.1.7]
[48.3.3]
[30.2]
[12.2.2]
[42.3.3] [42.4.1]
[43.3.2]
[44.6.1.3]
[43.3.1]
[48.3.3]
[48.3.3]
getMidiDeviceInfo
getMidiFileTypes
getMinimum
getMinimumSize
getMinorTickSpacing
getMixer
getMixerInfo
getMnemonic
getModel
getModifiers
getName
getNewLeadSelectionPath
getNewValue
getNextException
getNumberInstance
getNumberOfPages
getObject
getOldLeadSelectionPath
getOldValue
getOrientation
getOutputStream
getPageDimension
getPageFormat
getPageResolution
[48.3.3]
[48.3.5.2]
[32.10] [37.4.1]
[48.2.2.3]
[33.2.2] [36.3.1.7]
[37.4.2]
[48.2.2.2]
[48.2.2.2]
[36.2.2.3] [37.2.1]
[37.3.1.2]
[28.1.2] [43.3.1]
[43.3.3]
[13.5.3.2
(FileContents)] [21.2
(File)] [22.5.1
(Thread)] [26.4.1.2
(CommPortIdentifier)]
[30.7.2 (Clipboard)]
[43.3.1 (Method)]
[43.3.3 (Constructor)]
[38.3.2.2]
[44.4.1]
[42.2.5]
[17.4.2]
[26.3.2.5]
[17.4.4.2]
[38.3.2.2]
[44.4.1]
[26.3.2.2]
[13.5.3.2
(FileContents)] [16.4.2
(Process)] [26.4.1.2
(CommPort)] [45.2.2
(Socket)]
[26.2.2]
[26.3.2.5]
[26.2.2]
getPaper
getParameter
getParameterInfo
getParameterTypes
getParent
getPath
getPathToRoot
getPoint
getPortIdentifier
getPortIdentifiers
getPortType
getPosition
getPreferredSize
getPrintable
getPrinterJob
getPrintJob
getPriority
getProperties
getProperty
getPropertyDescriptors
getPropertyName
getPublicKey
getReceiver
getRed
getResource
getResourceAsStream
getRootPane
getRowCount
getRuntime
getScreenResolution
getScreenSize
getScrollPosition
getSelectedColumn
getSelectedColumns
getSelectedComponent
[26.3.2.2]
[39.2.3]
[39.1.4.3]
[43.3.1] [43.3.3]
[21.2] [22.5.2]
[38.3.1]
[21.2] [38.3.2.2]
[38.3.4]
[28.1.2]
[26.4.1.2]
[26.4.1.2]
[26.4.1.2]
[29.3]
[32.11] [33.2.2]
[36.3.1.7]
[26.3.2.5]
[26.3.2.1]
[26.2.1]
[22.5.1]
[16.3.1]
[14.4.4] [16.3.1]
[44.6.1.2]
[44.4.1]
[47.1.6.2]
[48.3.3]
[25.2]
[44.2.2]
[13.4.2]
[36.1.1.1]
[38.2.4.1] [38.2.5]
[16.4.1]
[26.2.2]
[27.3]
[32.11]
[38.2.3.2]
[38.2.3.2]
[38.1.3]
getSelectedIndex
getSelectedIndexes
getSelectedIndices
getSelectedItem
getSelectedItems
getSelectedRow
getSelectedRows
getSelectedText
getSelectedValue
getSelectedValues
getSelection
getSelectionEnd
getSelectionMode
getSelectionModel
getSelectionPath
getSelectionPaths
getSelectionRows
getSelectionStart
getSequence
getSequencer
getShort
getSize
getSource
getSQLState
getState
getString
getStringArray
getStyle
getSuperClass
getSynthesizer
[32.8 (Choice)]
[37.3.1.1 (JList)]
[37.3.2 (JComboBox)]
[38.1.3
(JTabbedPane)]
[32.9]
[37.3.1.1]
[32.8] [37.3.2]
[32.9]
[38.2.3.2]
[38.2.3.2]
[32.6] [37.1.2]
[37.3.1.1]
[37.3.1.1]
[37.2.3]
[32.6] [37.1.2]
[37.3.1.1]
[38.2.8] [38.3.2.1]
[38.3.2.2]
[38.3.2.2]
[38.3.2.2]
[32.6] [37.1.2]
[48.3.5.1]
[48.3.4]
[42.2.3] [43.5.2]
[24.1] [24.3.1] [27.3]
[28.1.2] [29.1] [32.6]
[42.2.5]
[36.2.3.3] [27.4
(Frame)] [30.4.2
(CheckboxMenuItem)]
[32.4 (Checkbox)]
[17.4.4.2] [42.2.3]
[17.4.4.2]
[24.3.1]
[43.4]
[48.3.3]
[30.7.2]
getSystemClipboard
[38.1.3]
getTabCount
getTableCellRendererComponent[38.2.7]
[42.4.1]
getTableName
[38.1.3]
getTabPlacement
[37.1.4]
getTabSize
[44.6.2.2]
getTags
getter-Methoden
[44.2.3]
[32.2] [32.6] [37.1.2]
getText
[22.5.2]
getThreadGroup
[16.2.4] [42.2.3]
getTime
[17.4.3]
getTimeInstance
[42.2.3]
getTimestamp
[27.5.1]
getTitle
[34.1.1]
getToolkit
[36.3.1.2]
getToolTipText
[42.4.3]
getTransactionIsolation
[30.7.2]
getTransferData
[30.7.2]
getTransferDataFlavors
[43.4]
getType
[42.4.4]
getTypeInfo
[32.10] [37.4.1]
getUnitIncrement
[26.3.2.1]
getUserName
[38.3.1]
getUserObject
[32.11]
getVAdjustable
[48.2.2.3] [15.6.1
getValue
(Map)] [32.10
(Scrollbar)] [32.10
(AdjustmentEvent)]
[37.4.1 (JScrollBar)]
[37.4.3
(JProgressBar)]
[44.6.2.1
(PropertyEditor)]
[38.2.4.1] [38.2.5]
getValueAt
[37.4.2]
getValueIsAdjusting
[37.4.1]
getValueIsAdjusting()
getVariant
getVerticalAlignment
getViewportSize
getVisibleAmount
getWhen
getWidth
getWindow
getWrapStyleWord
getX
getY
gif-Format
gk.util
GlassPane
Gleichheits-Operator
Globale Funktionen
GoF - Gang of Four
GoldWave
Gosling, James
Grafikkontext
Graphical User Interface
Graphics
Graphics2D
GraphicsEnvironment
gray
green
Green-OS
Green-Projekt
GregorianCalendar
GridBagConstraints
GridBagLayout
gridheight
GridLayout
gridwidth
gridx
gridy
GROUP BY
[17.4.1]
[37.1.1] [37.2.1]
[32.11]
[32.10] [37.4.1]
[29.3]
[26.3.2.2] [34.1.2]
[29.1]
[37.1.4]
[29.3]
[29.3]
[34.1.1]
[42.3.2] [13.2.3
(installieren)]
[36.1.1.1]
[5.3]
[7.3.1]
[10.3]
[39.3.1]
[1.1]
[23.2.2]
[44.1]
[23.2.2]
[26.3.2.4]
[24.2.2]
[25.2]
[25.2]
[1.1]
[1.1]
[16.2]
[31.2.4]
[31.2.4]
[31.2.4]
[31.2.2]
[31.2.4]
[31.2.4]
[31.2.4]
[42.4.7.3]
java.awt
java.awt
java.awt
java.util
java.awt
java.awt
java.awt
Grundlinie
Grer-Operator
Grergleich-Operator
GUI-Designer
GZIPInputStream
GZIPOutputStream
Titel
<<
[24.3.2]
[5.3]
[5.3]
[44.1]
[19.3.3.5]
[19.2.3.4]
>
>>
API
java.util.zip
java.util.zip
-H[28.1.1]
[17.4.4.2]
[2.1.1]
[38.3.3]
[14.4] [15.6.2]
[8.1.2] [15.1] [47.1.3]
[15.5.2] [15.6.2]
[15.5.2]
[1.2.4] [14.4] [15.6.2]
[1.2.4] [14.2.3]
[17.1.2]
[15.3.1]
[15.3.2]
[42.4.7.3]
[45.3.3]
[15.7.2]
[35.1.2.1]
[39.2.1]
[10.3]
[24.2.1]
[36.1.6.2]
[31.2.3]
[22.2.1]
[27.5]
[31.2.4
(GridBagConstraints)]
[32.10 (Scrollbar)]
[36.2.3.2
(SwingConstants)]
[38.1.1]
HORIZONTAL_SCROLLBAR_ALWAYS
HORIZONTAL_SCROLLBAR_AS_NEEDED[38.1.1]
handleEvent
handleGetObject
Hardware-Voraussetzungen
hasBeenExpanded
Hash-Funktion
hashCode
HashMap
HashSet
Hashtable
hasMoreElements
hasMoreTokens
hasNext
hasPrevious
HAVING
HEAD
headSet
Heavyweight Components
HEIGHT
Helm, Richard
Helvetica
HIDE_ON_CLOSE
Himmelsrichtungen
Hintergrund-Thread
Hintergrundfarbe
HORIZONTAL
java.util
java.util
java.util
HORIZONTAL_SCROLLBAR_NEVER
HORIZONTAL_SPLIT
Host-ID
Host-Namen
Host-Variablen
hosts
HotJava
HotJava Views
HotSpot
HOUR
HOUR_OF_DAY
hprof
HSPACE
HSQLDB
HTML-Adresse
HTML-Ausgabe des Buchs
HTML-Dokument
HTMLConverter
http
Hybride Kryptosysteme
Hypersonic SQL
Hhe einer Zeile
<
>
>>
[38.1.1]
[38.1.2]
[45.1.3.1]
[40.1.1]
[42.1.1]
[50.4.3]
[1.1]
[1.1]
[1.1] [50.2.2]
[16.2.2]
[16.2.2]
[49.3.1]
[39.2.2]
[42.3.1]
[40.1.1]
[3.2.3]
[39.2.1]
[40.4.2.2]
[40.1.1]
[47.1.5]
[42.3.3.1]
[24.3.2]
-II18N
[17.4]
IAB
[45.1.5]
IBM
[1.1]
ICMP
[45.1.2]
Icon
[27.5]
[36.2.3.2]
Icon
[27.4]
ICONIFIED
[44.6.1.1]
ICON_COLOR_16x16
[44.6.1.1]
ICON_COLOR_32x32
[44.6.1.1]
ICON_MONO_16x16
[44.6.1.1]
ICON_MONO_32x32
[1.2.1] [6.2] [6.2.1.2]
if
if-Anweisung
[6.2.1]
[6.2]
if-else
IIOP
[46.1.2]
[6.4.1.4] [15.2.1]
IllegalArgumentException
IllegalThreadStateException[16.4.2]
[34.1.1]
Image
[36.2.3.2]
ImageIcon
[34.1.1]
ImageObserver
immutable
[10.2.2] [15.5.1]
Immutable-Pattern
[10.3.2]
Implementierung eines Interfaces
[9.1.2]
[9.1.2]
implements
[2.3.3] [13.2.1]
import
[13.2.1.1]
Import von java.lang
[13.2.1.1]
[42.4.7.3]
IN
[16.3.2]
in
[25.4]
inactiveCaption
[25.4]
inactiveCaptionBorder
javax.swing
java.lang
java.lang
java.awt
javax.swing
java.awt.image
inactiveCaptionText
indexOf
IndexOutOfBoundsException
Indizierte Eigenschaft
InetAddress
inetd.conf
InflaterInputStream
info
Informationen im Internet
INFORMATION_MESSAGE
infoText
init
Initialisieren
initSign
initVerify
Inkrement-Operator
Inner Classes
Input Method Framework
InputEvent
InputStream
InputStream
InputStreamReader
insert
INSERT INTO
insertElementAt
insertItemAt
insertNodeInto
insertSeparator
insertTab
insets
Installation
instanceof
[25.4]
[11.2.5]
[12.2.5]
java.lang
[44.2.3.2]
[45.2.1]
java.net
[45.3.1]
[19.3.3.5]
java.util.zip
[25.4]
[3.2.2]
[36.1.4.1]
[25.4]
[36.1.5] [39.1.1]
[39.1.3.2]
[4.3.2 (von Variablen)]
[4.4.1 (eines Arrays)]
[47.1.6.2]
[47.1.6.2]
[4.3.1]
[10.1.1]
[35.1.3]
[28.1.2] [29.6]
java.awt.event
[19.1]
[19.1] [19.3.1]
java.io
[2.3.3] [18.3.2]
java.io
[18.3.2.1]
[11.4.2] [32.7] [38.3.1]
[42.2.4] [42.3.4]
[42.4.7.2]
[14.2.1]
[37.3.2]
[38.3.4]
[30.3]
[38.1.3]
[31.2.4]
[2.1.2]
[5.7.2.3] [14.2.1]
[32.6]
InstantDB
Instanzmerkmale
Instanzvariablen
int
Integer
Integer.TYPE
Integrale Typen
interface
Interfaces
Internationalisierung
Internet
Internet Activity Board
Interpreter
interrupt
interrupted
InterruptedException
InterruptedIOException
Introspection
Introspector
intValue
invalidate
InvalidClassException
Invalidierung
Invariante
invoke
IOException
IP-Adresse
IP-Protokoll
ipadx
ipady
IPv6
IS NOT NULL
IS NULL
isAbsolute
[42.3.1]
[7.1.3]
[4.3.1] [4.3.3] [7.1.3]
[4.2] [4.2.3] [10.2.1]
[10.2.1]
[43.3.1]
[4.2.3]
[9.1.1]
[8.1] [9.1] [10.3.3
(Design-Pattern)]
[17.4]
[3.2.2]
[45.1.5]
[50.2]
[22.2.2]
[22.2.2]
[22.2.2] [34.1.1.1]
[45.2.3]
[43.1] [44.1]
[44.6.1]
[10.2.1.2]
[36.3.1.8]
[41.2.1]
[36.3.1.8]
[6.4.1.2]
[43.3.1] [43.3.2]
[14.4.4] [18.2.2.1]
[20.1]
[45.1.3.1]
[45.1.2]
[31.2.4]
[31.2.4]
[45.1.3.1]
[42.4.7.3]
[42.4.7.3]
[21.3]
java.lang
java.lang
java.io
java.beans
java.io
java.io
isAbstract
isAlive
isAltDown
isCancelled
isCellEditable
isCollapsed
isControlDown
isControlSupported
isConversionSupported
isDataFlavorSupported
isDirectory
isDoubleBuffered
isEditable
isEditing
isEmpty
isEnabled
isEnabledAt
isExpanded
isExplicit
isFile
isFinal
isFocusable
isFocusTraversable
isHidden
isIndeterminate
isInterface
isInterrupted
isLeaf
isMetaDown
isModal
isNative
ISO-3166
ISO-639
ISO-8859-1
ISO/OSI-7-Schichten-Modell
isOpen
[43.3.1]
[22.2.4.2]
[29.6]
[26.3.2.1]
[38.2.5]
[38.3.3]
[29.6]
[48.2.2.3]
[48.2.2.1]
[30.7.2]
[21.3]
[36.3.1.6]
[32.6] [37.3.2]
[38.2.4.2]
[14.2.1] [15.1] [15.6.1]
[27.3] [30.4.1] [37.2.1]
[38.1.3]
[38.3.3]
[43.3.1]
[21.3]
[43.3.1]
[33.2.3]
[33.2.3]
[21.3]
[37.4.3]
[43.3.1]
[22.2.2]
[38.3.1]
[29.6]
[31.3]
[43.3.1]
[17.4.1]
[17.4.1]
[4.1.1]
[45.1.2]
[48.3.3]
[44.6.2.3]
[38.3.2.2]
[30.6] [36.2.4]
[43.3.1]
[43.3.1]
[43.3.1]
[31.3]
[38.3.1]
[48.2.2.4] [48.3.4]
[36.2.3.3] [37.2.2]
[37.3.1.1]
[37.3.1.1] [38.3.2.2]
[29.6]
[43.3.1]
[43.3.1]
[43.3.1]
[29.5]
[43.3.1]
[38.3.3]
[43.3.1]
[24.2.1]
[28.1.6.10] [32.8]
[28.1.2] [28.1.6.10]
[32.4] [32.8]
[28.1.6.10] [32.4]
[32.8]
[28.1.6.10] [32.4]
[32.8]
[15.3.1] [15.4.2]
[1.2.4] [15.3.1]
[10.3.5]
isPaintable
isPathSelected
isPopupTrigger
isPrivate
isProtected
isPublic
isResizable
isRootVisible
isRunning
isSelected
isSelectedIndex
isSelectionEmpty
isShiftDown
isStatic
isStrict
isSynchronized
isTemporary
isTransient
isVisible
isVolatile
ITALIC
Item-Ereignisse
ItemEvent
ItemListener
itemStateChanged
iterator
Iterator
Iterator-Pattern
<
>
>>
java.awt.event
java.util
java.awt.event
-JJ++
J2EE
J2ME
J2SE
JAAS
Jakarta
JApplet
[1.1]
[1.1]
[1.1]
[1.1]
[47.1.5]
[13.3.2.2]
[35.2.2.2]
javax.swing
[36.1.5]
[50.6.2]
jar
JAR-Archiv
[39.2.2]
[19.2.3.4]
JarOutputStream
java.util.jar
[47.3.2]
jarsigner
[50.11.2]
[2.2.2.1]
java
[50.2.2]
Java 2 Enterprise Edition
[1.1]
Java 2 Micro Edition
[1.1]
Java 2 Platform
[1.1]
Java 2 SDK
[1.1]
Java 2 Standard Edition
[1.1]
Java 2D API
[1.1] [1.2.3]
Java Authentication and Authorization Service [47.1.5]
Java Beans Homepage
[44.3.1]
Java Communications API
[26.1] [26.4]
Java Community Process
[1.1]
JAVA Cryptography Extension
[47.1.5]
Java Database Connectivity
[1.2.4]
[42.1.1]
Java Developer's Connection
[1.1] [3.2.2.2]
Java Developer's Journal
[3.2.2.4]
Java Development Kit
[1.1]
[1.1] [1.2.3]
[35.1.1]
[48.1]
[46.2.6]
[13.5.1]
[50.4.2]
[3.2.2.4]
[2.1.2.1]
[50.2.3]
[47.1.5]
[1.3.2]
[3.2.2.4]
[49.3.1]
[13.5]
[45.3.3]
[13.5.1]
[3.2.2.4]
[35.1.2.4]
[40.4]
[1.1]
[13.2.1.2]
[13.2.1.2]
[23.2.1]
[30.7.1]
[28.1.2]
[1.2.3]
[26.3.1]
[13.2.1.2]
[44.4.1]
[16.3.1]
[16.3.1]
[50.2.2]
[16.3.1]
[13.2.1.2]
[18.1]
[13.2.1.1]
[13.2.1.2]
java.lang.ref
java.lang.reflect
java.math
java.net
java.nio
java.policy
java.rmi
java.rmi.server
java.security
java.security.cert
java.specification.name
java.specification.vendor
java.specification.version
java.sql
java.text
java.util
java.util.jar
java.util.zip
java.vendor
java.vendor.url
java.version
java.vm.name
java.vm.specification.name
java.vm.specification.vendor
java.vm.specification.version
java.vm.vendor
java.vm.version
JavaBeans
[13.2.1.2]
[13.2.1.2]
[43.3.1]
[13.2.1.2]
[17.3]
[13.2.1.2]
[45.2.1]
[13.2.1.2]
[47.3.4.1]
[13.2.1.2]
[46.2.2]
[46.2.3.1]
[13.2.1.2]
[47.1.6.2]
[16.3.1]
[16.3.1]
[16.3.1]
[13.2.1.2]
[42.2.1]
[13.2.1.2]
[17.4.2]
[13.2.1.2]
[15.1]
[28.1.2]
[19.2.3.4]
[19.2.3.4]
[16.3.1]
[16.3.1]
[16.3.1]
[16.3.1]
[16.3.1]
[16.3.1]
[16.3.1]
[16.3.1]
[16.3.1]
[1.1] [1.2.4]
javac
javadoc
javadt
javaidl
javakey
JavaLobby
JavaOne
JavaOS 1.0
javap
JavaScript
JavaSoft
JavaStation
javaw
javax-Pakete
javax.accessibility
javax.comm
javax.crypto
javax.imageio
javax.jnlp
javax.naming
javax.print
javax.security.auth
javax.sound
javax.sound.midi
javax.sound.sampled
javax.swing
javax.swing.event
javax.swing.plaf
javax.swing.table
javax.swing.text
javax.swing.tree
[2.2.1.3]
[2.2.2.1]
[50.1.2]
[4.1.2]
[50.5.2]
[50.4.2]
[1.2.4]
[47.1.6.1]
[3.2.2.2]
[1.1]
[1.1]
[50.7.2]
[1.2.2]
[1.1] [3.2.2.2]
[1.1]
[2.3.6.3]
[50.2.2]
[13.2.1.2]
[13.2.1.2]
[26.4.1.2]
[13.2.1.2]
[13.2.1.2]
[13.5.3.2]
[13.2.1.2]
[13.2.1.2]
[13.2.1.2]
[13.2.1.2]
[48.3.2]
[48.2.2]
[13.2.1.2]
[35.2.2.1]
[37.3.1.1]
[37.4.2]
[36.3.1.3]
[38.2.5]
[37.1.2]
[38.3.1]
javax.xml
JAVA_COMPILER
java_g
JButton
JCE
JCheckBox
JCheckBoxMenuItem
JComboBox
JComponent
jdb
JDBC
JDBC-ODBC-Bridge
JDC
JDesktopPane
JDialog
JDK
JEditorPane
JFC
JFormattedTextField
JFrame
JInsight
JInternalFrame
JIT
JLabel
[13.2.1.2]
[49.3.2.4]
[50.2.2]
[49.3.2.4]
[50.2.3]
[35.2.2.8]
[37.2.1]
[47.1.5]
[37.2.2]
[36.2.3.3]
[37.3.2]
[35.2.2.3]
[36.3.1]
[50.4.2]
[1.1] [1.2.4]
[42] [42.1.1]
[42.1.2.1]
[3.2.2.2]
[3.2.2.2
(Newsletter)]
[36.1.6.1]
[35.2.2.2]
[36.1.3]
[1.1]
[37.1.4]
[1.2.3]
[37.1.2]
[10.3.6]
[35.2.2.2]
[36.1.1]
[49.3.3]
[36.1.6.2]
[49.1]
[50.2.2]
[35.2.2.4]
[37.1.1]
javax.swing
javax.swing
javax.swing
javax.swing
javax.swing
javax.swing
javax.swing
javax.swing
javax.swing
javax.swing
javax.swing
javax.swing
JLayeredPane
JList
JMenu
JMenuBar
JMenuItem
JNDI
jnlp-Datei
Johnson, Ralph
join
JOptionPane
Joy, Bill
JPanel
JPasswordField
JPDA
jpeg-Format
JPopupMenu
JProbe
JProgressBar
JRadioButton
JRadioButtonMenuItem
JRE
jre
JRootPane
JScrollBar
JScrollPane
JSlider
JSpinner
JSplitPane
JSSE
JTabbedPane
[36.1.1.1]
[36.3.2.2]
[35.2.2.7]
[37.3.1]
[36.2.2.2]
[36.1.1.1]
[36.2.2.3]
[46.2.6]
[13.5.2.2]
[10.3]
[22.2.2]
[22.2.4.3]
[36.1.4]
[1.1]
[35.2.2.3]
[36.3.2.1]
[35.2.2.5]
[37.1.3]
[50.4.2]
[34.1.1]
[36.2.4]
[49.3.3]
[37.4.3]
[37.2.3]
[36.2.3.3]
[2.1.2.1]
[50.2.3]
[36.1.1.1]
[37.4.1]
[35.2.2.7]
[37.1.4]
[38.1.1]
[37.4.2]
[37.1.5]
[38.1.2]
[47.1.5]
[38.1.3]
javax.swing
javax.swing
javax.swing
javax.swing
javax.swing
javax.swing
javax.swing
javax.swing
javax.swing
javax.swing
javax.swing
javax.swing
javax.swing
javax.swing
javax.swing
javax.swing
javax.swing
javax.swing
javax.swing
[38.2.1]
[37.1.4]
[37.1.2]
[37.1.2]
[37.1.4]
[37.2.2]
[36.3.1.2]
[38.3.1]
[49.1]
[50.2.2]
[49.3.1]
[35.2.2.2]
[36.1.2]
JTable
JTextArea
JTextComponent
JTextField
JTextPane
JToggleButton
JToolTip
JTree
Just-In-Time-Compiler
JVMPI
JWindow
<
>
>>
javax.swing
javax.swing
javax.swing
javax.swing.text
javax.swing
javax.swing
javax.swing
javax.swing
javax.swing
-KKapselung
Kestrel
Key-Ereignisse
Key-Events
KeyAdapter
KeyEvent
KeyListener
keyPressed
keyReleased
keys
keySet
KeyStore
KeyStroke
keytool
keyTyped
KEY_EVENT_MASK
KEY_PRESSED
Klammerung
Klartext
Klassen
Klassendiagramme
Klassenmethoden
Klassenobjekt
[7.1.3]
[1.1]
[28.1.6.2]
[29.6]
[28.1.5] [28.2.2.1]
[28.2.2.2]
[28.1.2] [28.1.6.2]
[28.2.1] [29.6]
[28.1.6.2] [28.2.1]
[28.2.2.1] [29.6]
[28.1.6.2] [28.2.1]
[28.2.2.2] [29.6]
[28.1.6.2] [28.2.1]
[29.6]
[14.4.3]
[15.6.1]
[47.1.6.2]
[36.2.2.3]
[47.1.6.1] [47.3.3]
[50.9.2]
[28.1.6.2] [28.2.1]
[29.6]
[33.2.3]
[33.2.3]
[5.1]
[47.1.1]
[13.1.4]
[3.2.4]
[8.3.3] [13.1.3]
[43.2.2]
java.awt.event
java.awt.event
java.awt.event
java.security
javax.swing
Klassenvariablen
[4.3.1] [4.3.3]
[8.3.1]
Kleiner-Operator
[5.3]
Kleinergleich-Operator
[5.3]
Kommentare
[4.1.2]
Komposition
[7.1.5.2]
Konstanten
[8.3.2] [9.4.1 (in
Interfaces)]
Konstruktoren
[7.3.6]
Konstruktorenverkettung
[7.3.6.2] [8.1.4.1]
Kontext-URL
[40.1.3]
Kontextmens
[30.6 (im AWT)]
[36.2.4 (in Swing)]
Konvertierungen auf primitiven Datentypen [4.6.1]
Konvertierungsfunktionen
[11.2.7]
Koordinatensystem
[23.2.3]
Kopieren einer Datei
[19.3.2.1]
Kopieren von Flchen
[23.4.2]
Kopieren von Objekten
[41.3.2]
Kreisbgen zeichnen
[23.3.5]
Kreise zeichnen
[23.3.4]
Kritischer Bereich
[22.4.2]
Kryptoanalyse
[47.1.1]
Kryptographie
[47] [47.1.1]
Kryptologie
[47.1.1]
Handbuch der Java-Programmierung, 3. Auflage,
Addison Wesley, Version 3.0
1998-2002 Guido Krger,
API
http://www.javabuch.de
<
>
>>
-LL10N
Label
Ladefaktor
LANDSCAPE
last
lastElement
lastIndexOf
lastModified
Late Binding
Laufzeitfehler
LayeredPane
Layoutmanager
LEADING
Lebensdauer
Leere Anweisung
left
LEFT
length
Lexikalische Elemente
LIFO-Prinzip
lightGray
Lightweight Components
LIKE
Line
Line.Info
[17.4]
[32.2]
java.awt
[49.2.1.1]
[26.3.2.2]
[15.7.2]
[14.2.2]
[11.2.5]
[21.3]
[7.1.6]
[12.1]
[36.1.1.1]
[31.2]
[36.2.3.2]
[4.3.3] [8.2]
[6.1.1]
[39.2.2]
[31.2.1 (FlowLayout)]
[32.2 (Label)] [36.2.3.2
(SwingConstants)]
[4.4.2 (Array)] [4.5.1
(Array)] [11.2.3
(String)] [11.4.5
(StringBuffer)] [20.2.2
(RandomAccessFile)]
[4.1]
[14.3]
[25.2]
[35.1.2.1]
[42.4.7.3]
[48.2.2]
javax.sound.sampled
[48.2.2.2]
Logische Operatoren
Logischer Typ
LOG_OPTION
Lokale Klassen
Lokale Variable
Lokalisierung
Long
long
LONG
Long.TYPE
longValue
Look-and-Feel
lookup
loop
lostOwnership
Lotus
lowerCase
LOWER_LEFT_CORNER
LOWER_RIGHT_CORNER
Lnge der Zeichenkette
Lschen von Flchen
[5.4]
[4.2.1]
[36.3.1.4]
[28.2.2.1]
[4.3.1] [6.1.3.2]
[17.4]
[10.2.1]
[4.2] [4.2.3] [10.2.1]
[17.4.3]
[43.3.1]
[10.2.1.2]
[35.1.2.2] [35.2.2.9
(Umschalten)]
[13.5.3.2] [46.2.4.2]
[46.2.5]
[39.3.1]
[30.7.2]
[1.1]
[18.2.3.3]
[38.1.1]
[38.1.1]
[11.2.3]
[23.4.2]
<
>
>>
java.lang
-Mmagenta
main
Main-Class
make
makefile
makeVisible
MalformedURLException
Manifest-Datei
Map
Map.Entry
mark
markSupported
MASTER_GAIN
Math
Mauscursor
max
MAX_PRIORITY
MAX_VALUE
McNealy, Scott
MD5
MDI
Mediator-Pattern
[25.2]
[2.2.1.2]
[8.3.3.1]
[13.1.6]
[39.1.1]
[50.2.2]
[13.4.1.3]
[13.3.2.2]
[13.3.2.2]
[38.3.3]
[40.1.1]
java.net
[13.4.1.3]
[44.3.3.2]
[15.1] [15.6.1] java.util
[15.6.1]
[18.3.1]
[19.3.1]
[18.3.1]
[19.3.1]
[48.2.2.3]
[16.1.2.1]
java.lang
[17.2] [17.5]
[27.5] [27.5.3]
[17.2.2]
[22.5.1]
[4.2.4.1]
[10.2.1.4]
[1.1]
[47.1.3]
[36.1.6]
[28.2.3]
[34.1.1.1]
[17.4.3]
[4.4.3]
[32.9]
[7.1.5.1] [8.1]
[9.1]
Mehrfachverzweigung
[6.2.2.2]
Mehrstufige Client-Server-Architekturen [42.1.2.2]
Mehrzeilige Kommentare
[4.1.2]
Member-Zugriff
[5.7.2.5]
Membervariablen
[7.1.3] [7.2.1]
[30.1]
Menu
[25.4]
menu
[30.1]
MenuBar
[30.1] [30.4.1]
MenuItem
[30.4.3]
MenuShortcut
[25.4]
menuText
Men
[27.5]
Meneintrge
[30.4]
Menleiste
[30.2]
Message Digest
[19.2.3.5]
[47.1.3]
Message Queue
[19.2.2.4]
[47.1.3]
MessageDigest
meta-inf
[13.4.1.3]
Meta-Ressourcen
[3.2.2.2]
Metal-Look-and-Feel
[35.2.1]
[48.3.2]
MetaMessage
[43.3.1]
Method
[44.6.1.3]
[44.6.1.3]
MethodDescriptor
Methoden
[7.1.3] [7.3]
[13.1.3]
[5.7.2.6 (aufruf)] [8.1.3
(berlagerung)]
MediaTracker
MEDIUM
Mehrdimensionale Arrays
Mehrfachselektion
Mehrfachvererbung
java.awt
java.awt
java.awt
java.awt
java.awt
java.security
javax.sound.midi
java.lang.reflect
java.beans
Metriken fr Fonts
Meyer, Bertrand
Microsoft Internet Explorer
middle
Middleware
Midi
Midi-Dateien
Midi-Ereignisse
Midi-Format
Midi-Nachrichten
MidiDevice
MidiEvent
MidiMessage
MidiSystem
MILLISECOND
MIME-Spezifikation
MIME-Typ
min
Minimum und Maximum
MINUTE
MIN_VALUE
MissingResourceException
Mixer
Mixer
Mixer.Info
mkdir
mkdirs
Modale Dialoge
Model-Delegate-Prinzip
Model-View-Controller-Prinzip
Modifier
Modifier
Modulo-Operator
Monitor
Monospaced
[24.3.2]
[6.4.1.4]
[1.2.2]
[39.2.2]
[42.1.2.1]
[48.1] [48.3]
[1.2.3]
[48.3.1]
[39.3.1]
[48.3.1]
[48.3.2]
[48.3.2]
[48.3.2]
[48.3.2]
[16.2.2]
[30.7.1]
[45.3.3]
[17.2.2]
[17.2.2]
[16.2.2]
[4.2.4.1]
[10.2.1.4]
[17.4.4.1]
[48.1]
[48.2.2]
[48.2.2.2]
[21.4.2]
[21.4.2]
[31.3]
[35.1.2.3]
[35.1.2.3]
[7.3.1] [8.2]
[43.3.1]
[5.2]
[22.1]
[24.2.1]
javax.sound.midi
javax.sound.midi
javax.sound.midi
javax.sound.midi
java.util
javax.sound.sampled
java.lang.reflect
[16.2.2]
[28.1.6.3]
[29.3]
[28.1.5]
[28.1.6.3]
[28.1.6.4]
[28.1.6.3]
[28.1.2]
[28.1.6.3]
[28.1.6.4]
[29.3]
[28.1.6.3]
mouseExited
[28.1.6.3]
MouseListener
[29.3]
MouseMotion-Ereignisse
[28.1.6.4]
MouseMotion-Events
[29.4]
[28.1.5]
MouseMotionAdapter
[28.1.6.4]
MouseMotionListener
[28.1.6.4]
mouseMoved
[28.1.6.3]
mousePressed
[28.1.6.3]
mouseReleased
[33.2.3]
MOUSE_EVENT_MASK
[33.2.3]
MOUSE_PRESSED
[37.1.2]
moveCaretPosition
[27.5.3]
MOVE_CURSOR
MS-Access
[42.3.1]
Multi-Tier-Architekturen
[42.1.2.2]
Multiple Document Interface
[36.1.6]
MULTIPLE_INTERVAL_SELECTION [37.3.1.1]
[38.2.3.1]
Multiplikation
[5.2]
[17.3.1]
multiply
mutable
[15.5.1]
[38.3.1]
MutableTreeNode
MVC
[28.2.3]
[35.1.2.3]
MONTH
Mouse-Ereignisse
Mouse-Events
MouseAdapter
mouseClicked
mouseDragged
mouseEntered
MouseEvent
java.awt.event
java.awt.event
java.awt.event
java.awt.event
java.awt.event
javax.swing.tree
<
>
>>
-NNAME
Name-Server
Namens-Service
Namenskonventionen
Naming
NaN
National Institute of Standards and
Technology
Natrliche Ordnung
Naughton, Patrick
NCSA Mosaic
Nebeneffekte
Nebenlufigkeit
negate
NEGATIVE_INFINITY
Netscape
Network Information Center
Networking-API
Netzwerk-ID
Netzwerkprogrammierung
Netzwerkschicht
new
New I/O Package
newAudioClip
newInstance
newLine
Newsgroups
next
[39.2.2]
[45.1.3.2]
[46.1.1]
[2.3.5]
[46.2.4.2]
[4.2.4.1] [10.2.1.4]
[47.1.3]
[15.7.1]
[1.1]
[1.1]
[5.1] [6.1.4.2]
[22.1]
[17.3.1]
[4.2.4.1] [10.2.1.4]
[1.1] [1.2.2 (Navigator)]
[45.1.3.1]
[1.2.4]
[45.1.3.1]
[45]
[45.1.2]
[4.5.2] [5.7.2.4] [7.2.2]
[4.4.1 (bei Arrays)]
[13.2.1.2]
[39.3.2]
[43.2.2 (Class)] [43.3.3
(Constructor)] [43.5.1
(Array)]
[18.2.3.1]
[1.3.2] [3.2.2.1]
[15.3.1] [42.2.3]
java.rmi
nextBytes
nextDouble
nextElement
nextFloat
nextGaussian
nextIndex
nextInt
nextLong
nextToken
NIC
NICHT-Operator
NoClassDefFoundError
nodeChanged
NONE
NONE_OPTION
NORMAL
NORM_PRIORITY
NORTH
NORTHEAST
NORTHWEST
NORTH_EAST
NORTH_WEST
NoSuchElementException
NoSuchMethodException
NOT
NOTE_OFF
NOTE_ON
notify
NotSerializableException
NO_SUCH_PAGE
null
NULL
Null-Layout
[47.1.4]
[16.1.2.1]
[1.2.4] [14.2.3]
[16.1.2.1]
[16.1.2.2]
[15.3.2]
[16.1.2.1]
[16.1.2.1]
[17.1.2]
[45.1.3.1]
[5.4]
[2.2.1.4] [2.2.2.2] [2.3.1]
[38.3.4]
[31.2.4]
[36.3.1.4]
[27.4]
[22.5.1]
[31.1.3] [31.2.4
(GridBagConstraints)]
[36.2.3.2
(SwingConstants)]
[31.2.4]
[31.2.4]
[36.2.3.2]
[36.2.3.2]
[15.3.1]
[43.3.2]
[42.4.7.3]
[48.3.3]
[48.3.3]
[22.4.3]
[41.1.2] [41.2.4]
[26.3.2.4]
[4.1.3] [4.5.1] [7.2.2]
[42.2.3]
[31.2] [31.2.5]
java.util
java.lang
java.io
null-Objekt
NullPointerException
NumberFormat
NumberFormatException
<
>
>>
java.lang
java.text
java.lang
[7.1.5.2]
[43.3.2]
[17.4.2]
[12.2.1]
-OOak
[1.1]
Oberlnge
[24.3.2]
Object
[8.1.2]
[8.1.2]
Object
java.lang
[39.2.2]
OBJECT
[19.3.2.3] [41.1.3]
ObjectInputStream
java.io
[19.2.2.3] [41.1.2]
java.io
ObjectOutputStream
Objektorientierte Programmierung [7.1.1]
Objektvariable
[7.1.5.3]
Observer-Pattern
[10.3.9]
ODBC-Treiber
[42.1.2.1]
ODER-Operator
[5.4]
Offscreen-Image
[34.2.4.3]
[36.1.4.2]
OK_CANCEL_OPTION
Online-Dokumentationen
[3.2.2.4]
OOP
[1.2.1] [7.1.1]
[26.4.1.2] [48.2.2.4 (Line)] [48.3.3
open
(MidiDevice)]
[45.4]
openConnection
[45.4]
openStream
Operator-Vorrangregeln
[5.8]
Operatoren
[5.2 (Arithmetische)] [5.3
(Relationale)] [5.4 (Logische)] [5.5
(Bitweise)] [5.6 (Zuweisungs-)] [5.7
(Sonstige)] [5.7.2 (fr Objekte)]
OptimizeIt
[49.3.3]
[42.4.7.3]
OR
[14.5.2]
or
Oracle
[1.1]
[25.2]
orange
[42.4.7.3]
ORDER BY
org.omg
org.w3c
org.xml
os.arch
os.name
os.version
out
OutOfMemoryError
OutputStream
OutputStream
OutputStreamWriter
[13.2.1.2]
[13.2.1.2]
[13.2.1.2]
[16.3.1]
[16.3.1]
[16.3.1]
[1.2.4] [16.3.2]
[49.2.5]
[19.1]
[19.1] [19.2.1] [49.2.4.1]
[18.2.2] [18.2.2.1]
<
>
>>
java.io
java.io
java.lang
-Ppack
package
package scoped
package-list
Pageable
pageDialog
PageFormat
PAGE_EXISTS
paint
paintBorder
paintChildren
paintComponent
paintValue
Pakete
PAN
Panel
Paper
ParallelPort
PARAM-Tag
Parameter von Methoden
parse
parseByte
parseDouble
parseFloat
parseInt
parseLong
PATH
path.separator
PCM
peek
[31.1.4] [33.2.2]
[13.2.4.1]
[8.2.1] [8.2.2.4]
[50.5.4]
[26.3.1] [26.3.2.5]
[26.3.2.3]
[26.3.2.2]
[26.3.2.4]
[23.2.2] [27.5.4]
[36.3.1.3]
[36.3.1.3]
[36.3.1.3]
[44.6.2.3]
[13.1.5] [13.2] [13.2.4
(Selbst erstellen)]
[48.2.2.3]
[27.1] [31.2.6] [44.5]
[26.3.2.2]
[26.4.1.2]
[39.2.1]
[7.3.3]
[17.4.2]
[10.2.1.3]
[10.2.1.3]
[10.2.1.3]
[10.2.1.3]
[10.2.1.3]
[2.3.7.2] [2.3.7.3]
[16.3.1]
[48.2.3]
[14.3]
java.awt.print
java.awt.print
java.awt
java.awt.print
javax.comm
Performance-Tuning
PersistenceService
Persistenz
PGP
PicoJava
pink
Pipe
PipedInputStream
PipedOutputStream
PipedReader
[49]
[13.5.3.1]
[41.1.1]
[47.1.5]
[1.1]
[25.2]
[22.4.4]
[19.3.2.5] [22.4.4]
[19.2.2.4] [22.4.4]
[18.2.2] [18.3.2]
[22.4.4]
[18.2.2] [18.3.2]
PipedWriter
[22.4.4]
[19.2.3.4]
pkunzip
[24.2.1]
PLAIN
[30.7.2]
plainTextFlavor
[36.1.4.1]
PLAIN_MESSAGE
[39.3.1]
play
Pluggable Look-and-Feel
[1.2.3] [35.1.2.2]
Plugin fr HTML-Browser
[40.4]
[29.3]
Point
Policy-Datei
[47.2.3] [47.3.4]
[47.3.4.2] [50.10.2]
policytool
[23.3.3]
Polygon
Polygone zeichnen
[23.3.3]
Polymorphismus
[7.1.6] [8.4.2]
[14.3]
pop
Popup-Mens
[30.6 (im AWT)]
[36.2.4 (in Swing)]
[30.6]
PopupMenu
[48.2.2]
Port
Port-Nummer
[40.1.1] [45.1.4]
[26.3.2.2]
PORTRAIT
[26.4.1.2]
PORT_PARALLEL
[26.4.1.2]
PORT_SERIAL
Positionierung des Dateizeigers [20.2.1]
javax.jnlp
java.io
java.io
java.io
java.io
java.awt
java.awt
java.awt
javax.sound.sampled
POSITIVE_INFINITY
Postconditions
Postdekrement
Postinkrement
pow
PPQ
Preconditions
Prepared Statements
PreparedStatement
prepareStatement
Pretty Good Privacy
previous
previousIndex
Primitive Datentypen
Primitive Variable
print
Printable
printAll
printDialog
PrinterException
PrinterJob
PrintGraphics
PrintJob
println
PrintService
printStackTrace
PrintStream
PrintWriter
private
[4.2.4.1] [10.2.1.4]
[6.4.1.4]
[5.2]
[5.2]
[17.2.3] [17.3.1]
[48.3.4]
[6.4.1.4]
[42.4.6]
[42.4.6]
java.sql
[42.4.6]
[47.1.5]
[15.3.2]
[15.3.2]
[4.2]
[7.1.5.3]
[18.2.3.2 (PrintWriter)]
[19.2.3.2
(PrintStream)] [26.2.3
(Component)]
[26.3.2.4 (PrinterJob)]
[26.3.1] [26.3.2.4]
java.awt.print
[26.2.3]
[26.3.2.3]
[26.3.2.4]
java.awt.print
[26.3.1] [26.3.2.1]
java.awt.print
[26.2.1]
java.awt
[26.2.1]
java.awt
[16.3.2] [18.2.3.2]
[19.2.3.2]
[13.5.3.1]
javax.jnlp
[12.2.2]
[1.2.4] [16.3.2]
java.io
[19.2.3.2]
[18.2.2] [18.2.3]
java.io
[18.2.3.2]
[1.2.1] [8.1.3.1] [8.2.1]
[8.2.2.1] [49.2.2]
[16.4.2]
Process
java.lang
[28.2.4]
processActionEvent
processComponentEvent [33.2.3]
[28.2.4]
processEvent
[33.2.3]
processFocusEvent
[28.2.4] [33.2.3]
processKeyEvent
[28.2.4] [30.6] [33.2.3]
processMouseEvent
Producer/Consumer-Beispiel
[22.4.3]
Profiler
[49.3.1] [50.2.3]
Programmende
[22.2.1]
[48.3.3]
PROGRAM_CHANGE
Projektverwaltung
[13.3.2]
[14.4.4] [16.3.1]
Properties
java.util
[44.4.1]
propertyChange
[44.4.1]
PropertyChangeEvent
java.beans
java.beans
PropertyChangeListener[44.4.1]
java.beans
PropertyChangeSupport [44.4.1]
[44.6.1.2]
java.beans
PropertyDescriptor
java.beans
PropertyEditorSupport [44.6.2]
[14.4.4] [16.3.1]
propertyNames
[47.3.4.2]
PropertyPermission
java.util
java.util
PropertyResourceBundle[17.4.4.4]
java.beans
PropertyVetoException [44.4.2]
[1.2.1] [8.2.1] [8.2.2.2]
protected
Protokoll
[45.1.2]
Provider fr Security-API
[47.1.6.2]
Proxy-Server
[45.1.6]
Prdekrement
[5.2]
Prinkrement
[5.2]
Prprozessor
[4.1.4]
Pseudo-Zufallszahlen
[47.1.4]
[1.2.1] [8.2.1] [8.2.2.3]
public
[13.2.4.3] [13.3.2.1
(Klassen in Datei)]
Public-Key-Verschlsselung
[47.1.5]
[14.3]
push
PushbackInputStream
PushbackReader
put
putAll
[19.3.3.2]
[18.3.2] [18.3.3.3]
[14.4.1] [15.6.1]
[15.6.1]
<
>
>>
java.io
java.io
-Q[36.1.4.1]
[15.4.1]
QUESTION_MESSAGE
Queue
<
>
>>
Titel
<<
DOC
API
0 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
-RRadiobuttons
Random
Random-Access-I/O
RandomAccessFile
read
read-Methoden
readBoolean
readByte
readChar
readDouble
Reader
readFloat
readFully
readInt
readLine
readLong
readObject
readShort
readUnsignedByte
readUnsignedShort
readUTF
ready
rebind
Receiver
Rechtecke zeichnen
Rechtsschiebeoperator
Rectangle
red
Red-Black-Tree
Referenzgleichheit und -ungleichheit
Referenztypen
Reflection
regionMatches
registerKeyboardAction
Registry
[32.5]
[1.2.4] [16.1.1]
[20]
[19.2.3.3] [20.1] [49.2.4.3]
[18.3.1] [19.3.1]
[20.3]
[20.3] [41.1.3]
[20.3] [41.1.3]
[20.3] [41.1.3]
[20.3] [41.1.3]
[18.3.1]
[20.3] [41.1.3]
[20.3]
[20.3] [41.1.3]
[18.3.3.1] [20.3]
[20.3] [41.1.3]
[41.1.3]
[20.3] [41.1.3]
[20.3]
[19.3.3.3] [20.3]
[20.3] [41.1.3]
[18.3.1]
[46.2.4.2]
[48.3.2]
[23.3.2]
[5.5]
[34.2.3]
[25.2]
[15.7.2]
[5.7.2.2]
[4.5]
[1.2.4] [41.1.2] [43]
[11.2.4]
[36.3.1.10]
[2.1.2.1]
java.util
java.io
java.io
javax.sound.midi
java.awt
Relationale Operatoren
remainder
Remote
Remote Method Invocation
Remote-Interface
Remote-Objekte
Remote-Referenzen
RemoteException
remove
[5.3]
[17.3.1]
[46.2.2]
java.rmi
[1.2.4] [46]
[46.1.1]
[46.1.1]
[46.1.1]
[46.2.2]
java.rmi
[15.2.1 (List)] [15.3.1
(Iterator)] [15.6.1 (Map)]
[30.2 (MenuBar)] [30.3
(Menu)] [31.1.3
(Container)] [32.9 (List)]
[38.3.1
(DefaultMutableTreeNode)]
[36.2.2.3]
removeActionListener
[15.2.1] [38.1.3]
removeAll
[38.3.1]
removeAllChildren
[37.3.2]
removeAllItems
removeColumnSelectionInterval[38.2.3.3]
[37.3.2]
removeItem
[37.3.2]
removeItemAt
[38.3.4]
removeNodeFromParent
removePropertyChangeListener [44.4.1]
[38.2.3.3]
removeRowSelectionInterval
[37.3.1.1]
removeSelectionInterval
[38.1.3]
removeTabAt
[38.2.5]
removeTableModelListener
removeVetoableChangeListener [44.4.2]
[21.4.2]
renameTo
Rendering von Tabellen
[38.2.7]
[29.2] [33.2.3]
repaint
repaint-Schleife
[34.2.1.1]
[11.3.3] [11.2.6 (String)]
replace
[11.4.4 (StringBuffer)]
[32.9]
replaceItem
[32.7]
replaceRange
Request For Comments
[45.1.5]
[38.1.3]
requestDefaultFocus
[29.5] [33.2.3]
requestFocus
[28.1.2]
RESERVED_ID_MAX
reset
ResourceBundle
Ressourcen
Ressourcen-Dateien
Ressourcendatei
Restwertoperator
ResultSet
ResultSetMetaData
resume
retainAll
return
revalidate
REVERSE_LANDSCAPE
RFC
RFC 1521
RFC 1522
RGB-Farbmodell
right
RIGHT
Rivest, Ron
rmf-Format
RMI
RMI-Registry
rmic
rmiregistry
RMISecurityManager
rollback
RootPane
RootPaneContainer
round
ROUND_CEILING
ROUND_DOWN
ROUND_FLOOR
ROUND_HALF_DOWN
ROUND_HALF_EVEN
[18.2.2.2
(CharArrayWriter)] [18.3.1
(Reader)] [19.3.1
(InputStream)] [41.2.3
(ObjectOutputStream)]
[17.4.4]
java.util
[17.4.4]
[13.4.2]
[30.1]
[5.2]
[42.2.3]
java.sql
[42.4.1]
java.sql
[22.2.3]
[15.2.1]
[5.7.2.6] [7.3.4] [12.2.6]
[36.3.1.8]
[26.3.2.2]
[45.1.5]
[30.7.1]
[30.7.1]
[25.1]
[39.2.2]
[31.2.1 (FlowLayout)] [32.2
(Label)] [36.2.3.2
(SwingConstants)]
[47.1.3]
[39.3.1]
[46]
[46.1.1]
[46.2.3.2] [50.12.2]
[46.2.4.1] [50.13.2]
[46.2.4.3]
java.rmi
[42.4.3]
[36.1.1.1]
[36.1.1.1]
javax.swing
[17.2.4]
[17.3.2]
[17.3.2]
[17.3.2]
[17.3.2]
[17.3.2]
[17.3.2]
[17.3.2]
[17.3.2]
[38.2.8]
[47.1.5]
[13.2.2.3]
[22.1] [22.2.4.2]
[17.2.4]
[22.1] [22.3] [22.3.2]
[16.4.1]
[12.3.2.1]
[47.3.4.2]
[7.3.4]
ROUND_HALF_UP
ROUND_UNNECESSARY
ROUND_UP
rowAtPoint
RSA
rt.jar
run
Runden und Abschneiden
Runnable
Runtime
RuntimeException
RuntimePermission
Rckgabewert einer Methode
Titel
<<
>
>>
DOC
API
java.lang
java.lang
java.lang
java.lang
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
0 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
-SSamples
Sampling
Sandbox-Konzept
SansSerif
save
scale
Schachteln
Schiebeoperationen
Schieberegler
Schleifen
Schleifeninvarianten
Schlieen eines Fensters
Schlssel
Schlsseltext
Schlsseltransformation
Schlsselwrter
Schneier, Bruce
Schnittstellen (Interfaces)
Schriftarten
scrollbar
Scrollbar
SCROLLBARS_ALWAYS
SCROLLBARS_AS_NEEDED
SCROLLBARS_BOTH
SCROLLBARS_HORIZONTAL_ONLY
SCROLLBARS_NEVER
SCROLLBARS_NONE
SCROLLBARS_VERTICAL_ONLY
ScrollPane
search
SECOND
SecureRandom
SecurityException
SecurityManager
seek
[48.1]
[48.2.1]
[47.2.2]
[24.2.1]
[14.4.4]
[17.3.2]
[18.2.3 (von AusgabeStreams)] [18.3.3 (von
Eingabe-Streams)]
[31.2.6 (von
Layoutmanagern)]
[5.5]
[32.10]
[6.3]
[6.4.1.4]
[23.2.4]
[47.1.1]
[47.1.1]
[14.4]
[4.1.3]
[47.1.2.3]
[9.1]
[24.2]
[25.4]
[32.10]
[32.11]
[32.11]
[32.7]
[32.7]
[32.11]
[32.7]
[32.7]
[25.4] [32.11]
[14.3]
[16.2.2]
[47.1.4]
[47.3]
[47.3.5]
[20.2.1]
java.awt
java.awt
java.security
java.lang
java.lang
Segment7
SELECT
select
SELECT-Anweisung
selectAll
semidynamische Arrays
send
Separatoren
Sequence
SequenceInputStream
Sequencer
Sequencer
Sequenz
Sequenzer
Serialisierte Beans
Serialisierung
Serializable
SerialPort
serialver
serialVersionUID
Serif
Server-Hotspot
ServerSocket
ServiceManager
Services
Servlet-API
set
Set
setAccelerator
setActionCommand
setAsText
setAutoCommit
setAutoResizeMode
setBackground
setBlockIncrement
setBoolean
setBorder
setBounds
setByte
[33.2.2]
[42.4.7.3]
[32.6] [32.8] [32.9]
[42.2.3]
[32.6] [38.2.3.3]
[1.2.1]
[48.3.3 (Receiver)]
[30.3]
[48.3.2]
javax.sound.midi
[19.3.2.4]
java.io
[48.1]
[48.3.2]
javax.sound.midi
[48.3.1]
[48.3.1]
[44.3.4]
[1.2.4] [41.1.1]
[41.1.2] [41.1.3]
java.io
[26.4.1.2]
javax.comm
[41.2.1] [50.8.2]
[41.2.1]
[24.2.1]
[50.2.2]
[45.2.2] [45.3.1]
java.net
[13.5.3.2]
javax.jnlp
[13.5.3.1]
[1.3.2]
[2.3.7.3] [14.5.1
(BitSet)] [15.3.2
(ListIterator)] [16.2.2
(Calendar)] [43.4
(Field)] [43.5.2 (Array)]
[15.1] [15.5.1]
java.util
[36.2.2.3]
[30.4.3 (MenuItem)]
[30.5 (MenuItem)]
[32.3 (Button)]
[44.6.2.1]
[42.4.3]
[38.2.2]
[23.3] [27.5.4]
[32.10] [37.4.1]
[43.5.2]
[35.2.2.6] [36.3.1.1]
[27.3] [31.2.5]
[43.5.2]
setCaretPosition
setCellSelection
setChar
setCharAt
setCheckboxGroup
setClip
setClosable
setColor
setColumnHeaderView
setColumns
setColumnSelectionAllowed
setColumnSelectionInterval
setComponentAt
setConstraints
setContentPane
setContents
setCopies
setCorner
setCurrent
setCursor
setDebugGraphicsOptions
setDecimalSeparatorAlwaysShown
setDefaultButton
setDefaultCloseOperation
setDefaultRenderer
setDouble
setDoubleBuffered
setEchoChar
setEchoCharacter
setEditable
setEnabled
setEnabledAt
setErr
setFlashCount
setFlashTime
setFloat
setFont
setForeground
setGridColor
setGroupingSize
setHeaderValue
[32.6] [37.1.2]
[38.2.3.1]
[43.5.2]
[11.4.4]
[32.5]
[23.4.3]
[36.1.6.2]
[25.3]
[38.1.1]
[32.6]
[38.2.3.1]
[38.2.3.3]
[38.1.3]
[31.2.4]
[36.1.2]
[30.7.2]
[26.3.2.1]
[38.1.1]
[32.5]
[27.5.3]
[36.3.1.4]
[17.4.2]
[37.2.1]
[36.1.6.2]
[38.2.7]
[43.4] [43.5.2]
[36.3.1.6]
[37.1.3]
[32.6] [35.2.2.5]
[32.6] [37.3.2]
[27.3 (Component)]
[30.4.1 (MenuItem)]
[36.2.2.3 (JMenuItem)]
[37.2.1 (JButton)]
[38.1.3]
[16.3.2] [19.2.3.2]
[36.3.1.4]
[36.3.1.4]
[43.5.2]
[24.2.1] [27.5.5]
[27.5.4]
[38.2.2]
[17.4.2]
[38.2.6]
setHorizontalAlignment
setHorizontalTextPosition
setIcon
setIconifiable
setIconImage
setIn
setIndeterminate
setInt
setIntercellSpacing
setJMenuBar
setJobName
setLabel
setLayout
setLength
setLineNumber
setLineWrap
setLocation
setLong
setLookAndFeel
setMajorTickSpacing
setMaximizable
setMaximum
setMaximumSize
setMenuBar
setMessage
setMinimum
setMinimumSize
setMinorTickSpacing
setMnemonic
setModal
setName
setNextFocusableComponent
setOneTouchExpandable
setOpaque
setOut
setPage
setPaintLabels
setPaintTicks
setPreferredSize
setPrintable
setPriority
setPropertyEditorClass
setReceiver
[37.1.1] [37.2.1]
[36.2.3.2] [37.1.1]
[37.2.1]
[36.2.3.2]
[36.1.6.2]
[27.5.2]
[16.3.2]
[37.4.3]
[43.4] [43.5.2]
[38.2.2]
[36.2.2.3]
[26.3.2.1]
[30.4.1] [32.3] [32.4]
[31.1.2] [31.2]
[20.2.2]
[18.3.3.2]
[37.1.4]
[27.3]
[43.5.2]
[35.2.2.9]
[37.4.2]
[36.1.6.2]
[37.4.1]
[36.3.1.7]
[30.2]
[48.3.3]
[37.4.1]
[36.3.1.7] [38.1.2]
[37.4.2]
[36.2.2.3] [37.2.1]
[31.3]
[22.5.1]
[36.3.1.9]
[38.1.2]
[36.3.1.5]
[16.3.2] [19.2.3.2]
[26.3.2.5]
[37.4.2]
[37.4.2]
[36.3.1.7]
[26.3.2.4]
[22.5.1]
[44.6.1.2]
[48.3.4]
setResizable
setRootPaneCheckingEnabled
setRootVisible
setRowHeaderView
setRowHeight
setRowMargin
setRowSelectionAllowed
setRowSelectionInterval
setScale
setScrollPosition
setSeed
setSelected
setSelectedIndex
setSelectedIndices
setSelectedItem
setSelectionBackground
setSelectionEnd
setSelectionForeground
setSelectionInterval
setSelectionMode
setSelectionModel
setSelectionPath
setSelectionPaths
setSelectionStart
setSequence
setShort
setShortCut
setShowGrid
setShowHorizontalLines
setShowVerticalLines
setSize
setSnapToTicks
setSoTimeout
setState
setStringPainted
setTabPlacement
setTabSize
setTempoInBPM
setter-Methoden
[31.3] [36.1.6.2]
[36.1.1.2]
[38.3.1]
[38.1.1]
[38.2.2]
[38.2.2]
[38.2.3.1]
[38.2.3.3]
[17.3.2]
[32.11]
[47.1.4]
[36.2.3.3] [37.2.2]
[37.3.1.1] [37.3.2]
[38.1.3]
[37.3.1.1]
[37.3.2]
[38.2.2]
[37.1.2]
[38.2.2]
[37.3.1.1]
[37.3.1.1] [38.2.3.1]
[38.3.2.1]
[38.3.2.1]
[38.3.2.3]
[38.3.2.3]
[37.1.2]
[48.3.4]
[43.5.2]
[30.4.3]
[38.2.2]
[38.2.2]
[38.2.2]
[27.3] [31.2.5] [32.11]
[37.4.2]
[45.2.3]
[27.4 (Frame)] [30.4.2
(CheckboxMenuItem)]
[32.4 (Checkbox)]
[36.2.3.3
(JCheckBoxMenuItem)]
[37.4.3]
[38.1.3]
[37.1.4]
[48.3.4]
[44.2.3]
setText
setTime
setTimeZone
setTitle
setToolTipText
setTransactionIsolation
setUnitIncrement
setUserObject
setValue
setValueAt
setVerticalAlignment
setVisible
setVisibleAmount
setWindowAdapter
setWrapStyleWord
SHA
shallow copy
Shape
Shell
Sheridan, Mike
short
Short
SHORT
Short-Circuit-Evaluation
Short.TYPE
ShortMessage
show
showConfirmDialog
showDocument
showInputDialog
showMessageDialog
showStatus
Sicherheit
Sichtbarkeit
sign
Signatur einer Methode
Signature
signedjar
signierte Applets
java.awt
java.lang
javax.sound.midi
java.security
signum
SimpleBeanInfo
sin
Singleton-Pattern
SINGLE_INTERVAL_SELECTION
SINGLE_SELECTION
SINGLE_TREE_SELECTION
size
Skeleton
skip
skipBytes
sleep
SMPTE-Format
Socket
Socket
SocketException
SocketPermission
Sonstige Operatoren
sort
SortedMap
SortedSet
Sortierte Collections
Sound
Soundausgabe
Soundbanks
source
SourceDataLine
SOUTH
SOUTHEAST
SOUTHWEST
SOUTH_EAST
SOUTH_WEST
Speichermanagement
SpinnerDateModel
SpinnerListModel
SpinnerModel
SpinnerNumberModel
SplashScreen
Sprachmerkmale
[17.3.1]
[44.6.1]
[17.2.1]
[10.3.1]
[37.3.1.1] [38.2.3.1]
[37.3.1.1] [38.2.3.1]
[38.3.2.1]
[14.2.1 (Vector)] [15.1
(Collection)] [15.6.1
(Map)] [18.2.2.2
(CharArrayWriter)]
[46.1.2]
[18.3.1] [19.3.1]
[20.2.1]
[16.3.5] [22.2.4.1]
[48.3.4]
[45.2.2]
[45.2.2]
[45.2.3]
[47.3.4.2]
[5.7]
[15.8.1] [16.5]
[15.1] [15.7.3]
[15.1] [15.7.2]
[15.7]
[48]
[1.2.3] [39.3]
[48.1]
[50.1.3]
[48.2.2]
[31.1.3] [31.2.4
(GridBagConstraints)]
[36.2.3.2
(SwingConstants)]
[31.2.4]
[31.2.4]
[36.2.3.2]
[36.2.3.2]
[4.5.2]
[37.1.5]
[37.1.5]
[37.1.5]
[37.1.5]
[36.1.2]
[1.2.1]
java.beans
java.net
java.net
java.net
java.util
java.util
javax.sound.sampled
javax.swing
javax.swing
javax.swing
javax.swing
Sprunganweisungen
SQL-2 Entry-Level
SQLException
sqrt
Stack
Standard-Font
Standard-Sichtbarkeit
Standarderweiterungen
Standardschriftarten
Standardwert
Star Seven
start
Statische Konstruktoren
statische Methoden
Statische Variable
Statuszeile des HTML-Browsers
Stelligkeit eines Operators
stop
store
Stored Procedures
Stream
StreamTokenizer
String
String einlesen
String-Literale
[6.3.3.3]
[42.1.2.3]
[42.2.1] [42.2.4]
java.sql
[17.2.3]
[14.3]
java.util
[27.5]
[8.2.1]
[13.2.1.2]
[24.2.2]
[4.2]
[1.1]
[22.2.1 (Thread)]
[36.1.5 (JApplet)]
[39.1.1 (Applet)]
[39.1.3.3 (Applet)]
[48.2.2.4 (Clip)] [48.3.4
(Sequencer)]
[2.2.2.1]
[11.2.4]
[37.4.2]
[42.2.2]
java.sql
[8.1.3.1] [8.2.2.5]
[8.3.1] [8.3.3] [8.3.4
(Statischer
Konstruktor)]
[8.3.4]
[8.3.3]
[8.3.1]
[39.1.4.2]
[5.1]
[22.2.2 (Thread)]
[36.1.5 (JApplet)]
[39.1.3.4 (Applet)]
[39.3.1 (AudioClip)]
[48.2.2.4 (Clip)] [48.3.4
(Sequencer)]
[14.4.4]
[42.1]
[18.1]
[17.1.2]
java.io
[1.2.4] [4.5.1] [11.1]
java.lang
[4.2.2.1 (Literale)]
[2.3.3]
[11.3.2.1]
String-Verkettung
[5.7.2.1] [11.2.3]
[49.2.1.1]
[5.7.2.2]
String.intern
[1.2.4] [4.5.1] [11.4]
StringBuffer
java.lang
[19.3.2.2]
java.io
StringBufferInputStream
[30.7.2]
stringFlavor
java.lang
StringIndexOutOfBoundsException [11.2.2] [11.4.4]
[18.3.2] [18.3.2.2]
java.io
StringReader
Strings
[11]
[30.7.2]
java.awt.datatransfer
StringSelection
[17.1] [31.3]
StringTokenizer
java.util
[24.3.2]
stringWidth
[18.2.2] [18.2.2.2]
StringWriter
java.io
Stub
[46.1.2]
Subqueries
[42.4.7.3]
[15.7.2]
subSet
[11.2.2] [11.3.3]
substring
[49.2.1.4]
[17.3.1]
subtract
Subtraktion
[5.2]
Suchen in Zeichenketten
[11.2.5]
SUN HotJava
[1.2.2]
[13.2.2.2]
sun.boot.class.path
[18.2.2.1] [18.3.2.1]
sun.io
[42.2.1]
sun.jdbc.odbc.JdbcOdbcDriver
SunWorld '95
[1.1]
[8.1.4.1]
super
[9.4.2]
super.clone
Superklassenkonstruktor
[8.1.4.1]
Superklassenmethoden
[8.1.3.2]
[44.6.2.3]
supportsCustomEditor
supportsTransactionIsolationLevel[42.4.3]
[22.2.3]
suspend
Swing
[1.1] [1.2.3] [23.1.2.2]
[35.1.1]
[36.2.3.2]
SwingConstants
javax.swing
[35.2.2.9]
javax.swing
SwingUtilities
[1.2.1] [6.2] [6.2.2.2]
switch
Symantec
[1.1]
Symboldarstellung
[27.4]
symmetrische Verschlsselung
[47.1.2.2]
Synchronisieren
[15.8.2 (von
Collections)] [22.4 (von
Threads)]
synchronized
Synthesizer
Synthesizer
SysexMessage
System
System-Properties
System.currentTimeMillis
System.err
System.in
System.out
System.out.print
System.out.println
SystemColor
Systemfarben
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
javax.sound.midi
javax.sound.midi
java.lang
java.awt
Titel
<<
0 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
-TTA Trustcenter
TableCellRenderer
tableChanged
TableColumn
TableColumnModel
TableModel
TableModelListener
tailSet
tan
TargetDataLine
TCP/IP
TCP/UDP
Temporre Dateien
text
TextArea
Textausgaben
TextComponent
TextEvent
TextField
textHighlight
textHighlightText
textInactiveText
TextListener
textText
texttop
textValueChanged
TEXT_CURSOR
Thawte
this
Thread
Thread-Gruppen
Thread-Synchronisation
[47.1.7]
[38.2.7]
javax.swing.table
[38.2.8]
[38.2.6]
javax.swing.table
[38.2.1] [38.2.6] javax.swing.table
[38.2.1] [38.2.5] javax.swing.table
[38.2.5]
javax.swing.event
[15.7.2]
[17.2.1]
[48.2.2]
javax.sound.sampled
[45.1.2]
[45.1.2]
[21.5.1]
[25.4]
[32.7]
java.awt
[1.2.4]
[32.6]
java.awt
[28.1.2] [28.1.6.11] java.awt.event
[32.6]
[32.6]
java.awt
[25.4]
[25.4]
[25.4]
[28.1.6.11] [32.6] java.awt.event
[25.4]
[39.2.2]
[28.1.6.11] [32.6]
[27.5.3]
[47.1.7]
[7.3.2] [7.3.6.2]
[8.1.4.1] [13.1.2]
[22.1]
java.lang
[22.5.2]
[22.4]
ThreadGroup
Threads
throw
Throwable
throws
Time
Timecodes
Times New Roman
TimesRoman
Timestamp
TimeZone
Titelleiste
TitledBorder
toArray
toCharArray
toLowerCase
Toolkit
Toolkit-Pattern
Tooltips
TOP
top
toString
toUpperCase
Track
[22.5.2]
java.lang
[22.1] [34.2.1.2
(fr Animationen)]
[37.4.3 (in Swing)]
[12.3.3]
[12.2.2] [12.2.3] java.lang
[10.2.1.1] [12.3.2]
[12.3.3]
[42.2.3]
java.sql
[48.1]
[24.2.1]
[24.2.1]
[42.2.3]
java.sql
[17.4]
java.util
[27.5]
[36.3.1.1]
javax.swing.border
[15.1]
[18.2.2.2]
[11.2.6]
[24.2.2] [34.1.1] java.awt
[10.3.4.3]
[35.1.2.1]
[35.2.2.6]
[36.3.1.2]
[36.2.3.2]
[39.2.2]
[5.7.2.1] [8.1.2]
[10.2.1.2 (WrapperKlassen)] [11.4.6
(StringBuffer)]
[12.2.2
(Throwable)]
[18.2.2.2
(CharArrayWriter)]
[18.2.2.2
(StringWriter)]
[49.2.1.3
(StringBuffer)]
[11.2.6] [18.2.3.3]
[48.3.2] [48.3.4] javax.sound.midi
[32.10]
TRACK
Tracks
[48.1]
[36.2.3.2]
TRAILING
Transaction Isolation Level
[42.4.3]
[42.4.3]
TRANSACTION_NONE
TRANSACTION_READ_COMMITTED [42.4.3]
TRANSACTION_READ_UNCOMMITTED[42.4.3]
TRANSACTION_REPEATABLE_READ [42.4.3]
[42.4.3]
TRANSACTION_SERIALIZABLE
Transaktionen
[42.4.3]
[30.7.1]
Transferable
[41.1.2] [41.2.2]
transient
[48.3.2]
Transmitter
Transparente Komponente
[36.3.1.5]
Transportschicht
[45.1.2]
[15.7.2] [15.7.3]
TreeMap
[38.3.1]
TreeModel
[38.3.4]
TreeModelEvent
[38.3.4]
TreeModelListener
[38.3.1]
TreeNode
[38.3.2.2]
TreePath
[38.3.2.2]
TreeSelectionEvent
[38.3.2.2]
TreeSelectionListener
[38.3.2.1]
TreeSelectionModel
[15.7.2]
TreeSet
Treibermanager
[42.2.1]
Trigger
[42.1]
[11.2.2] [49.2.1.4]
trim
[4.1.3] [4.2]
true
[4.2.1.1]
[12.2.1]
try
try-catch-Anweisung
[12.2.1]
[43.3.1]
TYPE
Type-Cast-Operator
[5.7.1.2]
[42.4.1]
Types
Typkonvertierungen
[4.6]
Typsicherheit
[13.3.2.1]
Typberprfungen
[4.3.1]
java.awt.datatransfer
javax.sound.midi
java.util
javax.swing.tree
javax.swing.event
javax.swing.event
javax.swing.tree
javax.swing.tree
javax.swing.event
javax.swing.event
javax.swing.tree
java.util
java.sql
Titel
<<
>
>>
-UUDP
UI Delegate
UIManager
ULAW
UML
Umrandungen
UnavailableServiceException
unbind
UND-Operator
Ungarische Notation
Ungleichheits-Operator
UnicastRemoteObject
Unicode
[45.1.2]
[35.1.2.3]
[35.2.2.9]
[48.2.3]
[3.2.4]
[35.2.2.6]
[13.5.3.2]
[46.2.4.2]
[5.4]
[2.3.5]
[5.3]
[46.2.3.1]
[4.1.1] [4.2.2]
[4.2.2.1
(EscapeSequenzen)]
Unified Modeling Language
[3.2.4]
Uniform Resource Locator
[40.1.1]
[42.4.7.3]
UNION
[32.10]
UNIT_DECREMENT
[32.10]
UNIT_INCREMENT
[45.2.1]
UnknownHostException
[26.3.2.5]
UNKNOWN_NUMBER_OF_PAGES
[18.3.3.3]
unread
[19.3.3.2]
UnsupportedLookAndFeelException[35.2.2.9]
UnsupportedOperationException [15.1]
Unterlnge
[24.3.2]
Untermens
[30.4.4]
Unvernderliche Collections
[15.8.3]
unwrapping
[43.3.1]
javax.swing
javax.jnlp
java.rmi.server
java.net
javax.swing
java.lang
UPDATE
update
updateComponentTreeUI
UPPER_LEFT_CORNER
UPPER_RIGHT_CORNER
URL
URL
URLConnection
Usenet-Newsgroups
User Datagram Protocol
user.dir
user.home
user.name
user.timezone
UTF-8-Codierung
berladen von Methoden
berlagern von Methoden
bersetzen des Quelltextes
<
>
>>
[42.2.4]
[42.4.7.2]
[34.2.4.1]
[47.1.3]
[35.2.2.9]
[38.1.1]
[38.1.1]
[40.1.1] [40.1.3
(relativer)]
[40.1.1] [45.4] java.net
[45.4]
java.net
[3.2.2.1]
[45.1.2]
[16.3.1]
[16.3.1]
[16.3.1]
[16.2.2]
[19.2.3.3]
[7.3.5]
[7.1.6] [8.1.3]
[2.2.1.3]
-V[36.3.1.8]
[36.3.1.8]
[37.3.1.1] [38.3.2.2]
[11.2.7]
[15.6.1]
[1.1]
[4.3] [6.1.3]
[7.1.5.1]
[1.2.4] [14.2] [15.2.2]
[49.2.3]
Verdecken von Variablen
[13.1.2]
Vererbung
[7.1.5.1] [8.1]
Vergleichen von Zeichenketten
[11.2.4]
[47.1.6.2]
verify
VeriSign
[47.1.7]
Verkettung
[5.7.2.1 (von Strings)]
[7.3.6.2 (von
Konstruktoren)]
Verknfung auf dem Desktop
[2.3.6.2]
Verschlsseln
[47.1.1]
Versionierung von Klassen
[41.2.1]
[31.2.4
VERTICAL
(GridBagConstraints)]
[32.10 (Scrollbar)]
[36.2.3.2
(SwingConstants)]
[38.1.1]
VERTICAL_SCROLLBAR_ALWAYS
VERTICAL_SCROLLBAR_AS_NEEDED[38.1.1]
[38.1.1]
VERTICAL_SCROLLBAR_NEVER
[38.1.2]
VERTICAL_SPLIT
Verwalten von Threads
[22.5]
validate
Validierung
valueChanged
valueOf
values
van Hoff, Arthur
Variablen
Vaterklasse
Vector
java.util
[40.1]
[21.4]
[6.2]
[8.2]
[15.5.1]
[44.4.2]
[44.4.2]
[44.4.2]
[15.6.1]
[32.11]
[32.11]
[1.2.2] [49.1]
[10.3.8]
[1.1]
[29.6]
[29.6]
[29.6]
[29.6]
[29.6]
[29.6]
[29.6]
[29.6]
[28.2.1] [29.6]
[29.6]
[29.6]
[29.6]
[29.6]
[29.6]
[29.6]
[29.6]
[29.6]
[29.6]
[29.6]
[29.6]
[29.6]
[29.6]
java.beans
java.beans
Vlissides, John
VM
Void
void
Void.TYPE
volatile
Vordefinierte Pakete
Vordergrundfarbe
Vorrangregeln
VSPACE
[10.3]
[1.2.2]
[10.2.1]
[5.7.2.6] [7.3.4] [10.2.1]
[43.3.1]
[8.2.2.8]
[13.2.1.2]
[27.5]
[5.8]
[39.2.2]
<
>
>>
java.lang
Titel
<<
DOC
API
0 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
[20.1]
[22.2.2] [22.4.3]
[16.4.2]
[34.1.1.1]
[27.5.3]
[36.1.4.1]
[22.4.3]
[42.2.3]
[1.2.3] [39.3.1]
[48.2.2]
WebRunner
[1.1]
WebStart
[13.5.1]
[16.2.2]
WEEK_OF_MONTH
[16.2.2]
WEEK_OF_YEAR
[31.2.4]
weightx
[31.2.4]
weighty
Weitergabe einer Exception
[12.3.2]
[31.1.3] [31.2.4
WEST
(GridBagConstraints)]
[36.2.3.2
(SwingConstants)]
WHEN_ANCESTOR_OF_FOCUSED_COMPONENT[36.3.1.10]
[36.3.1.10]
WHEN_FOCUSED
[36.3.1.10]
WHEN_IN_FOCUSED_WINDOW
[42.4.7.3]
WHERE
[1.2.1] [6.3.1]
while
[6.3.3.3]
[25.2]
white
[39.2.1]
WIDTH
Wiederverwendung
[7.1.4]
[25.4]
window
[23.2.1] [27.1]
java.awt
Window
Window-Ereignisse
[28.1.6.7]
Window-Events
[29.1]
[28.1.6.7] [29.1]
windowActivated
[28.1.5] [29.1]
[25.4]
[28.1.6.7] [29.1]
[23.2.4] [28.1.6.7]
[29.1]
[2.2.2.2] [23.2.4]
[28.1.6.7] [29.1]
[28.1.6.7] [29.1]
[28.1.6.7] [29.1]
[28.1.6.7] [29.1]
[23.2.4] [28.1.6.7]
[29.1]
[28.1.6.7] [29.1]
[25.4]
[17.2.1]
[19.2.3.4]
[10.2]
[18.2.1] [19.2.1]
[20.4]
[20.4] [41.1.2]
[20.4] [41.1.2]
[20.4] [41.1.2]
[20.4] [41.1.2]
[20.4] [41.1.2]
[20.4] [41.1.2]
[20.4] [41.1.2]
[20.4] [41.1.2]
[20.4] [41.1.2]
[41.1.2]
[18.2.1] [49.2.4.1]
[20.4] [41.1.2]
[18.2.2.2]
[19.2.3.3] [20.4]
[41.1.2]
WindowAdapter
windowBorder
windowClosed
windowClosing
WindowClosingAdapter
windowDeactivated
windowDeiconified
WindowEvent
windowIconified
WindowListener
windowOpened
windowText
Winkelfunktionen
winzip
Wrapper-Klassen
write
write-Methoden
writeBoolean
writeByte
writeBytes
writeChar
writeChars
writeDouble
writeFloat
writeInt
writeLong
writeObject
Writer
writeShort
writeTo
writeUTF
Titel
<<
>
>>
DOC
API
java.awt.event
java.awt.event
java.awt.event
java.io
-XX.509-Zertifikat
XML-Dateien
XML-Untersttzung
xor
[47.1.7]
[13.2.1.2]
[1.1]
[14.5.2]
Handbuch der Java-Programmierung, 3. Auflage,
Addison Wesley, Version 3.0
1998-2002 Guido Krger,
API
http://www.javabuch.de
<
>
>>
-Y[16.2.2]
[25.2]
[36.1.4.2]
[36.1.4.2]
[45.2.3]
YEAR
yellow
YES_NO_CANCEL_OPTION
YES_NO_OPTION
yield
<
>
>>
-ZZapfDingbats
Zeichenextraktion
Zeichenketten
Zeichentasten
Zeichentyp
Zeilenabstand
Zeitzonen
Zeitzonenangabe
Zero-Knowledge Proof
Zertifikat
ZipEntry
ZipInputStream
ZipOutputStream
ZONE_OFFSET
Zufallszahlen
Zuweisung
Zuweisungsoperatoren
Zwischenablage
[24.2.2]
[11.2.2]
[1.2.4] [11.1]
[29.6]
[4.2.2]
[24.3.2]
[16.2]
[16.2.2]
[47.1.3.2]
[47.1.7]
[19.2.3.4]
[19.3.3.5]
[19.2.3.4]
[16.2.2]
[1.2.4] [16.1.1]
[4.3.1]
[5.6]
[30.7]
<
>
>>
java.util.zip
java.util.zip
java.util.zip
Titel
<<
>
>>
API
Falls Sie die ersten Gehversuche in Java machen wollen, ohne erst viele Grundlagen
lernen zu mssen, oder wenn Sie einfach nur daran interessiert sind, mglichst schnell
Ihr erstes Java-Programm auszufhren, dann sollten Sie den vorliegenden Abschnitt
lesen. Sie erfahren hier in komprimierter Form, wie Sie ein einfaches Programm
erstellen und mit den Werkzeugen des JDK bersetzen und ausfhren. Zustzlich gibt
es einige Hinweise, um einfache Ein- und Ausgaben durchzufhren.
/* Hello.java */
Hello.java
Achten Sie bei der Vergabe der Datei- und Klassennamen auf korrekte
Warnung
Gro-/Kleinschreibung. Sowohl der Compiler als auch das Laufzeitsystem
erwarten, da die Hauptklasse einer Quelldatei genauso geschrieben wird
wie der Name der Datei, in der sie sich befindet. Der Dateiname unserer
Beispielklasse Hello lautet daher Hello.java und nicht hello.java,
HELLO.JAVA oder HELLO.JAV. Erstellen Sie die Datei nicht auf
Systemen oder Laufwerken (beispielsweise im Netz), die keine langen
Dateinamen untersttzen. Der Compiler wrde die Datei in diesem Fall
nicht finden.
Sie knnen zum Editieren beispielsweise notepad oder edit (unter Windows) oder vi
oder Emacs (gibt es fr UNIX und Windows) verwenden. Ein guter Windows-Editor,
der fast ebensoviel kann wie seine professionellen (und sehr viel teureren) Brder, ist
TextPad von Helios Software Solutions. Eine Testversion kann von
http://www.textpad.com heruntergeladen werden, die Vollversion ist fr wenig Geld zu
haben. Wer ein wenig Einarbeitungszeit, Konfigurationsaufwand und das Erlernen von
Grundkenntnissen in LISP nicht scheut, der sollte sich Emacs nher ansehen.
Warnung
Falls Sie es trotz aller Bemhungen bis zu diesem Punkt noch nicht geschafft
haben, das Beispiel zu bersetzen und zu starten, kann Ihnen mglicherweise
Abschnitt 2.3.7 weiterhelfen. Er beschreibt einige hufig vorkommende Fehler,
gibt Tips zu ihrer Diagnose und zeigt, wie man sie behebt.
Tip
Titel
<<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Erstellen Sie die Quelldatei mit einem ASCII-Editor. Verwenden Sie keine Textverarbeitung, denn
die wrde dem Code unverstndliche Steuerzeichen hinzufgen.
Achten Sie darauf, da die Datei die Erweiterung .java trgt, und geben Sie ihr exakt denselben
Namen wie der Hauptklasse.
bersetzen Sie das Programm mit dem Compiler javac, und geben Sie dabei den Dateinamen als
Argument an. Um alle Java-Dateien in einem Verzeichnis zu bersetzen, knnen Sie auch *.java als
Argument verwenden.
Falls im Quelltext syntaktische Fehler enthalten sind, meldet der Compiler sie zusammen mit der
Zeilennummer und der Quelldatei, in der sie aufgetreten sind. Am Anfang passieren leicht Fehler wie
vergessene oder berflssige Klammern oder Semikolons. Bedenken Sie auch, da alle
Schlsselwrter kleingeschrieben werden und bei Bezeichnern streng zwischen Gro- und
Kleinschreibung unterschieden wird.
Mitunter gibt es Fehlermeldungen, weil Code nicht erreicht werden kann oder Variablen nicht
initialisiert werden. Das ist in Java nicht erlaubt und wird vom Compiler mittels Datenfluanalyse
berprft. Stellen Sie den Code in einem solchen Fall geeignet um.
Ignorieren Sie zunchst Warnungen, bei denen der Compiler die Verwendung von Klassen oder
Methoden, die als deprecated markiert wurden, kritisiert. Derartige Programmteile funktionieren
zwar meist noch, sind aber veraltet und sollten eigentlich nicht mehr verwendet werden.
Gibt der Compiler gar keine Meldung aus, wurde das Programm erfolgreich bersetzt.
Sie knnen das Programm nun mit dem Java-Interpreter java starten. Geben Sie als Argument nur
den Klassennamen ohne die Erweiterung .java oder .class an.
Wenn es beim Aufruf des Interpreters eine Fehlermeldung der Art NoClassDefFoundError
gibt, liegt das fast immer daran, da der Name der Klasse falsch geschrieben wurde oder da keine
oder eine falsch benannte main-Methode vorhanden ist. Beachten Sie, da main genauso deklariert
wird wie beispielsweise in Listing 2.1.
/* Listing0202.java */
Listing0202.java
Leider ist es etwas komplizierter, Daten zeichenweise von der Tastatur zu lesen. Zwar steht ein
vordefinierter Eingabe-Stream System.in zur Verfgung. Er ist aber nicht in der Lage, die eingelesenen
Zeichen in primitive Datentypen zu konvertieren. Statt dessen mu zunchst eine Instanz der Klasse
InputStreamReader und daraus ein BufferedReader erzeugt werden. Dieser kann dann dazu
verwendet werden, die Eingabe zeilenweise zu lesen und das Ergebnis in einen primitiven Typ
umzuwandeln. Weitere Informationen zur streambasierten Ein-/Ausgabe sind in Kapitel 18 und Kapitel 19
zu finden.
Das folgende Listing zeigt ein Programm, das zwei Ganzzahlen einliest, sie zusammenzhlt und das
Ergebnis auf dem Bildschirm ausgibt:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
/* Listing0203.java */
Listing0203.java
import java.io.*;
public class Listing0203
{
public static void main(String[] args)
throws IOException
{
int a, b, c;
BufferedReader din = new BufferedReader(
new InputStreamReader(System.in));
System.out.println("Bitte a eingeben: ");
a = Integer.parseInt(din.readLine());
System.out.println("Bitte b eingeben: ");
b = Integer.parseInt(din.readLine());
c = a + b;
System.out.println("a+b="+c);
}
}
Das Ergebnis von din.readLine ist ein String, der den Inhalt der Eingabezeile enthlt. Sollen
keine numerischen Datenwerte, sondern Zeichenketten eingelesen werden, kann der Rckgabewert
auch direkt verwendet werden.
Tip
import java.io.*;
public class Listing0204
{
public static void main(String[] args)
{
//Hier steht der Methodenrumpf
}
}
001
002
003
004
005
006
007
008
009
if (cmd.equals("Grer")) {
d.height *= 1.05;
d.width *= 1.05;
} else if (cmd.equals("Kleiner")) {
d.height *= 0.95;
d.width *= 0.95;
} else {
x = 10;
}
2.3.5 Namenskonventionen
Wie in allen Programmiersprachen, gibt es auch in Java Konventionen fr die Vergabe von Namen. Sie sind
zwar nicht zwingend erforderlich, erleichtern aber das Verstndnis der Quelltexte ungemein und sollten
daher unbedingt eingehalten werden. Die wichtigsten sind:
Klassennamen beginnen stets mit einem Grobuchstaben. Beispiele sind String, Vector oder
Hello. Besteht ein Klassenname aus mehreren Silben, knnen zur Steigerung der bersichtlichkeit
auch die Folgesilben mit einem Grobuchstaben beginnen. Beispiele dafr wren HelloWorld,
KeyAdapter oder NoSuchMethodException. Klassennamen, die nur aus Grobuchstaben
bestehen, sind unblich.
Methodennamen beginnen mit einem Kleinbuchstaben. Haben sie mehrere Silben, ist die erste oft ein
Verb. Weitere beginnen mit einem Grobuchstaben. Beispiele sind println,
hasMoreElements oder isEnabled.
Paketnamen bestehen ausschlielich aus Kleinbuchstaben. Beispiele sind java.lang,
javax.swing.event oder com.solution42.util (mehrteilige Paketnamen werden durch
Punkte separiert).
Fr Variablennamen gelten dieselben Konventionen wie fr Methoden. Es ist unblich,
Membervariablen mit einem Prfix wie z.B. "m_" zu versehen. Auch die Verwendung der
ungarischen Notation, bei der Variablen datentypbezogene Namensprfixe erhalten, ist in Java nicht
blich.
Weitere Konventionen werden, wenn erforderlich, im Buch beschrieben.
2.3.7 Troubleshooting
Nachfolgend noch einmal eine kurze Zusammenfassung der hufigsten Probleme beim Erstellen, bersetzen
und Starten eines Java-Programms, inklusive Beschreibung, wie man sie behebt oder umgeht.
Was ist eine DOS-Box und wie startet man sie?
Alle Werkzeuge des JDK arbeiten kommandozeilenorientiert. Anders als typische GUI-Programme wie
Adobe Photoshop, Microsoft Excel oder Netscape Navigator besitzen sie also keine grafische Oberflche,
sondern werden aus einer DOS-Box (bzw. einer UNIX-Shell) heraus aufgerufen. Diese wird unter
Windows-Betriebsystemen als MS-DOS-Eingabeaufforderung bezeichnet und ist meist im Startmen
(unter Programme) zu finden. In der DOS-Box werden Befehle und ihre Parameter ber die Tastatur
eingegeben und starten so die zugehrigen Programme. Um in einer DOS-Box zurecht zu kommen, sollte
man deren Kommandosyntax und die wichtigsten Befehle zum Starten von Programmen und fr Datei- und
Verzeichnisoperationen kennen. Beides wird in unzhligen Bchern und (zum Teil) in den Hilfedateien des
Betriebssystems beschrieben.
Das UNIX-Pendant zu einer DOS-Box ist die Shell. Auch sie dient primr dazu,
Hinweis
Benutzerkommandos zu empfangen und auszufhren. UNIX-Shells sind wesentlich mchtiger als
DOS-Boxen unter Windows, und im Laufe der Jahre haben sich verschiedene Varianten
entwickelt (Bourne-Shell, C-Shell, Korn-Shell, BASH etc.). Obwohl viele der elementaren ShellKommandos den DOS-Box-Pendants gleichen oder ihnen recht hnlich sind, lassen sich mit ihrer
detaillierten Beschreibung leicht ganze Bcher fllen. Das Kommando man liefert eine
Beschreibung der Shell und aller zugehrigen UNIX-Kommandos.
Wer nicht kommandozeilenorientiert arbeiten will, kann statt der im Buch beschriebenen JDK-Werkzeuge
natrlich auch eine integrierte Entwicklungsumgebung zum Erlernen der Java-Programmierung verwenden
(z.B. Borland JBuilder). In diesem Fall stehen Editor, Compiler, Interpreter und alle anderen Werkzeuge
unter einer gemeinsamen und einheitlichen grafischen Oberflche zur Verfgung und knnen auf
Knopfdruck gestartet werden. Der Nachteil dieser integrierten Werkzeuge ist allerdings eine etwas hhere
Einarbeitungszeit, denn von der Vielzahl der Mglichkeiten wird man zunchst erschlagen. Zudem sind
wichtige Techniken und Arbeitsschritte oftmals hinter Hilfsprogrammen und Assistenten verborgen und
erschweren so das Verstndnis grundlegender Konzepte.
java luft, javac aber nicht
Wenn unter Windows der Java-Interpreter java gestartet werden kann, beim Aufruf des Compilers javac
aber eine Fehlermeldung kommt, liegt das meist daran, da der PATH nicht korrekt gesetzt wurde. Whrend
die JDK-Installation den Interpreter unter anderem auch in Verzeichnisse kopiert, auf die der PATH bereits
verweist (so da dieser ohne weiteres Zutun aus jedem Verzeichnis aufgerufen werden kann), ist das fr den
Compiler und die brigen JDK-Werkzeuge nicht der Fall. Diese knnen nur dann ohne vorangestellten Pfad
aufgerufen werden, wenn der PATH auf das Unterverzeichnis bin des Installationsverzeichnisses verweist.
Bei der in diesem Buch angenommen Standard-Installation mu die PATH-Variable also das Verzeichnis
c:\jdk1.4\bin enthalten.
Was sind eigentlich Umgebungsvariablen?
Einer der Standardmechanismen zur Konfiguration von Programmen besteht darin, Umgebungsvariablen zu
setzen. Diese werden vom Anwender (meist innerhalb einer DOS-Box bzw. Shell mit Hilfe des setKommandos) gesetzt und vom Programm gelesen und zur Konfiguration verwendet. Die beiden fr das JDK
wichtigsten Umgebungsvariablen sind PATH und CLASSPATH. ber erstere wird dem Betriebssystem
mitgeteilt, wo es nach ausfhrbaren Programmen suchen soll, also etwa nach dem Compiler oder Interpreter
des JDK. Die Umgebungsvariable CLASSPATH (umgangssprachlich der CLASSPATH genannt) ist JDKspezifisch. Sie zeigt dem Compiler und Interpreter an, in welchen Verzeichnissen er nach Klassendateien
suchen soll. Im Gegensatz zu PATH kann der CLASSPATH bei der Standardinstallation eines aktuellen
JDKs (Version 1.3 oder spter) ungesetzt bleiben. Compiler und Interpreter finden ihr eigenes
Laufzeitsystem automatisch, und die selbst geschriebenen Klassen werden im aktuellen Verzeichnis gesucht.
Da es zu umstndlich ist, hufig bentigte Umgebungsvariablen nach jedem Aufruf einer DOS-Box neu zu
setzen, bieten alle Betriebssysteme die Mglichkeit, sie permanent festzulegen. Abschnitt 2.1.2 erlutert dies
am Beispiel der PATH-Variable.
Der Compiler javac arbeitet nicht
Wenn der Compiler javac die .java-Datei nicht findet, liegt dies in aller Regel daran, da sie nicht
existiert. Der Compiler unterscheidet zwischen Gro- und Kleinschreibung; die Datei Test1.java ist somit
streng von test1.java oder TEST1.JAVA zu unterscheiden. Zudem mu die Dateierweiterung .java lauten,
denn andere Erweiterungen werden nicht akzeptiert. Hat der Editor flschlicherweise die Erweiterung .txt
angehngt, wird die Datei vom Compiler nicht gefunden. Mitunter kommt es auch vor, da Beispieldateien
beim Kopieren oder Entpacken in das MS-DOS-8.3-Format konvertiert werden. Die Datei Test1.java wrde
dann TEST1.JAV heien und vom Compiler nicht akzeptiert werden.
Wichtig ist auch, da der Compiler korrekt aufgerufen wird. Anders als beim Interpreter wird die
Dateierweiterung stets angegeben. Ein Aufruf von javac Test1 wird also nicht funktionieren, javac
Test1.java dagegen sehr wohl. Schlielich ist zu beachten, da die zu bersetzende Klasse denselben
Basisnamen hat wie die Datei, in der sie definiert wurde. In der Datei Test1.java mu also eine ffentliche
Klasse mit dem Namen Test1 definiert worden sein.
Der Interpreter java arbeitet nicht korrekt
Der hufigste Fehler beim Aufruf des Interpreters ist ein NoClassDefFoundError. Ein trivialer Fehler
besteht darin, da der Interpreter falsch aufgerufen wurde. Im Gegensatz zum Compiler mchte er keinen
Dateinamen als Argument bergeben bekommen, sondern einen Klassennamen. Folglich darf die
Erweiterung .java oder .class nicht angegeben werden. Die in die Datei Test1.class bersetzte Klasse Test1
wird also durch Aufruf von java Test1 gestartet, und nicht durch java Test1.class.
Das funktioniert allerdings nur, und das ist auch schon die zweite Fehlerquelle, wenn Test1 auch
tatschlich eine Methode public static void main enthlt. Diese versucht nmlich der Interpreter
zu starten, und wenn sie nicht vorhanden ist, gibt es obige Fehlermeldung.
Ist der Aufruf korrekt und die main-Methode ebenso vorhanden wie die ntige Klassendatei, kann es
trotzdem vorkommen, da der Interpreter die Klasse nicht starten kann. Das liegt dann meist daran, da
etwas mit dem CLASSPATH nicht stimmt. Der CLASSPATH ist eine Umgebungsvariable, die eine Liste von
Verzeichnissen und Archivdateien enthlt, die vom Interpreter der Reihe nach durchsucht werden, um die
auszufhrenden Klassen zu finden. Die genaue Interpretation des CLASSPATH hat sich ber die Jahre etwas
gendert, aber bei aktuellen JDK-Versionen kann sie wie folgt zusammengefat werden:
Ist gar kein CLASSPATH angegeben, sucht der Interpreter im aktuellen Verzeichnis (bzw. bei Klassen in
Unterpaketen in darin befindlichen Unterverzeichnissen) nach der Klassendatei. Ist dagegen eine
Umgebungsvariable CLASSPATH vorhanden, durchsucht der Interpreter dessen Elemente der Reihe nach.
Befindet sich darin nicht das aktuelle Verzeichnis (entweder absolut oder als . angegeben), so werden
auch keine Klassen gefunden, die im aktuellen Verzeichnis liegen. Fr unsere ersten Versuche ist es bei
aktuellen JDKs also am besten, den CLASSPATH gar nicht zu setzen und alle Klassen in das aktuelle
Verzeichnis zu legen.
Mitunter wird der CLASSPATH von anderen installierten Java-Programmen verndert und sorgt so
unbeabsichtigt dafr, da die eigenen Klassen nicht mehr gefunden werden. Das ist zwar kein guter Stil,
kommt in der Praxis aber dennoch manchmal vor. In einem solchen Fall mu der CLASSPATH whrend der
eigenen Experimente zurckgesetzt oder so gendert werden, da das aktuelle Verzeichnis (bzw. das mit
den eigenen Klassendateien) darin enthalten ist.
Hinweis
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt Suchen
<
>
Index
>>
DOC
API
Beschreibung
General Info
API &
Language
Guide To
Features
Tool Docs
Im linken oberen Fenster findet sich eine Liste aller Pakete des JDK. Ein Klick auf eines der
Pakete stellt die Liste der zugehrigen Klassen im linken unteren Fenster dar.
Das linke untere Fenster zeigt alle Klassen, Interfaces und Exceptions des aktuellen Pakets.
Wird ein Element angeklickt, so erscheint auf der rechten Seite seine Beschreibung.
Auf der rechten Seite wird eine einzelne Klasse oder ein Interface beschrieben. Am oberen
Rand werden Vererbungsinformationen angezeigt, darunter folgt die allgemeine
Klassenbeschreibung. Anschlieend kommt eine Linkleiste fr Konstanten, Variablen,
Konstruktoren und Methoden. Ein Klick auf eines dieser Elemente verzweigt zu dessen
detaillierter Beschreibung. Abbildung 3.1 zeigt die Beschreibung der Methode addElement
der Klasse Vector des Pakets java.util.
Die API-Dokumentation ersetzt zwar nicht die konzeptionelle Beschreibung der Java-Themen (das
ist Aufgabe dieses Buchs), als Nachschlagewerk zu Details der Klassenbibliothek ist sie jedoch
unentbehrlich. Der Umgang mit ihr sollte jedem Java-Entwickler in Fleisch und Blut bergehen.
Manchmal bieten die Hilfesysteme der integrierten Entwicklungsumgebungen sogar noch
komfortablere Mglichkeiten, auf die Dokumentation von Klassen und Methoden zuzugreifen.
Durch die alphabetische Anordnung der Pakete mu im linken oberen Fenster sehr hufig
Tip
gescrollt werden. Einfacher wird es, wenn die wichtigsten und am hufigsten bentigten
Pakete in der Liste nach oben verschoben werden. Wer rudimentre HTML-Kenntnisse hat,
kann dazu die Datei c:\jdk1.4\docs\api\overview-frame.html mit einem Texteditor ffnen und
die gewnschten Zeilen editieren. Ntzlich wre es beispielsweise, die Pakete java.lang,
java.io, java.util, java.awt, java.awt.event, javax.swing, java.sql
und java.net an den Anfang der Liste zu setzen.
Inhalt
news:comp.lang.java.3d
news:comp.lang.java.advocacy
news:comp.lang.java.announce
news:comp.lang.java.api
news:comp.lang.java.beans
news:comp.lang.java.corba
news:comp.lang.java.databases
news:comp.lang.java.gui
news:comp.lang.java.help
news:comp.lang.java.machine
news:comp.lang.java.misc
news:comp.lang.java.setup
news:comp.lang.javascript
news:de.comp.lang.java
Dokumentationen geht. Hier gibt es auch Links zu weiteren Meta-Ressourcen, die hier nicht erwhnt
werden. Ein direkter Link auf die von SUN fr Java zur Verfgung gestellten
Entwicklungsumgebungen ist http://java.sun.com/products/. Unter der Adresse
http://java.sun.com/j2se/1.4/ gibt es alles rund um die aktuelle Java-Version 1.4.
Eine wichtige Adresse fr Entwickler ist auch die der Java Developer's Connection (JDC) unter
http://developer.java.sun.com/. Diese Seiten werden von SUN gepflegt, um eine zentrale
Anlaufstelle fr Java-Entwickler zur Verfgung zu stellen. Es gibt dort Diskussionsforen,
Schulungsangebote, weitere Software und jede Menge ntzliche Informationen. Wichtiges Organ
der JDC ist der JDC-Newsletter. Dabei handelt es sich um einen Newsletter, der per E-Mail
regelmig ber aktuelle Neuerungen informiert. Der Zutritt zur JDC ist kostenlos, erfordert aber
das Ausfllen einer Registrierungsseite.
Mitunter ebenfalls wichtig ist die - etwas euphemistisch als Bug Parade bezeichnete Fehlerdatenbank des Java Development Kit. Hier werden alle bekannten Fehler gelistet und mit
Beschreibung, Behebungs-Status und mglichen Workarounds beschrieben. Die Bug Parade kann
unter http://developer.java.sun.com/developer/bugParade/index.jshtml erreicht und online nach
Fehlern durchsucht werden. Registrierte Entwickler knnen neue Fehler eintragen oder zu bekannten
Fehlern ihre Stimme abgegeben - in der Hoffnung, dadurch die Behebung zu beschleunigen.
Auch in den groen Web-Verzeichnissen gibt es meist eigene Rubriken fr die Programmiersprache
Java. Yahoo stellt diese beispielsweise unter
http://www.yahoo.com/Computers_and_Internet/Programming_Languages/Java zur Verfgung, und
bei Google lautet die Adresse
http://directory.google.com/Top/Computers/Programming/Languages/Java/.
In der Anfangszeit der Java-Entwicklung gab es eine ganze Reihe von Sites, die Unmengen an freien
Java-Tools, -Applets und -Programmen oder frei zugnglichen Quellcode anboten. Viele von ihnen
sind mittlerweile verschwunden, in einem anderen Dienst aufgegangen oder wurden
kommerzialisiert. Einige Anlaufstellen sind http://www.componentsource.com/,
http://www.jguru.com/, http://www.sunsource.net/, http://www.jars.com/ oder das von SUN
verwaltete Verzeichnis von Java-Lsungen http://industry.java.sun.com/solutions/. Frei zugngliche
Java-Software und -Projekte gibt es unter anderem auf
http://www.gnu.org/software/software.html#Java, http://jakarta.apache.org/, http://sourceforge.net/
oder http://freshmeat.net/.
JavaLobby ist ein Zusammenschlu von Java-Enthusiasten, die das Ziel verfolgen, die Sprache zu
verbreiten und fr ein 100 % Pure Java einzutreten. Die Homepage unter
http://www.javalobby.org/ bietet auch eine ganze Menge Verweise zu Java-Ressourcen und
interessante Artikel rund um Java. Unter der Adresse http://www.apl.jhu.edu/~hall/java/ verwaltet
Marty Hall von der Johns Hopkins University eine interessante Liste von Java-Ressourcen mit Links
zu FAQs, weiteren Dokumentationen, Beispielanwendungen, Entwicklungsumgebungen,
Klassenbibliotheken und vielem anderen mehr.
FAQs
Eine Liste von Java-FAQs gibt es unter http://www.faqs.org/faqs/computer-lang/java/. Dort wird
auch auf das sehr umfangreiche, aber nicht mehr ganz aktuelle FAQ von Peter van der Linden
verwiesen, das unter http://www.afu.com/javafaq.html gefunden werden kann. Eine weitere Liste
von Java-FAQs gibt es unter http://www.links2go.com/topic/java_faqs.
Von Roedy Green gibt es unter http://mindprod.com/jgloss.html ein Glossar, in dem viele Begriffe
und Konzepte rund um Java erlutert werden.
Von SUN selbst gibt es ebenfalls ein FAQ, das unter http://www.javasoft.com/products/jdk/faq.html
zu finden ist. Dort sind auch einige Metainformationen und firmenbezogene Informationen ber Java
zu finden.
Einige FAQs zur deutschen Java-Newsgroup sind unter http://www.dclj.de/faq.html zu finden.
Online-Magazine und Dokumentationen
Unter http://www.sys-con.com/java/ ist die Onlineversion des Java Developer's Journal zu finden.
Unter http://www.javaworld.com/ findet sich die Java World, und die Adresse fr den Java Report
Online ist http://www.javareport.com/. Auch das nicht sprachgebundene Dr. Dobb's Journal hat eine
Java-Rubrik unter http://www.ddj.com/topics/java/. Das auch in deutscher Sprache erhltliche Java
Spektrum ist unter http://www.sigs-datacom.de/sd/publications/js/index.htm zu finden. Online steht
es allerdings nur in Auszgen zur Verfgung.
Auf dem SUN-Server gibt es weitere Dokumentationen zu Java. Auf http://java.sun.com/docs/books/
wird die Java Series vorgestellt, in der SUN zusammen mit Addison-Wesley eine groe Zahl von
Java-Bchern publiziert hat. Unter http://java.sun.com/docs/books/jls/index.html ist die
Sprachspezifikation zu finden, und die Beschreibung der virtuellen Maschine findet sich unter
http://java.sun.com/docs/books/vmspec/index.html.
Alle Seiten enthalten am oberen und unteren Ende eine Navigationsleiste, mit der folgende
Seiten direkt angesprungen werden knnen:
"Titel": Titelseite
"Inhalt": Globales Inhaltsverzeichnis
"Suchen": Suchfunktion
"Index": Index
"DOC": Die Hauptseite der JDK-Dokumentation
"<<": Voriges Kapitel
[t]: Titelseite
[Z]: Globales Inhaltsverzeichnis
[S]: Suchfunktion
[i]: Index
[p]: API-Dokumentation
[d]: JDK-Dokumentation
[h]: Voriges Kapitel
[j]: Voriger Abschnitt
[k]: Nchster Abschnitt
[l]: Nchstes Kapitel
[a]: Seitenanfang
[e]: Seitenende
Auf der Hauptseite des Index kann der gewnschte Indexbuchstabe auch ber die Tastatur
eingegeben werden. Die zuvor beschriebenen Krzel sind auf dieser Seite auer Kraft.
Einsatz von JavaScript
Die HTML-Ausgabe enthlt hauptschlich HTML-3.2-Code. Cascading Style Sheets oder hnliche
Erweiterungen wurden nicht verwendet. Ein Java-Applet wird nur fr die Suchfunktion verwendet,
und der Einsatz von JavaScript wurde so gering wie mglich gehalten. Die HTML-Ausgabe ist auch
verwendbar, wenn JavaScript im Browser deaktiviert ist oder nicht untersttzt wird. In diesem Fall
gibt es einige kleine Einschrnkungen:
Ist JavaScript aktiviert, kann die JDK-Dokumentation an einer beliebigen Stelle liegen. Damit Tip
die Links der HTML-Ausgabe korrekt funktionieren, mu in diesem Fall an der im
Unterverzeichnis html liegenden JavaScript-Datei hjp3lib.js eine kleine nderung
vorgenommen werden. In den Variablen jdkdocs und apidocs in den Zeilen 27 und 28
mu nmlich der JDK-Dokumentationspfad korrekt gesetzt sein. Er ist standardmig auf
c:\jdk1.4\docs\ bzw. c:\jdk1.4\docs\api\ eingestellt (passend fr eine WindowsStandardinstallation) und sollte der eigenen Installation entsprechend verndert werden. Wenn
alles korrekt eingestellt ist, mten die Schaltflchen "DOC" und "API" am Anfang und Ende
jeder Seite auf die Startseite der JDK- und API-Dokumentation verzweigen.
Klassen und Methoden knnen in Beziehungen zueinander stehen. Diese werden durch
Verbindungslinien grafisch dargestellt. Bei einer Vererbungsbeziehung wird ein Pfeil von der
abgeleiteten zur Basisklasse gezogen. Die Basisklasse steht in aller Regel ber der abgeleiteten
Klasse. Erben mehrere Klassen von einer Basisklasse, werden die Pfeile zur besseren
bersichtlichkeit zusammengefat. Die Implementierung eines Interfaces wird analog dargestellt,
allerdings mit gestrichelten Linien.
Aggregation und Komposition wird durch eine Verbindungslinine dargestellt, die auf der Seite mit
dem Container eine kleine Raute trgt. Wir unterscheiden dabei nicht zwischen den beiden
Varianten. Aufrufbeziehungen werden als gestrichelte Pfeile mit Beschriftung dargestellt. Der Text
beschreibt die Bedeutung des Aufrufs.
Abbildung 3.4 zeigt eine Basisklasse AbstractComponent, die das Interface Component
implementiert. Aus AbstractComponent sind die drei Klassen ConcreteComponent1,
ConcreteComponent2 und Container abgeleitet. Container ist Besitzer einer Sammlung
von AbstractComponent-Objekten. ConcreteComponent2 verwendet die Klasse Cache:
Titel
<<
Inhalt Suchen
<
>
Index
DOC
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
4.6 Typkonvertierungen
4.6 Typkonvertierungen
4.6.1 Standardkonvertierungen
4.6.2 Vorzeichenlose Bytes
4.6.1 Standardkonvertierungen
Es gibt diverse Konvertierungen zwischen unterschiedlichen Datentypen in Java. Diese werden einerseits
vom Compiler automatisch vorgenommen, beispielsweise bei der Auswertung von numerischen
Ausdrcken. Andererseits knnen sie verwendet werden, um mit Hilfe des Type-Cast-Operators (siehe
Kapitel 5) eigene Konvertierungen vorzunehmen.
Java unterscheidet prinzipiell zwischen erweiternden und einschrnkenden Konvertierungen und diese
noch einmal nach primitiven Typen und Referenztypen. Zunchst zu den Referenztypen:
Als erweiternde Konvertierung eines Referenztyps T wird vor allem die Umwandlung eines
Objekts vom Typ T in eine seiner Vaterklassen angesehen.
Als einschrnkende Konvertierung eines Referenztyps T wird vor allem die Umwandlung eines
Objekts vom Typ T in eine der aus T abgeleiteten Klassen angesehen.
Daneben gibt es noch eine ganze Reihe weiterer Regeln zur Definition von erweiternden und
einschrnkenden Konvertierungen von Referenztypen. Die Bedeutung von Vaterklassen und den daraus
abgeleiteten Unterklassen wird in Kapitel 8 ausfhrlich erlutert.
Konvertierungen auf primitiven Datentypen sind etwas aufwendiger zu erklren. Wir benutzen dazu
Abbildung 4.1:
Bei einer Zuweisung, wenn der Typ der Variablen und des zugewiesenen Ausdrucks nicht
identisch ist.
Bei der Auswertung eines arithmetischen Ausdrucks, wenn Operanden unterschiedlich typisiert
sind.
Beim Aufruf einer Methode, falls die Typen der aktuellen Parameter nicht mit denen der formalen
Parameter bereinstimmen.
Es ist daher beispielsweise ohne weiteres mglich, ein short und ein int gemeinsam in einem
Additionsausdruck zu verwenden, da ein short mit Hilfe einer erweiternden Konvertierung in ein int
verwandelt werden kann. Ebenso ist es mglich, ein char als Array-Index zu verwenden, da es
erweiternd in ein int konvertiert werden kann. Auch die Arithmetik in Ausdrcken, die sowohl integrale
als auch Fliekommawerte enthalten, ist mglich, da der Compiler alle integralen Parameter erweiternd in
Fliekommawerte umwandeln kann.
Es ist dagegen nicht ohne weiteres mglich, einer int-Variablen einen double-Wert zuzuweisen. Die
hierzu erforderliche einschrnkende Konvertierung nimmt der Compiler nicht selbst vor; sie kann
allerdings mit Hilfe des Type-Cast-Operators manuell durchgefhrt werden. Auch die Verwendung eines
long als Array-Index verbietet sich aus diesem Grund.
Bei den einschrnkenden Konvertierungen kann es passieren, da ein Wert verflscht wird, da Warnung
der Wertebereich des Zielobjekts kleiner ist. Aber auch erweiternde Konvertierungen sind
nicht immer gefahrlos mglich. So kann zwar beispielsweise ein float mindestens genauso
groe Werte aufnehmen wie ein long. Seine Genauigkeit ist aber auf ca. 8 Stellen
beschrnkt, und daher knnen grere Ganzzahlen (z.B. 1000000123) nicht mehr mit voller
Genauigkeit dargestellt werden.
int verarbeitet wird. Dafr ist ausschlielich die Anwendung selbst verantwortlich.
Das folgende Listing zeigt eine einfache Klasse ByteKit, mit der zwischen beiden Darstellungen
gewechselt werden kann:
ByteKit.java
001 /**
002 * ByteKit
003 *
004 * Einfache Klasse zur Umwandlung zwischen int, char und
005 * vorzeichenlosen Bytes.
006 */
007 public class ByteKit
008 {
009
/**
010
* Wandelt value (0 <= value <= 255) in ein byte um.
011
*/
012
public static byte fromUnsignedInt(int value)
013
{
014
return (byte)value;
015
}
016
017
/**
018
* Wandelt c in ein byte um. Das High-Byte wird ignoriert.
019
*/
020
public static byte fromChar(char c)
021
{
022
return (byte)(c & 0xFF);
023
}
024
025
/**
026
* Betrachtet value als vorzeichenloses byte und wandelt
027
* es in eine Ganzzahl im Bereich 0..255 um.
028
*/
029
public static int toUnsignedInt(byte value)
030
{
031
return (value & 0x7F) + (value < 0 ? 128 : 0);
032
}
033
034
/**
035
* Betrachtet value als vorzeichenloses byte und wandelt
036
* es in ein Unicode-Zeichen mit High-Byte 0 um.
037
*/
038
public static char toChar(byte value)
039
{
040
return (char)toUnsignedInt(value);
041
}
042
043
/**
044
* Liefert die Binaerdarstellung von value.
045
*/
046
public static String toBitString(byte value)
047
{
048
char[] chars = new char[8];
049
050
051
052
053
054
055
}
056 }
int mask = 1;
for (int i = 0; i < 8; ++i) {
chars[7 - i] = (value & mask) != 0 ? '1' : '0';
mask <<= 1;
}
return new String(chars);
Listing0413.java
/* Listing0413 */
public class Listing0413
{
public static void main(String[] args)
{
for (int i = 0; i < 256; ++i) {
System.out.print("i=" + i);
byte b = ByteKit.fromUnsignedInt(i);
System.out.print(" b=" + ByteKit.toBitString(b));
char c = ByteKit.toChar(b);
System.out.print(" c=" + (c >= 32 ? c : '.'));
System.out.println();
}
}
}
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
7.1.1 Einfhrung
Objektorientierte Programmierung (kurz OOP) ist das Programmierparadigma der
90er Jahre. Viele der heute verwendeten Programmiersprachen sind entweder von
Grund auf objektorientiert (Java, Eiffel, SmallTalk) oder wurden im Laufe der Zeit
mit objektorientierten Erweiterungen versehen (Basic, Pascal, ADA). Selbst manche
Scriptsprachen erlauben den Zugriff auf (mitunter vordefinierte) Objekte oder
besitzen objektorientierte Eigenschaften (JavaScript, Python). Die objektorientierte
Programmierung war eine der "Silver Bullets", die die Software-Industrie aus ihrer
Krise fhren und zu robusteren, fehlerrmeren und besser wartbaren Programmen
fhren sollte.
Was sind nun die Geheimnisse der objektorientierten Programmierung? Was verbirgt
sich hinter dem Begriff, und welches sind seine wichtigsten Konzepte? Wir wollen
uns zunchst mit den Grundideen objektorientierter Programmierung
auseinandersetzen und dann in diesem und den nchsten Kapiteln Schritt fr Schritt
7.1.2 Abstraktion
Eine der wichtigsten Ideen der objektorientierten Programmierung ist die Trennung
zwischen Konzept und Umsetzung, etwa zwischen einem Bauteil und seinem
Bauplan, einer Speise und dem fr die Zubereitung erforderlichen Rezept oder einem
technischen Handbuch und der konkreten Apparatur, die dadurch beschrieben wird.
Diese Art von Unterscheidung ist in der wirklichen Welt sehr bedeutsam. Wer wei,
wie man einen einzigen Lichtschalter bedient, kann andere, gleichartige Schalter
ebenfalls bedienen. Wer ein Rezept fr eine Sachertorte besitzt, ist in der Lage, diese
zu backen, selbst wenn er ansonsten ber keine Koch- oder Backknste verfgt. Wer
einen Fhrerschein gemacht hat, kann ein Auto fahren, ohne im Detail ber das
komplizierte Innenleben desselben unterrichtet zu sein.
In der objektorientierten Programmierung manifestiert sich diese Unterscheidung in
den Begriffen Objekt und Klasse. Ein Objekt ist ein tatschlich existierendes "Ding"
aus der Anwendungswelt des Programms. Es spielt dabei keine Rolle, ob es sich um
die programmierte Umsetzung eines konkret existierenden Gegenstandes handelt,
oder ob "nur" ein abstraktes Konzept modelliert wird. Eine "Klasse" ist dagegen die
Beschreibung eines oder mehrerer hnlicher Objekte. "hnlich" bedeutet dabei, da
eine Klasse nur Objekte eines bestimmten Typs beschreibt. Diese mssen sich zwar
nicht in allen Details gleichen, aber doch in so vielen von ihnen bereinstimmen, da
eine gemeinsame Beschreibung angebracht ist. Eine Klasse beschreibt mindestens
drei wichtige Dinge:
hnlich wie ein Rezept zur Herstellung von Hunderten von Sachertorten verwendet
werden kann, ermglicht eine Klasse das Erzeugen einer prinzipiell beliebigen
Anzahl von Objekten. Jedes hat dabei seine eigene Identitt und mag sich in
gewissen Details von allen anderen unterscheiden. Letzlich ist das Objekt aber immer
eine Instanz der Klasse, nach der es modelliert wurde. In einem Haus kann es
beispielsweise fnfzig Lichtschalter-Objekte geben. Sie alle sind Instanzen der
Klasse "Lichtschalter", lassen sich in vergleichbarer Weise bedienen und sind
identisch konstruiert. Dennoch unterscheiden wir sehr wohl zwischen dem
Lichschalter-Objekt, das die Flurbeleuchtung bedient, und jenem, das den Keller
erhellt. Und beide widerum unterscheiden sich eindeutig von allen anderen
Lichschalterinstanzen im Haus.
Es ist brigens kein Zufall, da wir die Begriffe "Instanz" und "Objekt"
synonym verwenden. In der objektorientierten Programmierung ist das
sehr verbreitet (auch wenn Puristen zwischen beiden Begriffen noch
Unterschiede sehen), und wir wollen uns diesem Wortgebrauch
anschlieen.
Hinweis
7.1.3 Kapselung
In objektorientierten Programmiersprachen wird eine Klasse durch die
Zusammenfassung einer Menge von Daten und darauf operierender Funktionen (die
nun Methoden genannt werden) definiert. Die Daten werden durch einen Satz
Variablen reprsentiert, der fr jedes instanziierte Objekt neu angelegt wird (diese
werden als Attribute, Membervariablen, Instanzvariablen oder Instanzmerkmale
bezeichnet). Die Methoden sind im ausfhrbaren Programmcode nur einmal
vorhanden, operieren aber bei jedem Aufruf auf den Daten eines ganz bestimmten
Objekts (das Laufzeitsystem bergibt bei jedem Aufruf einer Methode einen Verweis
auf den Satz Instanzvariablen, mit dem die Methode gerade arbeiten soll).
Die Instanzvariablen reprsentieren den Zustand eines Objekts. Sie knnen bei jeder
Instanz einer Klasse unterschiedlich sein und sich whrend seiner Lebensdauer
verndern. Die Methoden reprsentieren das Verhalten des Objekts. Sie sind - von
gewollten Ausnahmen abgesehen, bei denen Variablen bewut von auen zugnglich
gemacht werden - die einzige Mglichkeit, mit dem Objekt zu kommunizieren und so
Informationen ber seinen Zustand zu gewinnen oder diesen zu verndern. Das
Verhalten der Objekte einer Klasse wird in seinen Methodendefinitionen festgelegt
und ist von dem darin enthaltenen Programmcode und dem aktuellen Zustand des
Objekts abhngig.
Diese Zusammenfassung von Methoden und Variablen zu Klassen bezeichnet man
als Kapselung. Sie stellt die zweite wichtige Eigenschaft objektorientierter
Programmiersprachen dar. Kapselung hilft vor allem, die Komplexitt der Bedienung
eines Objekts zu reduzieren. Um eine Lampe anzuschalten, mu man nicht viel ber
den inneren Aufbau des Lichtschalters wissen. Sie vermindert aber auch die
Komplexitt der Implementierung, denn undefinierte Interaktionen mit anderen
Bestandteilen des Programms werden verhindert oder reduziert.
7.1.4 Wiederverwendung
Durch die Abstraktion und Kapselung wird die Wiederverwendung von
Programmelementen gefrdert, die dritte wichtige Eigenschaft objektorientierter
Programmiersprachen. Ein einfaches Beispiel dafr sind Collections, also Objekte,
die Sammlungen anderer Objekte aufnehmen und auf eine bestimmte Art und Weise
verarbeiten knnen. Collections sind oft sehr kompliziert aufgebaut (typischerweise
zur Geschwindigkeitssteigerung oder Reduzierung des Speicherbedarfs), besitzen
aber in aller Regel eine einfache Schnittstelle. Werden sie als Klasse implementiert
und werden durch die Kapselung der Code- und Datenstrukturen die komplexen
Details "wegabstrahiert", knnen sie sehr einfach wiederverwendet werden. Immer,
wenn im Programm eine entsprechende Collection bentigt wird, mu lediglich ein
Objekt der passenden Klasse instanziert werden, und das Programm kann ber die
einfach zu bedienende Schnittstelle darauf zugreifen. Wiederverwendung ist ein
wichtiger Schlssel zur Erhhung der Effizienz und Fehlerfreiheit beim
Programmieren.
7.1.5 Beziehungen
Objekte und Klassen existieren fr gewhnlich nicht vllig alleine, sondern stehen in
Beziehungen zueinander. So hnelt ein Fahrrad beispielsweise einem Motorrad, hat
aber auch mit einem Auto Gemeinsamkeiten. Ein Auto hnelt dagegen einem
Lastwagen. Dieser kann einen Anhnger haben, auf dem ein Motorrad steht. Ein
Fhrschiff ist ebenfalls ein Transportmittel und kann viele Autos oder Lastwagen
aufnehmen, genauso wie ein langer Gterzug. Dieser wird von einer Lokomotive
gezogen. Ein Lastwagen kann auch einen Anhnger ziehen, mu es aber nicht. Bei
einem Fhrschiff ist keine Zugmaschine erforderlich, und es kann nicht nur
Transportmittel befrdern, sondern auch Menschen, Tiere oder Lebensmittel.
Wir wollen ein wenig Licht in diese Beziehungen bringen und zeigen, wie sie sich in
objektorientierten Programmiersprachen auf wenige Grundtypen reduzieren lassen:
Motorrad, ist ein Straenfahrzeug, ebenso wie das Auto und der Lastwagen. All diese
Klassen reprsentieren Transportmittel, zu denen aber auch die Schiffe und
Gterzge zhlen.
Die "is-a"-Beziehung zwischen zwei Klassen A und B sagt aus, da "B ein A ist", also
alle Eigenschaften von A besitzt, und vermutlich noch ein paar mehr. B ist demnach
eine Spezialisierung von A. Andersherum betrachtet, ist A eine Generalisierung
(Verallgemeinerung) von B.
"is-a"-Beziehungen werden in objektorientierten Programmiersprachen durch
Vererbung ausgedrckt. Eine Klasse wird dabei nicht komplett neu definiert, sondern
von einer anderen Klasse abgeleitet. In diesem Fall erbt sie alle Eigenschaften dieser
Klasse und kann nach Belieben eigene hinzufgen. In unserem Fall wre also B von
A abgeleitet. A wird als Basisklasse (manchmal auch als Vaterklasse), B als
abgeleitete Klasse bezeichnet.
Vererbungen knnen mehrstufig sein, d.h. eine abgeleitete Klasse kann Basisklasse
fr weitere Klassen sein. Auf diese Weise knnen vielstufige Vererbungshierarchien
entstehen, die in natrlicher Weise die Taxonomie (also die gegliederte
Begriffsstruktur) der zu modellierenden Anwendungswelt reprsentieren.
Vererbungshierarchien werden wegen ihrer Baumstruktur auch als Ableitungsbume
bezeichnet. Sie werden meist durch Graphen dargestellt, in denen die abgeleiteten
Klassen durch Pfeile mit den Basisklassen verbunden sind und die Basisklassen
oberhalb der abgeleiteten Klassen stehen. Fr unsere Fahrzeugwelt ergbe sich
beispielsweise folgender Ableitungsbaum:
7.1.6 Polymorphismus
Als letztes wichtiges Konzept objektorientierter Programmiersprachen wollen wir
uns mit dem Polymorphismus beschftigen. Polymorphismus bedeutet direkt
bersetzt etwa "Vielgestaltigkeit" und bezeichnet zunchst einmal die Fhigkeit von
Objektvariablen, Objekte unterschiedlicher Klassen aufzunehmen. Das geschieht
7.1.7 Fazit
Objektorientierte Programmierung erlaubt eine natrliche Modellierung vieler
Problemstellungen. Sie vermindert die Komplexitt eines Programms durch
Abstraktion, Kapselung, definierte Schnittstellen und Reduzierung von
Querzugriffen. Sie stellt Hilfsmittel zur Darstellung von Beziehungen zwischen
Klassen und Objekten dar, und sie erhht die Effizienz des Entwicklers durch
Frderung der Wiederverwendung von Programmcode. Wir werden in den nchsten
Abschnitten zeigen, wie die objektorientierte Programmierung sich in Java gestaltet.
<
>
>>
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
10.3 Design-Patterns
10.3 Design-Patterns
10.3.1 Singleton
10.3.2 Immutable
10.3.3 Interface
10.3.4 Factory
Factory-Methode
Factory-Klasse
Abstrakte Factory
10.3.5 Iterator
10.3.6 Delegate
10.3.7 Composite
10.3.8 Visitor
10.3.9 Observer
Design-Patterns (oder Entwurfsmuster) sind eine der wichtigsten und interessantesten Entwicklungen der
objektorientierten Programmierung der letzten Jahre. Basierend auf den Ideen des Architekten Christopher Alexander
wurden sie durch das Buch "Design-Patterns - Elements of Reusable Object-Oriented Software" von Erich Gamma,
Richard Helm, Ralph Johnson und John Vlissides 1995 einer breiten ffentlichkeit bekannt.
Als Design-Patterns bezeichnet man (wohlberlegte) Designvorschlge fr den Entwurf objektorientierter
Softwaresysteme. Ein Design-Pattern deckt dabei ein ganz bestimmtes Entwurfsproblem ab und beschreibt in
rezeptartiger Weise das Zusammenwirken von Klassen, Objekten und Methoden. Meist sind daran mehrere Algorithmen
und/oder Datenstrukturen beteiligt. Design-Patterns stellen wie Datenstrukturen oder Algorithmen vordefinierte Lsungen
fr konkrete Programmierprobleme dar, allerdings auf einer hheren Abstraktionsebene.
Einer der wichtigsten Verdienste standardisierter Design-Patterns ist es, Softwaredesigns Namen zu geben. Zwar ist es in
der Praxis nicht immer mglich oder sinnvoll, ein bestimmtes Design-Pattern in allen Details zu bernehmen. Die
konsistente Verwendung ihrer Namen und ihres prinzipiellen Aufbaus erweitern jedoch das Handwerkszeug und die
Kommunikationsfhigkeit des OOP-Programmierers betrchtlich. Begriffe wie Factory, Iterator oder Singleton werden in
OO-Projekten routinemig verwendet und sollten fr jeden betroffenen Entwickler dieselbe Bedeutung haben.
Wir wollen nachfolgend einige der wichtigsten Design-Patterns vorstellen und ihre Implementierung in Java skizzieren.
Die Ausfhrungen sollten allerdings nur als erster Einstieg in das Thema angesehen werden. Viele Patterns knnen hier
aus Platzgrnden gar nicht erwhnt werden, obwohl sie in der Praxis einen hohen Stellenwert haben (z.B. Adapter,
Bridge, Mediator, Command etc.). Zudem ist die Bedeutung eines Patterns fr den OOP-Anfnger oft gar nicht
verstndlich, sondern erschliet sich erst nach Monaten oder Jahren zustzlicher Programmiererfahrung.
Die folgenden Abschnitte ersetzen also nicht die Lektre weiterfhrender Literatur zu diesem Thema. Das oben erwhnte
Werk von Gamma et al. ist nach wie vor einer der Klassiker schlechthin (die Autoren und ihr Buch werden meist als
"GoF" bezeichnet, ein Akronym fr "Gang of Four"). Daneben existieren auch spezifische Kataloge, in denen die DesignPatterns zu bestimmten Anwendungsgebieten oder auf der Basis einer ganz bestimmten Sprache, wie etwa C++ oder Java,
beschrieben werden.
10.3.1 Singleton
Ein Singleton ist eine Klasse, von der nur ein einziges Objekt erzeugt werden darf. Es stellt eine globale
Zugriffsmglichkeit auf dieses Objekt zur Verfgung und instanziert es beim ersten Zugriff automatisch. Es gibt viele
Beispiele fr Singletons. So ist etwa der Spooler in einem Drucksystem ein Singleton oder der Fenstermanager unter
Windows, der Firmenstamm in einem Abrechnungssystem oder die bersetzungstabelle in einem Parser.
Wichtige Designmerkmale einer Singleton-Klasse sind:
Sie besitzt eine statische Membervariable ihres eigenen Typs, in dem die einzige Instanz gespeichert wird.
Sie besitzt eine statische Methode getInstance, mit der auf die Instanz zugegriffen werden kann.
Sie besitzt einen privaten parameterlosen Konstruktor, um zu verhindern, da andere Klassen durch Anwendung
des new-Operators eine Instanz erzeugen (er verhindert allerdings auch das Ableiten anderer Klassen).
Singleton.java
10.3.2 Immutable
Als immutable (unvernderlich) bezeichnet man Objekte, die nach ihrer Instanzierung nicht mehr verndert werden
knnen. Ihre Membervariablen werden im Konstruktor oder in Initialisierern gesetzt und danach ausschlielich im
lesenden Zugriff verwendet. Unvernderliche Objekte gibt es an verschiedenen Stellen in der Java-Klassenbibliothek.
Bekannte Beispiele sind die Klassen String (siehe Kapitel 11) oder die in Abschnitt 10.2 erluterten Wrapper-Klassen.
Unvernderliche Objekte knnen gefahrlos mehrfach referenziert werden und erfordern im Multithreading keinen
Synchronisationsaufwand.
Wichtige Designmerkmale einer Immutable-Klasse sind:
Lesende Zugriffe auf Membervariablen sind verboten, wenn der Member ein vernderliches Objekt oder ein Array
ist.
Werden vernderliche Objekte oder Arrays an einen Konstruktor bergeben, so mssen sie geklont (also kopiert)
werden, bevor sie Membervariablen zugewiesen werden drfen.
Immutable.java
Warnung
10.3.3 Interface
Ein Interface trennt die Beschreibung von Eigenschaften einer Klasse von ihrer Implementierung. Dabei ist es sowohl
erlaubt, da ein Interface von mehr als einer Klasse implementiert wird, als auch, da eine Klasse mehrere Interfaces
implementiert. In Java ist ein Interface ein fundamentales Sprachelement. Es wurde in Kapitel 9 ausfhrlich beschrieben
und soll hier nur der Vollstndigkeit halber aufgezhlt werden. Fr Details verweisen wir auf die dort gemachten
Ausfhrungen.
10.3.4 Factory
Eine Factory ist ein Hilfsmittel zum Erzeugen von Objekten. Sie wird verwendet, wenn das Instanzieren eines Objekts
mit dem new-Operator alleine nicht mglich oder sinnvoll ist - etwa weil das Objekt schwierig zu konstruieren ist oder
aufwendig konfiguriert werden mu, bevor es verwendet werden kann. Manchmal mssen Objekte auch aus einer Datei,
ber eine Netzwerkverbindung oder aus einer Datenbank geladen werden, oder sie werden auf der Basis von
Konfigurationsinformationen aus systemnahen Modulen generiert. Eine Factory wird auch dann eingesetzt, wenn die
Menge der Klassen, aus denen Objekte erzeugbar sind, dynamisch ist und zur Laufzeit des Programms erweitert werden
kann.
In diesen Fllen ist es sinnvoll, das Erzeugen neuer Objekte von einer Factory erledigen zu lassen. Wir wollen
nachfolgend die drei wichtigsten Varianten einer Factory vorstellen.
Factory-Methode
Gibt es in einer Klasse, von der Instanzen erzeugt werden sollen, eine oder mehrere statische Methoden, die Objekte
desselben Typs erzeugen und an den Aufrufer zurckgeben, so bezeichnet man diese als Factory-Methoden. Sie rufen
implizit den new-Operator auf, um Objekte zu instanzieren, und fhren alle Konfigurationen durch, die erforderlich sind,
ein Objekt in der gewnschten Weise zu konstruieren.
Das Klassendiagramm fr eine Factory-Methode sieht so aus:
Icon.java
Hinweis
Factory-Klasse
Eine Erweiterung des Konzepts der Factory-Methode ist die Factory-Klasse. Hier ist nicht eine einzelne Methode
innerhalb der eigenen Klasse fr das Instanzieren neuer Objekte zustndig, sondern es gibt eine eigenstndige Klasse fr
diesen Vorgang. Das kann beispielsweise sinnvoll sein, wenn der Herstellungsvorgang zu aufwendig ist, um innerhalb der
zu instanzierenden Klasse vorgenommen zu werden. Eine Factory-Klasse knnte auch sinnvoll sein, wenn es spter
erforderlich werden knnte, die Factory selbst austauschbar zu machen. Ein dritter Grund kann sein, da es gar keine
Klasse gibt, in der eine Factory-Methode untergebracht werden knnte. Das ist insbesondere dann der Fall, wenn
unterschiedliche Objekte hergestellt werden sollen, die lediglich ein gemeinsames Interface implementieren.
DoubleMethodFactory.java
Hinweis
Eine Abstracte Factory ist eine recht aufwendige Erweiterung der Factory-Klasse, bei der zwei zustzliche Gedanken im
Vordergrund stehen:
Erstens soll nicht nur ein einzelner Objekttyp erzeugt werden, sondern mehrere von ihnen. Deren Klassen stehen in
einer bestimmten Beziehung zueinander und die erzeugten Objekte mssen miteinander vertrglich sein. Die
Objekttypen knnen als Produkte, die Gesamtheit der von einer Factory erzeugten Produkte als Produktgruppe
bezeichnet werden.
Zweitens soll die Factory austauschbar sein. Dann kann auf einfache Weise eine andere Implementierungsvariante
gemeinsam fr alle zu erzeugenden Objekte gewhlt werden.
Eine abstrakte Factory wird auch als Toolkit bezeichnet. Ein Beispiel dafr findet sich in grafischen Ausgabesystemen bei
der Erzeugung von Dialogelementen (Widgets) fr unterschiedliche Fenstermanager. Eine konkrete Factory mu in der
Lage sein, unterschiedliche Dialogelemente so zu erzeugen, da sie in Aussehen und Bedienung konsistent sind. Auch die
Schnittstelle fr Programme sollte ber Fenstergrenzen hinweg konstant sein. Konkrete Factories knnte es etwa fr
Windows, X-Window oder die Macintosh-Oberflche geben.
Eine abstrakte Factory kann durch folgende Bestandteile beschrieben werden:
Jedes Produkt besitzt eine Basisklasse, die seine spezifischen Eigenschaften angibt (die abstrakte Produktklasse).
Jede Factory-Methode hat einen Rckgabewert, dessen Typ einer der abstrakten Produktklassen entspricht.
Je Implementierungsvariante gibt es aus den abstrakten Produkten abgeleitete konkrete Produkte, die von der
zugehrigen konkreten Factory erzeugt werden.
Alle Factory-Methoden werden in der abstrakten Factory-Klasse zusammengefat. Sie bildet das Interface fr das
Erzeugen der Produkte.
Zu jeder Implementierungsvariante gibt es eine aus der abstrakten Factory abgeleitete konkrete Factory. Sie
implementiert alle abstrakten Factory-Methoden und ist in der Lage, die konkreten Produkte einer
Implementierungsvariante zu erzeugen.
Schlielich gibt es eine Mglichkeit, eine konkrete Factory zu erzeugen. Das kann beispielsweise durch eine
statische Methode in der abstrakten Factory realisiert werden.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
/* Listing1010.java */
Listing1010.java
//-----------------------------------------------------------------//Abstrakte Produkte
//-----------------------------------------------------------------abstract class Product1
{
}
abstract class Product2
{
}
//-----------------------------------------------------------------//Abstrakte Factory
//-----------------------------------------------------------------abstract class ProductFactory
{
public abstract Product1 createProduct1();
public abstract Product2 createProduct2();
public static ProductFactory getFactory(String variant)
{
ProductFactory ret = null;
if (variant.equals("A")) {
ret = new ConcreteFactoryVariantA();
} else if (variant.equals("B")) {
ret = new ConcreteFactoryVariantB();
}
return ret;
}
public static ProductFactory getDefaultFactory()
{
return getFactory("A");
}
}
//-----------------------------------------------------------------//Konkrete Produkte fr Implementierungsvariante A
//-----------------------------------------------------------------class Product1VariantA
extends Product1
{
}
class Product2VariantA
extends Product2
{
}
//-----------------------------------------------------------------//Konkrete Factory fr Implementierungsvariante A
//-----------------------------------------------------------------class ConcreteFactoryVariantA
extends ProductFactory
{
public Product1 createProduct1()
{
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
Ein wenig mehr Aufwand mu allerdings getrieben werden, wenn ein neues Produkt hinzukommt. Dann mssen nicht nur
neue abstrakte und konkrete Produktklassen definiert werden, sondern auch die Factories mssen um eine Methode
erweitert werden.
Mit Absicht wurde bei der Benennung der abstrakten Klassen nicht die Vor- oder Nachsilbe "Abstract" verwendet. Tip
Da die Clients nur mit den Schnittstellen der abstrakten Klassen arbeiten und die Namen der konkreten Klassen
normalerweise nie zu sehen bekommen, ist es vollkommen unntig, sie bei jeder Deklaration daran zu erinnern,
da sie eigentlich nur mit Abstraktionen arbeiten.
Auch in Java gibt es Klassen, die nach dem Prinzip der abstrakten Factory implementiert sind. Ein Beispiel ist Hinweis
die Klasse Toolkit des Pakets java.awt. Sie dient dazu, Fenster, Dialogelemente und andere
plattformabhngige Objekte fr die grafische Oberflche eines bestimmten Betriebssystems zu erzeugen. In
Abschnitt 24.2.2 finden sich ein paar Beispiele fr die Anwendung dieser Klasse.
10.3.5 Iterator
Ein Iterator ist ein Objekt, das es ermglicht, die Elemente eines Collection-Objekts nacheinander zu durchlaufen. Als
Collection-Objekt bezeichnet man ein Objekt, das eine Sammlung (meist gleichartiger) Elemente eines anderen Typs
enthlt. In Java gibt es eine Vielzahl von vordefinierten Collections, sie werden in Kapitel 14 und Kapitel 15 ausfhrlich
erlutert.
Obwohl die Objekte in den Collections unterschiedlich strukturiert und auf sehr unterschiedliche Art und Weise
gespeichert sein knnen, ist es bei den meisten von ihnen frher oder spter erforderlich, auf alle darin enthaltenen
Elemente zuzugreifen. Dazu stellt die Collection einen oder mehrere Iteratoren zur Verfgung, die das Durchlaufen der
Elemente ermglichen, ohne da die innere Struktur der Collection dem Aufrufer bekannt sein mu.
Ein Iterator enthlt folgende Bestandteile:
Eine Methode hasNext, mit der berprft wird, ob Elemente vorhanden sind, die noch nicht besucht wurden.
Eine Methode next, die das nchste unbesuchte Element liefert.
Die Collection-Klasse selbst enthlt Methoden wie getAllElements oder getKeys, mit denen Iteratoren fr
bestimmte Teilmengen der enthaltenen Objekte angefordert werden knnen.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
/* Listing1011.java */
Listing1011.java
interface StringIterator
{
public boolean hasNext();
public String next();
}
class StringArray
{
String[] data;
public StringArray(String[] data)
{
this.data = data;
}
public StringIterator getElements()
{
return new StringIterator()
{
int index = 0;
public boolean hasNext()
{
return index < data.length;
}
public String next()
{
return data[index++];
}
};
}
}
public class Listing1011
{
static final String[] SAYHI = {"Hi", "Iterator", "Buddy"};
public static void main(String[] args)
{
//Collection erzeugen
StringArray strar = new StringArray(SAYHI);
//Iterator beschaffen und Elemente durchlaufen
StringIterator it = strar.getElements();
while (it.hasNext()) {
System.out.println(it.next());
}
}
}
Die Implementierung eines Iterators erfolgt hufig mit Hilfe lokaler oder anonymer Klassen. Das hat den
Vorteil, da alle bentigten Hilfsvariablen je Aufruf angelegt werden. Wrde die Klasse StringArray
dagegen selbst das StringIterator-Interface implementieren (und die Hilfsvariable index als
Membervariable halten), so knnte sie jeweils nur einen einzigen aktiven Iterator zur Verfgung stellen.
Hinweis
Iteratoren knnen auch gut mit Hilfe von for-Schleifen verwendet werden. Das folgende Programmfragment ist
quivalent zum vorigen Beispiel:
Tip
10.3.6 Delegate
In objektorientierten Programmiersprachen gibt es zwei grundverschiedene Mglichkeiten, Programmcode
wiederzuverwenden. Die erste von ihnen ist die Vererbung, bei der eine abgeleitete Klasse alle Eigenschaften ihrer
Basisklasse erbt und deren nicht-privaten Methoden aufrufen kann. Die zweite Mglichkeit wird als Delegation
bezeichnet. Hierbei verwendet eine Klasse die Dienste von Objekten, aus denen sie nicht abgeleitet ist. Diese Objekte
werden oft als Membervariablen gehalten.
Das wre an sich noch nichts Besonderes, denn Programme verwenden fast immer Code, der in anderen Programmteilen
liegt, und delegieren damit einen Teil ihrer Aufgaben. Ein Designpattern wird daraus, wenn Aufgaben weitergegeben
werden mssen, die eigentlich in der eigenen Klasse erledigt werden sollten. Wenn also der Leser des Programmes spter
erwarten wrde, den Code in der eigenen Klasse vorzufinden. In diesem Fall ist es sinnvoll, die bertragung der
Aufgaben explizit zu machen und das Delegate-Designpattern anzuwenden.
Anwendungen fr das Delegate-Pattern finden sich meist, wenn identische Funktionalitten in Klassen
Hinweis
untergebracht werden sollen, die nicht in einer gemeinsamen Vererbungslinie stehen. Ein Beispiel bilden die
Klassen JFrame und JInternalFrame aus dem Swing-Toolkit (sie werden in Kapitel 36 ausfhrlich
besprochen). Beide Klassen stellen Hauptfenster fr die Grafikausgabe dar. Eines von ihnen ist ein
eigenstndiges Top-Level-Window, das andere wird meist zusammen mit anderen Fenstern in ein Desktop
eingebettet. Soll eine Anwendung wahlweise in einem JFrame oder einem JInternalFrame laufen,
mssen alle Funktionalitten in beiden Klassen zur Verfgung gestellt werden. Unglcklicherweise sind beide
nicht Bestandteil einer gemeinsamen Vererbungslinie. Hier empfiehlt es sich, die Gemeinsamkeiten in einer
neuen Klasse zusammenzufassen und beiden Fensterklassen durch Delegation zur Verfgung zu stellen.
Das Delegate-Pattern besitzt folgende Bestandteile:
Die zu delegierende Funktionalitt wird in einer Delegate-Klasse untergebracht und ber deren ffentliche
Schnittstelle zur Verfgung gestellt. Um deren Bedeutung deutlich zu machen, kann ihr Name mit "Delegate"
enden (z.B. XYZFrameDelegate).
Alle Clienten halten eine Delegate-Membervariable, die bei der Konstruktion initialisiert wird. Wird eine
bestimmte Funktionalitt delegiert, ist einfach die korrespondierende Methode des Delegate aufzurufen.
Falls das Delegate auch Methoden der Clients aufrufen mu, sollte ein Delegator-Interface definiert und von allen
Clients implementiert werden. Der Client bergibt bei der Konstruktion des Delegates einen Zeiger auf sich selbst
und stellt sich selbst dem Delegate als Delegator zur Verfgung.
/* Listing1012.java */
class Delegate
{
private Delegator delegator;
public Delegate(Delegator delegator)
{
this.delegator = delegator;
}
public void service1()
{
}
public void service2()
{
}
}
interface Delegator
{
public void commonDelegatorServiceA();
public void commonDelegatorServiceB();
}
class Client1
implements Delegator
{
private Delegate delegate;
public Client1()
{
delegate = new Delegate(this);
}
public void service1()
{
//implementiert einen Service und benutzt
//dazu eigene Methoden und die des
//Delegate-Objekts
}
public void commonDelegatorServiceA()
{
}
Listing1012.java
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
10.3.7 Composite
In der Programmierpraxis werden hufig Datenstrukturen bentigt, bei denen die einzelnen Objekte zu Baumstrukturen
zusammengesetzt werden knnen.
Es gibt viele Beispiele fr derartige Strukturen:
Die Mens in einem Programm enthalten Menpunkte und Untermens. Untermens enthalten weitere
Menpunkte, sind aber selbst Menpunkte im bergeordneten Men.
Ein Verzeichnis in einem Dateisystem enthlt Dateien und Unterverzeichnisse. Unterverzeichnisse weisen dieselbe
prinzipielle Struktur wie ihre bergeordneten Verzeichnisse auf. Sowohl Dateien als auch Unterverzeichnisse
haben gemeinsame Eigenschaften, wie etwa einen Namen oder zugeordnete Rechte.
Die Komponenten einer grafischen Oberflche knnen sowohl einfache Dialogelemente (Buttons, Textfelder,
Listboxen) als auch Container (Unterfenster, Split-Panels, Tab-Panels) sein. Container enthalten ebenfalls
Komponenten (vergleiche zum Beispiel die Hierarchie der AWT-Fensterklassen in Abbildung 27.1).
Ein mechanisches Bauteil besteht aus elementaren Teilen, die nicht mehr weiter zerlegt werden knnen, und
zusammengesetzten Bauteilen, die aus Unterbauteilen bestehen.
Fr diese hufig anzutreffende Abstraktion gibt es ein Design-Pattern, das als Composite bezeichnet wird. Es ermglicht
derartige Kompositionen und erlaubt eine einheitliche Handhabung von individuellen und zusammengesetzten Objekten.
Ein Composite enthlt folgende Bestandteile:
Eine (oft abstrakte) Basisklasse, die sowohl zusammengesetzte als auch elementare Objekte reprsentiert. Diese
Basisklasse wird auch als "Component" bezeichnet.
Alle elementaren Objekte sind aus dieser Basisklasse abgeleitet.
Daraus abgeleitet gibt es mindestens eine Containerklasse, die in der Lage ist, eine Menge von Objekten der
Basisklasse aufzunehmen.
Somit sind beide Bedingungen erfllt. Der Container ermglicht die Komposition der Objekte zu Baumstrukturen, und
die Basisklasse stellt die einheitliche Schnittstelle fr elementare Objekte und Container zur Verfgung. Das
Klassendiagramm fr ein Composite sieht so aus:
/* Listing1013.java */
class MenuEntry1
{
protected String name;
public MenuEntry1(String name)
{
this.name = name;
}
public String toString()
{
return name;
}
}
class IconizedMenuEntry1
extends MenuEntry1
{
private String iconName;
public IconizedMenuEntry1(String name, String iconName)
{
super(name);
this.iconName = iconName;
}
}
class CheckableMenuEntry1
extends MenuEntry1
Listing1013.java
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
{
private boolean checked;
public CheckableMenuEntry1(String name, boolean checked)
{
super(name);
this.checked = checked;
}
}
class Menu1
extends MenuEntry1
{
MenuEntry1[] entries;
int
entryCnt;
public Menu1(String name, int maxElements)
{
super(name);
this.entries = new MenuEntry1[maxElements];
entryCnt = 0;
}
public void add(MenuEntry1 entry)
{
entries[entryCnt++] = entry;
}
public String toString()
{
String ret = "(";
for (int i = 0; i < entryCnt; ++i) {
ret += (i != 0 ? "," : "") + entries[i].toString();
}
return ret + ")";
}
}
public class Listing1013
{
public static void main(String[] args)
{
Menu1 filemenu = new Menu1("Datei", 5);
filemenu.add(new MenuEntry1("Neu"));
filemenu.add(new MenuEntry1("Laden"));
filemenu.add(new MenuEntry1("Speichern"));
Menu1 confmenu = new Menu1("Konfiguration", 3);
confmenu.add(new MenuEntry1("Farben"));
confmenu.add(new MenuEntry1("Fenster"));
confmenu.add(new MenuEntry1("Pfade"));
filemenu.add(confmenu);
filemenu.add(new MenuEntry1("Beenden"));
System.out.println(filemenu.toString());
}
}
Die Komponentenklasse hat den Namen MenuEntry1. Sie reprsentiert Meneintrge und ist Vaterklasse der
spezialisierteren Meneintrge IconizedMenuEntry1 und CheckableMenuEntry1. Zudem ist sie Vaterklasse
des Containers Menu1, der Meneintrge aufnehmen kann.
Bestandteil der gemeinsamen Schnittstelle ist die Methode toString. In der Basisklasse und den elementaren
Meneintrgen liefert sie lediglich den Namen des Objekts. In der Containerklasse wird sie berlagert und liefert eine
geklammerte Liste aller darin enthaltenen Meneintrge. Dabei arbeitet sie unabhngig davon, ob es sich bei dem
jeweiligen Eintrag um einen elementaren oder einen zusammengesetzten Eintrag handelt, denn es wird lediglich die
immer verfgbare Methode toString aufgerufen.
Das Testprogramm erzeugt ein "Datei"-Men mit einigen Elementareintrgen und einem Untermen "Konfiguration" und
gibt es auf Standardausgabe aus:
(Neu,Laden,Speichern,(Farben,Fenster,Pfade),Beenden)
10.3.8 Visitor
Das vorige Pattern hat gezeigt, wie man komplexe Datenstrukturen mit einer inhrenten Teile-Ganzes-Beziehung aufbaut.
Solche Strukturen mssen oft auf unterschiedliche Arten durchlaufen und verarbeitet werden. Ein Men mu
beispielsweise auf dem Bildschirm angezeigt werden, aber es kann auch die Gliederung fr einen Teil eines
Benutzerhandbuchs zur Verfgung stellen. Verzeichnisse in einem Dateisystem mssen nach einem bestimmten Namen
durchsucht werden, die kumulierte Gre ihrer Verzeichnisse und Unterverzeichnisse soll ermittelt werden, oder es sollen
alle Dateien eines bestimmten Typs gelscht werden knnen.
All diese Operationen erfordern einen flexiblen Mechanismus zum Durchlaufen und Verarbeiten der Datenstruktur.
Natrlich knnte man die einzelnen Bestandteile jeder Operation in den Komponenten- und Containerklassen
unterbringen, aber dadurch wrden diese schnell unbersichtlich, und fr jede neu hinzugefgte Operation mten alle
Klassen gendert werden.
Das Visitor-Pattern zeigt einen eleganteren Weg, Datenstrukturen mit Verarbeitungsalgorithmen zu versehen. Es besteht
aus folgenden Teilen:
Einem Interface Visitor, das Methoden definiert, die beim Durchlaufen der Datenstruktur aufgerufen werden.
Typischerweise gibt es zu jeder Klasse der Datenstruktur eine einzelne Methode, die genau dann aufgerufen wird,
wenn ein Element dieses Typs durchlaufen wird. Bei Containern kann es sinnvoll sein, zwei Methoden zu
definieren: eine, die am Anfang aufgerufen wird, und eine zweite, die aufgerufen wird, nachdem alle Elemente des
Containers durchlaufen wurden. Jede Methode des Visitors erhlt ein Argument, das vom Typ der Klasse ist, die
sie besucht. Die Namen der Methoden setzen sich meist aus dem Prfix "visit" und dem Typ des besuchten
Elements zusammen.
Die Basisklasse der Datenstruktur (also die Komponentenklasse) erhlt eine Methode accept, die ein VisitorObjekt als Argument erhlt. accept ruft die zu seiner Klasse passende Methode des Visitors auf. In jeder
abgeleiteten Komponentenklasse kann accept berlagert werden, wenn fr diese Klasse eine eigene Methode im
Visitor zur Verfgung steht, die anstelle der Basisklassenmethode aufgerufen werden soll. Bei Containern ruft
accept zustzlich die accept-Methoden der darin enthaltenen Elemente auf.
Fr jede zu implementierende Operation wird ein konkreter Visitor definiert, der die bentigte Funktionalitt in
den Aufrufen der "visit..."-Methoden implementiert.
Will ein Client die Funktionalitt eines bestimmten Visitors benutzen, instanziert er ein passendes konkretes
Visitor-Objekt und bergibt es an das Hauptobjekt der Datenstruktur.
/* Listing1014.java */
interface MenuVisitor
{
abstract void visitMenuEntry(MenuEntry2 entry);
abstract void visitMenuStarted(Menu2 menu);
abstract void visitMenuEnded(Menu2 menu);
}
class MenuEntry2
{
protected String name;
public MenuEntry2(String name)
{
this.name = name;
}
public String toString()
{
return name;
}
public void accept(MenuVisitor visitor)
{
visitor.visitMenuEntry(this);
}
}
class Menu2
extends MenuEntry2
{
Listing1014.java
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
MenuEntry2[] entries;
int
entryCnt;
public Menu2(String name, int maxElements)
{
super(name);
this.entries = new MenuEntry2[maxElements];
entryCnt = 0;
}
public void add(MenuEntry2 entry)
{
entries[entryCnt++] = entry;
}
public String toString()
{
String ret = "(";
for (int i = 0; i < entryCnt; ++i) {
ret += (i != 0 ? "," : "") + entries[i].toString();
}
return ret + ")";
}
public void accept(MenuVisitor visitor)
{
visitor.visitMenuStarted(this);
for (int i = 0; i < entryCnt; ++i) {
entries[i].accept(visitor);
}
visitor.visitMenuEnded(this);
}
}
class MenuPrintVisitor
implements MenuVisitor
{
String indent = "";
public void visitMenuEntry(MenuEntry2 entry)
{
System.out.println(indent + entry.name);
}
public void visitMenuStarted(Menu2 menu)
{
System.out.println(indent + menu.name);
indent += " ";
}
public void visitMenuEnded(Menu2 menu)
{
indent = indent.substring(1);
}
}
public class Listing1014
{
public static void main(String[] args)
{
093
094
095
096
097
098
099
100
101
102
103
104
105
}
106 }
Hinweis
10.3.9 Observer
Bei der objektorientierten Programmierung werden Programme in viele kleine Bestandteile zerlegt, die fr sich
genommen autonom arbeiten. Mit zunehmender Anzahl von Bausteinen steigt allerdings der Kommunikationsbedarf
zwischen diesen Objekten, und der Aufwand, sie konsistent zu halten, wchst an.
Ein Observer ist ein Design-Pattern, das eine Beziehung zwischen einem Subject und seinen Beobachtern aufbaut. Als
Subject wird dabei ein Objekt bezeichnet, dessen Zustandsnderung fr andere Objekte interessant ist. Als Beobachter
werden die Objekte bezeichnet, die von Zustandsnderungen des Subjekts abhngig sind; deren Zustand also dem
Zustand des Subjekts konsistent folgen mu.
Das Observer-Pattern wird sehr hufig bei der Programmierung grafischer Oberflchen angewendet. Ist beispielsweise die
Grafikausgabe mehrerer Fenster von einer bestimmten Datenstruktur abhngig, so mssen die Fenster ihre Ausgabe
verndern, wenn die Datenstruktur sich ndert. Auch Dialogelemente wie Buttons, Auswahlfelder oder Listen mssen das
Programm benachrichtigen, wenn der Anwender eine Vernderung an ihnen vorgenommen hat. In diesen Fllen kann das
Ein Interface Observer definiert eine Methode update, die immer dann aufgerufen wird, wenn sich der
Zustand des beobachteten Subjekts ndert.
Eine Klasse Subject definiert Methoden, mit denen sich Observer registrieren und deregistrieren lassen knnen.
Tritt eine Zustandsnderung des Subjekts auf, wird bei allen registrierten Observern die update-Methode
aufgerufen.
Ein konkretes Subjekt wird aus Subject abgeleitet (oder verwendet ein Subject-Objekt per Delegation) und
ruft nach jeder Zustandsnderung fireUpdate auf. Dadurch werden alle registrierten Beobachter aufgerufen
und knnen auf die Zustandsnderung reagieren.
Ein konkreter Beobachter implementiert das Interface Observer und registriert sich bei allen Subjekten, ber
deren Zustandsnderungen er unterrichtet werden mchte. Er implementiert in update den Code, der zur
Bearbeitung der Zustandsnderung erforderlich ist.
/* Listing1015.java */
interface Observer
{
public void update(Subject subject);
}
class Subject
{
Observer[] observers
= new Observer[5];
int
observerCnt = 0;
public void attach(Observer observer)
{
observers[observerCnt++] = observer;
}
public void detach(Observer observer)
{
for (int i = 0; i < observerCnt; ++i) {
if (observers[i] == observer) {
--observerCnt;
for (;i < observerCnt; ++i) {
observers[i] = observers[i + 1];
}
break;
Listing1015.java
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
}
}
}
public void fireUpdate()
{
for (int i = 0; i < observerCnt; ++i) {
observers[i].update(this);
}
}
}
class Counter
{
int cnt = 0;
Subject subject = new Subject();
public void attach(Observer observer)
{
subject.attach(observer);
}
public void detach(Observer observer)
{
subject.detach(observer);
}
public void inc()
{
if (++cnt % 3 == 0) {
subject.fireUpdate();
}
}
}
public class Listing1015
{
public static void main(String[] args)
{
Counter counter = new Counter();
counter.attach(
new Observer()
{
public void update(Subject subject)
{
System.out.print("divisible by 3: ");
}
}
);
while (counter.cnt < 10) {
counter.inc();
System.out.println(counter.cnt);
}
}
}
Hauptprogramm instanzieren wir ein Counter-Objekt und registrieren eine lokale anonyme Klasse als Listener, die bei
jeder Benachrichtigung eine Meldung ausgibt. Whrend des anschlieenden Zhlerlaufs von 1 bis 10 wird sie dreimal
aufgerufen:
1
2
divisible by 3: 3
4
5
divisible by 3: 6
7
8
divisible by 3: 9
10
Das Observer-Pattern ist in Java sehr verbreitet, denn die Kommunikation zwischen graphischen
Hinweis
Dialogelementen und ihrer Anwendung basiert vollstndig auf dieser Idee. Allerdings wurde es etwas
erweitert, die Beobachter werden als Listener bezeichnet, und es gibt von ihnen eine Vielzahl unterschiedlicher
Typen mit unterschiedlichen Aufgaben. Da es zudem blich ist, da ein Listener sich bei mehr als einem
Subjekt registriert, wird ein Aufruf von update statt des einfachen Arguments jeweils ein Listenerspezifisches Ereignisobjekt bergeben. Darin werden neben dem Subjekt weitere spezifische Informationen
untergebracht. Zudem haben die Methoden gegenber der ursprnglichen Definition eine andere
Namensstruktur, und es kann sein, da ein Listener nicht nur eine, sonderen mehrere unterschiedliche UpdateMethoden zur Verfgung stellen mu, um auf unterschiedliche Ereignistypen zu reagieren. Das ListenerKonzept von Java wird auch als Delegation Based Event Handling bezeichnet und in Kapitel 28 ausfhrlich
erlutert.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Die Klasse String definiert eine Vielzahl von Methoden zur Manipulation und zur
Bestimmung der Eigenschaften von Zeichenketten. Da Strings und die auf ihnen
definierten Operationen bei der Anwendung einer Programmiersprache ein wichtiges
und fr die Effizienz der Programmentwicklung kritisches Merkmal sind, sollen hier
die wichtigsten String-Operationen vorgestellt werden. Weitere Methoden dieser
Klasse knnen den Referenzhandbchern der Java-Klassenbibliothek oder der OnlineHilfe entnommen werden.
11.2.1 Konstruktoren
Die Klasse String bietet eine Reihe von Mglichkeiten, neue Instanzen zu
erzeugen. Neben der bereits in Kapitel 4 erluterten Initialisierung mit Hilfe von
Literalen besitzt die Klasse eine Reihe von Konstruktoren, die dazu verwendet
werden knnen, String-Objekte explizit zu erzeugen.
String()
java.lang.String
String(String value)
java.lang.String
java.lang.String
Erzeugt einen neuen String aus einem vorhandenen Zeichen-Array. Dabei werden alle
Elemente des Arrays in den String bernommen.
Nachdem ein String erzeugt wurde, kann er wie jedes andere Objekt
verwendet werden. Insbesondere erfolgt damit im Gegensatz zu C oder
C++ das Speichermanagement vollautomatisch, ohne da sich der
Programmierer darum kmmern mu.
Hinweis
11.2.2 Zeichenextraktion
char charAt(int index)
throws StringIndexOutOfBoundsException
java.lang.String
Liefert das Zeichen an Position index. Dabei hat das erste Element eines Strings den
Index 0 und das letzte den Index length()-1. Falls der String krzer als index +
1 Zeichen ist, wird eine Ausnahme des Typs
StringIndexOutOfBoundsException erzeugt.
String substring(int begin, int end)
java.lang.String
Liefert den Teilstring, der an Position begin beginnt und an Position end endet.
Wie bei allen Zugriffen ber einen numerischen Index beginnt auch hier die Zhlung
bei 0.
Ungewhnlich bei der Verwendung dieser Methode ist die Tatsache, da
der Parameter end auf das erste Zeichen hinter den zu extrahierenden
Teilstring verweist (siehe Abbildung 11.1). Der Rckgabewert ist also die
Zeichenkette, die von Indexposition begin bis Indexposition end - 1
reicht.
Hinweis
java.lang.String
Die Methode liefert den String, der entsteht, wenn auf beiden Seiten der Zeichenkette
jeweils alle zusammenhngenden Leerzeichen entfernt werden. Dabei werden alle
Zeichen, die einen Code kleiner gleich 32 haben, als Leerzeichen angesehen. Leider
gibt es keine separaten Methoden fr das rechts- oder linksbndige Entfernen von
Leerzeichen, trim entfernt immer die Leerzeichen auf beiden Seiten. Da die Klasse
String als final deklariert wurde, gibt es auch keine Mglichkeit, entsprechende
Methoden nachzursten.
java.lang.String
Liefert die aktuelle Lnge des String-Objekts. Ist der Rckgabewert 0, so bedeutet
dies, da der String leer ist. Wird ein Wert n grer 0 zurckgegeben, so enthlt der
String n Zeichen, die an den Indexpositionen 0 bis n - 1 liegen.
Das folgende Beispiel zeigt die Verwendung der Methoden substring und
length und der String-Verkettung auf (letztere wird in Abschnitt 11.3.2 noch
einmal genauer behandelt).
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
Listing1101.java
/* Listing1101.java */
public class Listing1101
{
public static void main(String[] args)
{
String s1;
s1 = "Auf der Mauer";
s1 += ", auf der Lauer";
s1 += ", sitzt \'ne kleine Wanze";
System.out.println(s1);
for (int i = 1; i <= 5; ++i) {
s1 = s1.substring(0,s1.length()-1);
System.out.println(s1);
}
}
}
der
der
der
der
der
der
Mauer,
Mauer,
Mauer,
Mauer,
Mauer,
Mauer,
auf
auf
auf
auf
auf
auf
der
der
der
der
der
der
Lauer,
Lauer,
Lauer,
Lauer,
Lauer,
Lauer,
sitzt
sitzt
sitzt
sitzt
sitzt
sitzt
'ne
'ne
'ne
'ne
'ne
'ne
kleine
kleine
kleine
kleine
kleine
kleine
Wanze
Wanz
Wan
Wa
W
java.lang.String
boolean startsWith(String s)
boolean endsWith(String s)
int compareTo(String s)
boolean regionMatches(
int toffset,
String other,
int ooffset,
int len
)
equals liefert true, wenn das aktuelle String-Objekt und anObject gleich sind.
Analog zu der gleichnamigen Methode der Klasse Object berprft equals also
nicht, ob beide Strings dasselbe Objekt referenzieren, sondern testet auf inhaltliche
Gleichheit. Interessant an equals ist die Tatsache, da als Parameter ein Objekt der
Klasse Object erwartet wird. equals kann einen String also nicht nur mit einem
anderen String vergleichen, sondern mit der String-Darstellung eines beliebigen
Objekts.
Neben equals gibt es noch eine hnliche Methode, equalsIgnoreCase, die
eventuell vorhandene Unterschiede in der Gro-/Kleinschreibung beider
Zeichenketten ignoriert.
startsWith testet, ob das String-Objekt mit der Zeichenkette s beginnt. Ist das der
Fall, so gibt die Methode true zurck, andernfalls false. endsWith berprft
dagegen, ob das String-Objekt mit der Zeichenkette s endet. Ist das der Fall, gibt die
Methode ebenfalls true zurck, andernfalls false.
Die wichtige Methode compareTo fhrt einen lexikalischen Vergleich beider
Strings durch. Bei einem lexikalischen Vergleich werden die Zeichen paarweise von
links nach rechts verglichen. Tritt ein Unterschied auf oder ist einer der Strings
beendet, wird das Ergebnis ermittelt. Ist das aktuelle String-Objekt dabei kleiner als
s, wird ein negativer Wert zurckgegeben. Ist es grer, wird ein positiver Wert
zurckgegeben. Bei Gleichheit liefert die Methode den Rckgabewert 0.
regionMatches vergleicht zwei gleich lange String-Regionen, die in zwei
unterschiedlichen Strings an zwei unterschiedlichen Positionen liegen knnen. Die
Region im ersten String beginnt dabei an der Position toffset, die im zweiten
String other an der Position ooffset. Verglichen werden len Zeichen. Das
Ergebnis ist true, wenn beide Teilstrings identisch sind, andernfalls ist es false.
Die Methode steht auch in einer Variante zur Verfgung, bei der Gro- und
Kleinschreibung ignoriert werden. In diesem Fall wird als zustzlicher Parameter an
erster Stelle ein boolescher Wert ignoreCase bergeben.
Die Anwendung von regionMatches soll an folgendem Beispiel erlutert werden.
Der erste Vergleich liefert true (siehe Abbildung 11.2), der zweite false:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
/* Listing1102.java */
public class Listing1102
{
public static void main(String[] args)
{
System.out.println(
"Gre aus Hamburg".regionMatches(
8,
"Greetings from Australia",
8,
2
)
);
System.out.println(
"Gre aus Hamburg".regionMatches(
6,
"Greetings from Australia",
15,
3
)
);
}
}
Listing1102.java
java.lang.String
Die Methode indexOf sucht das erste Vorkommen der Zeichenkette s innerhalb des
String-Objekts. Wird s gefunden, liefert die Methode den Index des ersten
bereinstimmenden Zeichens zurck, andernfalls wird -1 zurckgegeben. Die
Methode gibt es auch in einer Version, die einen Parameter vom Typ char
akzeptiert. In diesem Fall sucht sie nach dem ersten Auftreten des angegebenen
Zeichens.
Die zweite Variante von indexOf arbeitet wie die erste, beginnt mit der Suche aber
erst ab Position fromIndex. Wird s beginnend ab dieser Position gefunden, liefert
die Methode den Index des ersten bereinstimmenden Zeichens, andernfalls -1. Auch
diese Methode gibt es in einer Variante, die anstelle eines String-Parameters ein
Argument des Typs char erwartet. Ihr Verhalten ist analog zur vorherigen.
Die Methode lastIndexOf sucht nach dem letzten Vorkommen des Teilstrings s
im aktuellen String-Objekt. Wird s gefunden, liefert die Methode den Index des
ersten bereinstimmenden Zeichens, andernfalls -1. Wie die beiden vorherigen
Methoden gibt es auch lastIndexOf wahlweise mit einem Parameter vom Typ
char und mit einem zweiten Parameter, der die Startposition der Suche bestimmt.
java.lang.String
Die Methode toLowerCase liefert den String zurck, der entsteht, wenn alle
Zeichen des Argumentstrings in Kleinbuchstaben umgewandelt werden. Besitzt der
String keine umwandelbaren Zeichen, wird der Original-String zurckgegeben.
toUpperCase arbeitet ganz hnlich, liefert aber den String, der entsteht, wenn alle
Zeichen in Grobuchstaben umgewandelt werden. Besitzt der String keine
umwandelbaren Zeichen, wird der Original-String zurckgegeben.
Mit Hilfe von replace wird eine zeichenweise Konvertierung des aktuellen StringObjekts durchgefhrt. Dabei wird jedes Auftreten des Zeichens oldchar durch das
Zeichen newchar ersetzt.
Leider gibt es in der Klasse String keine Methode, die das Ersetzen von
Teilstrings durch andere Teilstrings (von mglicherweise unterschiedlicher
Lnge) ermglicht. Die auf Einzelzeichen basierende Methode replace ist
das einzige Mittel, um berhaupt Ersetzungen vornehmen zu knnen. Seit der
Version 1.2 gibt es im JDK aber in der Klasse StringBuffer eine
entsprechende Methode. Sie wird in Abschnitt 11.4.4 beschrieben.
Tip
11.2.7 Konvertierungsfunktionen
Da es oftmals ntig ist, primitive Datentypen in Zeichenketten umzuwandeln, bietet
Java eine ganze Reihe von Methoden, die zu diesem Zweck entwickelt wurden. Allen
Methoden ist der Name valueOf und die Tatsache, da sie exakt einen Parameter
erwarten, gemein.
Alle valueOf-Methoden sind als Klassenmethoden implementiert, d.h. sie knnen
auch ohne ein String-Objekt aufgerufen werden. Dies macht Sinn, denn sie werden
ja in aller Regel erst dazu verwendet, Strings zu erzeugen. Ein Aufruf von valueOf
wandelt ein primitives Objekt mit Hilfe der Methode toString, die von der
zugehrigen Wrapper-Klasse zur Verfgung gestellt wird, in eine Zeichenkette um.
Die wichtigsten valueOf-Methoden sind:
static
static
static
static
static
static
static
static
String
String
String
String
String
String
String
String
valueOf(boolean b)
valueOf(char c)
valueOf(char[] c)
valueOf(double d)
valueOf(float f)
valueOf(int i)
valueOf(long l)
valueOf(Object obj)
java.lang.String
<
>
>>
Der Compiler erzeugt keine direkt ausfhrbaren Programme, sondern .classFiles, die von einem Java-Interpreter ausgefhrt werden. Mittlerweile arbeiten
bereits mehrere Betriebssystem-Hersteller daran, .class-Files direkt ausfhrbar
zu machen, und es gibt schon Prozessoren, die den Maschinencode in .classFiles ohne zwischengeschalteten Interpreter verstehen.
Es gibt keinen expliziten Link-Lauf, denn die verschiedenen .class-Files
werden zur Ausfhrungszeit gebunden.
Die .class-Files eines einzigen Projekts knnen auf unterschiedlichen
Plattformen mit unterschiedlichen Compilern erzeugt werden.
Die .class-Files lassen sich auf allen Plattformen ausfhren, die einen JavaInterpreter besitzen; unabhngig davon, wo sie ursprnglich entwickelt
wurden.
Abbildung 13.1 zeigt den schematischen Ablauf bei der Entwicklung eines JavaProgramms, das aus den Klassen A, B und C besteht.
13.3.2 Projektverwaltung
Getrenntes Kompilieren
Wie die Abbildungen deutlich machen, spielen die .class-Files fr den
Entwicklungsproze und die Portierbarkeit von Java-Programmen eine entscheidende
Rolle. Jede .class-Datei enthlt den Bytecode fr eine bersetzte Klasse. Zustzlich
sind in ihr Informationen enthalten, um dynamisches Linken und Late-Bindung zu
untersttzen und gegebenenfalls Symbole, Zeilennummern und andere Informationen
fr den Debugger zur Verfgung zu stellen.
Eine der Grundregeln bei der Entwicklung von Java-Programmen ist es,
Hinweis
genau eine Klassendefinition je Quelldatei aufzunehmen. Anders als etwa
in C++, ist es in Java grundstzlich nicht mglich, die Quellen einer
einzigen Klasse auf mehrere Dateien zu verteilen (und auf diese Weise
weniger als eine Klasse in einer Quelldatei zu speichern). Obwohl es - wie
wir gleich sehen werden - mglich ist, mehr als eine Klasse in einer
Quelldatei abzulegen, sollte dies in der Regel nicht getan werden. Wenn
diese Regeln eingehalten werden, ergibt sich eine eindeutige Abbildung
zwischen Klassen und Quelldateien, die bei der sauberen Strukturierung
eines Projektes hilft. Durch die Verwendung von Paketen kann eine
Verteilung der Quelltexte auf verschiedene Verzeichnisse und
Unterverzeichnisse erreicht werden.
Wie bereits angedeutet, drfen tatschlich auch zwei oder mehr Klassen in einer
Quelldatei enthalten sein. Voraussetzung dafr ist allerdings, da hchstens eine von
ihnen als public deklariert wurde. Eine solche Vorgehensweise kann
beispielsweise bei sehr kleinen Projekten, die nur aus ganz wenigen Klassen
bestehen, sinnvoll sein, um alle Klassen in eine einzige Datei zu bekommen. Sie kann
auch angewendet werden, wenn kleine Hilfsklassen bentigt werden, die nur fr eine
einzige andere Klasse von Bedeutung sind. Der Compiler erzeugt in jedem Fall eine
separate .class-Datei fr jede Klasse, die in einer Quelldatei enthalten ist. Wurde in
einer Quelldatei mehr als eine Klasse public deklariert, gibt es einen CompilerFehler.
Aus diesen Grnden ist es bei kleineren und mittleren Projekten oftmals
Tip
sinnvoll, auf den Einsatz von make zu verzichten und die Programme bei
Bedarf durch Aufruf von javac *.java komplett neu zu kompilieren. Der
Aufruf-Overhead entsteht in diesem Fall nur einmal, und der Compiler braucht
nicht wesentlich lnger, als wenn lediglich eine einzige Quelle bersetzt
wrde.
Eine Alternative zu make ist das Open-Source-Projekt ant, ein komplett in
Java geschriebenes make-Tool mit umfangreichen Scripting-Mglichkeiten.
ant kann am ehesten als Kombination aus make und Kommando-Shell
angesehen werden. Basierend auf XML als Steuerungssprache bietet es eine
Vielzahl von eingebauten Kommandos, die beim bersetzen von JavaProgramm und whrend des gesamten Build-Prozesses ntzlich sind. Eigene
Erweiterungen knnen in Java geschrieben und nahtlos integriert werden. ant
wird als Jakarta-Projekt entwickelt, und seine Homepage ist
http://jakarta.apache.org/ant.
Handbuch der Java-Programmierung, 3. Auflage,
Addison Wesley, Version 3.0
1998-2002 Guido Krger,
API
http://www.javabuch.de
<
>
>>
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Hinweis
Applets werden ab Kapitel 39 erklrt. Sie waren es, die mit der Mglichkeit, eigenstndige Programme mit grafischer
Oberflche in Webseiten einzubinden, einen groen Teil der anfnglichen Begeisterung fr Java auslsten. Spter erkannte
man noch einen zweiten, strategischen Vorteil: diese Programme muten nicht installiert werden, sondern wurden einfach
aufgerufen und waren immer up-to-date. Das brachte gute alte Zeiten in Erinnerung, bei denen die Anwender an
einfachen Terminals saen, die - wenn sie wirklich einmal defekt waren - ohne groen Aufwand ausgetauscht werden
konnten.
Schnell war die Idee geboren, dies mit aktueller (Java-)Technologie nachzuahmen. Warum nicht alle bentigten
Programme als Applets implementieren, die bei Bedarf von einem zentralen Server geladen werden? Man hatte nmlich
erkannt, da in den Unternehmen ein Groteil der durch Computerarbeitspltze verursachten Kosten gar nicht bei deren
Kauf entstand, sondern erst danach. Etwa durch Updates, Reparaturen und Wartungen; aber auch durch Probleme, die
durch nicht verfgbare, fehlerhafte oder inkonsistente Programmversionen entstand. Die aus dieser Idee entstandenen Java
Workstations konnten sich allerdings nicht durchsetzen. Trotz vielversprechender Anfnge wurden die durch langsame
Netzwerkverbindungen oder unzulngliche Java-Implementierungen in den Browsern verursachten Probleme nicht gelst.
Die Java-Stations verschwanden ebenso schnell von der Bildflche, wie sie zuvor aufgetaucht waren.
Whrend der Entwicklung des JDK 1.3 brachte SUN eine Technologie auf den Markt, mit der die Vorteile
von Applets und Applikationen kombiniert werden sollten. Sie wird als Java Web Start (kurz WebStart)
bezeichnet und ist seit dem JDK 1.4 fester Bestandteil jedes JDKs und JREs. Java Web Start bietet die
Mglichkeit, Applikationen ber einen Web-Server automatisch zu laden und zu aktualisieren, ohne da
dazu eine lokale Installation oder das manuelle Einspielen eines Softwareupdates erforderlich wre. Sie
kombiniert so die Installationsfreiheit von Applets mit der Geschwindigkeit und Flexibilitt von JavaApplikationen.
JDK1.1-1.4
Der Entwickler liefert seine Java-Applikation in dem von WebStart bentigten Format aus. Dazu mu er alle
Bestandteile seiner Applikation (Klassen und Ressourcen) in einem oder mehreren jar-Archiven unterbringen.
Zustzlich mu er eine Deskriptordatei zur Verfgung stellen, in der alle von WebStart bentigten Informationen
untergebracht sind.
Der Administrator stellt eine Web-Seite zur Verfgung, von der die WebStart-Applikation geladen werden kann. Er
kopiert dazu alle jar-Dateien und die Deskriptordatei auf den Server und macht letztere ber einen Link den
potentiellen Anwendern verfgbar. Einmalig mu er seinen Web-Server so konfigurieren, da dieser
Deskriptordateien an ihrer Extension .jnlp erkennt und den Browsern den korrekten Mime-Typ application/xjava-jnlp-file bertrgt.
Mchte der Anwender eine Java-Applikation verwenden, die noch nicht lokal auf seinem Rechner verfgbar ist,
ruft er die vom Administrator zur Verfgung gestellte Web-Seite auf und klickt auf den Link mit der DeskriptorDatei. Diese wird vom Web-Browser geladen, der WebStart-Anwendungsmanager wird automatisch aufgerufen
und berprft, ob die gewnschte Applikation bereits lokal vorhanden und alle Bestandteile auf dem neuesten Stand
sind. Ist das der Fall, wird die Applikation gestartet, andernfalls werden zuvor die fehlenden Bestandteile
nachgeladen. Der Anwender kann mit der Applikation nun wie mit jedem anderen Java-Programm arbeiten.
Hat der Anwender die Applikation bereits einmal geladen, ist die Web-Seite fr weitere Aufrufe nicht mehr
unbedingt erforderlich. WebStart speichert die Bestandteile der Applikation in einem Cache und kann sie auch dann
zur Verfgung stellen, wenn keine Verbindung zu dem Server besteht, von dem sie heruntergeladen wurde. Dazu
ruft der Anwender einfach das (zusammen mit dem JDK oder JRE installierte) Programm Java Web Start auf und
whlt aus der Liste der bereits heruntergeladenen Programme das zu startende aus. Wird das Programm oft
bentigt, kann der Anwender sich eine Verknpfung auf dem Desktop oder einen Eintrag im Startmen erstellen
lassen.
Die hier skizzierte Vorgehensweise beschreibt die Nutzungsmglichkeiten von Java Web Start nur in Anstzen. Hinweis
Tatschlich bietet diese Technologie weit mehr Mglichkeiten, als hier erlutert werden knnen. So ist es
beispielsweise mglich, die Anwendung in separate Teile zu zerlegen, die erst bei Bedarf geladen werden. Teile
von Anwendungen lassen sich versionieren und werden nur dann aktualisiert, wenn eine neuere Version zur
Verfgung steht. Man kann Applikationen auch signieren, um ihnen den Zugriff auf den kompletten
Arbeitsplatz zu ermglichen. Ohne Signatur laufen sie - hnlich wie Applets - lediglich in einer Sandbox ab,
in der sie nur beschrnkte Zugriffsmglichkeiten auf lokale Ressourcen haben. Weitere Informationen dazu
werden in Abschnitt 13.5.3 gegeben.
Ein Groteil dieser Mglichkeiten wird im Java Network Launching Protocol beschrieben, das ber die
Download-Seite der WebStart-Homepage heruntergeladen werden kann. Es findet sich wie alle anderen
WebStart-Ressourcen und -Dokumentationen auf http://java.sun.com/products/javawebstart/index.html.
Anwendungsmanager fr das Laden, Ausfhren und Darstellen der Applikation bentigt. Sie heit in unserem Beispiel
wstest.jnlp und hat folgenden Inhalt:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
In information werden beschreibende Angaben zur Applikation gemacht. Neben Titel, Icon, Beschreibung und
Hersteller des Programms kann eine Homepage angegeben werden, auf der der Anwender weitere Informationen
zum Programm finden kann. Wichtig ist der Eintrag offline-allowed. Er gibt an, da das Programm auch
ohne Verbindung zum Internet (genauer: zu seinen Download-Quellen) betrieben werden kann. Das geht natrlich
nur dann, wenn der WebStart-Anwendungsmanager bereits alle Teile der Applikation geladen und lokal im Cache
gespeichert hat. In diesem Fall wrde dann auch nicht geprft, ob Teile der Anwendung veraltet sind und ein
Update fllig wre.
Das information-Element kann ein optionales locale-Argument haben und mehrfach vorhanden sein. So
lassen sich Angaben machen, die nur fr eine bestimmte Sprachversion gelten. In unserem Beispiel gibt es ein
zweites information-Element, in dem die description in deutscher Sprache angegeben wird. Als mgliche
Werte fr das locale-Argument knnen die in Abschnitt 17.4 erluterten Sprachen-/Lnderstrings der LocaleKlasse verwendet werden.
Das Element security dient zur Angabe von sicherheitsrelevanten Informationen. Sein wichtigstes Element ist
all-permissions, mit dem der volle Zugriff auf alle Systemressourcen gefordert wird. Analog zu einem
Applet wird dieser aber nur dann gewhrt, wenn alle jar-Dateien signiert sind und der Anwender das zum Signieren
verwendete Zertifikat akzeptiert. In unserem Beispiel wurde dieses Element auskommentiert, und die Anwendung
hat daher nur eingeschrnkten Zugriff auf Systemressourcen. hnlich wie ein Applet kann eine unsignierte
WebStart-Applikation daher keinen nennenswerten Schaden am System anrichten. Allerdings sind diese
Einschrnkungen etwas weniger ausgeprgt als bei Applets, und es gibt einige APIs, um in kontrollierter Weise auf
diese Systemressourcen zuzugreifen.
Das Element resources dient dazu, die zum Betrieb der Anwendung erforderlichen Ressourcen zu spezifizieren.
Hier wird angegeben, welche Java-Version zum Starten der Anwendung verwendet werden soll (jede WebStartApplikation kann ihre eigene haben), und welche jar-Dateien oder plattformspezifischen Libraries bentigt werden.
In unserem Beispiel soll die Anwendung mit dem JRE 1.3 oder hher betrieben werden, und die zum Betrieb
erforderliche jar-Datei wstest.jar soll relativ zur codebase geladen werden.
Das letzte Element application-desc gibt an, wie die Applikation zu starten ist. In unserem Beispiel wird
lediglich der Name der Klasse mit der main-Methode bentigt. Zustzlich knnten hier bei Bedarf argumentUnterelemente angegeben werden, die beim Aufruf als Parameter an die Anwendung bergeben werden.
Anmerkung: das offline-allowed-Element wird hier nur deshalb dupliziert, weil es auf Grund eines
Bugs in der zum JDK 1.4 gehrenden WebStart-Version 1.0.1 nicht vererbt wurde. Laut BugParade sollte
dieser Fehler ab WebStart 1.2 behoben sein.
Warnung
Hinweis
Wir wollen nun noch eine einfache HTML-Datei wstest.html zum Starten der WebStart-Applikation erstellen. Sie enthlt
im wesentlichen einen Link auf die jnlp-Datei:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
<html>
<head>
<title>HJP3 WebStart Demo Application</title>
</head>
<body>
<h2>HJP3 WebStart Demo Application</h2>
<a href="wstest.jnlp">Anwendung laden/starten</a>
</body>
</html>
selbsterklrend.
Wird eine WebStart-Anwendung das zweite Mal gestartet, fragt der Anwendungsmanager, ob die Anwendung auf dem
Desktop bzw. im Startmen verankert werden soll. Stimmt der Anwender zu, kann sie anschlieend auch auf diesem Weg
gestartet werden.
Nachdem die Anwendung im lokalen Cache gespeichert wurde, kann sie auch offline betrieben werden, d.h.
Hinweis
ohne den auf Port 7777 laufenden Webserver. Sie kann dann zwar nicht mehr ber den Link im Browser
gestartet werden, aber die drei anderen Wege stehen ihr noch offen. Stellt WebStart beim Aufruf einer
Applikation fest, da die in der jnlp-Datei genannten Ressourcen nicht erreichbar sind, verwendet er die lokalen
Kopien und startet die Anwendung aus dem Cache. Das ist allerdings nur mglich, wenn in der jnlp-Datei das
Element offline-allowed gesetzt wurde. War das nicht der Fall, kann die Anwendung nicht offline
gestartet werden.
Der unkontrollierte Zugriff auf das lokale Dateisystem ist nicht erlaubt
Alle jar-Dateien mssen von demselben Host geladen werden
Netzwerkverbindungen drfen nur mit dem Host betrieben werden, vom dem die jar-Dateien geladen wurden
Native Libraries sind nicht erlaubt
Der Zugriff auf System-Properties ist eingeschrnkt
Damit unsignierte WebStart-Anwendungen dennoch ein gewisses Ma an praxisrelevanter Funktionalitt anbieten knnen,
stellt die WebStart-Laufzeitumgebung einige Services zur Verfgung, ber die Anwendungen in kontrollierter Weise auf
Systemressourcen zugreifen knnen:
Der BasicService stellt (hnlich dem AppletContext) Methoden zur Interaktion mit der Laufzeitumgebung
zur Verfgung
Der ClipboardService bietet Zugriff auf die systemweite Zwischenablage
Der DownloadService erlaubt einer Applikation zu kontrollieren, wie ihre Ressourcen heruntergeladen und
gecached werden
Mit dem FileOpenService kann die Anwendung mit Hilfe eines Dateiffnen-Dialogs lokale Dateien lesen
Mit dem FileSaveService kann die Anwendung mit Hilfe eines DateiSpeichern-Dialogs lokale Dateien
schreiben
Der PrintService erlaubt der Anwendung, auf den Drucker zuzugreifen
Der PersistenceService erlaubt das Erzeugen und Lesen lokaler persistenter Daten (hnlich den Cookies in
Web-Browsern)
All diese Services sind insofern als relativ sicher anzusehen, als sie dem Programm entweder nur sehr
beschrnkten Zugriff auf Systemressourcen erlauben (z.B. ClipboardService,
PersistenceService) oder der Anwender per GUI-Dialog ihrer Benutzung zustimmen mu und somit
selbst entscheiden kann, welche konkreten Ressourcen verwendet werden (z.B. FileOpenService,
FileSaveService, PrintService).
Dennoch sollten unsignierte WebStart-Applikationen aus potentiell unsicheren Quellen mit der ntigen
Vorsicht verwendet werden. Einerseits ist nicht auszuschlieen, da man als Anwender im Eifer des Gefechts
einmal unbeabsichtigt einer Service-Nutzung zustimmt, die spter Schaden anrichtet. Andererseits sind Denialof-Service-Attacken nicht auszuschlieen. Hierbei verwendet eine bsartige Anwendung eine Ressource in so
hohem Mae, da sie fr den ordnungsgemen Betrieb anderer Anwendungen nicht mehr ausreichend zur
Verfgung steht. Beispiele sind etwa Programme, die 100 Prozent CPU-Last verursachen oder solche, die das
Dateisystem durch Erzeugen riesiger Dateien zum berlauf bringen. Gegen derartige Programme bieten auch
die Service-Einschrnkungen der WebStart-Applikationen keinen Schutz.
Warnung
javax.jnlp.ServiceManager
Die Methode lookup erwartet einen String mit dem Servicenamen als Argument. Dies mu ein qualifizierter
Klassenname sein, etwa "javax.jnlp.FileOpenService" oder "javax.jnlp.PrintService". Kann der Service nicht zur
Verfgung gestellt werden (die meisten Services sind optional und mssen nicht von allen WebStart-Clients implementiert
werden), wird eine Ausnahme des Typs UnavailableServiceException ausgelst. Andernfalls wird der
Rckgabewert auf den gewnschten Typ konvertiert und kann dann verwendet werden.
Alle Klassen aus dem jnlp-API befinden sich im Paket javax.jnlp. Dieses ist - auch in der Version 1.4 nicht Bestandteil des JDKs, sondern mu separat heruntergeladen werden. Es ist wie alle anderen WebStartRessourcen im Download-Bereich von http://java.sun.com/products/javawebstart/index.html zu finden.
Bentigt wird meist lediglich die Datei jnlp.jar, die zum Kompilieren in den Klassenpfad einzubinden ist. Zur
Laufzeit mu sie nicht explizit angegeben werden, sondern wird vom WebStart-Applikationsmanager zur
Verfgung gestellt.
Hinweis
Die nun folgenden Schritte sind Service-spezifisch. Der FileOpenService stellt beispielsweise folgende Methode zur
Verfgung, um einen Dateiffnen-Dialog zu erzeugen:
public FileContents openFileDialog(
java.lang.String pathHint,
java.lang.String[] extensions
)
throws java.io.IOException
javax.jnlp.FileOpenService
Das erste Argument pathhint ist ein Vorgabewert der Anwendung fr das Verzeichnis, aus dem die Datei geladen
werden soll. Das zweite Argument enthlt eine Liste von Datei-Erweiterungen, die standardmig angezeigt werden. Der
Rckgabewert ist entweder null, falls der Dialog abgebrochen wurde, oder er enthlt ein FileContents-Objekt fr
die vom Anwender ausgewhlte Datei. Dieses kann verwendet werden, um Informationen ber die Datei abzufragen sowie
um Eingabe- oder Ausgabestreams zum Lesen und Schreiben der Datei zu beschaffen.
Wichtige Methoden der Klasse FileContents sind:
javax.jnlp.FileContents
getName liefert den Namen der Datei, und mit getLength kann ihre Lnge bestimmt werden. Mit canRead und
canWrite kann festgestellt werden, ob Lese- bzw. Schreibzugriffe erlaubt sind. getInputStream beschafft einen
InputStream, mit dem die Datei gelesen werden kann, und getOutputStream stellt einen OutputStream zum
Schreiben der Datei zur Verfgung. Das Argument overwrite gibt dabei an, ob der bestehende Inhalt berschrieben
oder die neuen Daten an das Ende der bestehenden Datei angehngt werden sollen.
Nach diesen Vorbemerkungen knnen wir uns nun das Beispielprogramm ansehen:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
/* WebStartTest2.java */
import java.io.*;
import javax.jnlp.*;
public class WebStartTest2
{
public static void main(String[] args)
{
try {
//FileOpenService anfordern
FileOpenService fos = (FileOpenService)ServiceManager.lookup(
"javax.jnlp.FileOpenService"
);
//Dateiffnen-Dialog aufrufen
FileContents fc = fos.openFileDialog(null, null);
if (fc == null) {
System.err.println("openFileDialog fehlgeschlagen");
} else {
//Dateiinhalt auf der Konsole ausgeben
InputStream is = fc.getInputStream();
int c;
while ((c = is.read()) != -1) {
System.out.print((char)c);
}
is.close();
}
} catch (UnavailableServiceException e) {
System.err.println("***" + e + "***");
} catch (IOException e) {
System.err.println("***" + e + "***");
}
//10 Sekunden warten, dann Programm beenden
try {
WebStartTest2.java
035
Thread.sleep(10000);
036
} catch (InterruptedException e) {
037
}
038
System.exit(0);
039
}
040 }
Listing 13.12: Ein Beispielprogramm fr das jnlp-API
Das Programm kann wie im vorigen Abschnitt gezeigt bersetzt, in eine WebStart-Applikation verpackt und ausgefhrt
werden. Wenn die Konsole im WebStart-Applikationsmanager aktiviert wurde, kann mit dem Programm jede beliebige
Datei gelesen werden - vorausgesetzt, der Anwender stimmt im Dateiffnen-Dialog zu.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Danach stehen alle Klassen aus dem Paket java.awt zur Verfgung.
Zur Ausgabe von grafischen Elementen bentigt die Anwendung ein Fenster, auf das die Ausgabeoperationen
angewendet werden knnen. Whrend bei der Programmierung eines Applets ein Standardfenster automatisch zur
Verfgung gestellt wird, mu eine Applikation ihre Fenster selbst erzeugen. Da die Kommunikation mit einem
Fenster ber eine Reihe von Callback-Methoden abgewickelt wird, wird eine Fensterklasse in der Regel nicht einfach
instanziert. Statt dessen ist es meist erforderlich, eine eigene Klasse aus einer der vorhandenen abzuleiten und die
bentigten Interfaces zu implementieren.
Zum Ableiten einer eigenen Fensterklasse wird in der Regel entweder die Klasse Frame oder die Klasse Dialog
verwendet, die beide aus Window abgeleitet sind. Da Dialog vorwiegend dafr verwendet wird, Dialogboxen zu
erstellen, die ber darin enthaltene Komponenten mit dem Anwender kommunizieren, wollen wir ihre Verwendung
bis zum Kapitel 31 zurckstellen. Die wichtigste Klasse zur Ausgabe von Grafiken in Java-Applikationen ist also
Frame.
Um ein einfaches Fenster zu erzeugen und auf dem Bildschirm anzuzeigen, mu ein neues Element der Klasse
Frame erzeugt, auf die gewnschte Gre gebracht und durch Aufruf der Methode setVisible sichtbar gemacht
werden:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
/* Listing2301.java */
Listing2301.java
import java.awt.*;
class Listing2301
{
public static void main(String[] args)
{
Frame wnd = new Frame("Einfaches Fenster");
wnd.setSize(400,300);
wnd.setVisible(true);
}
}
java.awt.Component
Graphics ist Javas Implementierung eines Device-Kontexts (auch Grafikkontext genannt) und stellt somit die
Abstraktion eines universellen Ausgabegerts fr Grafik und Schrift dar. Die Klasse bietet Methoden zur Erzeugung
von Linien-, Fll- und Textelementen. Darber hinaus verwaltet Graphics die Zeichenfarbe, in der alle Ausgaben
erfolgen, und einen Font, der zur Ausgabe von Schrift verwendet wird. Ein Device-Kontext kann daher als eine Art
universelles Ausgabegert angesehen werden, das elementare Funktionen zur Ausgabe von farbigen Grafik- und
Schriftzeichen zur Verfgung stellt.
/* WindowClosingAdapter.java */
import java.awt.*;
import java.awt.event.*;
public class WindowClosingAdapter
extends WindowAdapter
{
private boolean exitSystem;
/**
* Erzeugt einen WindowClosingAdapter zum Schliessen
* des Fensters. Ist exitSystem true, wird das komplette
* Programm beendet.
*/
public WindowClosingAdapter(boolean exitSystem)
{
this.exitSystem = exitSystem;
}
/**
* Erzeugt einen WindowClosingAdapter zum Schliessen
* des Fensters. Das Programm wird nicht beendet.
WindowClosingAdapter.java
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038 }
*/
public WindowClosingAdapter()
{
this(false);
}
public void windowClosing(WindowEvent event)
{
event.getWindow().setVisible(false);
event.getWindow().dispose();
if (exitSystem) {
System.exit(0);
}
}
Listing2303.java
/* Listing2303.java */
import java.awt.*;
class Listing2303
{
public static void main(String[] args)
{
Frame wnd = new Frame("Fenster schlieen");
wnd.addWindowListener(new WindowClosingAdapter(true));
wnd.setSize(400,300);
wnd.setVisible(true);
}
}
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Hinweis
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Die Klasse Graphics stellt neben vielen anderen Funktionen auch eine Sammlung von
linienbasierten Zeichenoperationen zur Verfgung. Diese sind zur Darstellung von einfachen Linien,
Rechtecken oder Polygonen sowie von Kreisen, Ellipsen und Kreisabschnitten geeignet. Wir wollen im
folgenden jede dieser Funktionsgruppen vorstellen und ihre Anwendung an einem Beispiel zeigen.
Um nicht jeweils eine komplette Klassendefinition angeben zu mssen, werden wir in den
folgenden Beispielen jeweils nur die Implementierung der paint-Methode zeigen. Diese
knnte dann beispielsweise in das folgende Programm eingebettet werden (auf der CD
haben diese Dateien die Erweiterung .inc):
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
/* GrafikBeispiel.java */
import java.awt.*;
import java.awt.event.*;
public class GrafikBeispiel
extends Frame
{
public static void main(String[] args)
{
GrafikBeispiel wnd = new GrafikBeispiel();
}
public GrafikBeispiel()
{
super("GrafikBeispiel");
addWindowListener(new WindowClosingAdapter(true));
setBackground(Color.lightGray);
setSize(300,200);
setVisible(true);
}
Hinweis
GrafikBeispiel.java
023
public void paint(Graphics g)
024
{
025
//wird in den folgenden Beispielen berlagert
026
}
027 }
Listing 23.4: Rahmenprogramm fr nachfolgende Beispiele
Da die paint-Methode in diesem Programm noch keine Ausgabeoperationen enthlt, erzeugt das
Programm lediglich ein leeres Fenster mit dem Titel Grafikbeispiel:
Hinweis
23.3.1 Linie
public void drawLine(int x1, int y1, int x2, int y2)
java.awt.Graphics
Zieht eine Linie von der Position (x1,y1) zur Position (x2,y2). Beide Punkte drfen an beliebiger
Stelle im Fenster liegen, das Einhalten einer bestimmten Reihenfolge ist nicht erforderlich. Teile der
Ausgabe, die auerhalb des darstellbaren Bereichs liegen, werden, wie in grafikorientierten Systemen
blich, unterdrckt.
Das folgende Beispiel zeichnet eine Reihe von gleich hohen Linien, deren horizontaler Abstand durch
einen Zufallszahlengenerator bestimmt wird. Das Ergebnis hat dadurch hnlichkeit mit einem Barcode
(ist aber keiner):
001
002
003
004
005
006
007
008
009
010
011
012
Linien.inc
/* Linien.inc */
public void paint(Graphics g)
{
int i;
int x = 80;
for (i=0; i<60; ++i) {
g.drawLine(x,40,x,100);
x += 1+3*Math.random();
}
}
Hinweis
23.3.2 Rechteck
public void drawRect(int x, int y, int width, int height)
java.awt.Graphics
Zeichnet ein Rechteck der Breite width und der Hhe height, dessen linke obere Ecke an der
Position (x,y) liegt. Eine grere Breite dehnt das Rechteck nach rechts aus, eine grere Hhe nach
unten.
Eine Variante von drawRect ist die Methode drawRoundRect:
public
int
int
int
)
void drawRoundRect(
x, int y,
width, int height,
arcWidth, int arcHeight
java.awt.Graphics
Gegenber drawRect sind hier die Parameter arcWidth und arcHeight dazugekommen. Sie
bestimmen den horizontalen und vertikalen Radius des Ellipsenabschnitts, der zur Darstellung der
runden Ecke verwendet wird.
Das folgende Beispiel zeichnet eine Kette von nebeneinanderliegenden Rechtecken, deren Gre durch
einen Zufallszahlengenerator bestimmt wird. Der Zufallszahlengenerator entscheidet auch, ob ein
Rechteck an der Ober- oder Unterseite seines Vorgngers festgemacht wird:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
/* Rechtecke.inc */
public void paint(Graphics g)
{
int x = 10, y = 80;
int sizex, sizey = 0;
while (x < 280 && y < 180) {
sizex = 4 + (int) (Math.random() * 9);
if (Math.random() > 0.5) {
y += sizey;
sizey = 4 + (int) (Math.random() * 6);
} else {
sizey = 4 + (int) (Math.random() * 6);
y -= sizey;
}
g.drawRect(x,y,sizex,sizey);
x += sizex;
}
}
Rechtecke.inc
23.3.3 Polygon
Mit Hilfe der Methode drawPolygon ist es mglich, Linienzge zu zeichnen, bei denen das Ende
eines Elements mit dem Anfang des jeweils nchsten verbunden ist:
public void drawPolygon(int[] arx, int[] ary, int cnt)
java.awt.Graphics
drawPolygon erwartet drei Parameter. Der erste ist ein Array mit einer Liste der x-Koordinaten und
der zweite ein Array mit einer Liste der y-Koordinaten. Beide Arrays mssen so synchronisiert sein,
da ein Paar von Werten an derselben Indexposition immer auch ein Koordinatenpaar ergibt. Die
Anzahl der gltigen Koordinatenpaare wird durch den dritten Parameter festgelegt.
Im Gegensatz zum JDK 1.0 wird das Polygon nach Abschlu der Ausgabe automatisch
Hinweis
geschlossen. Falls der erste und der letzte Punkt nicht identisch sind, werden diese durch
eine zustzliche Linie miteinander verbunden. Soll dagegen ein nichtgeschlossenes Polygon
gezeichnet werden, so kann dazu die Methode drawPolyline verwendet werden:
public void drawPolyline(int[] arx, int[] ary, int cnt)
java.awt.Graphics
Eine zweite Variante, Polygone zu zeichnen, besteht darin, zunchst ein Objekt der Klasse Polygon
zu konstruieren und dieses dann an drawPolygon zu bergeben. Polygon besitzt zwei
Konstruktoren, von denen einer parameterlos ist und der andere dieselben Parameter wie die oben
beschriebene Methode drawPolygon besitzt:
public void Polygon()
java.awt.Graphics
Mit Hilfe der Methode addPoint kann ein Polygon um weitere Punkte erweitert werden. Schlielich
kann das fertige Polygon an die Methode drawPolygon bergeben werden, die dann wie folgt
aufzurufen ist:
java.awt.Graphics
Das folgende Beispiel gibt den Buchstaben F mit Hilfe eines geschlossenen Polygons aus:
001
002
003
004
005
006
007
008
009
/* Polygon.inc */
Polygon.inc
Warnung
23.3.4 Kreis
Die Graphics-Klasse von Java erlaubt sowohl das Zeichnen von Kreisen als auch von Ellipsen und
Kreisabschnitten. Ein Kreis wird dabei als Verallgemeinerung einer Ellipse angesehen, und beide
java.awt.Graphics
Anders als in anderen Grafiksystemen werden bei dieser Methode nicht der Mittelpunkt und Hinweis
der Radius des Kreises angegeben, sondern die bergebenen Parameter spezifizieren ein
Rechteck der Gre width und heigth, dessen linke obere Ecke an der Position (x,y)
liegt. Gezeichnet wird dann der grte Kreis, der vollstndig in das Rechteck hineinpat.
Da diese Vorgehensweise zwar untypisch ist, aber durchaus ihre Vorteile haben kann, zeigt das
folgende Beispiel:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
/* Kreise.inc */
public void paint(Graphics g)
{
int r = 8;
int i, j;
int x, y;
for (i=1; i<=10; ++i) {
x = 150 - r * i;
y = (int) (40 + (i - 1) * 1.7321 * r);
for (j=1; j<=i; ++j) {
g.drawOval(x,y,2*r,2*r);
x += 2 * r;
}
}
}
Kreise.inc
23.3.5 Kreisbogen
Ein Kreisbogen ist ein zusammenhngender Abschnitt der Umfangslinie eines Kreises. Er kann mit der
Methode drawArc gezeichnet werden:
public void drawArc(
int x, int y, int width, int height,
int startAngle, int arcAngle
)
java.awt.Graphics
Die ersten vier Parameter bezeichnen dabei den Kreis bzw. die Ellipse so, wie dies auch bei
drawOval der Fall war. Mit startAngle wird der Winkel angegeben, an dem mit dem
Kreisabschnitt begonnen werden soll, und arcAngle gibt den zu berdeckenden Bereich an. Dabei
bezeichnet ein Winkel von 0 Grad die 3-Uhr-Position, und positive Winkel werden entgegen dem
Uhrzeigersinn gemessen. Als Einheit wird Grad verwendet und nicht das sonst bliche Bogenma.
Das folgende Beispiel zeichnet durch wiederholten Aufruf der Methode drawArc eine Ellipse mit
einer gestrichelten Umfangslinie. Ein Aufruf zeichnet dabei jeweils 4 Grad der Ellipse und lt dann
eine Lcke von 3 Grad, bevor das nchste Stck gezeichnet wird:
001
002
003
004
005
006
007
008
009
010
011
012
013
/* KreisBoegen.inc */
public void paint(Graphics g)
{
int line = 4;
int gap = 3;
int angle = 0;
while (angle < 360) {
g.drawArc(20,40,250,140,angle,line);
angle += gap + line;
}
}
KreisBoegen.inc
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
java.awt.Graphics
Das nachfolgende Beispiel verwendet die Methoden fillRect, fillPolygon, fillOval und fillArc,
um einen stilisierten Java-Schriftzug anzuzeigen:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
/* GefuellteFlaechen.inc */
GefuellteFlaechen.inc
Tip
java.awt.Graphics
void copyArea(
x, int y,
width, int height,
dx, int dy
Die Methode clearRect lscht das angegebene Rechteck, indem sie den Bereich mit der aktuellen
Hintergrundfarbe ausfllt. (x,y) bezeichnet die linke obere Ecke, (width,height) die Breite und Hhe
des Fensters.
Soll ein Teil des Fensters kopiert werden, kann dazu die Methode copyArea verwendet werden. Die ersten
vier Parameter bezeichnen in der blichen Weise den zu kopierenden Ausschnitt. (dx,dy) gibt die Entfernung
des Zielrechtecks in x- und y-Richtung an. copyArea kopiert das ausgewhlte Rechteck also immer an die
Position (x+dx, y+dy).
Das folgende Beispiel demonstriert die Anwendung von copyArea anhand eines Programms, das den
Bildschirm (hnlich dem Windows 95-Hintergrund Labyrinth) mit einem Muster fllt:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
/* Kopieren.inc */
public void paint(Graphics g)
{
int xorg = 4;
int yorg = 28;
int[] arx = {0,6,6,2,2,4,4,0,0};
int[] ary = {0,0,6,6,4,4,2,2,8};
for (int i = 0; i < arx.length; ++i) {
arx[i] += xorg;
ary[i] += yorg;
}
g.drawPolyline(arx,ary,arx.length);
for (int x = 0; x <= 300; x += 8) {
for (int y = 0; y <= 160; y += 8) {
if (x != 0 || y != 0) {
g.copyArea(xorg,yorg,8,8,x,y);
}
}
}
}
Kopieren.inc
java.awt.Graphics
Ein Aufruf dieser Methode begrenzt die Clipping-Region auf die Schnittmenge zwischen dem
angegebenen Rechteck und ihrer vorherigen Gre. Da eine Schnittmenge niemals grer werden
kann als eine der Mengen, aus denen sie gebildet wurde, kann clipRect also lediglich dazu
verwendet werden, die Clipping-Region zu verkleinern.
Hinweis
Soll die Clipping-Region auf einen beliebigen Bereich innerhalb des aktuellen Fensters ausgedehnt werden (der
dabei auch grer sein kann als die bisherige Clipping-Region), so kann dazu die Methode setClip verwendet
werden, die in zwei verschiedenen Ausprgungen zur Verfgung steht:
public abstract void setClip(int x, int y, int width, int height)java.awt.Graphics
public abstract void setClip(Shape clip)
Die erste Version bergibt ein Rechteck, das die Gre der gewnschten Clipping-Region in Client-Koordinaten
angibt. Die zweite Variante erlaubt die bergabe eines Objekts, welches das Shape-Interface implementiert.
Das Interface besitzt derzeit jedoch nur die Methode getBounds, mit der das umschlieende Rechteck
ermittelt werden kann:
public abstract Rectangle getBounds()
java.awt.Shape
Da auer Rechtecken derzeit keine anders geformten Shape-Objekte zur Definition der Clipping-Region
untersttzt werden, wollen wir hier nicht weiter auf die zweite Variante von setClip eingehen.
Soll die Ausdehnung der aktuellen Clipping-Region ermittelt werden, so kann dazu eine der Methoden
getClip oder getClipBounds verwendet werden:
public abstract Shape getClip()
java.awt.Graphics
Dabei liefert getClip ein Shape-Objekt, das derzeit denselben Einschrnkungen unterliegt wie das beim
Aufruf von setClip bergebene. getClipBounds liefert das kleinste Rechteck, das die aktuelle ClippingRegion vollstndig umschliet.
Das folgende Beispiel wiederholt die Ausgabe des Java-Logos, grenzt aber vorher die Clipping-Region auf ein
Fenster der Gre 150*80 Pixel ein, das seinen Ursprung an Position (50,50) hat:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
/* Clipping.inc */
public void paint(Graphics g)
{
int[] arx = {150,175,200,150};
int[] ary = {100,150,100,100};
g.setClip(50,50,150,80);
//---J
g.fillRect(70,40,20,80);
g.fillArc(30,90,60,60,225,180);
//---a
g.fillOval(100,100,40,50);
g.fillRect(120,100,20,50);
//---v
g.fillPolygon(arx,ary,arx.length);
//---a
g.fillOval(210,100,40,50);
g.fillRect(230,100,20,50);
}
Clipping.inc
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
24.1 Ausgabefunktionen
24.1 Ausgabefunktionen
Neben den Methoden zur Ausgabe von Linien- oder Flchengrafiken gibt es in Java die Mglichkeit, Text in
einem Fenster auszugeben. Die dafr vorgesehenen Methoden drawString, drawChars und drawBytes
gehren ebenfalls zur Klasse Graphics:
public void drawString(
String str, int x, int y
)
java.awt.Graphics
Die einfachste Methode, Text auszugeben, besteht darin, drawString aufzurufen und dadurch den String str
im Grafikfenster an der Position (x,y) auszugeben. Das Koordinatenpaar (x,y) bezeichnet dabei das linke
Ende der Basislinie des ersten Zeichens in str. Die Bedeutung der Basislinie wird spter bei der Beschreibung
der Font-Metriken erlutert.
In dem folgenden Beispiel wird versucht, einen String, der die Gre der Client-Area angibt, zentriert im
Ausgabefenster auszugeben (wir verwenden immer noch Listing 23.4 aus Kapitel 23):
001
002
003
004
005
006
007
008
009
010
011
012
013
/* Textausgabe.inc */
public void paint(Graphics g)
{
int maxX=getSize().width-getInsets().left-getInsets().right;
int maxY=getSize().height-getInsets().top-getInsets().bottom;
g.drawString(
"Die Client-Area ist "+maxX+"*"+maxY+" Pixel gro",
getInsets().left + maxX/2,
getInsets().top + maxY/2
);
}
Textausgabe.inc
Hinweis
Die Methode drawString ist leider nicht besonders vielseitig, denn sie kann Text weder drehen noch clippen.
Sie interpretiert auch keine eingebetteten Formatierungen, wie beispielsweise einen Zeilenumbruch oder einen
Tabulator. Da es im Windows-API all diese und noch weitere Mglichkeiten gibt, zeigt sich hier wie auch an
anderen Stellen des AWT, da eine Java-Portierung in der Regel nicht alle Funktionen des darunterliegenden
Betriebssystems zur Verfgung stellen kann. Das 2D-API des JDK 1.2 stellt darber hinaus viele zustzliche
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
24.2.1 Font-Objekte
Fhrt man die Schriftausgabe wie bisher besprochen durch, werden die Texte in einem systemabhngigen StandardFont ausgegeben. Soll ein anderer Font zur Textausgabe verwendet werden, so mu zunchst ein passendes Objekt der
Klasse Font erzeugt und dem verwendeten Graphics-Objekt zugewiesen werden:
public void setFont(Font font)
java.awt.Graphics
Die Methode setFont wird dazu verwendet, das Font-Objekt font in den Grafikkontext einzutragen, und mit
getFont kann der aktuelle Font abgefragt werden.
Das Erzeugen neuer Font-Objekte wird ber die drei Parameter name, style und size des Konstruktors der
Klasse Font gesteuert:
public Font(String name, int style, int size)
java.awt.Font
Der Parameter name gibt den Namen des gewnschten Fonts an. In allen Java-Systemen sollten die Namen
SansSerif (frher Helvetica), Serif (frher TimesRoman) und Monospaced (frher Courier)
untersttzt werden. Sie stehen fr die systemspezifischen Proportionalzeichenstze der Familien Helvetica und
TimesRoman bzw. fr die nichtproportionale Familie Courier. Unter Windows werden diese Standardnamen auf
die True-Type-Fonts Arial, Times New Roman und Courier New abgebildet.
Der zweite und dritte Parameter des Konstruktors sind beide vom Typ int. Ein beliebter Fehler besteht
darin, beide zu verwechseln und so die Angaben fr die Gre und die Textattribute zu vertauschen.
Leider kann der Fehler vom Compiler nicht gefunden werden, sondern wird frhestens zur Laufzeit
entdeckt. Selbst dann wird er leicht mit dem Fall verwechselt, da die gewnschten Schriftarten bzw.
Attribute auf dem Zielsystem nicht installiert sind. Beim Erzeugen von Font-Objekten ist also einige
Vorsicht geboten.
Warnung
Schriften sind generell recht unportabel, deshalb ist bei ihrer Verwendung Vorsicht angebracht.
Warnung
Insbesondere bei der Verwendung von systemspezifischen Schriftarten kann es sein, da der Font-Mapper
eines anderen Systems eine vllig verkehrte Schrift auswhlt und die Ausgabe des Programms dann
unbrauchbar wird. Werden nur die genannten Standardschriften verwendet, so sollte die Schriftausgabe
auf allen untersttzten Java-Systemen zumindest lesbar bleiben. Die alten Schriftnamen Helvetica,
TimesRoman und Courier aus dem JDK 1.0 werden zwar noch untersttzt, sind aber als
deprecated gekennzeichnet und sollten daher nicht mehr verwendet werden.
Der Parameter style wird verwendet, um auszuwhlen, ob ein Font in seiner Standardausprgung, fett oder kursiv
angezeigt werden soll. Java stellt dafr die in Tabelle 24.1 aufgefhrten numerischen Konstanten zur Verfgung. Die
Werte BOLD und ITALIC knnen auch gemeinsam verwendet werden, indem beide Konstanten addiert werden.
Name
Wert
Bedeutung
Font.PLAIN
Standard-Font
Font.BOLD
Fett
Font.ITALIC
Kursiv
/* Schriften.inc */
public void paint(Graphics g)
{
Font font;
String[] arfonts = {"Serif","SansSerif","Monospaced"};
for (int i=0; i<arfonts.length; ++i) {
font = new Font(arfonts[i],Font.PLAIN,36);
g.setFont(font);
g.drawString(arfonts[i],10,30 + (i+1)*(36+5));
}
}
Schriften.inc
Die Abbildung von Schriftnamen im JDK auf die dazu passenden Schriften des Betriebssystems wird
durch die Datei font.properties im lib-Verzeichnis des JDK gesteuert, die zur Laufzeit vom AWT
interpretiert wird. Werden hier Anpassungen vorgenommen, so ndert sich die Darstellung der
Standardschriften.
Hinweis
24.2.2 Standardschriftarten
Es gibt im AWT eine Klasse Toolkit, die als Hilfsklasse bei der Abbildung der portablen AWT-Eigenschaften
dient. Toolkit ist standardmig abstrakt, wird aber von jeder AWT-Implementierung konkretisiert und den
Anwendungen ber die Klassenmethode getDefaultToolkit zur Verfgung gestellt. Whrend die meisten
Methoden von Toolkit nur fr Implementatoren von AWT-Portierungen von Interesse sind, gibt es auch einige
Methoden, die in der Anwendung sinnvoll verwendet werden knnen. Eine dieser Methoden ist getFontList:
java.awt.Toolkit
Diese Methode liefert ein Array von Strings mit den Namen der Standardschriftarten, die in der vorliegenden AWTImplementierung verfgbar sind.
Die folgende paint-Methode listet alle verfgbaren Standardschriften auf dem Bildschirm auf:
001
002
003
004
005
006
007
008
009
010
011
012
013
Standardschriften.inc
/* Standardschriften.inc */
public void paint(Graphics g)
{
Font font;
String[] arfonts = Toolkit.getDefaultToolkit().getFontList();
for (int i=0; i<arfonts.length; ++i) {
font = new Font(arfonts[i],Font.PLAIN,36);
g.setFont(font);
g.drawString(arfonts[i],10,(i+1)*(36+5));
}
}
JDK1.1-1.4
java.awt.GraphicsEnvironment
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
Standardschriften12.inc
/* Standardschriften12.inc */
public void paint(Graphics g)
{
Font font;
GraphicsEnvironment ge =
GraphicsEnvironment.getLocalGraphicsEnvironment();
String[] arfonts = ge.getAvailableFontFamilyNames();
for (int i=0; i<arfonts.length; ++i) {
font = new Font(arfonts[i],Font.PLAIN,36);
g.setFont(font);
g.drawString(arfonts[i],10,(i+1)*(36+5));
}
}
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
24.3.1 Font-Informationen
Die Klasse Font besitzt Methoden, um Informationen ber den aktuellen Font zu gewinnen:
java.awt.Font
getFamily liefert den systemspezifischen Namen eines Fonts, getStyle den Style-Parameter und
getSize die Gre des Fonts. Auf diese Weise lt sich ein neues Font-Objekt als Variation eines
bestehenden Fonts erzeugen. Das folgende Beispiel zeigt eine paint-Methode, die den zur Anzeige
verwendeten Font bei jedem Aufruf um 1 Punkt vergrert:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
/* SchriftGroesser.inc */
SchriftGroesser.inc
Hinweis
24.3.2 Font-Metriken
Oft bentigt man Informationen ber die Gre eines einzelnen Zeichens oder eines kompletten Strings. Anders
als in textbasierten Systemen ist die Gre von Schriftzeichen bei der Programmierung unter einer grafischen
Oberflche nmlich nicht konstant, sondern von der Art des Fonts, dem Ausgabegert und vor allem von der
Breite der Zeichen selbst abhngig.
Das Grenmodell von Java sieht fr ein Textzeichen fnf unterschiedliche Mazahlen vor (siehe Abbildung
24.5), die teilweise zu den im Schriftsatz verwendeten Begriffen kompatibel sind. Wichtigste Bezugsmarke fr
die verschiedenen Mae ist dabei die Grundlinie des Zeichens, die den oberen und unteren Teil voneinander
trennt. Die Unterlnge gibt die Lnge zwischen Grundlinie und unterer Begrenzung des Zeichens an. Die
Oberlnge bezeichnet die Lnge zwischen Grundlinie und oberem Rand des Zeichens. Die Breite eines
Zeichens ist der Abstand vom linken Rand des Zeichens bis zum linken Rand des darauffolgenden Zeichens.
Der Zeilenabstand ist der Abstand zwischen dem unteren Rand einer Zeile und dem oberen Rand der nchsten
Zeile. Die Hhe ist die Summe aus Oberlnge, Unterlnge und Zeilenabstand.
java.awt.Toolkit
Die parameterlose Variante dient dazu, Metriken zum aktuellen Font zu ermitteln, an die andere kann der zu
untersuchende Font als Argument bergeben werden. Nun stehen Methoden zur Verfgung, die zur
Bestimmung der genannten Eigenschaften aufgerufen werden knnen. Nachfolgend werden die wichtigsten von
ihnen vorgestellt.
public int charWidth(char ch)
java.awt.FontMetrics
Mit charWidth wird die Breite eines einzelnen Zeichens ch bestimmt, mit stringWidth die eines
kompletten Strings. Der Rckgabewert dieser Methoden wird dabei stets in Bildschirmpixeln angegeben. Bei
der Anwendung von stringWidth werden auch Unterschneidungen oder andere Sonderbehandlungen
bercksichtigt, die bei der Ausgabe der Zeichenkette erfolgen wrden.
java.awt.FontMetrics
getAscent liefert die Oberlnge des Fonts, getDescent die Unterlnge, getLeading den Zeilenabstand
und getHeight die Hhe. Obwohl diese Informationen fr die meisten Zeichen des ausgewhlten Fonts
gltig sind, garantiert Java nicht, da dies fr alle Zeichen der Fall ist. Insbesondere kann es einzelne Zeichen
geben, die eine grere Ober- oder Unterlnge haben. Zur Behandlung dieser Sonderflle gibt es zustzliche
Methoden in FontMetrics, die hier nicht nher behandelt werden sollen.
Das nachfolgende Beispiel zeigt eine paint-Methode, die einige Schriftzeichen in 72 Punkt Gre zusammen
mit einem 10*10 Pixel groen Koordinatengitter ausgibt. Zustzlich gibt das Programm die Font-Metriken aus:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
Fontmetriken.inc
/* Fontmetriken.inc */
public void paint(Graphics g)
{
Font font = new Font("TimesRoman",Font.PLAIN,72);
//---Linien
g.setColor(Color.blue);
for (int x = 10; x <= 260; x += 10) {
g.drawLine(x,30,x,130);
}
for (int y = 30; y <= 130; y += 10) {
g.drawLine(10,y,260,y);
}
//---Schrift
g.setColor(Color.black);
g.drawLine(0,100,270,100);
g.setFont(font);
g.drawString("mgdAW",10,100);
//---Font-Metriken
FontMetrics fm = getFontMetrics(font);
System.out.println("Oberlnge
= " +
System.out.println("Unterlnge
= " +
System.out.println("Hhe
= " +
System.out.println("Zeilenabstand = " +
System.out.println("---");
System.out.println("Breite(m)
= " +
System.out.println("Breite(g)
= " +
System.out.println("Breite(d)
= " +
System.out.println("Breite(A)
= " +
System.out.println("Breite(W)
= " +
System.out.println("---");
}
fm.getAscent());
fm.getDescent());
fm.getHeight());
fm.getLeading());
fm.charWidth('m'));
fm.charWidth('g'));
fm.charWidth('d'));
fm.charWidth('A'));
fm.charWidth('W'));
=
=
=
=
73
16
93
4
=
=
=
=
=
55
35
36
52
68
Als weitere Anwendung der Font-Metriken wollen wir Listing 24.1 wie versprochen komplettieren und den
Text zentriert ausgeben:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
/* Zentriert.inc */
public void paint(Graphics g)
{
int maxX=getSize().width-getInsets().left-getInsets().right;
int maxY=getSize().height-getInsets().top-getInsets().bottom;
String s="Die Client-Area ist "+maxX+"*"+maxY+" Pixel gro";
FontMetrics fm = g.getFontMetrics();
int slen = fm.stringWidth(s);
g.drawString(
s,
getInsets().left + ((maxX - slen)/2),
getInsets().top + (maxY/2)
);
}
Zentriert.inc
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Um Farben bei der Ausgabe von Grafik oder Schrift zu verwenden, mu ein geeignetes ColorObjekt beschafft und dem Graphics-Objekt mit Hilfe der Methode setColor zugewiesen werden.
Die Ausgaben erfolgen dann so lange in der neuen Farbe, bis durch Aufruf von setColor eine
andere Farbe festgelegt wird. Mit Hilfe der Methode getColor kann die aktuelle Farbe ermittelt
werden:
public void setColor(Color c)
java.awt.Graphics
Das folgende Beispiel zeigt die Verwendung von Farben und ihre Zusammensetzung aus den drei
Grundfarben Rot, Grn und Blau. Es stellt das Prinzip der additiven Farbmischung mit Hilfe dreier
berlappender Kreise dar:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
/* Listing2501.java */
import java.awt.*;
import java.awt.event.*;
public class Listing2501
extends Frame
{
public static void main(String[] args)
{
Listing2501 wnd = new Listing2501();
}
public Listing2501()
{
super("Der Farbenkreis");
addWindowListener(new WindowClosingAdapter(true));
setSize(300,200);
setVisible(true);
}
Listing2501.java
022
public void paint(Graphics g)
023
{
024
int top = getInsets().top;
025
int left = getInsets().left;
026
int maxX = getSize().width-left-getInsets().right;
027
int maxY = getSize().height-top-getInsets().bottom;
028
Color col;
029
int[] arx
= {130,160,190};
030
int[] ary
= {60,110,60};
031
int[] arr
= {50,50,50};
032
int[] arcol = {0,0,0};
033
boolean paintit;
034
int dx, dy;
035
036
for (int y = 0; y < maxY; ++y) {
037
for (int x = 0; x < maxX; ++x) {
038
paintit = false;
039
for (int i = 0; i < arcol.length; ++i) {
040
dx = x - arx[i];
041
dy = y - ary[i];
042
arcol[i] = 0;
043
if ((dx*dx+dy*dy) <= arr[i]*arr[i]) {
044
arcol[i] = 255;
045
paintit = true;
046
}
047
}
048
if (paintit) {
049
col = new Color(arcol[0],arcol[1],arcol[2]);
050
g.setColor(col);
051
g.drawLine(x+left,y+top,x+left+1,y+top+1);
052
}
053
}
054
}
055
}
056 }
Listing 25.1: Darstellung des Farbenkreises
Das Programm arbeitet in der Weise, da fr jeden einzelnen Punkt der Zeichenflche
Hinweis
berechnet wird, ob dieser in einem der drei Kreise liegt. Ist dies der Fall, so wird die
zugehrige Farbkomponente auf 255 gesetzt, andernfalls auf 0. Die Bestimmung der
Kreiszugehrigkeit erfolgt mit Hilfe des Satzes von Pythagoras, nach dem ein Punkt genau
dann zu einem Kreis gehrt, wenn die Summe der Quadrate der Abstnde vom x- und yWert zum Mittelpunkt kleiner gleich dem Quadrat des Radius ist. Die drei Mittelpunkte
werden in unserem Beispiel in den Arrays arx und ary, die Radien der Kreise in arr
gespeichert. Die boolesche Variable paintit zeigt an, ob der Punkt in wenigstens einem
der drei Kreise liegt und daher berhaupt eine Ausgabe erforderlich ist.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt Suchen
<
>
Index
>>
DOC
API
25.4 Systemfarben
25.4 Systemfarben
Bedeutung
SystemColor.desktop
SystemColor.activeCaption
SystemColor.activeCaptionText
SystemColor.activeCaptionBorder
SystemColor.inactiveCaption
SystemColor.inactiveCaptionText
Hintergrundfarbe fr Fenster
SystemColor.windowBorder
Farbe fr Fensterrahmen
SystemColor.windowText
SystemColor.menu
Hintergrundfarbe fr Mens
SystemColor.menuText
Textfarbe fr Mens
SystemColor.text
Hintergrundfarbe fr Textfelder
SystemColor.textText
Textfarbe fr Textfelder
SystemColor.textHighlight
SystemColor.textHighlightText
SystemColor.textInactiveText
SystemColor.control
Hintergrundfarbe fr Dialogelemente
SystemColor.controlText
Textfarbe fr Dialogelemente
SystemColor.controlHighlight
SystemColor.controlLtHighlight
SystemColor.controlShadow
SystemColor.controlDkShadow
SystemColor.scrollbar
Hintergrundfarbe fr Schieberegler
SystemColor.info
Hintergrundfarbe fr Hilfetext
SystemColor.infoText
Textfarbe fr Hilfetext
/* Listing2502.java */
import java.awt.*;
import java.awt.event.*;
public class Listing2502
extends Frame
{
public static void main(String[] args)
{
Listing2502 wnd = new Listing2502();
}
Listing2502.java
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049 }
public Listing2502()
{
super("Systemfarben");
setBackground(SystemColor.desktop);
setSize(200,100);
setVisible(true);
addWindowListener(new WindowClosingAdapter(true));
}
public void paint(Graphics g)
{
g.setFont(new Font("Serif",Font.PLAIN,36));
FontMetrics fm = g.getFontMetrics();
int sheight
= fm.getHeight();
int curx
= 10;
int cury
= getInsets().top + 10;
//"Tag" in normaler Textfarbe
int swidth = fm.stringWidth("Tag");
g.setColor(SystemColor.text);
g.fillRect(curx,cury,swidth,sheight);
g.setColor(SystemColor.textText);
g.drawString("Tag",curx,cury+fm.getAscent());
//"&" in Blau auf normalem Hintergrund
curx += swidth + 5;
swidth = fm.stringWidth("&");
g.setColor(Color.blue);
g.drawString("&",curx,cury+fm.getAscent());
//"Nacht" in hervorgehobener Textfarbe
curx += swidth + 5;
swidth = fm.stringWidth("Nacht");
g.setColor(SystemColor.textHighlight);
g.fillRect(curx,cury,swidth,sheight);
g.setColor(SystemColor.textHighlightText);
g.drawString("Nacht",curx,cury+fm.getAscent());
}
Ein weiteres Beispiel zur Verwendung der Systemfarben findet sich in Kapitel 32. Das zu Hinweis
dem Dialogelement ScrollPane vorgestellte Beispielprogramm zeigt alle
Systemfarben zusammen mit ihrem Namen in einem verschiebbaren Fenster an.
Titel
<<
Inhalt Suchen
<
>
Index
DOC
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
26.2.1 Grundlagen
Das Druck-API der Version 1.1 ist recht bersichtlich und leicht zu verstehen. Es gibt zwar einige
Restriktionen und Besonderheiten, die beim Erstellen von Druckausgaben zu Problemen fhren knnen,
aber fr einfache Ausdrucke von Text und Grafik ist die Schnittstelle dennoch geeignet.
Grundlage der Druckausgabe ist die Methode getPrintJob der Klasse Toolkit:
public PrintJob getPrintJob(
Frame frame,
String jobtitle,
Properties props
)
java.awt.Toolkit
Sie liefert ein Objekt des Typs PrintJob, das zur Initialisierung eines Druckjobs verwendet werden kann.
Ein Aufruf von getPrintJob fhrt gleichzeitig dazu, da ein plattformspezifischer Druckdialog
aufgerufen wird, der vom Anwender besttigt werden mu. Bricht der Anwender den Druckdialog ab, liefert
getPrintJob den Rckgabewert null. Andernfalls wird der Drucker initialisiert und die Ausgabe kann
beginnen.
Die Klasse PrintJob stellt einige Methoden zur Verfgung, die fr den Ausdruck bentigt werden:
public Graphics getGraphics()
java.awt.PrintJob
Die wichtigste von ihnen ist getGraphics. Sie liefert den Devicekontext zur Ausgabe auf den Drucker.
Der Rckgabewert ist ein Objekt vom Typ PrintGraphics, das aus Graphics abgeleitet ist und wie
ein normaler Devicekontext verwendet werden kann (so, wie er beispielsweise auch an paint bergeben
wird). Mit Hilfe des von getGraphics zurckgegebenen Devicekontexts knnen alle Grafik- und
Textroutinen, die auch in Graphics zur Verfgung stehen, verwendet werden. Bezglich der Verwendung
von Farben gilt scheinbar, da diese bei den linienbezogenen Ausgaberoutinen nicht untersttzt werden.
Hier wird alles schwarz gezeichnet, was nicht den Farbwert (255, 255, 255) hat. Im Gegensatz dazu stellen
die Fllfunktionen Farben (auf einem Schwarzwei-Drucker) als Grauwerte dar. Dabei kann - je nach
Druckertyp - auch Wei eine Vollfarbe sein und dahinter liegende Objekte verdecken.
java.awt.Component
Auf diese Weise kann bereits ohne zustzliche Erweiterungen eine einfache Druckausgabe realisiert werden,
die der Bildschirmausgabe relativ hnlich sieht. Im Gegensatz zu print gibt printAll dabei nicht nur
die aktuelle Komponente, sondern die komplette Container-Hierarchie eines komplexen Dialogs aus. Soll
innerhalb von paint zwischen Bildschirm- und Druckerausgabe unterschieden werden, kann mit dem
Ausdruck g instanceof PrintGraphics das bergebene Graphics-Objekt g auf Zugehrigkeit
zur Klasse PrintGraphics getestet werden.
Die zweite Mglichkeit, die Druckausgabe zu plazieren, besteht darin, eine eigene Methode zu schreiben,
die nur fr die Ausgabe auf den Drucker verantwortlich ist. Diese knnte zunchst den PrintJob und das
PrintGraphics-Objekt beschaffen und anschlieend die Abmessungen der Ausgabeflche wie zuvor
besprochen bestimmen. Die Methode mte dann nicht so programmiert werden, da sie fr Bildschirmund Druckausgabe vernnftige Resultate liefert, sondern knnte ihre Ausgaben ausschlielich fr die
Druckausgabe optimieren. Der Nachteil bei dieser Lung ist natrlich, da Programmcode zum Erstellen der
Ausgabe mglicherweise doppelt vorhanden ist und doppelt gepflegt werden mu.
Das nachfolgende Listing kombiniert beide Varianten und zeigt den Ausdruck einer Testseite. Das
Programm erstellt ein Hauptfenster und ruft zwei Sekunden spter die Methode printTestPage zur
Druckausgabe auf. Darin wird zunchst ein PrintJob erzeugt und dann gem dem oben beschriebenen
Verfahren die Ausgabegre ermittelt. Anschlieend wird der Grafikkontext beschafft und ein Rahmen,
einige Textzeilen mit Angaben zu den Metriken und eine Graustufenmatrix ausgegeben. Nach Ende der
Druckausgabe wird die Seite mit dispose ausgegeben und der Druckjob mit end geschlossen. Der Code
fr die Darstellung der Graustufenmatrix wurde in der Methode paintGrayBoxes implementiert und
wird von der Bildschirm- und Druckausgabe gemeinsam verwendet:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
/* Listing2601.java */
import java.awt.*;
import java.awt.event.*;
public class Listing2601
extends Frame
{
public static void main(String[] args)
{
Listing2601 wnd = new Listing2601();
}
public Listing2601()
{
super("Drucken");
addWindowListener(new WindowClosingAdapter(true));
setBackground(Color.lightGray);
setSize(400,400);
setVisible(true);
//Ausdruck in 2 Sekunden starten
try {
Thread.sleep(2000);
Listing2601.java
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
} catch (InterruptedException e) {
//nichts
}
printTestPage();
}
public void paint(Graphics g)
{
paintGrayBoxes(g, 40, 50);
}
public void printTestPage()
{
PrintJob pjob = getToolkit().getPrintJob(
this,
"Testseite",
null
);
if (pjob != null) {
//Metriken
int pres = pjob.getPageResolution();
int sres = getToolkit().getScreenResolution();
Dimension d2 = new Dimension(
(int)(((21.0 - 2.0) / 2.54) * sres),
(int)(((29.7 - 2.0) / 2.54) * sres)
);
//Ausdruck beginnt
Graphics pg = pjob.getGraphics();
if (pg != null) {
//Rahmen
pg.drawRect(0, 0, d2.width, d2.height);
//Text
pg.setFont(new Font("TimesRoman",Font.PLAIN,24));
pg.drawString("Testseite",40,70);
pg.drawString(
"Druckerauflsung : " + pres + " dpi",
40,
100
);
pg.drawString(
"Bildschirmauflsung : " + sres + " dpi",
40,
130
);
pg.drawString(
"Seitengre : " + d2.width + " * " + d2.height,
40,
160
);
//Graustufenkstchen
paintGrayBoxes(pg, 40, 200);
//Seite ausgeben
pg.dispose();
}
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094 }
pjob.end();
}
}
private void paintGrayBoxes(Graphics g, int x, int y)
{
for (int i = 0; i < 16; ++i) {
for (int j = 0; j < 16; ++j) {
int level = 16 * i + j;
g.setColor(Color.black);
g.drawRect(x + 20 * j, y + 20 * i, 20, 20);
g.setColor(new Color(level, level, level));
g.fillRect(x + 1 + 20 * j, y + 1 + 20 * i, 19, 19);
}
}
}
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
26.3.1 berblick
Seit dem JDK 1.2 wurde ein anderer Ansatz verfolgt. Anstelle der im vorigen Abschnitt erwhnten Techniken sind nun die
Klassen und Interfaces aus dem Paket java.awt.print zu verwenden, die zwei unterschiedliche Arten von Funktionen zur
Verfgung stellen:
Die Klasse PrinterJob ist fr die Kontrolle des Druckauftrags verantwortlich. Sie dient zum Aufrufen der
plattformspezifischen Konfigurationsdialoge, registriert die Objekte zur Seitenausgabe und startet den eigentlichen
Druckvorgang.
Der eigentliche Ausdruck wird von Objekten des Typs Printable oder Pageable erledigt. Sie werden beim
PrinterJob registriert und zu geeigneter Zeit von diesem mit der Ausgabe der Druckdaten beauftragt.
Anders als im JDK 1.1 stehen nun zwei Konfigurationsdialoge zur Verfgung. Mit dem einen knnen Job-Parameter wie Anzahl
der Kopien, auszudruckende Seiten oder der Druckertyp eingestellt werden. Der andere dient zur Einstellung von
Seitenparametern, mit dem etwa das Papierformat, die Seitengre oder die Randeinstellungen konfiguriert werden knnen.
Beide Dialoge sind optional und knnen auch unterdrckt werden. Alle mit ihrer Hilfe konfigurierbaren Parameter knnen auch
programmgesteuert verndert werden.
java.awt.print.PrinterJob
PrinterJob besitzt Methoden, mit denen einige globale Eigenschaften des Druckauftrags kontrolliert werden knnen:
java.awt.print.PrinterJob
getUserName liefert den Namen des Benutzers, der den Job gestartet hat. getCopies und setCopies dienen zum Zugriff
auf die Anzahl der Kopien, die ausgedruckt werden sollen. Mit getJobName und setJobName kann der Name des
Druckjobs abgefragt bzw. eingestellt werden. Durch Aufruf von cancel kann ein laufender Druckjob abgebrochen werden.
isCancelled liefert true, falls whrend eines laufenden Druckjobs cancel aufgerufen wurde und der Job bei der
nchstmglichen Gelegenheit beendet wird.
Die Klassen Paper und PageFormat
Neben den eigentlichen Druckdaten besitzt eine auszudruckende Seite globale Eigenschaften wie Papiergre und
Randeinstellungen. Diese werden durch die Klassen PageFormat und Paper gekapselt. Paper reprsentiert ein Blatt Papier
mit festgelegten Abmessungen fr Breite, Hhe und bedruckbaren Bereich:
public double getHeight()
public double getWidth()
java.awt.print.Paper
getHeight und getWidth liefern die Hhe bzw. Breite des Papierblatts. getImageableX und getImageableY geben
den Abstand des bedruckbaren Bereichs vom oberen bzw. linken Blattrand an. getImageableWidth und
getImageableHeight geben die Breite bzw. Hhe des bedruckbaren Bereichs an.
Paper speichert und liefert alle Parameter in einer Einheit von 1/72 Zoll. Das entspricht etwa der typographischen Hinweis
Maeinheit "Punkt" und ist etwas lnger als ein Drittel Millimeter. Ein derartiges Raster reprsentiert die Auflsung
eines durchschnittlichen Monitors und ist fr Druckausgaben viel zu grob. Glcklicherweise werden die Werte als
Fliekommazahlen gespeichert und lassen sich zur Erhhung der Genauigkeit skalieren. Wir werden weiter unter auf
die dazu ntigen Techniken zurckkommen.
Die Klasse PageFormat kapselt ein Paper-Objekt und dessen Orientierung:
public static final int PORTRAIT
public static final int LANDSCAPE
public static final int REVERSE_LANDSCAPE
java.awt.print.PageFormat
Ist die Orientierung PORTRAIT, so wird das Blatt in seiner Standardausrichtung verwendet. Es ist also hher als breit, und der
Koordinatenursprung befindet sich links oben. Beim Drucken im LANDSCAPE-Modus wird das Papier dagegen quer verwendet,
und der rechte Rand des Ausdrucks liegt am oberen Blattrand. REVERSE_LANDSCAPE druckt ebenfalls quer, aber mit
umgekehrter Druckrichtung.
PageFormat stellt Methoden zum Zugriff auf das Papier, seine Orientierung und die aus Paper bekannten Methoden zum
Zugriff auf die Seitenabmessungen zur Verfgung:
java.awt.print.Paper
Im Gegensatz zu Paper sind die Rckgabewerte nun aber an die Orientierung angepat. Wird also beispielsweise
im LANDSCAPE-Modus ausgedruckt, liefert getHeight die Breite des Blattes und getWidth dessen Hhe. Die
anderen Methoden verhalten sich analog.
Hinweis
Ein PageFormat-Objekt wird gewhnlich mit der Methode defaultPage der Klasse PrinterJob beschafft:
public PageFormat defaultPage()
java.awt.print.PrinterJob
Es kann dann entweder programmgesteuert oder durch Aufruf des Seitendialogs konfiguriert werden.
Die Konfigurationsdialoge
Die Klasse PrinterJob besitzt zwei Konfigurationsdialoge. Der erste dient zur Konfiguration eines PageFormat-Objekts
und wird mit pageDialog aufgerufen. Der zweite dient zur Konfiguration der globalen Job-Parameter und wird mit
printDialog aufgerufen:
public PageFormat pageDialog(PageFormat page)
java.awt.print.PrinterJob
pageDialog erwartet ein PageFormat-Objekt (das typischerweise durch defaultPage erzeugt wird) und bietet dem
Anwender die Mglichkeit, dessen Eigenschaften zu verndern. Die dafr verwendeten Dialoge sind plattformabhngig, die
Windows-Variante ist in Abbildung 26.2 zu sehen.
Der Rckgabewert von pageDialog ist das konfigurierte PageFormat-Objekt. Hat der Anwender den Dialog mit "OK"
beendet, wird eine Kopie des als Parameter bergebenen Objekts erzeugt, mit den Einstellungen des Anwenders versehen und
an den Aufrufer zurckgegeben. Hat der Anwender den Dialog dagegen abgebrochen, wird das unvernderte Originalobjekt
zurckgegeben. Durch Vergleichen der beiden Objektreferenzen kann also unterschieden werden, ob der Druckvorgang
fortgesetzt oder abgebrochen werden soll.
java.awt.print.Printable
Ein Programm, das Druckausgaben erzeugen will, mu ein Objekt zur Verfgung stellen, das dieses Interface implementiert. Es
mu mit der Methode setPrintable an den PrinterJob bergeben werden und wird von PrinterJob automatisch
aufgerufen, nachdem der Druckvorgang mit print gestartet wurde:
public void setPrintable(Printable painter)
public void setPrintable(Printable painter, PageFormat format)
java.awt.print.PrinterJob
Printable sorgt dabei fr die Druckausgabe, hnlich wie es paint fr die Bildschirmausgabe bei GUI-Komponenten tut.
Jeder (von PrinterJob initiierte) Aufruf von print fordert das Printable-Objekt auf, eine bestimmte Seite
auszudrucken. Der Seitenindex wird dabei in dem Parameter page bergeben, dessen Zhlung bei 0 beginnt.
Zur Erzeugung von Druckausgaben wird das bergebene Graphics-Objekt verwendet, das wie bei der Erzeugung von
Bildschirmausgaben verwendet werden kann. Das bergebene Objekt kann dabei in ein Graphics2D-Objekt konvertiert
werden und stellt so die erweiterten Funktionen des 2D-APIs zur Verfgung.
Weiterhin wird das konfigurierte PageFormat als Argument an print bergeben. Innerhalb von print wird es dazu
benutzt, die Gre des Papiers und dessen bedruckbaren Bereich zu ermitteln.
Es ist sehr wichtig, zu wissen, da print fr ein und dieselbe Seite mehrfach aufgerufen werden kann (und
normalerweise auch wird). Da die JDK-Dokumentation keine Aussagen darber macht, wie oft und in welcher
Reihenfolge die Seiten abgefordert werden, mu das Printable-Objekt selbst fr eine geeignete Verwaltung der
bentigten Statusinformationen sorgen.
Hinweis
Der Rckgabewert von print gibt an, ob die Seitenzahl gltig war und die Druckausgabe erzeugt werden konnte, oder ob eine
ungltige Seitenzahl bergeben wurde. Im ersten Fall gibt print die in Printable definierte Konstante PAGE_EXISTS
zurck, im zweiten Fall NO_SUCH_PAGE. Da der PrinterJob nicht von Anfang an wei, wie viele Seiten insgesamt
ausgedruckt werden sollen, mu er beim Aufruf von print schrittweise den Seitenzhler erhhen und darauf warten, da
NO_SUCH_PAGE zurckgegeben wird. Danach erfolgen keine weiteren Aufrufe von print.
Will das Printable-Objekt den kompletten Job whrend der Ausgabe abbrechen, braucht es dazu lediglich whrend
des Aufrufs von print eine PrinterException auszulsen. Diese wird vom PrinterJob abgefangen und fhrt
zum Abbruch des Druckjobs.
Tip
java.awt.print.Pageable
Ein Pageable-Objekt kennt (normalerweise) die Anzahl der auszudruckenden Seiten und stellt diese auf Anfrage als
Rckgabewert von getNumberOfPages zur Verfgung. Wahlweise kann aber auch die Konstante
UNKNOWN_NUMBER_OF_PAGES zurckgegeben werden, wenn die Seitenzahl nicht von vorneherein bekannt ist. Im
Unterschied zu einem Printable-Objekt (das ja auch mehrere Seiten ausdrucken kann), kann ein Pageable zu den
einzelnen Seiten unterschiedliche PageFormat- und Printable-Objekte zur Verfgung stellen. Sie werden whrend des
Druckvorgangs mit den Methoden getPageFormat und getPrintable abgefragt.
Mit der Klasse Book stellt java.awt.print eine konkrete Implementierung von Pageable zur Verfgung. Ein Book ist
dabei nichts anderes als eine nach Seitenzahlen indizierte Liste von (PageFormat/Printable)-Paaren, die mit einigen
einfachen Methoden gepflegt werden kann:
public void append(Printable painter, PageFormat pf)
public void append(Printable painter, PageFormat pf, int numPages)
java.awt.print.Book
Mit append werden eine oder mehrere Seiten mit den angegebenen Printable- und PageFormat-Objekten an das Ende
des Buchs angehngt. Mit setPage kann gezielt eine bereits bestehende Seite verndert werden.
/* FilePrintHelper.java */
import java.util.*;
public class FilePrintHelper
{
//---Membervariablen---------------------------------Vector pageinfo;
//---Konstruktor-------------------------------------public FilePrintHelper()
{
pageinfo = new Vector();
}
//---Seitendefinition und -abfrage-------------------public void createPage(int page)
{
for (int i = pageinfo.size(); i <= page; ++i) {
pageinfo.addElement(new Entry());
FilePrintHelper.java
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052 }
}
}
public boolean knownPage(int page)
{
return page < pageinfo.size();
}
//---Verwaltung der Offsets--------------------------public long getFileOffset(int page)
{
Entry entry = (Entry)pageinfo.elementAt(page);
return entry.fileoffset;
}
public void setFileOffset(int page, long fileoffset)
{
Entry entry = (Entry)pageinfo.elementAt(page);
entry.fileoffset = fileoffset;
}
//---Lokale Klasse Entry-----------------------------static class Entry
{
public long fileoffset;
public Entry()
{
this.fileoffset = -1;
}
}
Zunchst wird ein SimpleFilePrinter instanziert. Es erzeugt durch Aufruf von getPrinterJob eine Instanz der
Klasse PrinterJob.
In setupPageFormat wird durch Aufruf von defaultPage ein PageFormat beschafft und zur Bearbeitung an
den Seitendialog bergeben. Die Methode prft anschlieend, ob der Dialog mit "OK" oder "Abbrechen" verlassen
wurde, und gibt entweder true oder false zurck (letzteres fhrt in main zum Abbruch des Druckvorgangs).
In setupJobOptions wird der Dialog zur Konfiguration der Job-Parameter aufgerufen. Auch hier fhrt ein Abbruch
des Anwenders zum Ende des gesamten Druckvorgangs.
Schlielich wird in printFile der eigentliche Druckvorgang angestoen. Dazu wird zunchst ein
RandomAccessFile fr die auszudruckende Datei angelegt und ein FilePrintHelper instanziert. Anschlieend
wird die Druckausgabe durch Aufruf von print gestartet.
Nachfolgend zeigen wir die Implementierung von SimpleFilePrinter. Der eigentlich komplizierte Code steckt natrlich
in der Methode print, die wir im Anschlu an das Listing erlutern wollen.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
/* SimpleFilePrinter.java */
import java.awt.*;
import java.awt.print.*;
import java.io.*;
public class SimpleFilePrinter
implements Printable
{
//---Konstanten-------------------------------------private static final int RESMUL = 4;
//---Membervariablen--------------------------------private PrinterJob
pjob;
private PageFormat
pageformat;
private FilePrintHelper fph;
private String
fname;
private RandomAccessFile in;
//---Konstruktoren----------------------------------public SimpleFilePrinter(String fname)
{
this.pjob = PrinterJob.getPrinterJob();
this.fname = fname;
}
//---ffentliche Methoden---------------------------public boolean setupPageFormat()
{
PageFormat defaultPF = pjob.defaultPage();
this.pageformat = pjob.pageDialog(defaultPF);
pjob.setPrintable(this, this.pageformat);
return (this.pageformat != defaultPF);
}
public boolean setupJobOptions()
{
return pjob.printDialog();
}
public void printFile()
throws PrinterException, IOException
{
fph = new FilePrintHelper();
in = new RandomAccessFile(fname, "r");
pjob.print();
in.close();
}
//---Implementierung von Printable------------------public int print(Graphics g, PageFormat pf, int page)
throws PrinterException
{
int ret = PAGE_EXISTS;
String line = null;
try {
if (fph.knownPage(page)) {
in.seek(fph.getFileOffset(page));
line = in.readLine();
} else {
long offset = in.getFilePointer();
line = in.readLine();
if (line == null) {
SimpleFilePrinter.java
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124 }
ret = NO_SUCH_PAGE;
} else {
fph.createPage(page);
fph.setFileOffset(page, offset);
}
}
if (ret == PAGE_EXISTS) {
//Seite ausgeben, Grafikkontext vorbereiten
Graphics2D g2 = (Graphics2D)g;
g2.scale(1.0 / RESMUL, 1.0 / RESMUL);
int ypos = (int)pf.getImageableY() * RESMUL;
int xpos = ((int)pf.getImageableX() + 2) * RESMUL;
int yd = 12 * RESMUL;
int ymax = ypos + (int)pf.getImageableHeight() * RESMUL - yd;
//Seitentitel ausgeben
ypos += yd;
g2.setColor(Color.black);
g2.setFont(new Font("Monospaced", Font.BOLD, 10 * RESMUL));
g.drawString(fname + ", Seite " + (page + 1), xpos, ypos);
g.drawLine(
xpos,
ypos + 6 * RESMUL,
xpos + (int)pf.getImageableWidth() * RESMUL,
ypos + 6 * RESMUL
);
ypos += 2 * yd;
//Zeilen ausgeben
g2.setColor(new Color(0, 0, 127));
g2.setFont(new Font("Monospaced", Font.PLAIN, 10 * RESMUL));
while (line != null) {
g.drawString(line, xpos, ypos);
ypos += yd;
if (ypos >= ymax) {
break;
}
line = in.readLine();
}
}
} catch (IOException e) {
throw new PrinterException(e.toString());
}
return ret;
}
//---Main-------------------------------------------public static void main(String[] args)
{
SimpleFilePrinter sfp = new SimpleFilePrinter(args[0]);
if (sfp.setupPageFormat()) {
if (sfp.setupJobOptions()) {
try {
sfp.printFile();
} catch (Exception e) {
System.err.println(e.toString());
System.exit(1);
}
}
}
System.exit(0);
}
In print wird zunchst geprft, ob die Seite schon einmal aufgerufen wurde. Ist das der Fall, wird ihr Dateioffset vom
FilePrintHelper geholt und die erste Zeile der Seite erneut eingelesen. Falls nicht, prft die Methode, ob berhaupt noch
weitere Daten vorhanden sind. In diesem Fall bergibt sie den Dateioffset zur Speicherung an den FilePrintHelper.
Andernfalls setzt es den Rckgabewert auf NO_SUCH_PAGE, und die Ausgabe der Seite ist beendet.
Falls die Seite existiert, wird sie nun ausgegeben (im Listing ab Zeile 072). Dazu wird der Grafikkontext nach Graphics2D
konvertiert und anschlieend auf den Kehrwert der Konstante RESMUL skaliert. Da RESMUL den Wert 4 hat, fhrt das dazu, da
alle Ausgaben um den Faktor 4 verkleinert werden. Dieser Kunstgriff wird angewendet, um bei der Ausgabe nicht mit der
groben Standardauflsung von 72 dpi arbeiten zu mssen. Die auf diese Weise simulierten 288 dpi lassen die Linie unter der
Kopfzeile hinreichend dnn erscheinen. Sie wre ansonsten mit einem viel zu krftigen 1/72 Zoll breiten Pixelstrich gezeichnet
worden. Da nun auch alle anderen Positions- und Lngenangaben um den Faktor 4 verkleinert werden, multiplizieren wir sie in
den folgenden Programmzeilen jeweils mit RESMUL und skalieren sie so auf ihre normale Gre zurck.
Ab Zeile 079 wird der Seitentitel ausgeben. Dazu wird die Schriftfarbe auf schwarz eingestellt und eine fette Schrift ausgewhlt.
Die Variable ypos reprsentiert den vertikalen Offset vom oberen Papierrand. Sie wird am Anfang auf das obere Ende des
bedruckbaren Bereichs gestellt und nach jeder Zeile um einen konstanten Wert erhht. Ab Zeile 091 werden die Textzeilen
ausgegeben. Mit readLine wird dazu in einer Schleife die jeweils nchste Zeile aus der Eingabedatei gelesen und - wenn
vorhanden - an der aktuellen y-Position ausgegeben. Falls keine weiteren Zeilen vorhanden sind oder das Ende des
bedruckbaren Bereichs erreicht ist, wird die Schleife beendet.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Das Abstract Windowing Toolkit von Java enthlt verschiedene Fensterklassen, die
ber eine gemeinsame Vererbungshierarchie miteinander in Verbindung stehen
(siehe Abbildung 27.1). Oberste Fensterklasse ist Component, daraus wurde
Container abgeleitet. Container ist die Oberklasse der beiden Klassen
Window und Panel. Whrend Window sich in die Unterklassen Frame und
Dialog verzweigt, wird aus Panel die Klasse Applet abgeleitet. Unterhalb von
Dialog gibt es noch den Standard-File-Dialog in der Klasse FileDialog.
Die Klasse Frame reprsentiert ein Top-Level-Window mit Rahmen, Titelleiste und
optionalem Men. Einem Frame kann ein Icon zugeordnet werden, das angezeigt
wird, wenn das Fenster minimiert wird. Es kann eingestellt werden, ob das Fenster
vom Anwender in der Gre verndert werden kann. Zustzlich besteht die
Mglichkeit, das Aussehen des Mauszeigers zu verndern.
Die zweite aus Window abgeleitete Klasse ist Dialog. Sie ist dafr vorgesehen,
modale oder nicht-modale Dialoge zu realisieren. Ein modaler Dialog ist ein Fenster,
das immer im Vordergrund des Fensters bleibt, von dem es erzeugt wurde, und das
alle brigen Fensteraktivitten und Ereignisse so lange blockiert, bis es geschlossen
wird. Ein nicht-modaler Dialog kann mit anderen Fenstern koexistieren und erlaubt
es, im aufrufenden Fenster weiterzuarbeiten.
Aufgrund von Bugs in der Windows-Portierung des AWT gab es bis zur
Version 1.0.2 des AWT keine vernnftige Mglichkeit, modale Dialoge
zu erzeugen. Dies war ein ziemlich schwerwiegendes Problem, denn
modale Dialoge kommen in der Programmentwicklung sehr hufig vor.
Sie werden immer dann bentigt, wenn das Programm auf die Eingabe
eines Anwenders warten mu, bevor es fortfahren kann. Leider
funktionierte gerade dieser Wartemechanismus nicht, und es wurden eine
Reihe aufwendiger Workarounds verffentlicht. Mit der Version 1.1 des
JDK war es erstmals auch unter Windows 95 mglich, echte modale
Dialoge in Java zu erstellen. Wir werden auf die Details in Kapitel 31
zurckkommen.
Hinweis
Die unterste Klasse in der Hierarchie der Fenster ist FileDialog. Sie stellt den
Standard-Dateidialog des jeweiligen Betriebssystems zur Verfgung. Dieser kann
beim Laden oder Speichern einer Datei zur Eingabe oder Auswahl eines Dateinamens
verwendet werden. Gegenber den Mglichkeiten des Standard-Dateidialogs, wie er
vom Windows-API zur Verfgung gestellt wird, sind die Fhigkeiten der Klasse
FileDialog allerdings etwas eingeschrnkt.
<
>
>>
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Ein Fenster besitzt eine Reihe von Eigenschaften, die sein Aussehen bestimmen. Dazu gehren die Art
des Rahmens, die Position und Gre des Fensters und die Anordnung des Fensters in Relation zu
anderen Fenstern auf dem Bildschirm.
Wie schon erwhnt, wird die Art des Rahmens durch die Klasse bestimmt, die zur Erzeugung eines
Fensters verwendet wird. Whrend die Klasse Window ein Fenster ohne Rahmen darstellt, besitzt ein
Frame einen Rahmen, eine Titelleiste und auf Wunsch ein Men.
Die Gre und Position eines Fensters knnen mit den Methoden setSize, setBounds und
setLocation bestimmt werden:
public void setSize(int width, int height)
public void setSize(Dimension d)
java.awt.Component
setSize verndert die Gre des Fensters auf den Wert (width, height), und setLocation
bewegt die linke obere Ecke an die Bildschirmposition (x,y). Die Methode setBounds kombiniert
die Funktionen von setSize und setLocation und positioniert ein Fenster der Gre
(width,height) an der Position (x,y). Mit getSize, getBounds und getLocation
knnen diese Eigenschaften auch abgefragt werden.
Mit den Methoden setEnabled und isEnabled kann auf den Aktivierungszustand einer
Komponente zugegriffen werden:
java.awt.Component
Ist die Komponente aktiv, kann sie Benutzereingaben empfangen und Nachrichten versenden. In
deaktiviertem Zustand geht das nicht. Dialogelemente wie Textfelder oder Buttons werden deaktiviert,
um dem Anwender anzuzeigen, da eine bestimmte Funktion oder Eingabemglichkeit derzeit nicht zur
Verfgung steht. Sie zeigen den Unterschied zwischen beiden Zustnden auch optisch an. Bei
Fensterkomponenten wird der Aktivierungszustand meist nicht verndert.
Das folgende Beispiel stellt eine sehr einfache Form eines (manuell zu aktivierenden)
Bildschirmschoners dar, der den gesamten Bildschirm dunkel schaltet und die Anmerkung Bitte eine
Taste drcken in die linke obere Ecke schreibt. Zustzlich gibt das Programm die aktuelle Auflsung
des Bildschirms aus, nachdem sie mit der Methode getScreenSize der Klasse Toolkit ermittelt
wurde. Nach Drcken einer Taste wird das Fenster geschlossen und das Programm beendet:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
/* Listing2702.java */
import java.awt.*;
import java.awt.event.*;
public class Listing2702
extends Window
{
public static void main(String[] args)
{
final Listing2702 wnd = new Listing2702();
wnd.setLocation(new Point(0,0));
wnd.setSize(wnd.getToolkit().getScreenSize());
wnd.setVisible(true);
wnd.requestFocus();
wnd.addKeyListener(
new KeyAdapter() {
public void keyPressed(KeyEvent event)
{
wnd.setVisible(false);
wnd.dispose();
System.exit(0);
}
}
);
}
public Listing2702()
{
super(new Frame());
setBackground(Color.black);
}
public void paint(Graphics g)
{
Listing2702.java
036
037
038
039
040
041
042
043
044
}
045 }
g.setColor(Color.red);
g.drawString(
"Bildschirmgre ist "+
getSize().width+"*"+getSize().height,
10,
20
);
g.drawString("Bitte eine Taste drcken",10,40);
Hinweis
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt Suchen
<
>
Index
>>
DOC
API
27.5 Fensterelemente
27.5 Fensterelemente
27.5.1 Der Fenstertitel
27.5.2 Das Icon des Fensters
27.5.3 Der Mauscursor
27.5.4 Die Vorder- und Hintergrundfarbe
27.5.5 Der Standard-Font
Neben den visuellen Eigenschaften eines Fensters gibt es noch eine Reihe weiterer Elemente, die
einem Fenster zugeordnet werden knnen. Hierzu zhlen die Titelleiste, das Men, ein Icon, ein
Mauscursor, eine bestimmte Vorder- und Hintergrundfarbe und ein Standard-Font zur Ausgabe von
Schriften. Bis auf die Definition und die Zuordnung von Mens, die in Kapitel 30 erlutert werden,
soll der Umgang mit diesen Fensterelementen in den folgenden Unterabschnitten erklrt werden.
java.awt.Frame
Ein Aufruf dieser Methode ndert die Beschriftung der Titelleiste in den als String bergebenen
Parameter title. Mit der Methode getTitle kann die Titelleiste abgefragt werden.
java.awt.Frame
Beim Design des Icons steht man nun vor dem Konflikt, entscheiden zu mssen, in welcher Gre
das Icon entworfen werden soll. Ein Windows-Programm hat meist ein Haupticon in der Gre
32*32 Pixel und ein kleineres Icon mit 16*16 Pixeln. Beide werden an unterschiedlichen Stellen im
Programm bentigt. Das JDK ist glcklicherweise in der Lage, die bergebenen Images so zu
skalieren, da sie die jeweils bentigte Gre annehmen. Die Ergebnisse sind im Falle des groen
Icons durchaus brauchbar (so hat z.B. das in Abbildung 27.3 gezeigte und in Listing 27.4 verwendete
Icon eine Originalgre von 60*56 Pixeln), im Falle des kleinen Icons sind die Ergebnisse in Pr-1.2JDKs jedoch nicht so befriedigend. Hier scheint die Skalierungsroutine lediglich ein schwarz-weies
Ergebnis zu erzeugen.
Hinweis
Die Klasse Cursor besitzt einen Konstruktor, der als einziges Argument eine ganzzahlige
Konstante erwartet, mit der der gewnschte Cursor aus der Liste der vordefinierten Cursortypen
ausgewhlt werden kann. Tabelle 27.1 gibt eine Auswahl der verfgbaren Cursortypen an.
Das freie Erstellen benutzerdefinierter Cursortypen wird seit dem JDK 1.2 untersttzt. JDK1.1-1.4
Dazu gibt es in der Klasse Toolkit die Methoden createCustomCursor,
getBestCursorSize und getMaximumCursorColors. Wir wollen darauf
nicht nher eingehen.
Konstante aus Frame
Cursorform
Cursor.CROSSHAIR_CURSOR
Fadenkreuz
Cursor.DEFAULT_CURSOR
Standardpfeil
Cursor.MOVE_CURSOR
Vierfachpfeil
Cursor.TEXT_CURSOR
Senkrechter Strich
Cursor.WAIT_CURSOR
Eieruhr
java.awt.Component
java.awt.Component
Das folgende Beispielprogramm erzeugt ein Fenster mit einer Titelleiste, gesetzter Vorder- und
Hintergrundfarbe und der Eieruhr als Cursor. Zustzlich besitzt das Programm das in Abbildung 27.3
gezeigte Icon, das beim Minimieren des Programms und in der Taskleiste angezeigt wird.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
/* Listing2704.java */
import java.awt.*;
import java.awt.event.*;
public class Listing2704
extends Frame
{
public static void main(String[] args)
{
Listing2704 wnd = new Listing2704();
wnd.setSize(300,200);
wnd.setLocation(50,50);
wnd.setVisible(true);
Listing2704.java
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
}
public Listing2704()
{
super("");
assignTitle();
assignIcon();
assignCursor();
assignColors();
assignFont();
addWindowListener(new WindowClosingAdapter(true));
}
private void assignTitle()
{
setTitle("Vernderte Fensterelemente");
}
private void assignIcon()
{
Image img = getToolkit().getImage("testicon.gif");
MediaTracker mt = new MediaTracker(this);
mt.addImage(img, 0);
try {
//Warten, bis das Image vollstndig geladen ist,
mt.waitForAll();
} catch (InterruptedException e) {
//nothing
}
setIconImage(img);
}
private void assignCursor()
{
setCursor(new Cursor(Cursor.WAIT_CURSOR));
}
private void assignColors()
{
setForeground(Color.white);
setBackground(Color.black);
}
private void assignFont()
{
setFont(new Font("Serif", Font.PLAIN, 28));
}
public void paint(Graphics g)
065
{
066
g.drawString("Test in Vordergrundfarbe",10,70);
067
}
068 }
Listing 27.4: Ein Programm mit vernderten Fensterelementen
Die Programmausgabe ist wie folgt:
Inhalt Suchen
<
>
Index
DOC
>>
API
28.1.1 Grundlagen
Bei der Programmierung unter einer grafischen Oberflche erfolgt die
Kommunikation zwischen Betriebssystem und Anwendungsprogramm zu einem
wesentlichen Teil durch das Versenden von Nachrichten. Die Anwendung wird dabei
ber alle Arten von Ereignissen und Zustandsnderungen vom Betriebssystem
informiert. Dazu zhlen beispielsweise Mausklicks, Bewegungen des Mauszeigers,
Tastatureingaben oder Vernderungen an der Gre oder Lage des Fensters.
Bei der Verarbeitung des Nachrichtenverkehrs sind zwei verschiedene Arten von
Objekten beteiligt. Die Ereignisquellen (Event Sources) sind die Auslser der
Nachrichten. Eine Ereignisquelle kann beispielsweise ein Button sein, der auf einen
Mausklick reagiert, oder ein Fenster, das mitteilt, da es ber das Systemmen
geschlossen werden soll. Die Reaktion auf diese Nachrichten erfolgt in den speziellen
Ereignisempfngern (den EventListeners); das sind Objekte, die das zum Ereignis
passende Empfnger-Interface implementieren. Damit ein Ereignisempfnger die
Nachrichten einer bestimmten Ereignisquelle erhlt, mu er sich bei dieser
registrieren.
Dieses Kommunikationsmodell nennt sich Delegation Event Model oder Delegation
Based Event Handling und wurde mit der Version 1.1 des JDK eingefhrt. Im
Gegensatz zum alten Modell, bei dem jedes Ereignis die Verteilermethode
handleEvent der Klasse Component durchlaufen mute und von ihr an die
verschiedenen Empfnger verteilt wurde, hat dieses neue Modell zwei wesentliche
Vorteile:
Die Fensterklasse implementiert die erforderlichen EventListenerInterfaces, stellt die erforderlichen Callback-Methoden zur Verfgung und
registriert sich selbst bei den Ereignisquellen.
In der Fensterklasse werden lokale oder anonyme Klassen definiert, die einen
EventListener implementieren oder sich aus einer Adapterklasse ableiten
Wir werden uns jede dieser Varianten in den nachfolgenden Abschnitten ansehen und
ihre jeweiligen Einsatzmglichkeiten anhand eines Beispiels aufzeigen. Zunchst
sollen jedoch die verschiedenen Ereignistypen sowie die Ereignisempfnger und
Ereignisquellen nher beleuchtet werden. Wir wollen dabei (etwas unscharf) die
Begriffe Ereignis, Nachricht und Event in diesem Kapitel synonym verwenden, wenn
wir innerhalb eines GUI-Programms die ausgetauschten Nachrichten oder ihre
auslsenden Ereignisse meinen.
Das vorliegende Kapitel beschrnkt sich auf das Erlutern der
grundlegenden Eigenschaften des Event-Handlings im JDK 1.1. Die
Details einzelner Ereignisarten werden in den nachfolgenden Kapiteln
schrittweise erklrt und zusammen mit den zugehrigen Ereignisquellen
vorgestellt.
Hinweis
28.1.2 Ereignistypen
Im Gegensatz zur Version 1.0 werden im JDK 1.1 die Ereignistypen nicht mehr
durch eine einzige Klasse Event reprsentiert, sondern durch eine Hierarchie von
Ereignisklassen, die aus der Klasse java.util.EventObject abgeleitet sind.
Die Motivation der Java-Designer, diese Klasse in das Paket java.util zu legen,
resultierte wohl aus der berlegung, da der Transfer von Nachrichten nicht allein
auf den Oberflchenteil beschrnkt sein mu, sondern auch zwischen anderen
Elementen einer komplexen Anwendung sinnvoll sein kann. Die Klasse
java.util.EventObject fungiert damit als allgemeine Oberklasse aller Arten
von Ereignissen, die zwischen verschiedenen Programmteilen ausgetauscht werden
knnen. Ihre einzige nennenswerte Fhigkeit besteht darin, das Objekt zu speichern,
das die Nachricht ausgelst hat, und durch Aufruf der Methode getSource
anzugeben:
public Object getSource()
java.util.EventObject
Die Hierarchie der AWT-spezifischen Ereignisklassen beginnt eine Ebene tiefer mit
der Klasse AWTEvent, die aus EventObject abgeleitet wurde und sich im Paket
java.awt befindet. AWTEvent ist Oberklasse aller Ereignisklassen des AWT.
Diese befinden sich im Paket java.awt.event, das damit praktisch in jede
Klasse einzubinden ist, die sich mit dem Event-Handling von GUI-Anwendungen
beschftigt. Abbildung 28.1 gibt einen berblick ber die Vererbungshierarchie der
Ereignisklassen.
Hinweis
java.awt.AWTEvent
Falls das Programm eigene Eventklassen definieren will und Konstanten zur Vergabe
der Event-Ids vergeben mu, sollten diese oberhalb der symbolischen Konstante
RESERVED_ID_MAX liegen.
Die Eventklassen im JDK 1.1 enthalten keine frei zugnglichen Felder.
Hinweis
Statt dessen sind alle Eigenschaften durch set-/get-Methoden gekapselt,
die je nach Ereignisklasse unterschiedlich sind. So gibt es beispielsweise
in MouseEvent die Methode getPoint, mit der die
Mauszeigerposition abgefragt werden kann, in KeyEvent die Methode
getKeyChar zur Bestimmung der gedrckten Taste und in der
bergeordneten Klasse InputEvent die Methode getModifiers, mit
der sowohl fr Maus- als auch fr Tastaturevents der Zustand der
Sondertasten bestimmt werden kann. Wir werden bei der Besprechung der
einzelnen Ereignisarten auf die Details eingehen.
28.1.3 Ereignisempfnger
Damit ein Objekt Nachrichten empfangen kann, mu es eine Reihe von Methoden
implementieren, die von der Nachrichtenquelle, bei der es sich registriert hat,
aufgerufen werden knnen. Um sicherzustellen, da diese Methoden vorhanden sind,
mssen die Ereignisempfnger bestimmte Interfaces implementieren, die aus der
Klasse EventListener des Pakets java.util abgeleitet sind. Diese
EventListener-Interfaces befinden sich im Paket java.awt.events.
Je Ereignisklasse gibt es ein EventListener-Interface. Es definiert eine separate
28.1.4 Ereignisquellen
Die Ereignisse stammen von den Ereignisquellen, also von Fenstern,
Dialogelementen oder hheren Programmobjekten. Eine Ereignisquelle sendet aber
nur dann Ereignisse an einen Ereignisempfnger, wenn dieser sich bei der
Ereignisquelle registriert hat. Fehlt eine solche Registrierung, wird die
Ereignisquelle keine Ereignisse senden und der Empfnger folglich auch keine
erhalten.
Die Registrierung erfolgt mit speziellen Methoden, an die ein Objekt bergeben wird,
das das jeweilige EventListener-Interface implementiert. So gibt es
beispielsweise eine Methode addMouseListener in der Klasse Component, mit
der ein Objekt, das das Interface MouseListener implementiert, sich fr den
Empfang von Mausereignissen bei der Komponente registrieren lassen kann. Nach
erfolgter Registrierung wird bei jedem Mausereignis die entsprechende Methode
mouseClicked, mouseEntered, mouseExited, mousePressed oder
mouseReleased aufgerufen.
Die Ereignisquellen untersttzen das Multicasting von Ereignissen. Dabei Hinweis
kann eine Ereignisquelle nicht nur einen einzelnen EventListener mit
Nachrichten versorgen, sondern eine beliebige Anzahl von ihnen. Jeder
Aufruf von addXYZListener registriert dabei einen weiteren Listener
in der Ereignisquelle. Das AWT definiert nicht, in welcher Reihenfolge
die verschiedenen Ereignisempfnger beim Auftreten eines Ereignisses
aufgerufen werden, und empfiehlt den Programmen, hierber keine
Annahmen zu treffen.
28.1.5 Adapterklassen
Eine Adapterklasse in Java ist eine Klasse, die ein vorgegebenes Interface mit leeren
Methodenrmpfen implementiert. Adapterklassen knnen verwendet werden, wenn
aus einem Interface lediglich ein Teil der Methoden bentigt wird, der Rest aber
uninteressant ist. In diesem Fall leitet man einfach eine neue Klasse aus der
Adapterklasse ab, anstatt das zugehrige Interface zu implementieren, und berlagert
die bentigten Methoden. Alle brigen Methoden des Interfaces werden von der
Basisklasse zur Verfgung gestellt.
Zu jedem der Low-Level-Ereignisempfnger stellt das Paket java.awt.event
eine passende Adapterklasse zur Verfgung. So gibt es die Adapterklassen
FocusAdapter, KeyAdapter, MouseAdapter, MouseMotionAdapter,
ComponentAdapter, ContainerAdapter und WindowAdapter. Sie
implementieren die korrespondierenden Interfaces. Wir werden spter bei der
Beschreibung der lokalen und anonymen Klassen sehen, wie die Adapterklassen bei
der Realisierung der Ereignisempfnger hilfreich sein knnen.
28.1.6 Zusammenfassung
Der folgende berblick bndelt die bisherigen Ausfhrungen und gibt eine
Zusammenfassung aller Ereignisse nebst zugehrigen Ereignisempfngern und ihren
Methoden. Auerdem sind die Ereignisquellen und die Methoden zur Registrierung
der Ereignisse angegeben. Jeder Ereignistyp wird dabei durch zwei Tabellen
dargestellt. Die erste gibt die zugehrige Ereignisklasse, das Listener-Interface und
den Namen der Methode zur Registrierung von Ereignisempfngern an. Sie listet
auerdem die als Ereignisquelle in Frage kommenden Klassen auf. Die zweite
Tabelle enthlt alle Methoden des zugehrigen Listener-Interfaces und beschreibt
damit die zu diesem Ereignistyp gehrenden Elementarereignisse.
Genaugenommen nimmt dieser Abschnitt Informationen vorweg, die in
Hinweis
spteren Teilen des Buches konkretisiert werden. Obwohl zum jetzigen
Zeitpunkt nicht alle Ereignisse in ihrer vollen Bedeutung dargestellt
werden knnen, mag diese bersicht fr die weitere Arbeit mit dem AWT
und zum Nachschlagen hilfreich sein.
Focus-Ereignisse
Eigenschaft
Ereignisklasse
FocusEvent
Listener-Interface
FocusListener
Registrierungsmethode
addFocusListener
Mgliche Ereignisquellen
Component
Bedeutung
focusGained
focusLost
Ereignisklasse
KeyEvent
Listener-Interface
KeyListener
Registrierungsmethode
addKeyListener
Mgliche Ereignisquellen
Component
Bedeutung
keyPressed
keyReleased
keyTyped
Ereignisklasse
MouseEvent
Listener-Interface
MouseListener
Registrierungsmethode
addMouseListener
Mgliche Ereignisquellen
Component
Bedeutung
mouseClicked
mouseEntered
mouseExited
mousePressed
mouseReleased
Eigenschaft
Ereignisklasse
MouseEvent
Listener-Interface
MouseMotionListener
Registrierungsmethode
addMouseMotionListener
Mgliche Ereignisquellen
Component
Bedeutung
mouseDragged
mouseMoved
Ereignisklasse
ComponentEvent
Listener-Interface
ComponentListener
Registrierungsmethode
addComponentListener
Mgliche Ereignisquellen
Component
Bedeutung
componentHidden
componentMoved
componentResized
componentShown
Container-Ereignisse
Eigenschaft
Ereignisklasse
ContainerEvent
Listener-Interface
ContainerListener
Registrierungsmethode
addContainerListener
Mgliche Ereignisquellen
Container
Bedeutung
componentAdded
componentRemoved
Ereignisklasse
WindowEvent
Listener-Interface
WindowListener
Registrierungsmethode
addWindowListener
Mgliche Ereignisquellen
Dialog, Frame
Bedeutung
windowActivated
windowClosed
windowClosing
windowDeactivated
windowDeiconified
windowIconified
windowOpened
Ereignisklasse
ActionEvent
Listener-Interface
ActionListener
Registrierungsmethode
addActionListener
Mgliche Ereignisquellen
Bedeutung
actionPerformed
Ereignisklasse
AdjustmentEvent
Listener-Interface
AdjustmentListener
Registrierungsmethode
addAdjustmentListener
Mgliche Ereignisquellen
Scrollbar
Bedeutung
Ereignisklasse
ItemEvent
Listener-Interface
ItemListener
Registrierungsmethode
addItemListener
Mgliche Ereignisquellen
Bedeutung
itemStateChanged
Ereignisklasse
TextEvent
Listener-Interface
TextListener
Registrierungsmethode
addTextListener
Mgliche Ereignisquellen
TextField, TextArea
Bedeutung
textValueChanged
<
>
>>
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Wir wollen uns in diesem Abschnitt damit beschftigen, die oben erwhnten Entwurfsmuster fr die
Abwicklung des Nachrichtenverkehrs in Java-Programmen vorzustellen. Wie bereits erwhnt, hat jedes
dieser Verfahren seine ganz spezifischen Vor- und Nachteile und ist fr verschiedene Programmieraufgaben
unterschiedlich gut geeignet.
Als Basis fr unsere Experimente wollen wir ein einfaches Programm schreiben, das die folgenden
Anforderungen erfllt:
Nach dem Start soll das Programm ein Fenster mit dem Titel Nachrichtentransfer auf dem
Bildschirm anzeigen.
Das Fenster soll einen grauen Hintergrund haben und in der Client-Area die Meldung Zum Beenden
bitte ESC drcken ... anzeigen.
Nach Drcken der Taste [ESC] soll das Fenster geschlossen und das Programm beendet werden.
Andere Tastendrcke, Mausklicks oder hnliche Ereignisse werden ignoriert.
/* Listing2801.java */
import java.awt.*;
import java.awt.event.*;
public class Listing2801
extends Frame
{
public static void main(String[] args)
{
Listing2801 wnd = new Listing2801();
}
public Listing2801()
{
Listing2801.java
016
017
018
019
020
021
022
023
024
025
026
027
028 }
super("Nachrichtentransfer");
setBackground(Color.lightGray);
setSize(300,200);
setLocation(200,100);
setVisible(true);
}
public void paint(Graphics g)
{
g.setFont(new Font("Serif",Font.PLAIN,18));
g.drawString("Zum Beenden bitte ESC drcken...",10,50);
}
Hinweis
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
/* Listing2802.java */
Listing2802.java
import java.awt.*;
import java.awt.event.*;
public class Listing2802
extends Frame
implements KeyListener
{
public static void main(String[] args)
{
Listing2802 wnd = new Listing2802();
}
public Listing2802()
{
super("Nachrichtentransfer");
setBackground(Color.lightGray);
setSize(300,200);
setLocation(200,100);
setVisible(true);
addKeyListener(this);
}
public void paint(Graphics g)
{
g.setFont(new Font("Serif",Font.PLAIN,18));
g.drawString("Zum Beenden bitte ESC drcken...",10,50);
}
public void keyPressed(KeyEvent event)
{
if (event.getKeyCode() == KeyEvent.VK_ESCAPE) {
setVisible(false);
dispose();
System.exit(0);
}
}
public void keyReleased(KeyEvent event)
{
}
public void keyTyped(KeyEvent event)
{
}
}
weitergeleitet und fhren zum Aufruf der Methoden keyPressed, keyReleased oder keyTyped des
Interfaces KeyListener. Diese Implementierung ist sehr naheliegend, denn sie ist einfach zu
implementieren und erfordert keine weiteren Klassen. Nachteilig ist dabei allerdings:
Es besteht keine Trennung zwischen GUI-Code und Applikationslogik. Dies kann groe Programme
unbersichtlich und schwer wartbar machen.
Fr jeden Ereignistyp mu eine passende Listener-Klasse registriert werden. Da viele der
EventListener-Interfaces mehr als eine Methode definieren, werden dadurch schnell viele leere
Methodenrmpfe in der Fensterklasse zu finden sein. In diesem Beispiel sind es schon
keyReleased und keyTyped, bei zustzlichen Interfaces wrden schnell weitere hinzukommen.
Es bleibt festzuhalten, da diese Technik bestenfalls fr kleine Programme geeignet ist, die nur begrenzt
erweitert werden mssen. Durch die Vielzahl leerer Methodenrmpfe knnen aber auch kleine Programme
schnell unbersichtlich werden.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
/* Listing2803.java */
Listing2803.java
import java.awt.*;
import java.awt.event.*;
public class Listing2803
extends Frame
{
public static void main(String[] args)
{
Listing2803 wnd = new Listing2803();
}
public Listing2803()
{
super("Nachrichtentransfer");
setBackground(Color.lightGray);
setSize(300,200);
setLocation(200,100);
setVisible(true);
addKeyListener(new MyKeyListener());
}
public void paint(Graphics g)
{
g.setFont(new Font("Serif",Font.PLAIN,18));
g.drawString("Zum Beenden bitte ESC drcken...",10,50);
}
class MyKeyListener
extends KeyAdapter
{
public void keyPressed(KeyEvent event)
{
if (event.getKeyCode() == KeyEvent.VK_ESCAPE) {
setVisible(false);
dispose();
System.exit(0);
}
}
}
}
Klasse, die aus KeyAdapter abgeleitet wurde, als Ereignisempfnger. Zum Instanzierungszeitpunkt
erfolgt die Definition der berlagernden Methode keyPressed, in der der Code zur Reaktion auf das
Drcken der Taste [ESC] untergebracht wird.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
/* Listing2804.java */
Listing2804.java
import java.awt.*;
import java.awt.event.*;
public class Listing2804
extends Frame
{
public static void main(String[] args)
{
Listing2804 wnd = new Listing2804();
}
public Listing2804()
{
super("Nachrichtentransfer");
setBackground(Color.lightGray);
setSize(300,200);
setLocation(200,100);
setVisible(true);
addKeyListener(
new KeyAdapter() {
public void keyPressed(KeyEvent event)
{
if (event.getKeyCode() == KeyEvent.VK_ESCAPE) {
setVisible(false);
dispose();
System.exit(0);
}
}
}
);
}
public void paint(Graphics g)
{
g.setFont(new Font("Serif",Font.PLAIN,18));
g.drawString("Zum Beenden bitte ESC drcken...",10,50);
}
}
Hinweis
Das nachfolgende Beispiel zeigt diese Vorgehensweise, indem es unser Beispielprogramm in die drei
Klassen Listing2805, MainFrameCommand und MainFrameGUI aufteilt. Listing2805 enthlt
nur noch die main-Methode und dient lediglich dazu, die anderen beiden Klassen zu instanzieren.
MainFrameGUI realisiert die GUI-Funktionalitt und stellt das Fenster auf dem Bildschirm dar.
MainFrameCommand spielt die Rolle des Kommandointerpreters, der immer dann aufgerufen wird, wenn
im Fenster ein Tastaturereignis aufgetreten ist.
Die Verbindung zwischen beiden Klassen erfolgt durch Aufruf der Methode addKeyListener in
MainFrameGUI, an die das an den Konstruktor bergebene MainFrameCommand-Objekt weitergereicht
wird. Dazu ist es erforderlich, da das Hauptprogramm den Ereignisempfnger cmd zuerst instanziert, um
ihn bei der Instanzierung des GUI-Objekts gui bergeben zu knnen.
Umgekehrt bentigt natrlich auch das Kommando-Objekt Kenntnis ber das GUI-Objekt, denn es
Tip
soll ja das zugeordnete Fenster schlieen und das Programm beenden. Der scheinbare
Instanzierungskonflikt durch diese zirkulre Beziehung ist aber in Wirklichkeit gar keiner, denn bei
jedem Aufruf einer der Methoden von MainFrameCommand wird an das KeyEvent-Objekt der
Auslser der Nachricht bergeben, und das ist in diesem Fall stets das MainFrameGUI-Objekt gui.
So kann innerhalb des Kommando-Objekts auf alle ffentlichen Methoden des GUI-Objekts
zugegriffen werden.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
/* Listing2805.java */
import java.awt.*;
import java.awt.event.*;
public class Listing2805
{
public static void main(String[] args)
{
MainFrameCommand cmd = new MainFrameCommand();
MainFrameGUI
gui = new MainFrameGUI(cmd);
}
}
class MainFrameGUI
extends Frame
{
Listing2805.java
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
spezialisierter Kommandoklassen). Empfehlenswert ist in diesem Zusammenhang die Lektre der JDKDokumentation, die ein hnliches Beispiel in leicht vernderter Form enthlt.
java.awt.Component
Warnung
Wenn ein passender Ereignisempfnger ber die zugehrige addEventListenerMethode registriert wurde.
Wenn der Ereignistyp explizit durch Aufruf der Methode enableEvents aktiviert
wurde.
Die Methode enableEvents erwartet als Argument eine Maske, die durch eine bitweise OderVerknpfung der passenden Maskenkonstanten aus der Klasse AWTEvent zusammengesetzt werden kann:
protected final void enableEvents(long eventsToEnable)
java.awt.Component
Die verfgbaren Masken sind analog zu den Ereignistypen benannt und heien ACTION_EVENT_MASK,
ADJUSTMENT_EVENT_MASK, COMPONENT_EVENT_MASK usw.
Das folgende Beispiel berlagert die Methode processKeyEvent in der Klasse Frame (die sie aus
Component geerbt hat). Durch Aufruf von enableEvents wird die Weiterleitung der Tastaturereignisse
aktiviert, und das Programm zeigt dasselbe Verhalten wie die vorigen Programme.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
/* Listing2806.java */
Listing2806.java
import java.awt.*;
import java.awt.event.*;
public class Listing2806
extends Frame
{
public static void main(String[] args)
{
Listing2806 wnd = new Listing2806();
}
public Listing2806()
{
super("Nachrichtentransfer");
setBackground(Color.lightGray);
setSize(300,200);
setLocation(200,100);
setVisible(true);
enableEvents(AWTEvent.KEY_EVENT_MASK);
}
public void paint(Graphics g)
{
g.setFont(new Font("Serif",Font.PLAIN,18));
g.drawString("Zum Beenden bitte ESC drcken...",10,50);
}
public void processKeyEvent(KeyEvent event)
{
if (event.getID() == KeyEvent.KEY_PRESSED) {
if (event.getKeyCode() == KeyEvent.VK_ESCAPE) {
setVisible(false);
dispose();
System.exit(0);
}
}
super.processKeyEvent(event);
}
}
Das hier vorgestellte Verfahren umgeht das Delegation Event Model vollstndig und hat damit
die gleichen inhrenten Nachteile wie das Event-Handling des alten JDK. Die Dokumentation
zum JDK empfiehlt daher ausdrcklich, fr alle normalen Anwendungsflle das Delegation
Event Model zu verwenden und die Anwendungen nach einem der in den ersten drei Beispielen
genannten Entwurfsmuster zu implementieren.
Hinweis
28.2.5 Ausblick
Die hier vorgestellten Entwurfsmuster geben einen berblick ber die wichtigsten Designtechniken fr das
Event-Handling in Java-Programmen. Whrend die ersten beiden Beispiele fr kleine bis mittelgroe
Programme gut geeignet sind, kommen die Vorteile der in Variante 3 vorgestellten Trennung zwischen GUICode und Anwendungslogik vor allem bei greren Programmen zum Tragen. Die vierte Variante ist
vornehmlich fr Spezialflle geeignet und sollte entsprechend umsichtig eingesetzt werden.
Wir werden in den nachfolgenden Kapiteln vorwiegend die ersten beiden Varianten einsetzen.
Hinweis
Wenn es darum geht, Ereignishandler fr die Beispielprogramme zu implementieren, werden wir
also entweder die erforderlichen Listener-Interfaces in der Fensterklasse selbst implementieren
oder sie in lokalen oder anonymen Klassen unterbringen. Da das Event-Handling des JDK 1.1
eine Vielzahl von Designvarianten erlaubt, werden wir uns nicht immer sklavisch an die
vorgestellten Entwurfsmuster halten, sondern teilweise leicht davon abweichen oder
Mischformen verwenden. Dies ist beabsichtigt und soll den mglichen Formenreichtum
demonstrieren. Wo ntig, werden wir auf spezielle Implementierungsdetails gesondert eingehen.
Kapitel 29 widmet sich den wichtigsten Low-Level-Events und demonstriert den genauen Einsatz ihrer
Listener- und Event-Methoden anhand vieler Beispiele. In spteren Kapiteln werden die meisten der HighLevel-Events erlutert. Sie werden in der Regel dort eingefhrt, wo ihr Einsatz durch das korrespondierende
Dialogelement motiviert wird. So erlutert Kapitel 30 in Zusammenhang mit der Vorstellung von Mens die
Action-Ereignisse, und in Kapitel 32 werden Ereignisse erlutert, die von den dort vorgestellten
Dialogelementen ausgelst werden.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
29.2 Component-Events
29.2 Component-Events
Ein Component-Event wird generiert, wenn eine Komponente verschoben oder ihre Gre verndert wurde
oder wenn sich der Anzeigezustand einer Komponente verndert hat. Da sowohl Fenster als auch alle
Dialogelemente aus der Klasse Component abgeleitet sind, haben die hier erwhnten Ereignisse fr nahezu
alle GUI-Elemente des AWT Gltigkeit.
Ein Empfnger fr Component-Events mu das Interface ComponentListener implementieren und
bekommt Events des Typs ComponentEvent bergeben. ComponentEvent erweitert die Klasse
AWTEvent und stellt neben getID und getSource die Methode getComponent zur Verfgung, mit der
die Komponente ermittelt werden kann, die die Nachricht ausgelst hat.
public Component getComponent()
java.awt.event.ComponentEvent
Die Registrierung der Empfngerklasse erfolgt mit der Methode addComponentListener, die in allen aus
Component abgeleiteten Klassen zur Verfgung steht:
public void addComponentListener(ComponentListener l)
java.awt.Component
Tabelle 29.2 gibt eine bersicht der Methoden von ComponentListener und erklrt ihre Bedeutung:
Ereignismethode
Bedeutung
componentShown
componentHidden
componentMoved
componentResized
/* Listing2902.java */
import java.awt.*;
import java.awt.event.*;
class ComponentRepaintAdapter
extends ComponentAdapter
{
public void componentMoved(ComponentEvent event)
{
event.getComponent().repaint();
}
public void componentResized(ComponentEvent event)
{
event.getComponent().repaint();
}
}
class BirdsEyeFrame
extends Frame
{
public BirdsEyeFrame()
{
super("BirdsEyeFrame");
addWindowListener(new WindowClosingAdapter(true));
addComponentListener(new ComponentRepaintAdapter());
setBackground(Color.lightGray);
}
public void paint(Graphics g)
{
Dimension screensize = getToolkit().getScreenSize();
Dimension framesize = getSize();
double qx = framesize.width / (double)screensize.width;
double qy = framesize.height / (double)screensize.height;
g.setColor(Color.white);
g.fillRect(
(int)(qx * getLocation().x),
(int)(qy * getLocation().y),
(int)(qx * framesize.width),
(int)(qy * framesize.height)
);
g.setColor(Color.darkGray);
g.fillRect(
(int)(qx * getLocation().x),
(int)(qy * getLocation().y),
(int)(qx * framesize.width),
(int)(qy * getInsets().top)
Listing2902.java
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
);
g.drawRect(
(int)(qx *
(int)(qy *
(int)(qx *
(int)(qy *
);
getLocation().x),
getLocation().y),
framesize.width),
framesize.height)
}
}
public class Listing2902
{
public static void main(String[] args)
{
BirdsEyeFrame wnd = new BirdsEyeFrame();
wnd.setSize(300,200);
wnd.setLocation(200,100);
wnd.setVisible(true);
}
}
Abbildung 29.1: Das Fenster sieht sich selbst aus der Vogelperspektive
In vielen Beispielen in diesem Buch wird der Einfachheit halber die in Abschnitt 23.2.4 vorgestellte Hinweis
Klasse WindowClosingAdapter verwendet, um einen Listener zum Schlieen des Fensters zu
registrieren. Damit ein solches Beispiel sich kompilieren lt, mu die Datei
WindowClosingAdapter.java im aktuellen Verzeichnis vorhanden sein. Sie befindet sich auf der CDROM zum Buch oder in Listing 23.2.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
29.3 Mouse-Events
29.3 Mouse-Events
Ein Mouse-Event entsteht, wenn der Anwender innerhalb der Client-Area des Fensters eine der
Maustasten drckt oder loslt. Dabei reagiert das Programm sowohl auf Klicks der linken als auch falls vorhanden - der rechten Maustaste und zeigt an, welche der Umschalttasten [STRG], [ALT],
[UMSCHALT] oder [META] whrend des Mausklicks gedrckt waren. Des weiteren ist es mglich,
zwischen einfachen und doppelten Mausklicks zu unterscheiden.
Ein Empfnger fr Mouse-Events mu das Interface MouseListener implementieren und
bekommt Events des Typs MouseEvent bergeben. MouseEvent erweitert die Klasse
InputEvent und stellt neben getID und getSource eine Reihe zustzlicher Methoden zur
Verfgung, die wichtige Informationen liefern. Die Registrierung der Empfngerklasse erfolgt mit der
Methode addMouseListener, die in allen Klassen zur Verfgung steht, die aus Component
abgeleitet wurden:
public void addMouseListener(MouseListener l)
java.awt.Component
Tabelle 29.3 gibt eine bersicht der Methoden von MouseListener und erklrt ihre Bedeutung:
Ereignismethode
Bedeutung
mousePressed
mouseReleased
mouseClicked
mouseEntered
mouseExited
Damit ein Programm auf Mausklicks angemessen reagieren kann, bentigt es zustzliche
Informationen, die es sich mit den Methoden der Klasse MouseEvent bzw. ihrer
Vaterklasse InputEvent beschaffen kann. Zunchst kann mit getX, getY und
getPosition die Position des Mauszeigers zum Zeitpunkt des Ereignisses ermittelt
werden:
public int getX()
public int getY()
Hinweis
java.awt.event.MouseEvent
getX liefert die x- und getY die y-Koordinate des Punktes, an dem der Mauszeiger sich beim
Auftreten des Ereignisses befindet. getPoint liefert dieselbe Information zusammengefat als
Point-Objekt.
Die Koordinatenwerte werden relativ zum Ursprung der auslsenden Komponente angegeben.
Bei einem Fenster des Typs Frame bedeutet dies, da bei einem Klick in der oberen linken
Ecke nicht (0,0) zurckgegeben wird, sondern (getInsets().left,
getInsets().top). Dies stimmt mit dem Ursprung des Koordinatensystems berein, das
auch beim Aufruf der Grafikroutinen zugrunde gelegt wird.
Tip
Des weiteren gibt es in MouseEvent die Methode isPopupTrigger, mit der abgefragt werden
kann, ob das Klickereignis den Aufruf eines Popup-Mens anzeigen soll. Wir werden darauf in
Kapitel 30 zurckkommen.
Die Methode getClickCount liefert die Anzahl der Mausklicks, die dem aktuellen Ereignis
zugeordnet ist:
public int getClickCount()
java.awt.event.MouseEvent
Normalerweise hat dieser Zhler den Wert 1, bei Mehrfachklicks gibt es aber auch MouseEvents
mit dem Wert 2 oder (je nach Schnelligkeit der Klicks) noch hheren Werten. Die Abfrage von
getClickCount kann dazu verwendet werden, auf Doppelklicks zu reagieren. Hierzu ist lediglich
beim Aufruf von mousePressed oder mouseClicked zu berprfen, ob getClickCount den
Wert 2 liefert. In diesem Fall handelt es sich um einen Doppelklick. Zu beachten ist, da bei einem
Doppelklick zwei aufeinanderfolgende mousePressed-mouseReleased-mouseClickedSequenzen gesendet werden. In der ersten hat der Zhler den Wert 1, um den ersten (einfachen)
Mausklick zu signalisieren, und erst in der zweiten steht er auf 2. Vor einem Doppelklick wird also
immer erst der zugehrige Einzelklick bertragen.
Eine alternative Methode, Doppelklicks zu erkennen, besteht darin, die Zeitspanne
Hinweis
zwischen zwei Klicks und den Abstand der beiden Koordinatenpaare voneinander zu
messen und bei hinreichend kleinen Differenzen einen Doppelklick anstelle zweier
Einfachklicks zu melden. Dazu wird mit der Methode getWhen der Klasse InputEvent
der Zeitpunkt (in Millisekunden) und mit getPoint oder getX und getY die
Mausposition zweier aufeinanderfolgender Events bestimmt:
java.awt.event.InputEvent
Diese Vorgehensweise hat den Vorteil, da die zeitliche und positionelle Toleranz fr Doppelklicks
selbst beeinflut werden kann, was bei dem oben beschriebenen Standardverfahren nicht mglich ist.
Neben den bisher besprochenen Methoden stehen die aus InputEvent geerbten Methoden zur
Verfgung. InputEvent ist die gemeinsame Basisklasse von MouseEvent und KeyEvent und
stellt Methoden zur Verfgung, die generelle Informationen ber den Zustand der Umschalttasten
[STRG], [ALT], [UMSCHALT] oder [META] zum Zeitpunkt des Ereignisses liefern:
public
public
public
public
boolean
boolean
boolean
boolean
isShiftDown()
isControlDown()
isMetaDown()
isAltDown()
java.awt.event.InputEvent
/* Listing2903.java */
import java.awt.*;
import java.awt.event.*;
public class Listing2903
extends Frame
{
public static void main(String[] args)
{
Listing2903 wnd = new Listing2903();
wnd.setSize(300,200);
wnd.setLocation(200,100);
wnd.setVisible(true);
}
public Listing2903()
{
super("Mausklicks");
addWindowListener(new WindowClosingAdapter(true));
addMouseListener(new MyMouseListener());
}
class MyMouseListener
Listing2903.java
025
extends MouseAdapter
026
{
027
int cnt = 0;
028
029
public void mousePressed(MouseEvent event)
030
{
031
Graphics g = getGraphics();
032
int x = event.getX();
033
int y = event.getY();
034
if (event.getClickCount() == 1) { //Gesicht
035
++cnt;
036
//Kopf und Augen
037
g.drawOval(x-10,y-10,20,20);
038
g.fillRect(x-6,y-5,4,5);
039
g.fillRect(x+3,y-5,4,5);
040
//Mund
041
if (event.isMetaDown()) { //grimmig
042
g.drawLine(x-5,y+7,x+5,y+7);
043
} else {
//lcheln
044
g.drawArc(x-7,y-7,14,14,225,100);
045
}
046
//Zhler
047
g.drawString(""+cnt,x+10,y-10);
048
} else if (event.getClickCount() == 2) { //Brille
049
g.drawLine(x-9,y-3,x+9,y-3);
050
}
051
}
052
}
053 }
Listing 29.3: Reaktion auf Mausklicks
Beim Drcken der linken Maustaste lchelt das Gesicht, bei der rechten sieht es grimmig aus. Bei
einem Doppelklick bekommt der Smiley zustzlich eine Brille aufgesetzt. Die Ausgabe des
Programms sieht beispielsweise so aus:
Hinweis
Leider hat das Beispielprogramm eine gravierende konzeptionelle Schwche, denn die gezeichneten
Smileys sind nicht dauerhaft. Wird das Fenster in den Hintergrund gestellt, nachdem einige
Mausklicks durchgefhrt wurden, sind bei erneuter Anzeige des Fensters die zuvor gezeichneten
Smileys wieder verschwunden. Der Grund dafr ist, da wir in unserem Beispiel nicht die paintMethode berlagert haben, sondern die Ausgaben als direkte Reaktion auf ein Mausereignis
erledigten. Da Windows nicht dafr sorgt, den Inhalt eines zuvor verdeckten Fensters zu
rekonstruieren, ist die Anwendung selbst dafr verantwortlich, dies in ihrer paint-Methode zu tun.
Eine bessere Implementierung dieses Programms wrde also darin bestehen, die Koordinaten und
Eigenschaften aller gezeichneten Objekte zu speichern und sie beim Aufruf von paint spter selbst
zu rekonstruieren. Wir werden diese Vorgehensweise in einem spteren Beispiel genauer
kennenlernen.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
29.4 MouseMotion-Events
29.4 MouseMotion-Events
Im JDK 1.1 gibt es eine klare Trennung zwischen Mouse-Events und MouseMotion-Events. Whrend die
Mouse-Events fr Mausklicks und das Betreten oder Verlassen der Komponente zustndig sind, geben
MouseMotion-Events Auskunft ber die Bewegung des Mauszeigers. Neben der verbesserten Modularisierung
(sehr viele Programme wollen lediglich von Mausklicks, nicht aber von Mausbewegungen unterrichtet werden)
wurde die Trennung vor allem deshalb vorgenommen, um Performance-Verbesserungen zu erzielen. Durch die
Entkopplung der Ereignishandler fr Mausbewegungen wird die Anzahl der Events, mit denen die meisten
Event-Handler beschossen werden, im Vergleich zum JDK 1.0 drastisch reduziert.
Ein Empfnger fr MouseMotion-Events mu das Interface MouseMotionListener implementieren. Er
wird mit der Methode addMouseMotionListener registriert, die in allen Objekten der Klasse
Component oder daraus abgeleiteten Klassen zur Verfgung steht.
public void addMouseMotionListener(MouseListener l)
java.awt.Component
Warnung
java.awt.event.MouseMotionListener
mouseMoved wird aufgerufen, wenn die Maus bewegt wird, ohne da dabei eine der Maustasten gedrckt ist.
mouseDragged wird dagegen aufgerufen, wenn die Maus bei gedrckter linker oder rechter Maustaste
bewegt wird.
Das folgende Listing zeigt den Einsatz von mouseDragged am Beispiel eines Programms, mit dem
Rechtecke gezeichnet werden knnen. Das Drcken der linken Maustaste legt den Anfangspunkt des Rechtecks
fest, und durch Ziehen der Maus wird seine Gre bestimmt. Nach dem Loslassen der Maustaste wird das
Rechteck in die Liste der gezeichneten Objekte eingetragen und beim nchsten Aufruf von paint gezeichnet.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
/* Listing2904.java */
Listing2904.java
import java.awt.*;
import java.awt.event.*;
import java.util.*;
public class Listing2904
extends Frame
{
private Vector drawlist;
private Rectangle actrect;
public static void main(String[] args)
{
Listing2904 wnd = new Listing2904();
wnd.setLocation(200,200);
wnd.setSize(400,300);
wnd.setVisible(true);
}
public Listing2904()
{
super("Rechtecke zeichnen");
drawlist = new Vector();
actrect = new Rectangle(0,0,0,0);
addWindowListener(new MyWindowListener());
addMouseListener(new MyMouseListener());
addMouseMotionListener(new MyMouseMotionListener());
}
public void paint(Graphics g)
{
Rectangle r;
Enumeration e;
for (e = drawlist.elements(); e.hasMoreElements(); ) {
r = (Rectangle)e.nextElement();
g.drawRect(r.x, r.y, r.width, r.height);
}
if (actrect.x > 0 || actrect.y > 0) {
g.drawRect(
actrect.x,
actrect.y,
actrect.width,
actrect.height
);
}
}
class MyMouseListener
extends MouseAdapter
{
public void mousePressed(MouseEvent event)
{
actrect = new Rectangle(event.getX(),event.getY(),0,0);
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092 }
}
public void mouseReleased(MouseEvent event)
{
if (actrect.width > 0 || actrect.height > 0) {
drawlist.addElement(actrect);
}
repaint();
}
}
class MyMouseMotionListener
extends MouseMotionAdapter
{
public void mouseDragged(MouseEvent event)
{
int x = event.getX();
int y = event.getY();
if (x > actrect.x && y > actrect.y) {
actrect.width = x - actrect.x;
actrect.height = y - actrect.y;
}
repaint();
}
}
class MyWindowListener
extends WindowAdapter
{
public void windowClosing(WindowEvent event)
{
setVisible(false);
dispose();
System.exit(0);
}
}
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt Suchen
<
>
Index
>>
DOC
API
29.5 Focus-Events
29.5 Focus-Events
Der Fokus zeigt an, welches Fenster die Tastatureingaben erhlt. Sind mehrere Fenster gleichzeitig
geffnet, so kann immer nur eines von ihnen den Fokus beanspruchen. Sind auf einem aktiven
Fenster mehrere Dialogelemente aktiv, so kann ebenfalls nur eines davon den Fokus erhalten, denn
jedes Dialogelement wird ebenfalls durch ein (meist unsichtbares) Fenster dargestellt.
Ein Empfnger fr Focus-Events mu das Interface FocusListener implementieren und
bekommt Events des Typs FocusEvent bergeben. FocusEvent erweitert die Klasse
ComponentEvent und stellt neben getID und getSource die Methode isTemporary zur
Verfgung, die anzeigt, ob der Fokuswechsel temporr oder permanent ist:
public boolean isTemporary()
java.awt.event.FocusEvent
Die Registrierung von Focus-Events erfolgt mit der Methode addFocusListener, die auf allen
Objekten des Typs Component oder daraus abgeleiteten Objekten zur Verfgung steht:
public void addFocusListener(FocusListener l)
java.awt.Component
java.awt.event.FocusListener
focusGained wird aufgerufen, wenn die Komponente den Fokus erhlt, und focusLost, wenn
sie ihn wieder abgibt. Eine Komponente erhlt den Fokus beispielsweise, wenn sie mit der Maus
angeklickt oder (unter Windows) mit Hilfe der Tasten [TAB] oder [UMSCHALT]+[TAB] oder
ber einen Beschleuniger angesprungen wurde. Auerdem gibt es die Mglichkeit, den Fokus
programmgesteuert zu verndern. Dazu gibt es die Methode requestFocus der Klasse
Component, mit der eine Komponente den Fokus fr sich selbst beanspruchen bzw. ihn einer
anderen Komponente zuweisen kann:
java.awt.Component
Der explizite Umgang mit Focus-Events ist etwa bei Dialogelementen sinnvoll. Dadurch
kann ein Edit-Control beispielsweise feststellen, da das nchste Dialogelement
angesprungen werden soll und ein eventuell genderter Text zuvor gespeichert werden
mu. Focus-Events sind oft schwierig zu debuggen, denn durch den Wechsel zwischen
Debug-Window und Anwendung werden meist zustzliche Fokuswechsel ausgelst, die
den Fehler verschleiern oder die Fehlerstelle unerreichbar machen. Wird innerhalb von
Fokus-Handlern die Methode requestFocus aufgerufen, kann es zudem leicht zu
Endlosschleifen kommen.
Hinweis
/* Listing2905.java */
import java.awt.*;
import java.awt.event.*;
public class Listing2905
extends Frame
implements FocusListener
{
boolean havefocus = false;
public static void main(String[] args)
{
Listing2905 wnd = new Listing2905();
}
public Listing2905()
{
super("Focus-Listener");
addFocusListener(this);
addWindowListener(new WindowClosingAdapter(true));
setBackground(Color.lightGray);
setSize(300,200);
setLocation(200,100);
setVisible(true);
}
public void paint(Graphics g)
{
if (havefocus) {
Listing2905.java
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052 }
g.setColor(Color.black);
g.drawString("Fokus erhalten",10,50);
} else {
g.setColor(Color.darkGray);
g.drawString("Kein Fokus",10,50);
}
}
public void focusGained(FocusEvent event)
{
havefocus = true;
setBackground(Color.yellow);
repaint();
}
public void focusLost(FocusEvent event)
{
havefocus = false;
setBackground(Color.lightGray);
repaint();
}
Titel
<<
Inhalt Suchen
<
>
Index
DOC
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
29.6 Key-Events
29.6 Key-Events
Unter Windows werden alle Tastatureingaben an die fokussierte Komponente gesendet. Ein Empfnger fr KeyEvents mu das Interface KeyListener implementieren und bekommt Events des Typs KeyEvent
bergeben. KeyEvent erweitert die Klasse InputEvent, die ihrerseits aus ComponentEvent abgeleitet ist,
und stellt neben getID und getSource eine ganze Reihe von Methoden zur Verfgung, mit denen die
Erkennung und Bearbeitung der Tastencodes vereinfacht wird.
Die Registrierung von Key-Events erfolgt mit der Methode addKeyListener, die auf allen Objekten des
Typs Component oder daraus abgeleiteten Klassen zur Verfgung steht:
public void addKeyListener(KeyListener l)
java.awt.Component
java.awt.event.KeyListener
Um die Funktionsweise dieser Methoden im Zusammenspiel mit den Methoden der Klasse
KeyEvent besser verstehen zu knnen, wollen wir zwischen Zeichentasten und Funktionstasten
unterscheiden. Zeichentasten sind dabei solche Tasten, mit denen Buchstaben, Ziffern oder sonstige
gltige Unicode-Zeichen eingegeben werden, wie z.B. [a], [A], [B], [1], [2], [%], [+], aber
auch [ESC], [LEER] oder [TAB]. Zu den Funktionstasten gehren beispielsweise [F1], [F2],
[POS1] oder [CURSORLINKS], aber auch die Umschalttasten [STRG], [ALT] und
[UMSCHALT].
Hinweis
Die Methode keyTyped wird immer dann aufgerufen, wenn eine Zeichentaste gedrckt wurde. Beim Drcken
einer Funktionstaste wird sie dagegen nicht aufgerufen. Im Gegensatz dazu wird keyPressed bei jedem
Tastendruck aufgerufen, unabhngig davon, ob es sich um eine Zeichentaste oder eine Funktionstaste handelt.
Mit keyPressed knnen sogar in beschrnktem Umfang die Feststelltasten wie [NUMLOCK] oder
[CAPSLOCK] erkannt werden. Beide Methoden erhalten auch Tastatur-Repeats, werden also bei lngerem
Festhalten einer Taste wiederholt aufgerufen. Die Methode keyReleased wird aufgerufen, wenn eine
gedrckte Taste losgelassen wurde, unabhngig davon, ob es sich um eine Zeichen- oder Funktionstaste handelt.
Um beim Auftreten eines Tastatur-Events zu erkennen, welche Taste gedrckt wurde, stellt die Klasse
KeyEvent die Methoden getKeyCode und getKeyChar und zustzlich die aus InputEvent geerbten
Methoden isShiftDown, isControlDown, isMetaDown und isAltDown zur Verfgung:
java.awt.event.KeyEvent
boolean
boolean
boolean
boolean
isShiftDown()
isControlDown()
isMetaDown()
isAltDown()
getKeyChar liefert das Zeichen, das der gedrckten Zeichentaste entspricht, also ein 'a', wenn die Taste [A]
gedrckt wurde, und ein 'A', wenn die Tastenkombination [UMSCHALT]+[A] gedrckt wurde. getKeyCode
liefert dagegen virtuelle Tastencodes, die in KeyEvent als symbolische Konstanten definiert wurden. Hier wird
beim Drcken der Taste [A] immer der Code VK_A geliefert, unabhngig davon, ob [UMSCHALT] gedrckt
wurde oder nicht. Tabelle 29.4 gibt eine bersicht der wichtigsten virtuellen Keycodes der Klasse KeyEvent.
Symbolischer Name
Bedeutung
VK_ENTER
[ENTER]
VK_SPACE
[LEER]
VK_TAB
[TAB]
VK_ESCAPE
[ESC]
VK_BACK_SPACE
[RCK]
VK_HOME, VK_END
[HOME], [END]
VK_INSERT, VK_DELETE
[EINFG], [ENTF]
VK_PAGE_UP, VK_PAGE_DOWN
[BILDHOCH], [BILDRUNTER]
VK_DOWN, VK_UP
[CURSORHOCH], [CURSORRUNTER]
VK_LEFT, VK_RIGHT
[CURSORLINKS], [CURSORRECHTS]
Insgesamt ist das Handling von Tastatur-Events nicht ganz trivial und erfordert ein wenig Aufwand bei der
Unterscheidung von Zeichen-, Funktions-, Umschalt- oder Feststelltasten. Tabelle 29.5 fat die bisherigen
Ausfhrungen noch einmal zusammen. Die erste Zeile zeigt das Verhalten beim Aufruf der Listener-Methode
keyTyped an, die zweite beim Aufruf von keyPressed. Die erste Spalte liefert dazu den Rckgabewert von
getKeyCode, die zweite den von getKeyChar. Jedes Element beschreibt in der oberen Hlfte den
Rckgabewert beim Drcken einer Zeichentaste und in der unteren den beim Drcken einer Funktionstaste.
keyTyped
getKeyCode
getKeyChar
Zeichentaste: VK_UNDEFINED
Funktionstaste: --
/* Listing2906.java */
import java.awt.*;
import java.awt.event.*;
public class Listing2906
extends Frame
implements KeyListener
{
String msg1 = "";
String msg2 = "";
public static void main(String[] args)
{
Listing2906 wnd = new Listing2906();
}
public Listing2906()
{
super("Tastaturereignisse");
addKeyListener(this);
addWindowListener(new WindowClosingAdapter(true));
setBackground(Color.lightGray);
setSize(300,200);
setLocation(200,100);
setVisible(true);
}
public void paint(Graphics g)
Listing2906.java
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
{
if (msg1.length() > 0) {
draw3DRect(g,20,50,250,30);
g.setColor(Color.black);
g.drawString(msg1,30,70);
}
if (msg2.length() > 0) {
draw3DRect(g,20,100,250,30);
g.setColor(Color.black);
g.drawString(msg2,30,120);
}
}
void draw3DRect(Graphics g,int x,int y,int width,int height)
{
g.setColor(Color.darkGray);
g.drawLine(x,y,x,y+height);
g.drawLine(x,y,x+width,y);
g.setColor(Color.white);
g.drawLine(x+width,y+height,x,y+height);
g.drawLine(x+width,y+height,x+width,y);
}
public void keyPressed(KeyEvent event)
{
msg1 = "";
System.out.println(
"key pressed: " +
"key char = " + event.getKeyChar() + " " +
"key code = " + event.getKeyCode()
);
if (event.getKeyChar() == KeyEvent.CHAR_UNDEFINED) {
int key = event.getKeyCode();
//Funktionstaste abfragen
if (key == KeyEvent.VK_F1) {
msg1 = "F1";
} else if (key == KeyEvent.VK_F2) {
msg1 = "F2";
} else if (key == KeyEvent.VK_F3) {
msg1 = "F3";
}
//Modifier abfragen
if (msg1.length() > 0) {
if (event.isAltDown()) {
msg1 = "ALT + " + msg1;
}
if (event.isControlDown()) {
msg1 = "STRG + " + msg1;
}
if (event.isShiftDown()) {
msg1 = "UMSCHALT + " + msg1;
}
}
}
repaint();
}
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109 }
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt Suchen
<
>
Index
>>
DOC
API
30.4 Meneintrge
30.4 Meneintrge
30.4.1 Einfache Meneintrge
30.4.2 CheckboxMenuItem
30.4.3 Beschleunigertasten
30.4.4 Untermens
java.awt.MenuItem
Auch nach der Konstruktion eines Meneintrags ist ein Zugriff auf seinen Namen mglich. Mit der
Methode getLabel kann der Name des Meneintrags abgefragt und mit setLabel sogar
verndert werden:
public String getLabel()
java.awt.MenuItem
Neben einem Namen besitzt ein Meneintrag eine interne Zustandsvariable, die anzeigt, ob er aktiv
ist oder nicht. Nur ein aktiver Eintrag kann vom Anwender ausgewhlt werden und so eine Nachricht
auslsen. Ein inaktiver Eintrag dagegen wird im Men grau dargestellt, kann vom Anwender nicht
mehr ausgewhlt werden und daher auch keine Nachricht mehr auslsen.
Nach dem Aufruf des Konstruktors ist ein Meneintrag zunchst aktiviert. Er kann durch Aufruf von
setEnabled(false) deaktiviert und mit setEnabled(true) aktiviert werden. Durch
Aufruf von isEnabled kann der aktuelle Zustand abgefragt werden:
java.awt.MenuItem
30.4.2 CheckboxMenuItem
Neben der Klasse MenuItem gibt es mit der Klasse CheckboxMenuItem eine zweite Klasse zum
Erzeugen von Meneintrgen. CheckboxMenuItem ist aus MenuItem abgeleitet und bietet als
zustzliches Feature eine interne Zustandsvariable, die zwischen true und false umgeschaltet
werden kann. Die visuelle Darstellung der Zustandsvariablen erfolgt durch Anfgen oder Entfernen
eines Hkchens neben dem Meneintrag. Der Nutzen der Klasse CheckboxMenuItem besteht
darin, da eine logische Programmvariable durch Auswhlen des Menpunkts abwechselnd an- und
ausgeschaltet werden kann.
Die Instanzierung eines CheckboxMenuItem erfolgt wie bei einem MenuItem. Zustzlich
stehen die beiden Methoden setState und getState zum Setzen und Abfragen des Zustands
zur Verfgung:
public void setState(boolean state)
java.awt.CheckboxMenuItem
Das folgende Programm stellt alle bisher erwhnten Eigenschaften in einem Beispiel dar. Es leitet
dazu die Klasse MainMenu1 aus MenuBar ab und erzeugt im Konstruktor die Mens und
Meneintrge. Gegenber der einfachen Instanzierung von MenuBar bietet die Ableitung den
Vorteil, da die neue Klasse Methoden zur Verfgung stellen kann, die zum Zugriff auf Mens oder
Meneintrge verwendet werden knnen.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
/* Listing3001.java */
import java.awt.*;
import java.awt.event.*;
class MainMenu1
extends MenuBar
{
private MenuItem miRueck;
private CheckboxMenuItem miFarbe;
public MainMenu1()
{
Menu m;
//Datei
m = new Menu("Datei");
m.add(new MenuItem("Neu"));
Listing3001.java
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
m.add(new MenuItem("Laden"));
m.add(new MenuItem("Speichern"));
m.addSeparator();
m.add(new MenuItem("Beenden"));
add(m);
//Bearbeiten
m = new Menu("Bearbeiten");
m.add((miRueck = new MenuItem("Rueckgaengig")));
m.addSeparator();
m.add(new MenuItem("Ausschneiden"));
m.add(new MenuItem("Kopieren"));
m.add(new MenuItem("Einfuegen"));
m.add(new MenuItem("Loeschen"));
add(m);
//Optionen
m = new Menu("Optionen");
m.add(new MenuItem("Einstellungen"));
m.add((miFarbe = new CheckboxMenuItem("Farbe")));
add(m);
//Rueckgaengig deaktivieren
enableRueckgaengig(false);
//Farbe anschalten
setFarbe(true);
}
public void enableRueckgaengig(boolean ena)
{
if (ena) {
miRueck.setEnabled(true);
} else {
miRueck.setEnabled(false);
}
}
public void setFarbe(boolean on)
{
miFarbe.setState(on);
}
}
public class Listing3001
extends Frame
{
public static void main(String[] args)
{
Listing3001 wnd = new Listing3001();
}
public Listing3001()
{
069
070
071
072
073
074
075
}
076 }
super("Mens");
setLocation(100,100);
setSize(400,300);
setMenuBar(new MainMenu1());
setVisible(true);
addWindowListener(new WindowClosingAdapter(true));
30.4.3 Beschleunigertasten
In den meisten Programmen lassen sich Mens nicht nur mit der Maus bedienen,
Warnung
sondern ber Beschleunigertasten auch mit der Tastatur. Im JDK 1.0 konnten
Beschleunigertasten unter Windows 95 ganz einfach dadurch eingefgt werden, da an
beliebiger Stelle im Mentext das Zeichen & eingefgt und so die nachfolgende Taste
als Beschleuniger definiert wurde. Dies war natrlich nicht portabel, funktionierte nur
unter Windows und wurde folglich als Bug angesehen und eliminiert.
Das JDK 1.1 implementiert nun ein eigenes Beschleunigerkonzept, das ber Plattformgrenzen
hinweg funktioniert. Dazu wurde die Klasse MenuShortcut eingefhrt, mit deren Hilfe
Beschleunigertasten definiert und an einzelne Meneintrge angehngt werden knnen. Eine
Beschleunigertaste ist dabei immer ein einzelnes Zeichen der Tastatur, das zusammen mit der
java.awt.MenuShortcut
Der erste Konstruktor erwartet den virtuellen Tastencode der gewnschten Beschleunigertaste (siehe
Kapitel 29). Fr einfache alphanumerische Zeichen kann hier auch das Zeichen selbst bergeben
werden. Die bergabe einer Funktionstaste ist leider nicht ohne weiteres mglich, denn deren
virtuelle Tastencodes berschneiden sich mit den ASCII-Codes der Kleinbuchstaben.
Funktionstasten knnen daher nur dann als Beschleuniger verwendet werden, wenn ihre virtuellen
Tastencodes die Umwandlung in Grobuchstaben unverndert berstehen (z.B. VK_DELETE). Der
zweite Konstruktor erlaubt zustzlich die bergabe eines booleschen Parameters
useShiftModifier, der dafr sorgt, da der Beschleuniger nur dann greift, wenn neben der
systemspezifischen Umschalttaste fr Beschleuniger zustzlich die Taste [UMSCHALT] gedrckt
wird.
Um einen Beschleuniger an einen Menpunkt zu binden, ist das MenuShortcut-Objekt als
zweites Argument an den Konstruktor von MenuItem zu bergeben:
public MenuItem(String label, MenuShortcut s)
java.awt.MenuItem
java.awt.MenuItem
Durch Aufruf von deleteShortCut kann der einem Meneintrag zugeordnete Beschleuniger
gelscht werden:
public void deleteShortcut()
java.awt.MenuItem
Aufgrund eines Bugs im AWT (der auch im JDK 1.2 noch enthalten war) mu nach der Definition
eines Beschleunigers zustzlich die Methode setActionCommand aufgerufen werden, um den
String, der beim Auslsen des Beschleunigers an den ActionListener gesendet werden soll,
festzulegen:
public void setActionCommand(String command)
java.awt.MenuItem
Ohne diesen Aufruf wrde ein null-Objekt gesendet werden. Eine beispielhafte Aufrufsequenz zur
Erzeugung eines Meneintrags mit Beschleuniger sieht damit so aus:
001
002
003
004
005
006
007
008
009
010
011
012
Menu m;
MenuItem mi;
MenuShortcut ms;
//Datei
m = new Menu("Datei");
ms = new MenuShortcut(KeyEvent.VK_N);
mi = new MenuItem("Neu",ms);
mi.setActionCommand("Neu");
mi.addActionListener(listener);
m.add(mi);
/* MainMenu2.inc */
class MainMenu2
extends MenuBar
{
public MainMenu2()
{
Menu m;
MenuItem mi;
MenuShortcut ms;
//Datei
m = new Menu("Datei");
ms = new MenuShortcut(KeyEvent.VK_N);
mi = new MenuItem("Neu",ms);
mi.setActionCommand("Neu");
m.add(mi);
ms = new MenuShortcut(KeyEvent.VK_L);
mi = new MenuItem("Laden",ms);
mi.setActionCommand("Laden");
m.add(mi);
ms = new MenuShortcut(KeyEvent.VK_S);
mi = new MenuItem("Speichern",ms);
MainMenu2.inc
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
}
055 }
mi.setActionCommand("Speichern");
m.add(mi);
ms = new MenuShortcut(KeyEvent.VK_E);
mi = new MenuItem("Beenden",ms);
mi.setActionCommand("Beenden");
m.add(mi);
add(m);
//Bearbeiten
m = new Menu("Bearbeiten");
ms = new MenuShortcut(KeyEvent.VK_X);
mi = new MenuItem("Ausschneiden",ms);
mi.setActionCommand("Ausschneiden");
m.add(mi);
ms = new MenuShortcut(KeyEvent.VK_C);
mi = new MenuItem("Kopieren",ms);
mi.setActionCommand("Kopieren");
m.add(mi);
ms = new MenuShortcut(KeyEvent.VK_V);
mi = new MenuItem("Einfgen",ms);
mi.setActionCommand("Einfgen");
m.add(mi);
add(m);
30.4.4 Untermens
Mens lassen sich auf einfache Art und Weise schachteln. Dazu ist beim Aufruf der add-Methode
anstelle einer Instanz der Klasse MenuItem ein Objekt der Klasse Menu zu bergeben, das das
gewnschte Untermen reprsentiert. Das folgende Beispiel erweitert das Men Optionen der
Klasse MainMenu1 um den Meneintrag Schriftart, der auf ein Untermen mit den verfgbaren
Schriftarten verzweigt (der Code zur Erzeugung des Untermens steht in den Zeilen 034 bis 041):
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
/* MainMenu3.inc */
class MainMenu3
extends MenuBar
{
private MenuItem miRueck;
private CheckboxMenuItem miFarbe;
public MainMenu3()
{
Menu m;
//Datei
m = new Menu("Datei");
m.add(new MenuItem("Neu"));
m.add(new MenuItem("Laden"));
m.add(new MenuItem("Speichern"));
m.addSeparator();
m.add(new MenuItem("Beenden"));
add(m);
//Bearbeiten
m = new Menu("Bearbeiten");
m.add((miRueck = new MenuItem("Rueckgaengig")));
m.addSeparator();
m.add(new MenuItem("Ausschneiden"));
m.add(new MenuItem("Kopieren"));
m.add(new MenuItem("Einfuegen"));
m.add(new MenuItem("Loeschen"));
add(m);
//Optionen
m = new Menu("Optionen");
m.add(new MenuItem("Einstellungen"));
//Untermen Schriftart
Menu m1 = new Menu("Schriftart");
m1.add(new MenuItem("Arial"));
m1.add(new MenuItem("TimesRoman"));
m1.add(new MenuItem("Courier"));
m1.add(new MenuItem("System"));
m.add(m1);
MainMenu3.inc
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064 }
Titel
<<
Inhalt Suchen
<
>
Index
DOC
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
30.5 Action-Events
30.5 Action-Events
Ein Action-Event wird generiert, wenn der Anwender einen Menpunkt selektiert und ausgewhlt hat. Das
Programm kann den auslsenden Meneintrag bestimmen und so geeignet darauf reagieren. Action-Events werden
von der Klasse MenuItem und drei weiteren Klassen (auf die wir in den folgenden Kapiteln zurckkommen)
gesendet:
Ein Empfnger fr Action-Events mu das Interface ActionListener implementieren und bekommt Events des
Typs ActionEvent bergeben. ActionEvent erweitert die Klasse AWTEvent und stellt neben getID und
getSource vor allem die Methode getActionCommand zur Verfgung, mit der die verschiedenen
Ereignisquellen unterschieden werden knnen:
public String getActionCommand()
java.awt.event.ActionEvent
Wird das Action-Event durch ein MenuItem ausgelst, liefert dessen Methode getActionCommand
die Bezeichnung des Menpunktes, wie sie an den Konstruktor bergeben wurde. Durch einen expliziten
Aufruf von setActionCommand kann dieser String auch unabhngig von der Beschriftung des
Menpunkts gendert werden. Ein Aufruf von getActionCommand liefert den aktuellen Inhalt des
Menpunkts bzw. seine Beschriftung, falls setActionCommand noch nicht aufgerufen wurde:
public void setActionCommand(String command)
Hinweis
java.awt.MenuItem
Die Registrierung der Empfngerklasse erfolgt mit der Methode addActionListener, die in den Klassen
MenuItem, Button, List und TextField zur Verfgung steht:
public void addActionListener(ActionListener l)
java.awt.Button
Das Interface ActionListener stellt lediglich die Methode actionPerformed zur Verfgung, die beim
Aufruf ein ActionEvent bergeben bekommt:
public abstract void actionPerformed(ActionEvent e)
java.awt.event.ActionListener
Tip
Das folgende Programm zeigt die Reaktion auf Action-Events. Das Programm ffnet ein Fenster, das mit Hilfe von
Meneintrgen auf dem Bildschirm verschoben oder in der Gre verndert werden kann:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
/* Listing3005.java */
import java.awt.*;
import java.awt.event.*;
class MainMenu4
extends MenuBar
{
private MenuItem miRueck;
private CheckboxMenuItem miFarbe;
private static void
addNewMenuItem(Menu menu, String name, ActionListener listener)
{
int pos = name.indexOf('&');
MenuShortcut shortcut = null;
MenuItem mi;
if (pos != -1) {
if (pos < name.length() - 1) {
char c = name.charAt(pos + 1);
shortcut=new MenuShortcut(Character.toLowerCase(c));
name=name.substring(0,pos)+name.substring(pos + 1);
}
}
if (shortcut != null) {
mi = new MenuItem(name, shortcut);
} else {
mi = new MenuItem(name);
}
mi.setActionCommand(name);
mi.addActionListener(listener);
menu.add(mi);
}
public MainMenu4(ActionListener listener)
{
Menu m;
//Men "Gre"
m = new Menu("Gre");
addNewMenuItem(m, "&Grer", listener);
addNewMenuItem(m, "&Kleiner", listener);
m.addSeparator();
addNewMenuItem(m, "B&eenden", listener);
add(m);
//Men "Position"
m = new Menu("Position");
addNewMenuItem(m, "&Links", listener);
addNewMenuItem(m, "&Rechts", listener);
addNewMenuItem(m, "&Oben", listener);
Listing3005.java
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
}
118 }
} else if (cmd.equals("Rechts")) {
setLocation(getLocation().x+10, getLocation().y);
} else if (cmd.equals("Oben")) {
setLocation(getLocation().x, getLocation().y-10);
} else if (cmd.equals("Unten")) {
setLocation(getLocation().x, getLocation().y+10);
}
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt Suchen
<
>
Index
>>
DOC
API
30.6 Kontextmens
30.6 Kontextmens
Ein wichtiger Bestandteil grafischer Oberflchen, der in den letzten Jahren verstrkt in die
Programme Einzug gehalten hat, sind die Kontext- oder Popup-Mens. Sie liefern auf rechten
Mausklick ein Men mit den wichtigsten Aktionen, die innerhalb der ausgewhlten Komponente zur
Verfgung stehen. Auf diese Weise steht eine Vielzahl von Funktionen dort zur Verfgung, wo sie
bentigt wird, anstatt umstndlich aus dem Hauptmen des Programms ausgewhlt oder per
Kommando eingegeben werden zu mssen.
Auch im JDK 1.1 gibt es Kontextmens, die so funktionieren. Sie werden durch die Klasse
PopupMenu, die aus Menu abgeleitet ist, implementiert. PopupMenu ist genauso zu bedienen wie
Menu und wird daher vor allem mittels geeigneter Aufrufe von add mit Meneintrgen bestckt.
Diesen kann ein ActionListener zugeordnet werden, der bei Auslsen des Menpunkts
aufgerufen wird. Gegenber der Klasse Menu besitzt PopupMenu eine zustzliche Methode show,
mit der das Kontextmen angezeigt wird:
public void show(Component origin, int x, int y)
java.awt.PopupMenu
Der erste Parameter origin ist die Komponente, an die das Kontextmen gebunden wird. Diese
Komponente hat lediglich administrative Aufgaben, spielt aber beim Aufruf des Kontextmens keine
Rolle. Die Argumente x und y geben die Position des Kontextmens relativ zum Ursprung von
origin an.
Um ein Kontextmen aufzurufen, sind mehrere Dinge zu tun. Zunchst mu das instanzierte
Kontextmen durch Aufruf von add an die Komponente gebunden werden, die auf Mausereignisse
fr den Aufruf reagieren soll. Dies kann beispielsweise das Fenster sein, in dem die Komponente
untergebracht ist, oder die Komponente selbst. Anschlieend mu in der Komponente durch Aufruf
von enableEvents die Behandlung von Maus-Events freigeschaltet werden. Drittens mu die
Methode processMouseEvent berlagert werden, und es mu bei jedem Mausereignis mit
isPopupTrigger abgefragt werden, ob es sich um das Ereignis zum Aufruf des Kontextmens
handelte. In diesem Fall kann das Kontextmen durch Aufruf von show angezeigt werden.
Kontextmens sind von Plattform zu Plattform leicht unterschiedlich implementiert, und Hinweis
insbesondere die Art des Aufrufs unterscheidet sich voneinander (zweite oder dritte
Maustaste, Aufruf beim Drcken oder Loslassen usw.). Um das Look-and-Feel des
jeweiligen Systems beizubehalten, sollte processMouseEvent berlagert werden, um
bei jeder Art von Mausereignis feststellen zu knnen, ob der PopupMenu-Trigger
ausgelst wurde. Der einfache Aufruf von show aus einem mousePressed- oder
mouseReleased-Ereignis heraus ist nicht portabel und sollte daher vermieden werden.
Das folgende Beispiel zeigt ein Programm mit einem Kontextmen, das die Punkte Rckgngig,
Ausschneiden, Kopieren und Einfgen enthlt. Das Kontextmen wird an das Hauptfenster
gehngt und von Mausereignissen dieses Fensters aufgerufen. Beim Auslsen einer Option des
Kontextmens wird eine entsprechende Meldung auf die Systemkonsole geschrieben.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
/* Listing3006.java */
import java.awt.*;
import java.awt.event.*;
class MyPopupMenu
extends PopupMenu
{
public MyPopupMenu(ActionListener listener)
{
MenuItem mi;
mi = new MenuItem("Rueckgaengig");
mi.addActionListener(listener);
add(mi);
addSeparator();
mi = new MenuItem("Ausschneiden");
mi.addActionListener(listener);
add(mi);
mi = new MenuItem("Kopieren");
mi.addActionListener(listener);
add(mi);
mi = new MenuItem("Einfuegen");
mi.addActionListener(listener);
add(mi);
}
}
public class Listing3006
extends Frame
implements ActionListener
{
MyPopupMenu popup;
Listing3006.java
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073 }
Titel
<<
Inhalt Suchen
<
>
Index
DOC
>>
API
Titel
<<
Inhalt Suchen
<
>
Index
>>
DOC
API
In diesem Kapitel soll das Erzeugen von Fenstern mit eingebetteten Dialogelementen vorgestellt
werden. Derartige Fenster werden in der Praxis sehr viel hufiger bentigt als Fenster, deren
Ausgabe mit primitiven Grafikoperationen erzeugt wurde, wie sie in den Kapiteln 23, 24 und 25
vorgestellt wurden. Der Einfachheit halber wollen wir derartige Fenster in diesem Kapitel als
Dialoge bezeichnen. Sie dienen dazu, Programmdaten anzuzeigen, und geben dem Anwender die
Mglichkeit, diese zu ndern.
Das Erstellen eines Dialogs erfolgt in vier Schritten:
Da Java prinzipiell keinen Unterschied zwischen Fenstern zur Ausgabe eines Dialogs und Hinweis
solchen zur Anzeige von Grafiken macht, ist es mglich, ein Dialogfenster wahlweise aus
Frame oder Dialog abzuleiten. Die Klasse Dialog erlaubt es, das Verndern der
Fenstergre durch den Anwender zu unterbinden, und bietet die Mglichkeit, den Dialog
modal zu machen. Dadurch wird die Interaktion des Anwenders mit anderen Fenstern der
Anwendung bis zum Schlieen des Dialogfensters blockiert. Im Gegensatz zu Frame
fehlt jedoch die Mglichkeit, eine Menleiste zu erzeugen oder dem Fenster ein Icon
zuzuordnen. Wir werden in den nachfolgenden Beispielen meist die Klasse Frame
verwenden, um Dialoge zu erzeugen. Die Klasse Dialog werden wir am Ende dieses
Kapitels vorstellen. Dabei werden wir insbesondere das Erzeugen modaler Dialoge und
die Rckgabe von Ergebniswerten aufzeigen.
java.awt.Container
java.awt.Container
Bei der ersten Variante wird lediglich die einzufgende Komponente bergeben und vom
Layoutmanager an der dafr vorgesehenen Position untergebracht. Die zweite Variante erlaubt das
Einfgen der aktuellen Komponente an beliebiger Stelle in der Liste der Komponenten.
Die dritte Variante erwartet zustzlich ein Constraints-Objekt, der bei bestimmten Layoutmanagern
weitere Informationen zur Positionierung der Komponente angibt. Wird beispielsweise die Klasse
BorderLayout zur Anordnung der Dialogelemente verwendet, kann hier eine der Konstanten
SOUTH, NORTH, WEST, EAST oder CENTER bergeben werden, um anzuzeigen, an welcher Stelle
des Fensters das Element plaziert werden soll.
Sollen Komponenten, die bereits an das Fenster bergeben wurden, wieder daraus entfernt werden,
so kann dazu die Methode remove verwendet werden. Als Parameter ist dabei das zu lschende
Objekt zu bergeben:
public void remove(Component comp)
java.awt.Container
Container stellt auch Methoden zur Verfgung, um auf die bereits eingefgten Dialogelemente
zuzugreifen:
public int getComponentCount()
public Component getComponent(int n)
public Component[] getComponents()
java.awt.Container
Mit getComponentCount kann die Anzahl aller eingefgten Komponenten ermittelt werden.
getComponent liefert die Komponente mit dem angegebenen Index, und getComponents gibt
ein Array mit allen eingefgten Komponenten zurck.
Wir wollen uns ein einfaches Beispiel ansehen, das diese vier Schritte demonstriert:
java.awt.Window
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
Listing3101.java
/* Listing3101.java */
import java.awt.*;
import java.awt.event.*;
public class Listing3101
extends Frame
{
public static void main(String[] args)
{
Listing3101 wnd = new Listing3101();
wnd.setVisible(true);
}
public Listing3101()
{
super("Dialogtest");
addWindowListener(new WindowClosingAdapter(true));
setLayout(new FlowLayout());
add(new Button("Abbruch"));
add(new Button("OK"));
pack();
}
}
Inhalt Suchen
<
>
Index
DOC
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
In vielen grafischen Oberflchen wird die Anordnung der Elemente eines Dialoges durch Angabe absoluter
Koordinaten vorgenommen. Dabei wird fr jede Komponente manuell oder mit Hilfe eines Ressourcen-Editors
pixelgenau festgelegt, an welcher Stelle im Dialog sie zu erscheinen hat.
Da Java-Programme auf vielen unterschiedlichen Plattformen mit unterschiedlichen Ausgabegerten laufen sollen,
war eine solche Vorgehensweise fr die Designer des AWT nicht akzeptabel. Sie whlten statt dessen den Umweg
ber einen Layoutmanager, der fr die Anordnung der Dialogelemente verantwortlich ist. Um einen
Layoutmanager verwenden zu knnen, wird dieser dem Fenster vor der bergabe der Dialogelemente mit der
Methode setLayout zugeordnet. Er ordnet dann die per add bergebenen Elemente auf dem Fenster an.
Jeder Layoutmanager implementiert seine eigene Logik bezglich der optimalen Anordnung der Komponenten:
Das FlowLayout ordnet Dialogelemente nebeneinander in einer Zeile an. Wenn keine weiteren Elemente
in die Zeile passen, wird mit der nchsten Zeile fortgefahren.
Das GridLayout ordnet die Dialogelemente in einem rechteckigen Gitter an, dessen Zeilen- und
Spaltenzahl beim Erstellen des Layoutmanagers angegeben wird.
Das BorderLayout verteilt die Dialogelemente nach Vorgabe des Programms auf die vier Randbereiche
und den Mittelbereich des Fensters.
Das CardLayout ist in der Lage, mehrere Unterdialoge in einem Fenster unterzubringen und jeweils
einen davon auf Anforderung des Programms anzuzeigen.
Das GridBagLayout ist ein komplexer Layoutmanager, der die Fhigkeiten von GridLayout
erweitert und es ermglicht, mit Hilfe von Bedingungsobjekten sehr komplexe Layouts zu erzeugen.
Neben den gestalterischen Fhigkeiten eines Layoutmanagers bestimmt in der Regel die Reihenfolge der Aufrufe
der add-Methode des Fensters die tatschliche Anordnung der Komponenten auf dem Bildschirm. Wenn nicht wie es z.B. beim BorderLayout mglich ist - zustzliche Positionierungsinformationen an das Fenster
bergeben werden, ordnet der jeweilige Layoutmanager die Komponenten in der Reihenfolge ihres Eintreffens an.
Um komplexere Layouts realisieren zu knnen, als die Layoutmanager sie in ihren jeweiligen Grundausprgungen
bieten, gibt es die Mglichkeit, Layoutmanager zu schachteln. Auf diese Weise kann auch ohne Vorgabe fester
Koordinaten fast jede gewnschte Komponentenanordnung realisiert werden. Sollte auch diese Variante nicht
genau genug sein, so bietet sich schlielich durch Verwendung eines Null-Layouts die Mglichkeit an,
Komponenten durch Vorgabe fester Koordinaten zu plazieren.
31.2.1 FlowLayout
Die Klasse FlowLayout stellt den einfachsten Layoutmanager dar. Alle Elemente werden so lange nacheinander
in einer Zeile angeordnet, bis kein Platz mehr vorhanden ist und in der nchsten Zeile fortgefahren wird.
Das FlowLayout wird einem Fenster durch folgende Anweisung zugeordnet:
setLayout(new FlowLayout());
Neben dem parameterlosen gibt es weitere Konstruktoren, die die Mglichkeit bieten, die Anordnung der
Elemente und ihre Abstnde voneinander vorzugeben:
public FlowLayout(int align)
java.awt.FlowLayout
Der Parameter align gibt an, ob die Elemente einer Zeile linksbndig, rechtsbndig oder zentriert angeordnet
werden. Hierzu stehen die Konstanten FlowLayout.CENTER, FlowLayout.LEFT und
FlowLayout.RIGHT zur Verfgung, als Voreinstellung wird FlowLayout.CENTER verwendet. Mit hgap
und vgap knnen die horizontalen und vertikalen Abstnde zwischen den Komponenten vorgegeben werden. Die
Voreinstellung ist 5.
Das folgende Listing zeigt die Verwendung der Klasse FlowLayout am Beispiel von fnf Buttons, die in einem
Grafikfenster angezeigt werden. Anstelle der normalen zentrierten Anordnung werden die Elemente linksbndig
angeordnet, und der Abstand zwischen den Buttons betrgt 20 Pixel:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
/* Listing3102.java */
import java.awt.*;
import java.awt.event.*;
public class Listing3102
extends Frame
{
public static void main(String[] args)
{
Listing3102 wnd = new Listing3102();
wnd.setVisible(true);
}
public Listing3102()
{
super("Test FlowLayout");
addWindowListener(new WindowClosingAdapter(true));
//Layout setzen und Komponenten hinzufgen
setLayout(new FlowLayout(FlowLayout.LEFT,20,20));
add(new Button("Button 1"));
add(new Button("Button 2"));
add(new Button("Button 3"));
add(new Button("Button 4"));
add(new Button("Button 5"));
pack();
}
}
Listing3102.java
31.2.2 GridLayout
Ein GridLayout bietet eine grere Kontrolle ber die Anordnung der Elemente als ein FlowLayout. Hier
werden die Komponenten nicht einfach nacheinander auf dem Bildschirm positioniert, sondern innerhalb eines
rechteckigen Gitters angeordnet, dessen Elemente eine feste Gre haben.
Das Programm bergibt dazu beim Aufruf des Konstruktors zwei Parameter, rows und columns, mit denen die
vertikale und horizontale Anzahl an Elementen festgelegt wird:
public void GridLayout(int rows, int columns)
java.awt.GridLayout
Beim Aufruf von add werden die Komponenten dann nacheinander in die einzelnen Zellen der Gittermatrix
gelegt. Analog zum FlowLayout wird dabei zunchst die erste Zeile von links nach rechts gefllt, dann die
zweite usw.
hnlich wie beim FlowLayout steht ein zustzlicher Konstruktor zur Verfgung, der es erlaubt, die Gre der
horizontalen und vertikalen Lcken zu verndern:
public void GridLayout(int rows, int columns, int hgap, int vgap)java.awt.GridLayout
Die grere Kontrolle des Programms ber das Layout besteht nun darin, da der Umbruch in die nchste Zeile
genau vorhergesagt werden kann. Anders als beim FlowLayout erfolgt dieser nicht erst dann, wenn keine
weiteren Elemente in die Zeile passen, sondern wenn dort so viele Elemente plaziert wurden, wie das Programm
vorgegeben hat.
Das folgende Beispiel zeigt ein Fenster mit einem Gitter der Gre 4*2 Elemente, das ingesamt sieben Buttons
anzeigt:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
/* Listing3103.java */
Listing3103.java
import java.awt.*;
import java.awt.event.*;
public class Listing3103
extends Frame
{
public static void main(String[] args)
{
Listing3103 wnd = new Listing3103();
wnd.setVisible(true);
}
public Listing3103()
{
super("Test GridLayout");
addWindowListener(new WindowClosingAdapter(true));
//Layout setzen und Komponenten hinzufgen
setLayout(new GridLayout(4,2));
add(new Button("Button 1"));
add(new Button("Button 2"));
add(new Button("Button 3"));
add(new Button("Button 4"));
add(new Button("Button 5"));
add(new Button("Button 6"));
add(new Button("Button 7"));
pack();
}
}
31.2.3 BorderLayout
Das BorderLayout verfolgt einen anderen Ansatz als die beiden vorigen Layoutmanager, denn die
Positionierung der Komponenten wird nicht mehr primr durch die Reihenfolge der Aufrufe von add bestimmt.
Statt dessen teilt das BorderLayout den Bildschirm in fnf Bereiche auf, und zwar in die vier Rnder und das
Zentrum. Durch Angabe eines Himmelsrichtungs-Constraints wird beim Aufruf von add angegeben, auf welchem
dieser Bereiche die Komponente plaziert werden soll. BorderLayout stellt dazu folgende Konstanten zur
Verfgung:
Zur bergabe dieses Parameters gibt es die Methode add der Klasse Container in einer Variante mit einem
Object als zweiten Parameter:
public void add(Component comp, Object constraints)
java.awt.Container
Das folgende Beispiel zeigt die Anordnung von fnf Buttons in einem BorderLayout. Jeweils einer der Buttons
wird auf die vier Randbereiche verteilt, und der fnfte Button steht in der Mitte. Die Gre des Fensters wird fest
auf 300*200 Pixel eingestellt:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
/* Listing3104.java */
import java.awt.*;
import java.awt.event.*;
public class Listing3104
extends Frame
{
public static void main(String[] args)
{
Listing3104 wnd = new Listing3104();
wnd.setVisible(true);
}
public Listing3104()
{
super("Test BorderLayout");
addWindowListener(new WindowClosingAdapter(true));
//Layout setzen und Komponenten hinzufgen
setSize(300,200);
setLayout(new BorderLayout());
add(new Button("Button 1"), BorderLayout.NORTH);
add(new Button("Button 2"), BorderLayout.SOUTH);
add(new Button("Button 3"), BorderLayout.WEST);
add(new Button("Button 4"), BorderLayout.EAST);
add(new Button("Button 5"), BorderLayout.CENTER);
}
}
Listing3104.java
Nord- und Sdelement behalten ihre gewnschte Hhe, werden aber auf die volle Fensterbreite skaliert.
Ost- und Westelement behalten ihre gewnschte Breite, werden aber in der Hhe so skaliert, da sie genau
zwischen Nord- und Sdelement passen.
Das Mittelelement wird in der Hhe und Breite so angepat, da es den verbleibenden freien Raum
einnimmt.
Auch beim BorderLayout kann die Gre der Lcken zwischen den Elementen an den Konstruktor bergeben
werden:
public BorderLayout(int hgap, int vgap)
java.awt.BorderLayout
Wenn das vorige Beispiel ein BorderLayout mit einer vertikalen und horizontalen Lcke von 10 Pixeln
definieren wrde, wre die Ausgabe des Programms wie folgt:
31.2.4 GridBagLayout
Das GridBagLayout ist der aufwendigste Layoutmanager in Java. Er erlaubt eine sehr flexible Gestaltung der
Oberflche und bietet viele Mglichkeiten, die in den anderen Layoutmanagern nicht zu finden sind. Der Preis
dafr ist eine etwas kompliziertere Bedienung und ein hherer Einarbeitungsaufwand. Wir wollen in diesem
Zunchst ist ein Objekt des Typs GridBagLayout zu instanzieren und durch Aufruf von setLayout
dem Fenster zuzuweisen.
Fr jedes einzufgende Dialogelement ist nun ein Objekt des Typs GridBagConstraints anzulegen,
um ber dessen Membervariablen die spezifischen Layouteigenschaften des Dialogelements zu
beschreiben.
Sowohl das Dialogelement als auch das Eigenschaftenobjekt werden durch Aufruf der Methode
setConstraints an den Layoutmanager bergeben.
Schlielich wird das Dialogelement mit add an das Fenster bergeben.
...
GridBagLayout gbl = new GridBagLayout();
GridBagConstraints gbc = new GridBagConstraints();
setLayout(gbl);
List list = new List();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.gridwidth = 1;
gbc.gridheight = 1;
gbc.weightx = 100;
gbc.weighty = 100;
gbc.fill = GridBagConstraints.BOTH;
gbl.setConstraints(list, gbc);
add(list);
...
java.awt.GridBagConstraints
Ein GridBagLayout unterteilt das Fenster hnlich wie das GridLayout in ein rechteckiges Gitter von
Zellen, in denen die Dialogelemente plaziert werden. Im Gegensatz zum GridLayout knnen die Zellen
allerdings unterschiedliche Gren haben, und die Dialogelemente knnen auf verschiedene Weise
innerhalb der Zellen plaziert werden. Zudem kann ein Dialogelement sich in beiden Richtungen ber mehr
als eine einzige Zelle erstrecken.
Der Parameter gridx gibt an, in welcher Spalte des logischen Gitters der linke Rand des Dialogelements
liegen soll. Die erste Spalte hat den Wert 0. gridy gibt analog die logische Zeile des oberen Rands des
Dialogelements an. Beide Werte werden nicht etwa in Pixeln angegeben, sondern bezeichnen die Zellen des
logischen Gitternetzes. (0, 0) ist die linke obere Ecke.
Die Parameter gridwidth und gridheight geben die horizontale und vertikale Ausdehnung des
Dialogelements an. Soll ein Dialogelement beispielsweise zwei Zellen breit sein, so ist fr gridwidth
der Wert 2 anzugeben.
Mit dem Parameter fill wird definiert, wie sich die Abmessungen des Dialogelements verndern, wenn
die Gre des Fensters verndert wird. Wird hier GridBagConstraints.NONE angegeben, bleibt das
Element immer in seiner ursprnglichen Gre. Wird einer der Werte
GridBagConstraints.HORIZONTAL, GridBagConstraints.VERTICAL oder
GridBagConstraints.BOTH angegeben, skaliert der Layoutmanager das Dialogelement automatisch
in horizontaler, vertikaler oder in beiden Richtungen.
Der Parameter anchor bestimmt, an welcher Kante der Zelle das Dialogelement festgemacht wird, wenn
nach dem Skalieren noch Platz in der Zelle verbleibt. Hier knnen die folgenden Konstanten angegeben
werden:
GridBagConstraints.CENTER
GridBagConstraints.NORTH
GridBagConstraints.NORTHEAST
GridBagConstraints.EAST
GridBagConstraints.SOUTHEAST
GridBagConstraints.SOUTH
GridBagConstraints.SOUTHWEST
GridBagConstraints.WEST
GridBagConstraints.NORTHWEST
Die Parameter weightx und weighty sind etwas trickreich. Sie bestimmen das Verhltnis, in dem
berschssiger Platz auf die Zellen einer Zeile bzw. einer Spalte verteilt wird. Ist der Wert 0, bekommt die
Zelle nichts von eventuell berschssigem Platz ab. Bedeutsam ist dabei nur das relative Verhltnis, der
absolute Wert dieses Parameters spielt keine Rolle. Die Werte sollten allerdings nicht negativ sein.
Mit dem Parameter insets kann ein Rand gesetzt werden. Er wird um das Dialogelement herum auf
jeden Fall freigelassen.
Mit ipadx und ipady kann ein konstanter Wert zur minimalen Breite und Hhe der Komponente
hinzugefgt werden.
Um die Anwendung der Parameter zu veranschaulichen, wollen wir uns ein Beispiel ansehen. Das zu erstellende
Programm soll ein Fenster mit sechs Dialogelementen entsprechend Abbildung 31.8 aufbauen. Die Liste auf der
linken Seite soll beim Skalieren in beide Richtungen vergrert werden. Die Textfelder sollen dagegen lediglich in
der Breite wachsen, ihre ursprngliche Hhe aber beibehalten. Bei vertikaler Vergrerung des Fensters sollen sie
untereinander stehen bleiben. Die Beschriftungen sollen ihre anfngliche Gre beibehalten und - unabhngig von
der Fenstergre - direkt vor den Textfeldern stehen bleiben. Auch der Button soll seine ursprngliche Gre
beibehalten, wenn das Fenster skaliert wird. Zudem soll er immer in der rechten unteren Ecke stehen. Zwischen
den Dialogelementen soll ein Standardabstand von zwei Pixeln eingehalten werden.
/* Listing3106.java */
import java.awt.*;
import java.awt.event.*;
public class Listing3106
extends Frame
{
public static void main(String[] args)
{
Listing3106 wnd = new Listing3106();
wnd.setVisible(true);
}
public Listing3106()
{
super("Test GridBagLayout");
setBackground(Color.lightGray);
addWindowListener(new WindowClosingAdapter(true));
//Layout setzen und Komponenten hinzufgen
GridBagLayout gbl = new GridBagLayout();
GridBagConstraints gbc;
setLayout(gbl);
//List hinzufgen
Listing3106.java
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074 }
JDK1.1-1.4
31.2.5 NULL-Layout
Ein Null-Layout wird erzeugt, indem die Methode setLayout mit dem Argument null aufgerufen wird. In
diesem Fall verwendet das Fenster keinen Layoutmanager, sondern berlt die Positionierung der Komponenten
der Anwendung. Fr jedes einzufgende Dialogelement sind dann drei Schritte auszufhren:
Der erste und letzte Schritt unterscheiden sich nicht von unseren bisherigen Versuchen, bei denen wir vordefinierte
Layoutmanager verwendet haben. Im zweiten Schritt ist allerdings etwas Handarbeit notwendig. Gre und
Position des Dialogelements knnen mit den Methoden move und setSize oder - noch einfacher - mit
setBounds festgelegt werden:
public void setBounds(
int x, int y, int width, int height
)
java.awt.Component
Der Punkt (x,y) bestimmt die neue Anfangsposition des Dialogelements und (width, height) seine Gre.
Das folgende Listing demonstriert diese Vorgehensweise am Beispiel einer treppenartigen Anordnung von fnf
Buttons:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
/* Listing3107.java */
Listing3107.java
import java.awt.*;
import java.awt.event.*;
public class Listing3107
extends Frame
{
public static void main(String[] args)
{
Listing3107 wnd = new Listing3107();
wnd.setVisible(true);
}
public Listing3107()
{
super("Dialogelemente ohne Layoutmanager");
addWindowListener(new WindowClosingAdapter(true));
//Layout setzen und Komponenten hinzufgen
setSize(300,250);
setLayout(null);
for (int i = 0; i < 5; ++i) {
Button button = new Button("Button"+(i+1));
button.setBounds(10+i*35,40+i*35,100,30);
add(button);
}
}
}
/* Listing3108.java */
import java.awt.*;
import java.awt.event.*;
public class Listing3108
extends Frame
{
public static void main(String[] args)
{
Listing3108 wnd = new Listing3108();
wnd.setVisible(true);
}
public Listing3108()
{
super("Geschachtelte Layoutmanager");
addWindowListener(new WindowClosingAdapter(true));
//Layout setzen und Komponenten hinzufgen
//Panel 1
Panel panel1 = new Panel();
panel1.setLayout(new GridLayout(3,1));
panel1.add(new Button("Button1"));
panel1.add(new Button("Button2"));
panel1.add(new Button("Button3"));
//Panel 2
Panel panel2 = new Panel();
panel2.setLayout(new BorderLayout());
panel2.add(new Button("Button4"), BorderLayout.NORTH);
panel2.add(new Button("Button5"), BorderLayout.SOUTH);
panel2.add(new Button("Button6"), BorderLayout.WEST);
panel2.add(new Button("Button7"), BorderLayout.EAST);
panel2.add(new Button("Button8"), BorderLayout.CENTER);
//Hauptfenster
setLayout(new GridLayout(1,2));
add(panel1);
add(panel2);
pack();
}
}
Listing3108.java
/* Listing3109.java */
import java.awt.*;
import java.awt.event.*;
public class Listing3109
extends Frame
{
public static void main(String[] args)
{
Listing3109 wnd = new Listing3109();
wnd.setVisible(true);
}
public Listing3109()
{
super("Geschachtelte Layoutmanager, Teil II");
addWindowListener(new WindowClosingAdapter(true));
//Layout setzen und Komponenten hinzufgen
setSize(300,200);
//Panel 1
Panel panel1 = new Panel();
panel1.setLayout(new GridLayout(3,2));
panel1.add(new Button("Button1"));
panel1.add(new Button("Button2"));
panel1.add(new Button("Button3"));
panel1.add(new Button("Button4"));
panel1.add(new Button("Button5"));
panel1.add(new Button("Button6"));
//Panel 2
Panel panel2 = new Panel();
panel2.setLayout(new FlowLayout(FlowLayout.RIGHT));
panel2.add(new Button("Abbruch"));
panel2.add(new Button("OK"));
//Hauptfenster
setLayout(new BorderLayout());
add(panel1, BorderLayout.CENTER);
add(panel2, BorderLayout.SOUTH);
}
Listing3109.java
040 }
Listing 31.9: Eine weitere Anwendung fr geschachtelte Layoutmanager
Die Ausgabe des Programms ist:
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Modale Dialoge sind solche, die alle Benutzereingaben des Programmes beanspruchen und andere Fenster erst dann
wieder zum Zuge kommen lassen, wenn das Dialogfenster geschlossen wird. Eine wichtige Eigenschaft modaler
Dialoge ist es, da im Programm der Aufruf zur Anzeige des Dialogs so lange blockiert, bis der Dialog beendet ist.
Auf diese Weise kann an einer bestimmten Stelle im Programm auf eine Eingabe gewartet werden und erst dann mit
der Bearbeitung fortgefahren werden, wenn die Eingabe erfolgt ist.
Im AWT des JDK 1.0 gab es einen schwerwiegenden Fehler, durch den das Erzeugen modaler Dialoge unmglich
gemacht wurde. Der Fehler hielt sich bis zur Version 1.0.2 und wurde erst mit Erscheinen des JDK 1.1 behoben. Da
viele Anwenderdialoge von Natur aus modalen Charakter haben, wurde dieser Fehler von vielen als schwerwiegend
angesehen, und die Java-Gemeinde entwickelte eine Reihe von Workarounds, die aber allesamt nicht voll
zufriedenstellen konnten. Glcklicherweise wurden die Probleme mit dem JDK 1.1 behoben, und wir wollen in
diesem Kapitel aufzeigen, wie modale Dialoge in Java erzeugt werden knnen.
Ein modaler Dialog mu immer aus der Klasse Dialog abgeleitet werden. Nur sie bietet die Mglichkeit, an den
Konstruktor einen booleschen Wert zu bergeben, der festlegt, da die brigen Fenster der Anwendung whrend
der Anzeige des Dialogs suspendiert werden. Dialog besitzt folgende Konstruktoren:
public Dialog(Frame owner)
java.awt.Dialog
Als erstes Argument mu in jedem Fall ein Frame- oder Dialog-Objekt als Vaterfenster bergeben werden (bis
zur Version 1.1 waren nur Frame-Objekte erlaubt). Mit title kann der Inhalt der Titelzeile vorgegeben werden,
und der Parameter modal entscheidet, ob der Dialog modal dargestellt wird oder nicht.
Dialog bietet die Methoden isModal und setModal, mit denen auf die Modalitt des Dialogs zugegriffen
werden kann:
public boolean isModal()
public void setModal(boolean b)
java.awt.Dialog
Der Rckgabewert von isModal ist true, falls der Dialog modal ist. Andernfalls ist er false.
Die Methode setModal, deren Fhigkeit darin besteht, einen bestehenden Dialog zwischen den
Warnung
Zustnden modal und nicht-modal umzuschalten, ist mit Vorsicht zu genieen. Unter Windows 95 hat
ein Aufruf im JDK 1.1 mitunter dazu gefhrt, da beim Schlieen des Fensters die Nachrichtenschleife
des Aufrufers deaktiviert blieb und das Programm sich aufhngte. Am besten, man entscheidet schon
im Konstruktor, ob der Dialog modal sein soll oder nicht.
Ein zustzliches Feature der Klasse Dialog besteht darin, die Vernderbarkeit der Gre eines Fensters durch den
Anwender zu unterbinden:
public void setResizable(boolean resizable)
java.awt.Dialog
Nach einem Aufruf von setResizable mit false als Argument kann die Gre des Fensters nicht mehr vom
Anwender verndert werden. Nach einem Aufruf von setResizable(true) ist dies wieder mglich. Mit
isResizable kann der aktuelle Zustand dieses Schalters abgefragt werden.
Auch die Methode setResizable ist nicht ganz unkritisch, denn sie hat auf einigen UNIXSystemen zu Problemen gefhrt. Sowohl unter SUN Solaris als auch unter LINUX kam es im JDK 1.1
zu Hngern bei Programmen, die diese Methode benutzten. Falls sich die nachfolgenden
Beispielprogramme auf Ihrem System nicht so verhalten wie angegeben (typischerweise wird der
Dialog gar nicht erst angezeigt), sollten Sie versuchsweise den Aufruf von setResizable
auskommentieren.
Warnung
Das folgende Beispiel demonstriert die Anwendung der Klasse Dialog. Es zeigt einen Frame, aus dem per
Buttonklick ein modaler Dialog aufgerufen werden kann. Der Dialog fragt den Anwender, ob die Anwendung
beendet werden soll, und wartet, bis einer der Buttons Ja oder Nein gedrckt wurde. In diesem Fall wird ein
boolescher Rckgabewert generiert, der nach dem Schlieen des Dialogs an das Vaterfenster zurckgegeben wird.
Falls der Rckgabewert true (entprechend dem Button Ja) ist, wird die Anwendung beendet, andernfalls luft
sie weiter:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
/* Listing3110.java */
import java.awt.*;
import java.awt.event.*;
class YesNoDialog
extends Dialog
implements ActionListener
{
boolean result;
public YesNoDialog(Frame owner, String msg)
{
super(owner, "Ja-/Nein-Auswahl", true);
//Fenster
setBackground(Color.lightGray);
setLayout(new BorderLayout());
setResizable(false); //Hinweis im Text beachten
Point parloc = owner.getLocation();
setLocation(parloc.x + 30, parloc.y + 30);
//Message
Listing3110.java
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
}
089
}
090 }
Hinweis
Nach dem Start zeigt das Programm zunchst das in Abbildung 31.14 dargestellte Fenster an:
Ausstattung mit Buttons variabel ist und zum Zeitpunkt der Instanzierung festgelegt werden kann:
public ModalDialog(
Frame owner,
String title,
String msg,
String buttons
);
Der Konstruktor erwartet neben dem Vater-Frame drei weitere Parameter, title, msg und buttons. In title
wird der Inhalt der Titelzeile bergeben, und msg spezifiziert den Text innerhalb des Dialogs. Der Parameter
buttons erwartet eine Liste von Button-Bezeichnern, die zur Festlegung der Anzahl und Beschriftung der
Buttons dienen. Die einzelnen Elemente sind durch Kommata zu trennen und werden innerhalb des Dialogs mit
einem StringTokenizer zerlegt. Der in getResult gelieferte Rckgabewert des Dialogs ist - anders als im
vorigen Beispiel - ein String und entspricht der Beschriftung des zum Schlieen verwendeten Buttons.
ModalDialog enthlt einige statische Methoden, mit denen Dialoge mit einer festen Buttonstruktur einfach
erzeugt werden knnen. So erzeugt OKButton lediglich einen Button mit der Beschriftung OK,
YesNoDialog zwei Buttons, Ja und Nein, und YesNoCancelDialog erzeugt einen Dialog mit drei
Buttons, Ja, Nein und Abbruch. Das folgende Listing zeigt die Klasse ModalDialog und ein
Rahmenprogramm zum Testen:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
/* Listing3111.java */
import java.awt.*;
import java.awt.event.*;
import java.util.*;
class ModalDialog
extends Dialog
implements ActionListener
{
String result;
public static String OKDialog(Frame owner, String msg)
{
ModalDialog dlg;
dlg = new ModalDialog(owner,"Nachricht",msg,"OK");
dlg.setVisible(true);
return dlg.getResult();
}
public static String YesNoDialog(Frame owner, String msg)
{
ModalDialog dlg;
dlg = new ModalDialog(owner,"Frage",msg,"Ja,Nein");
dlg.setVisible(true);
return dlg.getResult();
}
public static String YesNoCancelDialog(Frame owner,String msg)
{
ModalDialog dlg;
dlg = new ModalDialog(owner,"Frage",msg,"Ja,Nein,Abbruch");
dlg.setVisible(true);
return dlg.getResult();
Listing3111.java
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
}
public ModalDialog(
Frame owner,
String title,
String msg,
String buttons
)
{
super(owner, title, true);
//Fenster
setBackground(Color.lightGray);
setLayout(new BorderLayout());
setResizable(false);
Point parloc = owner.getLocation();
setLocation(parloc.x + 30, parloc.y + 30);
//Message
add(new Label(msg), BorderLayout.CENTER);
//Buttons
Panel panel = new Panel();
panel.setLayout(new FlowLayout(FlowLayout.CENTER));
StringTokenizer strtok = new StringTokenizer(buttons,",");
while (strtok.hasMoreTokens()) {
Button button = new Button(strtok.nextToken());
button.addActionListener(this);
panel.add(button);
}
add(panel, BorderLayout.SOUTH);
pack();
}
public void actionPerformed(ActionEvent event)
{
result = event.getActionCommand();
setVisible(false);
dispose();
}
public String getResult()
{
return result;
}
}
public class Listing3111
extends Frame
implements ActionListener
{
public static void main(String[] args)
{
Listing3111 wnd = new Listing3111();
wnd.setVisible(true);
}
public Listing3111()
{
super("Drei modale Standarddialoge");
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129 }
setLayout(new FlowLayout());
setBackground(Color.lightGray);
Button button = new Button("OKDialog");
button.addActionListener(this);
add(button);
button = new Button("YesNoDialog");
button.addActionListener(this);
add(button);
button = new Button("YesNoCancelDialog");
button.addActionListener(this);
add(button);
setLocation(100,100);
setSize(400,200);
setVisible(true);
}
public void actionPerformed(ActionEvent event)
{
String cmd = event.getActionCommand();
if (cmd.equals("OKDialog")) {
ModalDialog.OKDialog(this,"Dream, dream, dream, ...");
} else if (cmd.equals("YesNoDialog")) {
String ret = ModalDialog.YesNoDialog(
this,
"Programm beenden?"
);
if (ret.equals("Ja")) {
setVisible(false);
dispose();
System.exit(0);
}
} else if (cmd.equals("YesNoCancelDialog")) {
String msg = "Verzeichnis erstellen?";
String ret = ModalDialog.YesNoCancelDialog(this,msg);
ModalDialog.OKDialog(this,"Rckgabe: " + ret);
}
}
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
32.1 Rahmenprogramm
32.1 Rahmenprogramm
Nachdem in Kapitel 31 die grundstzliche Vorgehensweise beim Anlegen eines Dialogs besprochen
wurde, behandelt dieses Kapitel nun die verschiedenen Dialogelemente. Zu jedem Dialogelement
werden die Konstruktoren und die verschiedenen Methoden zur Steuerung seines Verhaltens
vorgestellt. Falls ein Dialogelement auch Nachrichten sendet, werden diese erlutert und ihre
Anwendung demonstriert.
Jedes Dialogelement wird in Java durch eine eigene Klasse reprsentiert, die dessen Verhalten und
Eigenschaften kapselt. Zur Aufnahme eines neuen Dialogelements in einen Dialog wird eine neue
Instanz der gewnschten Klasse angelegt und das resultierende Objekt mit add in den Dialog
eingefgt. Die Methoden des Dialogelements knnen dann zu einem beliebigen Zeitpunkt aufgerufen
werden, um dessen Verhalten zu beeinflussen. Alle Dialogelemente sind aus der Klasse Component
abgeleitet. Sie verfgen ber die grundlegenden Eigenschaften eines Fensters, besitzen eine Gre und
Position und sind in der Lage, Nachrichten zu empfangen und zu bearbeiten.
Damit nicht jedesmal ein komplettes Programm abgedruckt werden mu, wollen wir die
Hinweis
Beispiele in ein vordefiniertes Rahmen-Programm einbetten. Dieses erzeugt ein Fenster mit
zwei Buttons, die zum Aufrufen des Dialogs bzw. zum Beenden des Programms verwendet
werden knnen:
ein Panel plaziert, das an die Methode customizeLayout weitergegeben wird, die darin die
Beispielkomponenten plaziert. In der Basisversion ist diese Methode leer, und der Dialog hat folgendes
Aussehen:
/* DialogBeispiel.java */
import java.awt.*;
import java.awt.event.*;
class MyDialog
extends Dialog
implements ActionListener
{
public MyDialog(Frame parent)
{
super(parent,"MyDialog",true);
Point parloc = parent.getLocation();
setBounds(parloc.x + 30, parloc.y + 30,400,300);
setBackground(Color.lightGray);
setLayout(new BorderLayout());
//Panel
Panel panel = new Panel();
customizeLayout(panel);
add(panel, BorderLayout.CENTER);
//Ende-Button
Button button = new Button("Ende");
button.addActionListener(this);
add(button, BorderLayout.SOUTH);
//Window-Listener
addWindowListener(
new WindowAdapter() {
public void windowClosing(WindowEvent event)
{
endDialog();
}
}
);
pack();
}
DialogBeispiel.java
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
}
098 }
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
>
>>
API
32.2 Label
32.2 Label
Ein Label dient zur Beschriftung von Dialogboxen. Es enthlt eine Zeile Text, die
auf dem Bildschirm angezeigt wird und vom Programm verndert werden kann.
public Label()
java.awt.Label
Der parameterlose Konstruktor erzeugt ein Label ohne Text. Wird der Parameter
text bergeben, verwendet das Label diesen als Beschriftung. Der Parameter align
bestimmt die Ausrichtung des Texts. Hier kann eine der Konstanten Label.LEFT,
Label.RIGHT oder Label.CENTER bergeben werden.
public void setText(String text)
java.awt.Label
setText und getText erlauben den Zugriff auf den Text des Labels und
setAlignment und getAlignment auf die Ausrichtung des Texts. Die Parameter
dieser Methoden haben dieselbe Bedeutung wie im Konstruktor.
001
002
003
004
005
006
007
008
009
010
Label.inc
/* Label.inc */
private void customizeLayout(Panel panel)
{
panel.setLayout(new GridLayout(4,1));
panel.add(new Label("Default"));
panel.add(new Label("Links",Label.LEFT));
panel.add(new Label("Zentriert",Label.CENTER));
panel.add(new Label("Rechts",Label.RIGHT));
}
Titel
<<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
32.4 Checkbox
32.4 Checkbox
Eine Checkbox ist ein Eingabeelement, das eine Zustandsvariable besitzt, die zwischen den Werten true
und false umgeschaltet werden kann. Der aktuelle Zustand wird durch ein Hkchen oder Kreuz in der
Checkbox angezeigt.
public Checkbox()
java.awt.Checkbox
Der parameterlose Konstruktor erzeugt eine Checkbox ohne Beschriftung mit dem Anfangszustand false.
Wird der String label angegeben, bekommt die Checkbox die angegebene Beschriftung. Durch bergabe
des booleschen Parameters state ist es mglich, den anfnglichen Zustand der Checkbox vorzugeben. Der
in den letzten beiden Konstruktoren verwendete Parameter cbg dient zur Gruppierung von Radiobuttons und
sollte bei einer Checkbox stets null sein.
public String getLabel()
java.awt.Checkbox
Die Methoden ermglichen den Zugriff auf die Beschriftung und den aktuellen Zustand der Checkbox.
Oft reprsentiert eine Checkbox einen logischen Schalter, dessen Zustand abgefragt wird, nachdem der
Dialog beendet wurde. Zustzlich kann aber auch unmittelbar auf eine Zustandsnderung reagiert werden,
denn eine Checkbox generiert ein Item-Event, wenn die Markierung vom Anwender gesetzt oder
zurckgenommen wird. Um auf dieses Event zu reagieren, ist durch Aufruf von addItemListener ein
Objekt, das das Interface ItemListener implementiert, bei der Checkbox zu registrieren:
java.awt.Checkbox
Ein Item-Event wird immer dann gesendet, wenn sich der Zustand einer Checkbox verndert hat. In diesem
Fall wird im Ereignisempfnger die Methode itemStateChanged mit einem Argument vom Typ
ItemEvent aufgerufen:
public abstract void itemStateChanged(ItemEvent e)
java.awt.event.ItemListener
Das ItemEvent stellt die Methode getItemSelectable zur Verfgung, mit der ermittelt werden kann,
durch welche Checkbox das Ereignis ausgelst wurde:
public ItemSelectable getItemSelectable()
java.awt.event.ItemEvent
Der Rckgabewert kann dann in ein Objekt des Typs Checkbox konvertiert werden. Durch Aufruf von
getState kann der aktuelle Zustand bestimmt werden.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
/* Checkbox1.inc */
Checkbox1.inc
001
002
003
004
005
006
007
Checkbox2.inc
/* Checkbox2.inc */
public void itemStateChanged(ItemEvent event)
{
Checkbox cb = (Checkbox) event.getItemSelectable();
System.out.println(cb.getLabel() + ": " + cb.getState());
}
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
32.5 CheckboxGroup
32.5 CheckboxGroup
Eine CheckboxGroup ist die Java-Variante einer Gruppe von Radiobuttons, also einer Kollektion von
Buttons, von denen immer genau einer aktiviert ist. Wird ein anderer Button aktiviert, so ndert er seinen
internen Status auf true, und der zuvor gesetzte wird false.
Eine CheckboxGroup ist nichts anderes als eine Checkbox, deren CheckboxGroup-Parameter gesetzt
ist.
public Checkbox(String label, CheckboxGroup cbg, boolean state)java.awt.Checkbox
public Checkbox(String label, boolean state, CheckboxGroup cbg)
Anders als bei einer gewhnlichen Checkbox stehen jetzt nur noch zwei Konstruktoren zur Verfgung. An
diese werden die Beschriftung, der Anfangszustand und ein Objekt der Klasse CheckboxGroup bergeben.
Die CheckboxGroup sorgt dabei fr den Gruppierungseffekt und die Logik beim Umschalten der
Radiobuttons. Fr jede zusammenhngende Gruppe von Radiobuttons ist daher ein eigenes Objekt der Klasse
CheckboxGroup zu instanzieren und an den Konstruktor zu bergeben.
Die CheckboxGroup bietet alle Methoden, die auch bei einer Checkbox verfgbar sind. setState
funktioniert allerdings nur dann, wenn als Parameter true bergeben wird. In diesem Fall wird der Zustand
der zugehrigen Checkbox gesetzt und der aller anderen Checkboxes derselben Gruppe zurckgenommen.
Bei der bergabe von false an setState passiert gar nichts. Zustzlich gibt es zwei Methoden
getCheckboxGroup und setCheckboxGroup, die den Zugriff auf die zugeordnete CheckboxGroup
erlauben:
public void setCheckboxGroup(CheckboxGroup cbg)
java.awt.CheckboxGroup
Mit setCheckboxGroup kann die Zuordnung der Checkboxes zu einer CheckboxGroup auch nach der
Konstruktion gendert werden. Das beim Aufruf von getCheckboxGroup zurckgegebene Objekt kann
dazu verwendet werden, mit Hilfe der Methoden getCurrent und setCurrent der Klasse
CheckboxGroup auf das selektierte Element der CheckboxGroup zuzugreifen:
java.awt.CheckboxGroup
getCurrent liefert die Checkbox, deren Zustand true ist. Um mit setCurrent eine Checkbox zu
aktivieren, mu diese als Parameter bergeben werden. Alle anderen Checkboxes derselben Gruppe
bekommen dann den Zustand false.
Die einzelnen Checkboxes einer CheckboxGroup senden ein ITEM-Event, wenn die Checkbox selektiert
wird. Dagegen wird kein Ereignis gesendet, wenn die Checkbox deselektiert wird oder wenn ihr Zustand
nicht vom Anwender, sondern vom Programm verndert wird.
Das folgende Beispiel zeigt die Definition von zwei Objekten des Typs CheckboxGroup mit jeweils drei
Elementen. In jeder der beiden Gruppen ist immer genau ein Element markiert, das unabhngig von den
Elementen der anderen Gruppe ausgewhlt werden kann:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
CheckboxGroup.inc
/* CheckboxGroup.inc */
private void customizeLayout(Panel panel)
{
panel.setLayout(new GridLayout(3,2));
CheckboxGroup cbg1 = new CheckboxGroup();
CheckboxGroup cbg2 = new CheckboxGroup();
panel.add(new Checkbox("rot",cbg1,true));
panel.add(new Checkbox("eckig",cbg2,true));
panel.add(new Checkbox("blau",cbg1,false));
panel.add(new Checkbox("rund",cbg2,false));
panel.add(new Checkbox("gelb",cbg1,false));
panel.add(new Checkbox("schief",cbg2,false));
}
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
32.6 TextField
32.6 TextField
Ein TextField dient zur Darstellung und zur Eingabe von Text. Sowohl der Anwender als auch das
Programm knnen den dargestellten Text auslesen und verndern.
public TextField()
java.awt.TextField
Der parameterlose Konstruktor erzeugt ein leeres Textfeld, in das der Anwender beliebig viel Text
eingeben kann. Wird mit width die Breite des Textfeldes vorgegeben, so beschrnkt sich die Anzahl
der auf dem Bildschirm angezeigten Zeichen auf den angegebenen Wert. Die Anzahl einzugebender
Zeichen wird allerdings nicht begrenzt. Ist der Text lnger, scrollt er innerhalb des Textfelds. Mit dem
Parameter text kann eine Zeichenkette vorgegeben werden, die beim Aufruf des Textfeldes
vorgelegt wird.
Die Klasse Textfield ist aus TextComponent abgeleitet. Sie besitzt eine ganze Reihe von
Methoden, mit denen es mglich ist, auf den Text zuzugreifen oder die Eigenschaften des Textfeldes
zu verndern. Die Methoden getText und setText werden zum Lesen und Verndern des Textes
verwendet:
public String getText()
java.awt.TextField
Mit getColumns kann die Anzahl der darstellbaren Zeichen des Textfeldes abgefragt und mit
setColumns verndert werden:
public int getColumns()
public void setColumns(int columns)
java.awt.TextField
Zwei weitere Methoden stehen fr das Markieren von Text zur Verfgung:
public void selectAll()
java.awt.TextField
selectAll markiert den kompletten Text und select den Bereich von first bis last (die
Zhlung beginnt bei 0). Mit den beiden Methoden getSelectionStart und
getSelectionEnd kann die aktuelle Selektion abgefragt werden, getSelectedText liefert
den selektierten Text:
public int getSelectionStart()
java.awt.TextField
Auf die aktuelle Cursorposition innerhalb des Textes kann mit den Methoden getCaretPosition
und setCaretPosition zugegriffen werden:
public int getCaretPosition()
java.awt.TextField
Des weiteren kann man verhindern, da der Text gendert wird, und es besteht die Mglichkeit,
verdeckte Eingaben (etwa fr Pawrter) zu realisieren:
public void setEditable(boolean allowed)
java.awt.TextField
Durch einen Aufruf von setEditable mit false als Parameter werden weitere Eingaben
unterbunden. Der aktuelle Status kann mit isEditable abgefragt werden. Mit Hilfe von
setEchoCharacter kann ein Zeichen bergeben werden, das bei jedem Tastendruck anstelle des
vom Anwender eingegebenen Zeichens ausgegeben wird. Durch bergabe eines '*' kann
beispielsweise die verdeckte Eingabe eines Paworts realisiert werden.
Ein Textfeld generiert ein Action-Event, wenn der Anwender innerhalb des Textfeldes die ENTERTaste drckt. In diesem Fall liefert die Methode getActionCommand des Action-Events den Inhalt
des Textfeldes. Eine Methode setActionCommand, die es wie bei Buttons erlaubt, den
/* TextField1.inc */
TextField1.inc
/* TextField2.inc */
TextField2.inc
Panels nebeneinander angeordnet. Das erste Panel enthlt ein GridLayout mit drei Zeilen
Beschriftung und das zweite ein GridLayout mit drei Textfeldern. Da die Hhe der Elemente im
GridLayout in beiden Panels identisch ist, stehen Beschriftung und Text jeweils auf der gleichen
Hhe nebeneinander.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
TextField3.inc
/* TextField3.inc */
private void customizeLayout(Panel panel)
{
panel.setLayout(new FlowLayout(FlowLayout.LEFT));
Panel labelPanel = new Panel();
labelPanel.setLayout(new GridLayout(3,1));
labelPanel.add(new Label("Name",Label.LEFT));
labelPanel.add(new Label("Vorname",Label.LEFT));
labelPanel.add(new Label("Ort",Label.LEFT));
Panel editPanel = new Panel();
editPanel.setLayout(new GridLayout(3,1));
//Dieses Textfeld sendet Action- und Text-Ereignisse
TextField tf = new TextField("Meier",20);
tf.addActionListener(this);
tf.addTextListener(this);
editPanel.add(tf);
//Diese Textfelder senden keine Ereignisse
editPanel.add(new TextField("Luise",20));
editPanel.add(new TextField("Hamburg",20));
panel.add(labelPanel);
panel.add(editPanel);
}
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
32.7 TextArea
32.7 TextArea
Im Gegensatz zur Klasse TextField, bei der der Text auf eine einzige Zeile beschrnkt ist, knnen mit
der Klasse TextArea mehrzeilige Textfelder erzeugt werden. Zustzlich kann der Text in alle
Richtungen scrollen, so da auch grere Texte bearbeitet werden knnen.
public TextArea()
java.awt.TextArea
Der parameterlose Konstruktor erzeugt ein leeres TextArea-Objekt in einer vom System vorgegebenen
Gre. Werden die Parameter rows und cols vorgegeben, legen sie die Anzahl der sichtbaren Zeilen
und Spalten fest. Mit dem Parameter text kann die zu editierende Zeichenkette bergeben werden. Mit
Hilfe des zustzlichen Parameters scroll kann die Ausstattung der TextArea mit Schiebereglern
festgelegt werden. Dazu stellt TextArea die Konstanten SCROLLBARS_BOTH,
SCROLLBARS_VERTICAL_ONLY, SCROLLBARS_HORIZONTAL_ONLY und SCROLLBARS_NONE
zur Verfgung, die als Argument bergeben werden knnen.
Ebenso wie TextField ist auch TextArea aus der Klasse TextComponent abgeleitet und besitzt
mit Ausnahme von setEchoCharacter und getEchoCharacter alle Methoden, die bereits bei
TextField vorgestellt wurden. Zustzlich gibt es einige Methoden, mit denen Teile des Textes
verndert werden knnen:
public void insert(String str, int pos)
java.awt.TextArea
Mit insert wird die Zeichenkette str an der Position pos eingefgt, und der dahinter stehende Text
wird entsprechend nach hinten geschoben. append hngt den als Argument bergebenen Text hinten an
den bestehenden Text an, und replaceRange ersetzt den Text zwischen start und end durch die
bergebene Zeichenkette text.
Ein Objekt der Klasse TextArea sendet Text-Events, so wie es bei TextField beschrieben wurde.
Action-Events werden dagegen nicht gesendet, denn die ENTER-Taste erzeugt eine Zeilenschaltung. Ein
zum folgenden Beispiel passender Event-Handler knnte so realisiert werden:
001
002
003
004
005
006
007
TextArea2.inc
/* TextArea2.inc */
public void textValueChanged(TextEvent event)
{
TextArea tf = (TextArea)event.getSource();
System.out.println("textValueChanged: "+tf.getText());
}
TextArea1.inc
/* TextArea1.inc */
private void customizeLayout(Panel panel)
{
panel.setLayout(new FlowLayout(FlowLayout.LEFT));
TextArea ta = new TextArea(10,40);
ta.addTextListener(this);
panel.add(ta);
}
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
DOC
API
32.8 Choice
32.8 Choice
Ein Choice-Men ist ein aufklappbares Textfeld, dessen Inhalt aus einer vom Programm
vorgegebenen Liste ausgewhlt werden kann. Dialogelemente des Typs Choice entsprechen den
unter Windows blichen Drop-Down-Listboxen, einer eingeschrnkten Form der Comboboxen, bei
denen das zugehrige Editfeld nicht separat gendert werden kann.
public Choice()
java.awt.Choice
Der parameterlose Konstruktor erstellt ein Choice-Objekt mit einer leeren Liste.
Eine der wichtigsten Methoden der Klasse Choice ist add:
public void add(String item)
java.awt.Choice
Jeder Aufruf von add hngt das bergebene Element item an das Ende der Liste an. Die Elemente
werden dabei in der Reihenfolge der Aufrufe von add eingefgt. Eine automatische Sortierung
wurde in Java nicht vorgesehen.
Analog zur Methode add gibt es eine bedeutungsgleiche Methode addItem.
Whrend in der Klasse Choice beide Methoden verwendet werden knnen, wurde
die Methode addItem in der Klasse List mit dem JDK 1.2 als deprecated
markiert, sollte also nicht mehr verwendet werden.
JDK1.1-1.4
Fr den Zugriff auf die Elemente der Combobox stehen die Methoden getItemCount,
getSelectedIndex, getItem und getSelectedItem zur Verfgung:
java.awt.Choice
getItemCount liefert die Anzahl der Elemente, die sich gegenwrtig in der Liste befinden. Mit
getItem kann auf das n-te Element der Liste ber seinen Index zugegriffen werden; die Zhlung
beginnt bei 0.
getSelectedIndex liefert den Index des selektierten Elements, so da durch bergabe dieses
Wertes an getItem das aktuelle Element beschafft werden kann. Alternativ dazu kann
getSelectedItem aufgerufen werden, um ohne explizite Kenntnis der internen Nummer das
selektierte Element zu beschaffen.
Ebenso wie der Anwender kann auch das Programm selbst ein Element aus der Liste selektieren:
public select(int)
public select(String)
java.awt.Choice
Hierbei wird an select wahlweise ein int oder ein String bergeben. Im ersten Fall wird das
Element ber seinen numerischen Index selektiert, im zweiten direkt durch Suche des angegebenen
Wertes.
Ebenso wie eine Checkbox sendet auch ein Choice-Element Item-Ereignisse, wenn ein Element
selektiert wurde. Um auf dieses Event zu reagieren, ist durch Aufruf von addItemListener ein
ItemListener zu registrieren:
public void addItemListener(ItemListener l)
java.awt.Choice
Ein Item-Event wird immer dann gesendet, wenn ein anderes Element ausgewhlt wurde. In diesem
Fall wird im Ereignisempfnger die Methode itemStateChanged mit einem Argument vom
Typ ItemEvent aufgerufen:
public abstract void itemStateChanged(ItemEvent e)
java.awt.event.ItemEvent
Das ItemEvent stellt die Methode getItemSelectable zur Verfgung, mit der ermittelt
werden kann, durch welches Choice-Element das Ereignis ausgelst wurde. Zustzlich gibt es die
Methode getItem, die das ausgewhlte Element als String zur Verfgung stellt:
java.awt.event.ItemEvent
Damit kann offensichtlich auf zwei unterschiedliche Arten auf die nderung der Auswahl reagiert
werden. Einerseits kann mit getItemSelectable ein Choice-Element beschafft und mit
getSelectedItem das selektierte Element ermittelt werden. Andererseits kann getItem
aufgerufen und das Ergebnis in einen String umgewandelt werden. Das folgende Listing
demonstriert dies beispielhaft:
001
002
003
004
005
006
007
008
009
010
/* Choice2.inc */
Choice2.inc
/* Choice1.inc */
private void customizeLayout(Panel panel)
{
panel.setLayout(new FlowLayout());
Choice choice = new Choice();
choice.addItemListener(this);
choice.add("rot");
choice.add("grn");
choice.add("gelb");
choice.add("blau");
choice.add("rosa");
choice.add("lila");
panel.add(choice);
}
Choice1.inc
Titel
<<
>
>>
DOC
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
32.9 List
32.9 List
Eine List ist eine listenartige Darstellung von Werten, aus denen der Anwender einen oder mehrere auswhlen
kann. Anders als ein Choice-Element ist ein Element der Klasse List stndig in voller Gre auf dem
Bildschirm sichtbar. Unter Windows werden Objekte der Klasse List durch Listboxen dargestellt.
public List()
java.awt.List
Der parameterlose Konstruktor legt eine leere Listbox an, deren dargestellte Gre vom Layoutmanager
begrenzt wird. Der Parameter size legt die Anzahl der angezeigten Zeilen fest. Dabei knnen auch mehr
Elemente in der Listbox enthalten sein, als auf einmal angezeigt werden knnen. List-Dialogelemente knnen
wahlweise die Mehrfachselektion von Elementen zulassen. Wird bei der Konstruktion der Listbox der
Parameter multiselect auf true gesetzt, kann der Anwender nicht nur ein Element, sondern auch mehrere
Elemente selektieren.
Das Dialogelement List bietet nahezu dieselbe Funktionalitt wie Choice. Zustzlich stehen die Methoden
getSelectedIndexes und getSelectedItems zur Verfgung, um bei einer Mehrfachselektion auf die
selektierten Elemente zugreifen zu knnen:
public int[] getSelectedIndexes()
java.awt.List
getSelectedIndexes liefert ein Array mit den Indexpositionen aller selektierten Elemente, und
getSelectedItems liefert eine Liste der Werte selbst.
Mit den Methoden select und deselect lassen sich einzelne Elemente selektieren oder deselektieren:
public void select(int index)
java.awt.List
Im Gegensatz zu Choice ist es bei einer Komponente des Typs List auch mglich, Elemente zu entfernen
oder ihren Wert zu ndern:
java.awt.List
Sowohl delItem als auch remove lschen das Element an der Position index, und replaceItem ersetzt
das Element an der Position index durch den neuen Wert newValue.
Eine List sendet sowohl Item-Ereignisse als auch Action-Ereignisse. Ein Action-Ereignis wird dann generiert,
wenn ein Listenelement per Doppelklick ausgewhlt wurde. Das Drcken der ENTER-Taste lst - wenigstens in
der aktuellen Windows-Implementierung - kein Action-Ereignis aus. Ein Item-Ereignis wird ausgelst, nachdem
in der Liste ein Element ausgewhlt wurde. Bezglich der Implementierung von Event-Handlern fr diese
Ereignisse verweisen wir auf die Beschreibungen der Klassen Choice und TextField weiter oben.
Falls ein Item-Ereignis ausgelst wurde, verhlt sich die Methode getItemSelectable des
Warnung
ItemEvent analog zu Choice und liefert das List-Objekt, das das Ereignis ausgelst hat. Der
Rckgabewert von getItem ist dagegen anders zu interpretieren, denn er liefert nicht das
ausgewhlte Element als String, sondern dessen Position als Integer-Objekt. Dieses kann mit
intValue in ein int konvertiert werden, das die Position des ausgewhlten Elements liefert.
Beispielhafte Implementierungen von actionPerformed und itemStateChanged knnten
folgendermaen aussehen:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
/* List2.inc */
public void itemStateChanged(ItemEvent event)
{
List list = (List) event.getItemSelectable();
String str1 = list.getSelectedItem();
int pos = ((Integer) event.getItem()).intValue();
System.out.println("list.getSelectedItem: " + str1);
System.out.println("event.getItem:
" + pos);
}
public void actionPerformed(ActionEvent event)
{
Object obj = event.getSource();
if (obj instanceof List) {
System.out.println("ListAction: "+event.getActionCommand());
} else if (obj instanceof Button) {
if (event.getActionCommand().equals("Ende")) {
endDialog();
}
}
}
List2.inc
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
List1.inc
/* List1.inc */
private void customizeLayout(Panel panel)
{
panel.setLayout(new FlowLayout(FlowLayout.LEFT));
List list = new List(6,false);
list.addActionListener(this);
list.addItemListener(this);
list.add("pfel");
list.add("Birnen");
list.add("Bananen");
list.add("Pfirsiche");
list.add("Kirschen");
list.add("Kiwis");
list.add("Ananas");
list.add("Erdbeeren");
list.add("Blaubeeren");
list.add("Mandarinen");
panel.add(list);
list.select(1);
}
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
32.10 Scrollbar
32.10 Scrollbar
Ein Scrollbar ist ein Schieberegler, der zur quasianalogen Anzeige und Eingabe eines Wertes aus einem vorgegebenen
Wertebereich verwendet werden kann. Der Schieberegler kann entweder horizontal oder vertikal angeordnet werden und
besitzt einen Schieber, dessen Gre vernderlich ist. Der interne Wert eines Schiebereglers und die Anzeigeposition
seines Schiebers sind untrennbar miteinander verbunden. ndert der Anwender die Position des Schiebers, ndert sich
automatisch auch sein interner Wert. Wird vom Programm der Wert verndert, fhrt dies auch zu einer Repositionierung
des Schiebers.
public Scrollbar()
java.awt.Scrollbar
Der parameterlose Konstruktor erzeugt einen vertikalen Schieberegler. Mit dem Parameter orientation kann die
Orientierung festgelegt werden. Hier kann eine der Konstanten Scrollbar.HORIZONTAL oder
Scrollbar.VERTICAL angegeben werden.
Der dritte Konstruktor erlaubt die Angabe weiterer Eigenschaften. minimum und maximum spezifizieren die Grenzen
des reprsentierten Wertebereichs. Die untere Grenze liegt bei einem vertikalen Schieberegler oben und bei einem
horizontalen Schieberegler auf der linken Seite. Mit value kann der Anfangswert des Schiebers festgelegt werden.
Der Parameter visible dient dazu, die Seitengre des Schiebers zu bestimmen. Diese mu kleiner als der
Wertebereich des Schiebereglers sein. Die Seitengre bestimmt einerseits die visuelle Gre des Schiebers und
andererseits die Gre der Vernderung des Wertes, wenn der Anwender auf die Schaltflche zwischen Schieber und
Button des Schiebereglers klickt.
Die Methoden der Klasse Scrollbar realisieren den Zugriff auf die Attribute des Schiebereglers. Die meisten von
ihnen sind im Interface Adjustable definiert, das von Scrollbar implementiert wird. Mit getValue und
setValue wird auf den aktuellen Wert des Schiebers zugegriffen, mit getMinimum und getMaximum auf die
Grenzen des Wertebereichs und mit getVisibleAmount auf die Gre des Schiebers. Zustzlich kann mit
getUnitIncrement und setUnitIncrement sowie mit getBlockIncrement und setBlockIncrement
auf die Parameter zugegriffen werden, die die Strke der Vernderung des Wertes beim Klicken auf die Buttons bzw. die
Schaltflche zwischen Schieber und Buttons bestimmen.
java.awt.Scrollbar
Ein Scrollbar sendet Adjustment-Ereignisse an seine Ereignisempfnger. Diese mssen das Interface
AdjustmentListener implementieren und sich durch Aufruf von addAdjustmentListener registrieren:
public void addAdjustmentListener(AdjustmentListener l)
java.awt.Scrollbar
Das Adjustment-Ereignis fhrt im Ereignisempfnger zum Aufruf der Methode adjustmentValueChanged, die ein
AdjustmentEvent bergeben bekommt:
public abstract void adjustmentValueChanged(AdjustmentEvent e)java.awt.event.AdjustmentListener
Dieses besitzt die Methoden getAdjustable und getValue. Mit der erstgenannten kann der auslsende Scrollbar,
mit letzterer der aktuelle Wert des Schiebereglers bestimmt werden. Zustzlich gibt es die Methode
getAdjustmentType, die Auskunft darber gibt, welche Benutzeraktion zur Auslsung des Ereignisses fhrte.
Tabelle 32.1 listet die mglichen Konstanten auf, die von getAdjustmentType zurckgegeben werden knnen, und
beschreibt ihre Bedeutung.
Konstante
Bedeutung
UNIT_INCREMENT
Der Wert wurde durch Klicken eines Buttons um eine Einheit erhht.
UNIT_DECREMENT
BLOCK_INCREMENT
Der Wert wurde durch Klicken der Schaltflche zwischen Button und
Schieber um eine Seite erhht.
BLOCK_DECREMENT
Der Wert wurde durch Klicken der Schaltflche zwischen Button und
Schieber um eine Seite vermindert.
TRACK
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
/* Scrollbar.inc */
public void adjustmentValueChanged(AdjustmentEvent event)
{
Adjustable sb = event.getAdjustable();
if (sb.getOrientation() == Scrollbar.HORIZONTAL) {
System.out.print("Horizontal: ");
} else {
System.out.print("Vertikal: ");
}
switch (event.getAdjustmentType()) {
case AdjustmentEvent.UNIT_INCREMENT:
System.out.println("AdjustmentEvent.UNIT_INCREMENT");
break;
case AdjustmentEvent.UNIT_DECREMENT:
System.out.println("AdjustmentEvent.UNIT_DECREMENT");
break;
case AdjustmentEvent.BLOCK_DECREMENT:
System.out.println("AdjustmentEvent.BLOCK_DECREMENT");
break;
case AdjustmentEvent.BLOCK_INCREMENT:
System.out.println("AdjustmentEvent.BLOCK_INCREMENT");
break;
case AdjustmentEvent.TRACK:
System.out.println("AdjustmentEvent.TRACK");
break;
}
System.out.println(" value: " + event.getValue());
}
private void customizeLayout(Panel panel)
{
panel.setLayout(new BorderLayout());
Scrollbar hsb=new Scrollbar(Scrollbar.HORIZONTAL,1,10,1,100);
hsb.addAdjustmentListener(this);
panel.add(hsb, BorderLayout.SOUTH);
Scrollbar vsb=new Scrollbar(Scrollbar.VERTICAL, 1,10,1,100);
vsb.addAdjustmentListener(this);
panel.add(vsb, BorderLayout.EAST);
}
Scrollbar.inc
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
32.11 ScrollPane
32.11 ScrollPane
Ein sehr ntzliches Dialogelement, das mit der Version 1.1 des JDK eingefhrt wurde, ist ScrollPane,
ein Container fr automatisches horizontales und vertikales Scrolling. Ein ScrollPane ist von der
Funktion her einem Panel hnlich und kann wie jedes andere Dialogelement innerhalb eines Fensters
verwendet werden. ScrollPane unterscheidet sich allerdings durch zwei wichtige Eigenschaften von
einem gewhnlichen Panel:
Es kann genau ein Dialogelement aufnehmen und bentigt keinen expliziten Layoutmanager.
Es ist in der Lage, eine virtuelle Ausgabeflche zu verwalten, die grer ist als die auf dem
Bildschirm zur Verfgung stehende.
Die innerhalb von ScrollPane angezeigte Komponente arbeitet dabei immer mit der
virtuellen Ausgabeflche und merkt nichts von eventuellen Grenbeschrnkungen auf dem
Bildschirm. Falls die bentigte Ausgabeflche grer ist als die anzeigbare, blendet
ScrollPane automatisch die erforderlichen Schieberegler ein, um das Dialogelement
horizontal und vertikal verschieben zu knnen.
Hinweis
public ScrollPane()
public ScrollPane(int scrollbarDisplayPolicy)
Der Parameter scrollbarDisplayPolicy definiert die Strategie zur Anzeige der Schieberegler
entsprechend den in Tabelle 32.2 aufgelisteten Konstanten.
Konstante
Bedeutung
ScrollPane.SCROLLBARS_NEVER
java.awt.ScrollPane
Mit getHAdjustable wird ein Objekt beschafft, das den Zugriff auf den horizontalen Schieberegler
ermglicht, und mit getVAdjustable eines fr den vertikalen Schieberegler. Das Interface
Adjustable wurde bereits beim Scrollbar vorgestellt, es abstrahiert den Zugriff auf einen
Schieberegler. Da die meisten Eigenschaften der Schieberegler bereits von ScrollPane voreingestellt
werden, sollte sich das eigene Programm darauf beschrnken, durch Aufruf von setUnitIncrement
die Schrittweite der Schieberegler einzustellen.
Des weiteren gibt es einige Methoden fr den Zugriff auf den Viewport und seine Position innerhalb des
virtuellen Ausgabebereichs:
public Dimension getViewportSize()
java.awt.ScrollPane
getViewportSize liefert die aktuelle Gre des Viewports. Mit getScrollPosition kann die
Position desjenigen Punktes der virtuellen Ausgabeflche ermittelt werden, der gerade in der linken
oberen Ecke des Viewports angezeigt wird. Mit setScrollPosition kann der Viewport vom
Programm verschoben werden. Die bergebenen Werte mssen dabei zwischen 0,0 und der jeweils
maximalen Gre des virtuellen Ausgabebereichs minus der Gre des Viewports liegen.
Wir wollen ein einfaches Beispiel konstruieren, das die Anwendung von ScrollPane demonstriert.
Dazu soll ein Programm geschrieben werden, das eine Liste aller Systemfarben mit ihrer Bezeichnung
und einer Farbprobe auf dem Bildschirm ausgibt. Da es insgesamt 26 verschiedene Systemfarben gibt,
soll das Programm jeweils nur einen kleinen Ausschnitt darstellen, der vom Anwender verschoben
werden kann.
Zur Erstellung des Programms gehen wir in drei Schritten vor:
Der nachfolgende Quellcode enthlt alle drei Klassen und zeigt, wie sie zusammenarbeiten:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
/* Listing3216.java */
import java.awt.*;
import java.awt.event.*;
class NamedSystemColors
{
String[]
names;
SystemColor[] colors;
public NamedSystemColors()
{
names = new String[SystemColor.NUM_COLORS];
colors = new SystemColor[SystemColor.NUM_COLORS];
names [ 0] = "desktop";
colors[ 0] = SystemColor.desktop;
names [ 1]= "activeCaption";
colors[ 1] = SystemColor.activeCaption;
names [ 2] = "activeCaptionText";
colors[ 2] = SystemColor.activeCaptionText;
names [ 3] = "activeCaptionBorder";
colors[ 3] = SystemColor.activeCaptionBorder;
names [ 4] = "inactiveCaption";
colors[ 4] = SystemColor.inactiveCaption;
names [ 5] = "inactiveCaptionText";
colors[ 5] = SystemColor.inactiveCaptionText;
names [ 6] = "inactiveCaptionBorder";
colors[ 6] = SystemColor.inactiveCaptionBorder;
names [ 7] = "window";
colors[ 7] = SystemColor.window;
names [ 8] = "windowBorder";
colors[ 8] = SystemColor.windowBorder;
names [ 9] = "windowText";
colors[ 9] = SystemColor.windowText;
names [10] = "menu";
colors[10] = SystemColor.menu;
names [11] = "menuText";
colors[11] = SystemColor.menuText;
names [12] = "text";
colors[12] = SystemColor.text;
names [13] = "textText";
colors[13] = SystemColor.textText;
names [14] = "textHighlight";
colors[14] = SystemColor.textHighlight;
names [15] = "textHighlightText";
colors[15] = SystemColor.textHighlightText;
names [16] = "textInactiveText";
colors[16] = SystemColor.textInactiveText;
names [17] = "control";
colors[17] = SystemColor.control;
names [18] = "controlText";
colors[18] = SystemColor.controlText;
Listing3216.java
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
names [19]
colors[19]
names [20]
colors[20]
names [21]
colors[21]
names [22]
colors[22]
names [23]
colors[23]
names [24]
colors[24]
names [25]
colors[25]
=
=
=
=
=
=
=
=
=
=
=
=
=
=
"controlHighlight";
SystemColor.controlHighlight;
"controlLtHighlight";
SystemColor.controlLtHighlight;
"controlShadow";
SystemColor.controlShadow;
"controlDkShadow";
SystemColor.controlDkShadow;
"scrollbar";
SystemColor.scrollbar;
"info";
SystemColor.info;
"infoText";
SystemColor.infoText;
}
public int getSize()
{
return SystemColor.NUM_COLORS;
}
public String getName(int i)
{
return names[i];
}
public SystemColor getColor(int i)
{
return colors[i];
}
}
class SystemColorViewer
extends Canvas
{
NamedSystemColors colors;
public SystemColorViewer()
{
colors = new NamedSystemColors();
}
public Dimension getPreferredSize()
{
return new Dimension(150,16 + colors.getSize() * 20);
}
public void paint(Graphics g)
{
for (int i = 0; i < colors.getSize(); ++i) {
//Rahmen fr Farbbox
g.setColor(Color.black);
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
g.drawRect(10,16+20*i,16,16);
//Farbbox
g.setColor(colors.getColor(i));
g.fillRect(11,17+20*i,15,15);
//Bezeichnung
g.setColor(Color.black);
g.drawString(colors.getName(i),30,30+20*i);
}
}
}
public class Listing3216
extends Frame
{
public static void main(String[] args)
{
Listing3216 wnd = new Listing3216();
wnd.setLocation(200,100);
wnd.setVisible(true);
}
public Listing3216()
{
super("ScrollPane");
setBackground(Color.lightGray);
//ScrollPane
ScrollPane sc = new ScrollPane(
ScrollPane.SCROLLBARS_AS_NEEDED
);
sc.add(new SystemColorViewer());
sc.getVAdjustable().setUnitIncrement(1);
sc.getHAdjustable().setUnitIncrement(1);
sc.setSize(200,200);
add(sc);
//Window-Listener
addWindowListener(new WindowClosingAdapter(true));
//Dialogelement anordnen
pack();
}
}
Tip
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
33.2.1 Anforderungen
In diesem Abschnitt wollen wir uns ein konkretes Beispiel zur Komponentenentwicklung ansehen. Dazu
soll eine einstellige 7-Segment-Anzeige entwickelt werden, die in der Lage ist, die Ziffern 0 bis 9
darzustellen. Des weiteren sollen folgende Eigenschaften realisiert werden:
Die Anzeige soll in der Gre skalierbar sein und sich zu allen Layoutmanagern kompatibel
verhalten.
Die 7-Segment-Anzeige soll auf Mausklicks reagieren. Nach einem Mausklick soll sie den Fokus
erhalten. Nach Drcken der linken Maustaste soll der Anzeigewert herunter- und nach Drcken der
rechten Maustaste heraufgezhlt werden. Wird whrend des Mausklicks die [UMSCHALT]-Taste
gedrckt, so soll lediglich der Fokus zugewiesen werden, der Wert aber unverndert bleiben.
Der Anzeigewert soll durch Drcken der Tasten [+] und [-] herauf- bzw. heruntergezhlt werden.
Wird eine Zifferntaste gedrckt, so soll die Anzeige auf diesen Wert gesetzt werden.
Eine Anzeige, die den Fokus hat, soll visuell von einer ohne Fokus unterscheidbar sein. Der Fokus
soll weiterhin - wie unter Windows blich - mit Hilfe der Tasten [TAB] und
[UMSCHALT]+[TAB] von einem Dialogelement zum nchsten weitergereicht werden knnen.
33.2.2 Bildschirmanzeige
Die Architektur unserer Anzeigekomponente ist denkbar einfach. Wir definieren dazu eine neue Klasse
Segment7, die aus Canvas abgeleitet wird. Segment7 besitzt eine Membervariable digit, die den
aktuellen Anzeigewert speichert. Dieser kann mit den ffentlichen Methoden getValue und setValue
abgefragt bzw. gesetzt werden. Die Klasse bekommt zwei Konstruktoren, die es erlauben, den Anzeigewert
wahlweise bei der Instanzierung zu setzen oder die Voreinstellung 0 zu verwenden.
Segment7 berlagert die Methoden getPreferredSize und getMinimumSize der Klasse
Component, um den Layoutmanagern die gewnschte Gre mitzuteilen:
public Dimension getPreferredSize()
java.awt.Component
Beide Methoden liefern ein Objekt der Klasse Dimension, also ein rechteckiges Element mit einer Hhe
und Breite. getPreferredSize teilt dem Layoutmanager mit, welches die gewnschte Gre der
Komponente ist, und getMinimumSize gibt an, welches die kleinste akzeptable Gre ist. Der
Layoutmanager FlowLayout beispielsweise verwendet getPreferredSize, um die Gre der
Komponente zu bestimmen. GridLayout gibt die Gre selbst vor und pat sie an die Gitterelemente an.
Durch Aufruf von pack kann allerdings auch GridLayout dazu veranlat werden, die gewnschte Gre
der Komponenten abzufragen und zur Dimensionierung des Fensters (und damit letztlich zur
Dimensionierung der Einzelkomponenten) zu verwenden. Seit dem JDK 1.1 gibt es noch eine dritte
Methode getMaximumSize, mit der die Komponente ihre maximale Gre mitteilen kann. Sie wird in
unserem Beispiel nicht bentigt.
Zur Darstellung der Leuchtdiodenanzeige auf dem Bildschirm wird die Methode paint berlagert; in ihr
befindet sich die Logik zur Darstellung der sieben Segmente (siehe Abbildung 33.1). In Segment7 wurden
dazu drei Arrays digits, polysx und polysy definiert, die die Belegung der Segmente fr jede
einzelne Ziffer darstellen und die Eckpunkte jedes einzelnen Segments vorgeben.
paint verwendet das Array digits, um herauszufinden, welche Segmente zur Darstellung der aktuellen
Ziffer verwendet werden. Fr jedes der beteiligten Segmente wird dann aus den Arrays polysx und
polysy das passende Polygon gebildet und mit fillPolygon angezeigt. Als interne
Berechnungseinheit werden zwei Parameter dx und dy verwendet, die beim Aufruf von paint aus dem
fr die Komponente verfgbaren Platz berechnet werden.
33.2.3 Ereignisbehandlung
Wie in Kapitel 28 erwhnt, erfolgt die Ereignisbehandlung in selbstdefinierten Komponenten blicherweise
auf der Basis des vierten vorgestellten Architekturmodells. Bei diesem werden die Ereignisse nicht durch
registrierte Listener-Klassen bearbeitet, sondern durch berlagern der Methoden process...Event der
Klasse Component.
Damit die Ereignisse tatschlich an diese Methoden weitergegeben werden, mssen sie zuvor durch Aufruf
von enableEvents und bergabe der zugehrigen Ereignismaske aktiviert werden. Da wir Component-,
Focus-, Key- und Mouse-Ereignisse behandeln wollen, rufen wir enableEvents mit den Konstanten
AWTEvent.COMPONENT_EVENT_MASK, AWTEvent.FOCUS_EVENT_MASK,
AWTEvent.MOUSE_EVENT_MASK und AWTEvent.KEY_EVENT_MASK auf. Beim berlagern dieser
Methoden sollte in jedem Fall der entsprechende Ereignishandler der Superklasse aufgerufen werden, um
die korrekte Standard-Ereignisbehandlung sicherzustellen.
Die Reaktion auf Mausklicks wird durch berlagern der Methode processMouseEvent realisiert. Hier
wird zunchst berprft, ob es sich um ein MOUSE_PRESSED-Ereignis handelt, also ob eine der
Maustasten gedrckt wurde. Ist dies der Fall, wird dem Anzeigeelement durch Aufruf von
requestFocus der Eingabefokus zugewiesen.
Anschlieend wird berprft, ob die [UMSCHALT]-Taste gedrckt wurde. Ist das der Fall, wird die
Ereignisbehandlung beendet, andernfalls wird der Anzeigewert hoch- bzw. heruntergezhlt, je nachdem, ob
die rechte oder linke Maustaste gedrckt wurde. Am Ende von processMouseEvent wird in jedem Fall
super.processMouseEvent aufgerufen, um sicherzustellen, da die normale Ereignisbehandlung
aufgerufen wird.
Ein selbstdefiniertes Dialogelement bekommt nicht automatisch den Fokus zugewiesen, wenn mit der Maus
darauf geklickt wird. Statt dessen mu es selbst auf Mausklicks reagieren und sich - wie zuvor beschrieben durch Aufruf von requestFocus selbst den Fokus zuweisen. Bei jeder Fokusnderung wird ein FocusEvent ausgelst, das wir durch berlagern der Methode processFocusEvent bearbeiten. Hier
unterscheiden wir zunchst, ob es sich um ein FOCUS_GAINED- oder FOCUS_LOST-Ereignis handelt, und
setzen eine interne Statusvariable hasfocus entsprechend. Diese wird nach dem anschlieenden Aufruf
von repaint verwendet, um in paint durch Modifikation der Anzeigefarbe ein visuelles Feedback zu
geben. Hat ein Element den Fokus, so ist die Farbe der Anzeigesegmente gelb, andernfalls rot.
Seit dem JDK 1.1 gibt es einen Mechanismus, der es erlaubt, mit den Tasten [TAB] und
[UMSCHALT]+[TAB] zwischen den Eingabefeldern eines Dialogs zu wechseln. Genauer gesagt wird
dadurch der Fokus an das nchste Element weiter- bzw. zum vorigen zurckgegeben. Da diese
Vorgehensweise nicht bei jedem Dialogelement sinnvoll ist, kann das Dialogelement sie durch berlagern
der Methode isFocusTraversable selbst bestimmen. Liefert isFocusTraversable den
Rckgabewert true, so nimmt das Objekt an der [TAB]-Behandlung teil, andernfalls nicht. Die Klasse
Segment7 berlagert isFocusTraversable und gibt true zurck. So kann mit [TAB] und
[UMSCHALT]+[TAB] wie besprochen zwischen den 7-Segment-Anzeigen gewechselt werden.
Mit der Version 1.4 des JDK wurde isFocusTraversable als deprecated markiert, und JDK1.1-1.4
sollte durch isFocusable ersetzt werden. Wegen der groen Anzahl vorhandener Bugs
und Inkonsistenzen wurde nmlich die Architektur des Fokus-Subsystems vollstndig
berarbeitet und mit einem teilweise neuen API versehen. Programmcode, der mit der
Fokussierung von AWT- oder Swing-Komponenten zu tun hat, mu daher beim Umstieg auf
das JDK 1.4 vermutlich berarbeitet werden.
Ein Dialogelement enthlt nur dann Tastatureingaben, wenn es den Fokus hat. Durch den zuvor
beschriebenen Mechanismus des Aufrufs von requestFocus stellen wir sicher, da nach einem
Mausklick bzw. nach dem Wechsel des Fokus mit [TAB] und [UMSCHALT]+[TAB] Tastaturereignisse
an das Element gesendet werden. Diese werden durch berlagern der Methode processKeyEvent
behandelt. Wir berprfen darin zunchst, ob das Ereignis vom Typ KEY_PRESSED ist, und besorgen dann
mit getKeyChar den Wert der gedrckten Taste. Ist er '+', so wird der Anzeigewert um 1 erhht, bei '-'
entsprechend verringert. Wurde eine der Zifferntasten gedrckt, so erhlt das Anzeigeelement diesen Wert.
Anschlieend wird durch Aufruf von repaint die Anzeige neu gezeichnet.
Ein Component-Ereignis brauchen wir in unserem Beispiel nur, damit wir dem Dialogelement unmittelbar
nach der Anzeige auf dem Bildschirm den Fokus zuweisen knnen. Dazu berlagern wir die Methode
processComponentEvent und berprfen, ob das Ereignis vom Typ COMPONENT_SHOWN ist. In
Segment7.java
/* Segment7.java */
import java.awt.*;
import java.awt.event.*;
class Segment7
extends Canvas
{
private int digit;
private boolean hasfocus;
private int[][] polysx = {
{ 1, 2, 8, 9, 8, 2},
//Segment
{ 9,10,10, 9, 8, 8},
//Segment
{ 9,10,10, 9, 8, 8},
//Segment
{ 1, 2, 8, 9, 8, 2},
//Segment
{ 1, 2, 2, 1, 0, 0},
//Segment
{ 1, 2, 2, 1, 0, 0},
//Segment
{ 1, 2, 8, 9, 8, 2},
//Segment
};
private int[][] polysy = {
{ 1, 0, 0, 1, 2, 2},
//Segment
{ 1, 2, 8, 9, 8, 2},
//Segment
{ 9,10,16,17,16,10},
//Segment
{17,16,16,17,18,18},
//Segment
{ 9,10,16,17,16,10},
//Segment
{ 1, 2, 8, 9, 8, 2},
//Segment
{ 9, 8, 8, 9,10,10},
//Segment
};
private int[][] digits = {
{1,1,1,1,1,1,0},
//Ziffer
{0,1,1,0,0,0,0},
//Ziffer
{1,1,0,1,1,0,1},
//Ziffer
{1,1,1,1,0,0,1},
//Ziffer
{0,1,1,0,0,1,1},
//Ziffer
{1,0,1,1,0,1,1},
//Ziffer
{1,0,1,1,1,1,1},
//Ziffer
{1,1,1,0,0,0,0},
//Ziffer
{1,1,1,1,1,1,1},
//Ziffer
{1,1,1,1,0,1,1}
//Ziffer
};
public Segment7()
{
this(0);
}
0
1
2
3
4
5
6
0
1
2
3
4
5
6
0
1
2
3
4
5
6
7
8
9
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
g.drawLine(0,0,dx*10,dy*10);
g.drawLine(0,8*dy,10*dx,18*dy);
g.drawLine(0,10*dy,10*dx,0);
g.drawLine(0,18*dy,10*dx,8*dy);
}
public int getValue()
{
return digit;
}
public void setValue(int value)
{
digit = value % 10;
}
protected void processComponentEvent(ComponentEvent event)
{
if (event.getID() == ComponentEvent.COMPONENT_SHOWN) {
requestFocus();
}
super.processComponentEvent(event);
}
protected void processFocusEvent(FocusEvent event)
{
if (event.getID() == FocusEvent.FOCUS_GAINED) {
hasfocus = true;
repaint();
} else if (event.getID() == FocusEvent.FOCUS_LOST) {
hasfocus = false;
repaint();
}
super.processFocusEvent(event);
}
protected void processMouseEvent(MouseEvent event)
{
if (event.getID() == MouseEvent.MOUSE_PRESSED) {
requestFocus();
if (!event.isShiftDown()) {
if (event.isMetaDown()) {
setValue(getValue() + 1); //increment by 1
} else {
setValue(getValue() + 9); //decrement by 1
}
}
repaint();
}
super.processMouseEvent(event);
}
protected void processKeyEvent(KeyEvent event)
{
155
if (event.getID() == KeyEvent.KEY_PRESSED) {
156
char key = event.getKeyChar();
157
if (key >= '0' && key <= '9') {
158
setValue(key - '0');
159
repaint();
160
} else if (key == '+') {
161
setValue(getValue() + 1); //increment by 1
162
repaint();
163
} else if (key == '-') {
164
setValue(getValue() + 9); //decrement by 1
165
repaint();
166
}
167
}
168
super.processKeyEvent(event);
169
}
170 }
Listing 33.1: Eine 7-Segment-Anzeige
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt Suchen
<
>
Index
>>
DOC
API
Auch die Einbindung dieser neuen Komponente ist sehr einfach und kann in Anlehnung an die
vorherigen Beispiele durch Aufruf von add in einem Container erfolgen:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
/* Listing3302.java */
import java.awt.*;
import java.awt.event.*;
class MyDialog3302
extends Dialog
implements ActionListener
{
public MyDialog3302(Frame parent)
{
super(parent,"MyDialog3302",true);
setBounds(100,100,400,300);
setBackground(Color.lightGray);
setLayout(new BorderLayout());
Panel panel = new Panel();
customizeLayout(panel);
add(panel, BorderLayout.CENTER);
//Ende-Button
Button button = new Button("Ende");
button.addActionListener(this);
add(button, BorderLayout.SOUTH);
pack();
//Window-Ereignisse
addWindowListener(
new WindowAdapter() {
public void windowClosing(WindowEvent event)
{
endDialog();
}
}
);
}
Listing3302.java
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107 }
Titel
<<
Inhalt Suchen
<
>
Index
DOC
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
34.1 Bitmaps
34.1 Bitmaps
34.1.1 Laden und Anzeigen einer Bitmap
Die Klasse MediaTracker
34.1.2 Entwicklung einer eigenen Bitmap-Komponente
das Laden der Bitmap von einem externen Speichermedium oder aus dem Netz
die eigentliche Ausgabe auf den Bildschirm
Das Laden erfolgt mit der Methode getImage, die eine Instanz der Klasse Image zurckgibt. Das
Image-Objekt kann dann mit der Methode drawImage der Klasse Graphics angezeigt werden.
getImage gibt es in verschiedenen Varianten, die sich dadurch unterscheiden, aus welcher Quelle sie die
Bitmap-Daten laden. In einer Java-Applikation wird in der Regel die Methode getImage aus der Klasse
Toolkit verwendet. Sie erwartet den Namen einer lokalen Datei als Parameter:
java.awt.Toolkit
getImage versteht in der aktuellen Version des AWT die beiden Bitmap-Typen gif und jpeg. Andere
Grafikformate, wie etwa das unter Windows gebruchliche bmp-Format, werden nicht untersttzt, sondern
mssen bei Bedarf konvertiert werden.
Neben den getImage-Methoden gibt es seit dem JDK 1.2 auch zwei Methoden mit dem
Namen createImage in der Klasse Toolkit:
public abstract Image createImage(String filename)
public abstract Image createImage(URL url)
java.awt.Toolkit
Sie laden ein Image bei jeder Verwendung neu und fhren (im Gegensatz zu getImage) kein
Caching des Bildes durch. Die JDK-Dokumentation empfiehlt sie gegenber getImage, weil
bei deren Verwendung Speicherlecks durch das unbegrenzte Zwischenspeichern der Bilddaten
entstehen knnen.
JDK1.11.4
Das Toolkit fr die aktuelle Umgebung kann mit der Methode getToolkit der Klasse Component
beschafft werden:
public Toolkit getToolkit()
java.awt.Component
Der gesamte Code zum Laden einer Bitmap duke.gif sieht daher so aus:
001 Image img;
002 img = getToolkit().getImage("duke.gif");
Listing 34.1: Laden einer Bitmap-Datei
Um das Image anzuzeigen, kann die Methode drawImage der Klasse Graphics aufgerufen werden:
public boolean drawImage(
Image img, int x, int y, ImageObserver observer
)
java.awt.Graphics
java.awt.MediaTracker
Als Komponente wird der this-Zeiger des aktuellen Fensters bergeben. Anschlieend werden durch
Aufruf von addImage alle Bilder, deren Ladevorgang berwacht werden soll, an den MediaTracker
bergeben:
public void addImage(Image img, int id)
java.awt.MediaTracker
Der zustzlich bergebene Parameter id kann dazu verwendet werden, einen Namen zu vergeben, unter
dem auf das Image zugegriffen werden kann. Zustzlich bestimmt er die Reihenfolge, in der die Images
geladen werden. Bitmaps mit kleineren Werten werden zuerst geladen.
Der MediaTracker bietet eine Reihe von Methoden, um den Ladezustand der Bilder zu berwachen.
Wir wollen hier nur die Methode waitForAll betrachten. Sie wartet, bis alle Images vollstndig geladen
sind:
public void waitForAll()
throws InterruptedException
java.awt.MediaTracker
Nach Abschlu des Ladevorgangs sendet waitForAll eine Ausnahme des Typs
InterruptedException.
Das vollstndige Beispielprogramm zur Anzeige von duke.gif sieht nun so aus:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
/* Listing3403.java */
import java.awt.*;
import java.awt.event.*;
public class Listing3403
extends Frame
{
private Image img;
public static void main(String[] args)
{
Listing3403 wnd = new Listing3403();
}
public Listing3403()
{
super("Bitmap");
setBackground(Color.lightGray);
setSize(250,150);
setVisible(true);
//WindowListener
addWindowListener(new WindowClosingAdapter(true));
//Bild laden
img = getToolkit().getImage("duke.gif");
MediaTracker mt = new MediaTracker(this);
mt.addImage(img, 0);
try {
//Warten, bis das Image vollstndig geladen ist,
mt.waitForAll();
} catch (InterruptedException e) {
//nothing
}
repaint();
}
public void paint(Graphics g)
{
if (img != null) {
g.drawImage(img,40,40,this);
}
}
}
Listing3403.java
/* BitmapComponent.java */
import java.awt.*;
class BitmapComponent
extends Canvas
{
private Image img;
public BitmapComponent(String fname)
{
img = getToolkit().getImage(fname);
MediaTracker mt = new MediaTracker(this);
mt.addImage(img, 0);
try {
//Warten, bis das Image vollstndig geladen ist,
//damit getWidth() und getHeight() funktionieren
mt.waitForAll();
} catch (InterruptedException e) {
//nothing
}
}
public void paint(Graphics g)
{
g.drawImage(img,1,1,this);
}
public Dimension getPreferredSize()
{
return new Dimension(
img.getWidth(this),
img.getHeight(this)
);
BitmapComponent.java
036
037
038
039
040
041
042
043
044
045 }
}
public Dimension getMinimumSize()
{
return new Dimension(
img.getWidth(this),
img.getHeight(this)
);
}
Warnung
Die Einbindung von BitmapComponent in einen Dialog erfolgt analog zur Einbindung jeder anderen
Komponente durch Aufruf der Methode add der Klasse Component. Das folgende Listing gibt ein
Beispiel fr die Einbindung der neuen Komponente:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
/* Listing3405.java */
import java.awt.*;
import java.awt.event.*;
public class Listing3405
extends Frame
{
public static void main(String[] args)
{
Listing3405 wnd = new Listing3405();
}
public Listing3405()
{
super("Bitmap-Komponente");
setBackground(Color.lightGray);
setSize(250,150);
setVisible(true);
//Hinzufgen der Komponenten
setLayout(new GridLayout(2,2));
add(new BitmapComponent("duke.gif"));
add(new BitmapComponent("duke.gif"));
add(new BitmapComponent("duke.gif"));
add(new BitmapComponent("duke.gif"));
pack();
//WindowListener
addWindowListener(new WindowClosingAdapter(true));
Listing3405.java
029
}
030 }
Listing 34.5: Verwenden der Bitmap-Komponente
Die Ausgabe des Programms ist:
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
34.2 Animation
34.2 Animation
34.2.1 Prinzipielle Vorgehensweise
Die repaint-Schleife
Verwendung von Threads
34.2.2 Abspielen einer Folge von Bitmaps
34.2.3 Animation mit Grafikprimitiven
34.2.4 Reduktion des Bildschirmflackerns
Bildschirm nicht lschen
Nur den wirklich bentigten Teil des Bildschirms lschen
Doppelpufferung
Fr die Anzeige der Einzelbilder mu das richtige Timing gewhlt werden. Werden zu wenig Bilder je
Zeiteinheit angezeigt, erscheint das Ergebnis ruckelig. Werden zu viele gezeigt, kann es sein, da paintEvents verlorengehen und Teile des Bildes nicht korrekt angezeigt werden.
Wird zur Anzeige der Einzelbilder der normale paint-/repaint-Mechanismus von Java verwendet,
wird die Darstellung durch ein starkes Flackern gestrt. Wir werden eine Reihe von Techniken
kennenlernen, mit denen dieses Flackern verhindert werden kann.
Die Darstellung der Einzelbilder darf nicht die Interaktivitt des Programms beeintrchtigen. Vor allem
darf die Nachrichtenschleife nicht blockiert werden. Wir werden lernen, wie man diese Probleme mit Hilfe
separater Threads umgehen kann.
All dies sind Standardprobleme, die vom Programmierer bei der Entwicklung von Animationen zu lsen sind. Wir
werden feststellen, da Java dafr durchweg brauchbare Lsungen zu bieten hat und die Programmierung kleiner
Animationen recht einfach zu realisieren ist.
Die repaint-Schleife
Das Grundprinzip einer Animation besteht darin, in einer Schleife die Methode repaint wiederholt aufzurufen.
Ein Aufruf von repaint fhrt dazu, da die paint-Methode aufgerufen wird, und innerhalb von paint
generiert die Anwendung dann die fr das aktuelle Einzelbild bentigte Bildschirmausgabe.
paint mu sich also merken (oder mitgeteilt bekommen), welches Bild bei welchem Aufruf erzeugt werden soll.
Typischerweise wird dazu ein Schleifenzhler verwendet, der das gerade anzuzeigende Bild bezeichnet. Nach dem
Ausfhren der Ausgabeanweisungen terminiert paint, und der Aufrufer wartet eine bestimmte Zeitspanne. Dann
zhlt er den Bildzhler hoch und fhrt den nchsten Aufruf von repaint durch. Dies setzt sich so lange fort, bis
die Animation beendet ist oder das Programm abgebrochen wird.
Das folgende Listing stellt eines der einfachsten Beispiele fr eine Grafikanimation dar:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
/* Listing3406.java */
import java.awt.*;
import java.awt.event.*;
public class Listing3406
extends Frame
{
int cnt = 0;
public static void main(String[] args)
{
Listing3406 wnd = new Listing3406();
wnd.setSize(250,150);
wnd.setVisible(true);
wnd.startAnimation();
}
public Listing3406()
{
super("Animierter Zhler");
setBackground(Color.lightGray);
addWindowListener(new WindowClosingAdapter(true));
}
public void startAnimation()
{
while (true) {
repaint();
}
}
public void paint(Graphics g)
{
++cnt;
g.drawString("Counter = "+cnt,10,50);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
}
Listing3406.java
/* Listing3407.java */
import java.awt.*;
import java.awt.event.*;
public class Listing3407
extends Frame
implements Runnable
{
int cnt = 0;
public static void main(String[] args)
{
Listing3407 wnd = new Listing3407();
wnd.setSize(250,150);
wnd.setVisible(true);
wnd.startAnimation();
}
public Listing3407()
{
super("Animations-Threads");
setBackground(Color.lightGray);
Listing3407.java
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050 }
addWindowListener(new WindowClosingAdapter(true));
}
public void startAnimation()
{
Thread th = new Thread(this);
th.start();
}
public void run()
{
while (true) {
repaint();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
//nichts
}
}
}
public void paint(Graphics g)
{
++cnt;
g.drawString("Counter = "+cnt,10,50);
}
/* Listing3408.java */
import java.awt.*;
import java.awt.event.*;
public class Listing3408
extends Frame
implements Runnable
{
Thread th;
Image[] arImg;
int actimage;
public static void main(String[] args)
{
Listing3408 wnd = new Listing3408();
wnd.setSize(200,150);
wnd.setVisible(true);
wnd.startAnimation();
}
public Listing3408()
{
super("Bitmap-Folge");
addWindowListener(new WindowClosingAdapter(true));
}
public void startAnimation()
{
th = new Thread(this);
actimage = -1;
th.start();
}
public void run()
{
//Bilder laden
arImg = new Image[30];
MediaTracker mt = new MediaTracker(this);
Toolkit tk = getToolkit();
for (int i = 1; i <= 30; ++i) {
arImg[i-1] = tk.getImage("images/jana"+i+".gif");
mt.addImage(arImg[i-1], 0);
actimage = -i;
repaint();
try {
mt.waitForAll();
} catch (InterruptedException e) {
//nothing
Listing3408.java
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073 }
}
}
//Animation beginnen
actimage = 0;
while (true) {
repaint();
actimage = (actimage + 1) % 30;
try {
Thread.sleep(50);
} catch (InterruptedException e) {
//nichts
}
}
}
public void paint(Graphics g)
{
if (actimage < 0) {
g.drawString("Lade Bitmap "+(-actimage),10,50);
} else {
g.drawImage(arImg[actimage],10,30,this);
}
}
Hinweis
Listing3409.java
/* Listing3409.java */
import java.awt.*;
import java.awt.event.*;
import java.util.*;
class ColorRectangle
extends Rectangle
{
public Color color;
}
public class Listing3409
extends Frame
implements Runnable
{
//Konstanten
private static final int
private static final int
private static final int
private static final Color
SIZERECT
SLEEP
NUMELEMENTS
BGCOLOR
=
=
=
=
7;
40;
20;
Color.lightGray;
//Instanzvariablen
private Thread th;
private Vector snake;
private int dx;
private int dy;
public static void main(String[] args)
{
Listing3409 frame = new Listing3409();
frame.setSize(200,150);
frame.setVisible(true);
frame.startAnimation();
}
public Listing3409()
{
super("Animierte Schlange");
setBackground(BGCOLOR);
addWindowListener(new WindowClosingAdapter(true));
snake = new Vector();
}
public void startAnimation()
{
th = new Thread(this);
th.start();
}
public void run()
{
//Schlange konstruieren
ColorRectangle cr;
int x = 100;
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
int y = 100;
for (int i=0; i < NUMELEMENTS; ++i) {
cr = new ColorRectangle();
cr.x = x;
cr.y = y;
cr.width = SIZERECT;
cr.height = SIZERECT;
x += SIZERECT;
cr.color = new Color(
i*(256/NUMELEMENTS),
0,
240-i*(256/NUMELEMENTS)
);
snake.addElement(cr);
}
//Vorzugsrichtung festlegen
dx = -1;
dy = -1;
//Schlange laufen lassen
while (true) {
repaint();
try {
Thread.sleep(SLEEP);
} catch (InterruptedException e){
//nichts
}
moveSnake();
}
}
public void moveSnake()
{
Dimension size = getSize();
int sizex = size.width-getInsets().left-getInsets().right;
int sizey = size.height-getInsets().top-getInsets().bottom;
ColorRectangle cr = (ColorRectangle)snake.firstElement();
boolean lBorder = false;
int xalt, yalt;
int xtmp, ytmp;
//Kopf der Schlange neu berechnen
if (cr.x <= 1) {
dx = 1;
lBorder = true;
}
if (cr.x + cr.width >= sizex) {
dx = -1;
lBorder = true;
}
if (cr.y <= 1) {
dy = 1;
lBorder = true;
}
if (cr.y + cr.height >= sizey) {
dy = -1;
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
lBorder = true;
}
if (! lBorder) {
if (rand(10) == 0) {
if (rand(2) == 0) {
switch (rand(5)) {
case 0: case 1:
dx = -1;
break;
case 2:
dx = 0;
break;
case 3: case 4:
dx = 1;
break;
}
} else {
switch (rand(5)) {
case 0: case 1:
dy = -1;
break;
case 2:
dy = 0;
break;
case 3: case 4:
dy = 1;
break;
}
}
}
}
xalt = cr.x + SIZERECT * dx;
yalt = cr.y + SIZERECT * dy;
//Rest der Schlange hinterherziehen
Enumeration e = snake.elements();
while (e.hasMoreElements()) {
cr = (ColorRectangle)e.nextElement();
xtmp = cr.x;
ytmp = cr.y;
cr.x = xalt;
cr.y = yalt;
xalt = xtmp;
yalt = ytmp;
}
}
public void paint(Graphics g)
{
ColorRectangle cr;
Enumeration e = snake.elements();
int inleft
= getInsets().left;
int intop
= getInsets().top;
while (e.hasMoreElements()) {
cr = (ColorRectangle)e.nextElement();
g.setColor(cr.color);
g.fillRect(cr.x+inleft,cr.y+intop,cr.width,cr.height);
}
170
171
172
173
174
175
176 }
}
private int rand(int limit)
{
return (int)(Math.random() * limit);
}
Hinweis
Leider besteht die Lsung fr dieses Problem nicht einfach darin, das Lschen zu unterdrcken. Bei einer
animierten Bewegung beispielsweise ist es erforderlich, all die Bestandteile des vorigen Bildes zu lschen, die im
aktuellen Bild nicht mehr oder an einer anderen Stelle angezeigt werden.
Auch wenn paint deshalb aufgerufen wird, weil ein bisher verdeckter Bildausschnitt wieder sichtbar wird, mu
natrlich der entsprechende Bildausschnitt zunchst gelscht werden, um die Bestandteile des anderen Fensters zu
entfernen. Im Grunde ist es also eine ganz vernnftige Vorgehensweise, das Fenster vor jedem Aufruf von paint
zu lschen.
Das Flackern kann nun auf unterschiedliche Weise unterdrckt werden. Die drei gebruchlichsten Methoden sind
folgende:
den Bildschirm gar nicht zu lschen (was - wie zuvor erwhnt - problematisch ist)
nur den wirklich bentigten Teil des Bildschirms zu lschen
das Verfahren der Doppelpufferung anzuwenden
Jedes dieser Verfahren hat Vor- und Nachteile und kann in verschiedenen Situationen unterschiedlich gut
angewendet werden. Wir werden sie in den folgenden Unterabschnitten kurz vorstellen und ein Beispiel fr ihre
Anwendung geben. Es gibt noch einige zustzliche Mglichkeiten, das Flackern zu unterdrcken oder
einzuschrnken, wie beispielsweise das Clipping der Ausgabe auf den tatschlich vernderten Bereich, aber darauf
wollen wir hier nicht nher eingehen.
Bildschirm nicht lschen
Den Bildschirm berhaupt nicht zu lschen, um das Flackern zu unterdrcken, ist nur bei nicht bewegten
Animationen mglich. Wir wollen uns als Beispiel fr ein Programm, das hierfr geeignet ist, das folgende
Lauflicht ansehen:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
Listing3410.java
/* Listing3410.java */
import java.awt.*;
import java.awt.event.*;
public class Listing3410
extends Frame
implements Runnable
{
//Konstanten
private static final int NUMLEDS =
private static final int SLEEP
=
private static final int LEDSIZE =
private static final Color ONCOLOR
private static final Color OFFCOLOR
20;
60;
10;
= new Color(255,0,0);
= new Color(100,0,0);
//Instanzvariablen
private Thread th;
private int switched;
private int dx;
public static void main(String[] args)
{
Listing3410 frame = new Listing3410();
frame.setSize(270,150);
frame.setVisible(true);
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069 }
frame.startAnimation();
}
public Listing3410()
{
super("Leuchtdiodenkette");
setBackground(Color.lightGray);
addWindowListener(new WindowClosingAdapter(true));
}
public void startAnimation()
{
th = new Thread(this);
th.start();
}
public void run()
{
switched = -1;
dx = 1;
while (true) {
repaint();
try {
Thread.sleep(SLEEP);
} catch (InterruptedException e){
//nichts
}
switched += dx;
if (switched < 0 || switched > NUMLEDS - 1) {
dx = -dx;
switched += 2*dx;
}
}
}
public void paint(Graphics g)
{
for (int i = 0; i < NUMLEDS; ++i) {
g.setColor(i == switched ? ONCOLOR : OFFCOLOR);
g.fillOval(10+i*(LEDSIZE+2),80,LEDSIZE,LEDSIZE);
}
}
Hinweis
/* update1.inc */
update1.inc
jeweiligen Vorgngers einnehmen, bleibt als einziges wirklich zu lschendes Element das letzte Element der
Schlange aus dem vorherigen Animationsschritt brig. Dessen Position knnte man sich bei jedem Schritt merken
und im nchsten Schritt in der Hintergrundfarbe neu zeichnen.
Noch einfacher geht es, indem man an die Schlange einfach ein zustzliches unsichtbares Element anhngt. Wird
nmlich das letzte Element grundstzlich in der Hintergrundfarbe dargestellt, hinterlt es keine Spuren auf dem
Bildschirm und braucht damit auch nicht explizit gelscht zu werden! Wir brauchen also nur hinter die for-nextSchleife zur Konstruktion der Schlange ein weiteres, unsichtbares Element an den snake-Vector anzuhngen
(in Listing 34.13 in den Zeilen 025 bis 031 eingefgt):
001 /* Schlange2.inc */
002
003 public void run()
004 {
005
//Schlange konstruieren
006
ColorRectangle cr;
007
int x = 100;
008
int y = 100;
009
for (int i=0; i < NUMELEMENTS; ++i) {
010
cr = new ColorRectangle();
011
cr.x = x;
012
cr.y = y;
013
cr.width = SIZERECT;
014
cr.height = SIZERECT;
015
x += SIZERECT;
016
cr.color = new Color(
017
i*(256/NUMELEMENTS),
018
0,
019
240-i*(256/NUMELEMENTS)
020
);
021
snake.addElement(cr);
022
}
023
024
//Lschelement anhngen
025
cr = new ColorRectangle();
026
cr.x = x;
027
cr.y = y;
028
cr.width = SIZERECT;
029
cr.height = SIZERECT;
030
cr.color = BGCOLOR;
031
snake.addElement(cr);
032
033
//Vorzugsrichtung festlegen
034
dx = -1;
035
dy = -1;
036
037
//Schlange laufen lassen
038
while (true) {
039
repaint();
040
try {
041
Thread.sleep(SLEEP);
042
} catch (InterruptedException e){
043
//nichts
044
}
045
moveSnake();
046
}
Schlange2.inc
047 }
Listing 34.13: Modifizierte Schlangenanimation
Wird nun zustzlich die Methode update berlagert, wie es auch im vorigen Abschnitt getan wurde, luft die
Schlange vollkommen flackerfrei.
Doppelpufferung
Das Doppelpuffern bietet sich immer dann an, wenn die beiden vorigen Methoden versagen. Das kann
beispielsweise dann der Fall sein, wenn es bei einer bewegten Animation zu aufwendig ist, nur den nicht mehr
bentigten Teil der Bildschirmausgabe zu lschen, oder wenn der aktuelle Animationsschritt keine Informationen
darber besitzt, welcher Teil zu lschen ist.
Beim Doppelpuffern wird bei jedem Animationsschritt zunchst die gesamte Bildschirmausgabe in ein OffscreenImage geschrieben. Erst wenn alle Ausgabeoperationen abgeschlossen sind, wird dieses Offscreen-Image auf die
Fensteroberflche kopiert. Im Detail sind dazu folgende Schritte erforderlich:
Das Fensterobjekt beschaft sich durch Aufruf von createImage ein Offscreen-Image und speichert es in
einer Instanzvariablen.
Durch Aufruf von getGraphics wird ein Grafikkontext zu diesem Image beschafft.
Alle Bildschirmausgaben (inklusive Lschen des Bildschirms) gehen zunchst auf den OffscreenGrafikkontext.
Wenn alle Ausgabeoperationen abgeschlossen sind, wird das Offscreen-Image mit drawImage in das
Ausgabefenster kopiert.
Durch diese Vorgehensweise wird erreicht, da das Bild komplett aufgebaut ist, bevor es angezeigt wird. Da beim
anschlieenden Kopieren die neuen Pixel direkt ber die alten kopiert werden, erscheinen dem Betrachter nur die
Teile des Bildes verndert, die auch tatschlich gendert wurden. Ein Flackern, das entsteht, weil Flchen fr
einen kurzen Zeitraum gelscht und dann wieder gefllt werden, kann nicht mehr auftreten.
Die Anwendung des Doppelpufferns ist nicht immer sinnvoll. Sollte eine der anderen Methoden mit
vertretbarem Aufwand implementiert werden knnen, kann es sinnvoller sein, diese zu verwenden.
Nachteilig sind vor allem der Speicherbedarf fr die Konstruktion des Offscreen-Images und die
Verzgerungen durch das doppelte Schreiben der Bilddaten. Hier mu im Einzelfall entschieden werden,
welche Variante zum Einsatz kommen soll. In vielen Fllen allerdings knnen die genannten Nachteile
vernachlssigt werden, und die Doppelpufferung ist ein probates Mittel, um das Bildschirmflackern zu
verhindern.
Tip
Das folgende Programm ist ein Beispiel fr die Anwendung des Doppelpufferns bei der Ausgabe einer bewegten
Animation. Wir wollen uns dafr die Aufabe stellen, eine groe Scheibe ber den Bildschirm laufen zu lassen,
ber deren Rand zwei stilisierte Ameisen mit unterschiedlicher Geschwindigkeit in entgegengesetzte
Richtungen laufen.
Das folgende Programm lst diese Aufgabe. Dabei folgt die Animation unserem bekannten Architekturschema fr
bewegte Grafik und braucht hier nicht weiter erklrt zu werden. Um das Flackern zu verhindern, deklarieren wir
zwei Instanzvariablen, dbImage und dbGraphics:
private Image dbImage;
private Graphics dbGraphics;
Glcklicherweise knnen die zum Doppelpuffern erforderlichen Schritte gekapselt werden, wenn man
die Methode update geeignet berlagert:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
/* update2.inc */
Hinweis
update2.inc
Falls nicht schon geschehen, werden hier zunchst die beiden Variablen dbImage und dbGraphics
initialisiert. Anschlieend wird der Hintergrund gelscht, wie es auch in der Standardversion von update der
Fall ist. Im Gegensatz zu dieser erfolgt das Lschen aber auf dem Offscreen-Image und ist somit fr den
Anwender nicht zu sehen. Nun wird paint aufgerufen und bekommt anstelle des normalen den OffscreenGrafikkontext bergeben. Ohne selbst etwas davon zu wissen, sendet paint damit alle seine Grafikbefehle auf
das Offscreen-Image. Nachdem paint beendet wurde, wird durch Aufruf von drawImage das Offscreen-Image
auf dem Bildschirm angezeigt.
Hier ist der komplette Quellcode des Programms:
001
002
003
004
005
006
007
008
009
010
011
012
/* Listing3415.java */
import java.awt.*;
import java.awt.event.*;
public class Listing3415
extends Frame
implements Runnable
{
private Thread th;
private int actx;
private int dx;
Listing3415.java
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
private
private
private
private
int actarc1;
int actarc2;
Image dbImage;
Graphics dbGraphics;
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099 }
}
//Hintergrund lschen
dbGraphics.setColor(getBackground());
dbGraphics.fillRect(
0,
0,
this.getSize().width,
this.getSize().height
);
//Vordergrund zeichnen
dbGraphics.setColor(getForeground());
paint(dbGraphics);
//Offscreen anzeigen
g.drawImage(dbImage,0,0,this);
}
public void paint(Graphics g)
{
int xoffs = getInsets().left;
int yoffs = getInsets().top;
g.setColor(Color.lightGray);
g.fillOval(xoffs+actx,yoffs+20,100,100);
g.setColor(Color.red);
g.drawArc(xoffs+actx,yoffs+20,100,100,actarc1,10);
g.drawArc(xoffs+actx-1,yoffs+19,102,102,actarc1,10);
g.setColor(Color.blue);
g.drawArc(xoffs+actx,yoffs+20,100,100,360-actarc2,10);
g.drawArc(xoffs+actx-1,yoffs+19,102,102,360-actarc2,10);
}
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Ganz oben befindet sich ein Panel mit einem Label und einem Textfeld, in dem ein Name eingegeben werden kann.
Das Label besitzt neben seiner Beschriftung ein Icon mit einem kleinen blauen Pfeil.
In der Mitte besitzt das Programm eine Liste, aus der ein Monatsname ausgewhlt werden kann.
Unten befindet sich ein Panel mit drei Buttons, mit denen zwischen den drei vordefinierten Look-and-Feels
umgeschaltet werden kann.
Die beiden Panels wurden mit einer Umrandung versehen, und alle aktiven Elemente zeigen Tooltips an, wenn man mit
dem Mauszeiger darauf zeigt. Durch Drcken auf den Schlieen-Button in der Titelleiste kann das Programm beendet
werden. Die brigen Dialogelemente besitzen - neben ihrem Standardverhalten - keinerlei zustzliche Funktionalitt.
Abbildung 35.1 zeigt das Beispielprogramm mit dem Standard-Look-and-Feel von Java-Programmen (das auch als MetalLook-and-Feel bezeichnet wird):
Hinweis
/* Listing3501.java */
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
public class Listing3501
extends JFrame
implements ActionListener
{
private static final String[] MONTHS = {
"Januar",
"Februar", "Mrz",
"April",
"Mai",
"Juni",
"Juli",
"August",
"September", "Oktober", "November", "Dezember"
};
public Listing3501()
{
super("Mein erstes Swing-Programm");
//Panel zur Namenseingabe hinzufgen
JPanel namePanel = new JPanel();
JLabel label = new JLabel(
"Name:",
new ImageIcon("triblue.gif"),
SwingConstants.LEFT
);
namePanel.add(label);
JTextField tf = new JTextField(30);
tf.setToolTipText("Geben Sie ihren Namen ein");
namePanel.add(tf);
namePanel.setBorder(BorderFactory.createEtchedBorder());
getContentPane().add(namePanel, BorderLayout.NORTH);
//Monatsliste hinzufgen
JList list = new JList(MONTHS);
list.setToolTipText("Whlen Sie ihren Geburtsmonat aus");
Listing3501.java
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091 }
In vielen Beispielen in diesem Buch wird der Einfachheit halber die in Abschnitt 23.2.4 vorgestellte Klasse
Hinweis
WindowClosingAdapter verwendet, um einen Listener zum Schlieen des Fensters zu registrieren. Damit
ein solches Beispiel sich kompilieren lt, mu die Datei WindowClosingAdapter.java im aktuellen Verzeichnis
vorhanden sein. Sie befindet sich auf der CD-ROM zum Buch oder in Listing 23.2.
Die import-Anweisungen
Im Gegensatz zu AWT-Programmen besitzt unser Beispielprogramm keine Anweisung, um das Paket java.awt zu
importieren. Da alle Swing-Komponenten in javax.swing und seinen Unterpaketen stecken, bentigen wir diese in
diesem Beispiel nicht. Neben den Dialogelementen gibt es allerdings andere Klassen aus java.awt, die hufig auch in
Swing-Programmen bentigt werden (nur eben in diesem einfachen Beispiel nicht), etwa die Layoutmanager, Color- oder
Font-Objekte oder die Klasse Graphics.
Das Paket java.awt.event wird dagegen regelmig auch in Swing-Programmen bentigt, denn es enthlt die Klassen
fr das Versenden von Events und Registrieren von Event-Handlern. Diese werden in Swing-Programmen in der gleichen
Art und Weise bentigt wie in AWT-Programmen.
JFrame
Das Hauptfenster unserer Anwendung ist aus der Klasse JFrame abgeleitet, die ihrerseits aus Frame abgeleitet ist.
JFrame ist eine der vier Klassen, mit denen in Swing Hauptfenster erzeugt werden knnen (JDialog, JWindow und
JApplet sind die anderen drei). JFrame hat hnliche Eigenschaften wie Frame und wird auch hnlich programmiert
wie diese. Beide unterscheiden sich jedoch in einem bedeutenden Punkt voneinander.
In Zeile 032 ist zu sehen, da eine Komponente nicht einfach mit add auf dem Fenster plaziert wird. Statt dessen wird
zunchst eine Methode getContentPane aufgerufen, und erst auf dem von ihr zurckgegebenen Container wird das
Dialogelement mit add plaziert. Diese - auf den ersten Blick umstndliche - Vorgehensweise ist allen SwingHauptfenstern gemein und wird durch deren innere Struktur verursacht. Der eigentliche Container mit den sichtbaren
Dialogelementen (die ContentPane) ist nmlich Bestandteil einer ganzen Reihe geschachtelter Komponenten, in denen die
brigen Bestandteile des Fensters (etwa dessen Men) untergebracht sind.
Neben add darf auch die Methode setLayout nicht mehr direkt auf dem Hauptfenster aufgerufen werden.
Analog zu add ist statt dessen getContentPane().setLayout() aufzurufen. Ein Versto gegen diese
Regel lst in beiden Fllen eine Exception aus.
Hinweis
JPanel
Die Buttonleiste und das Label mit seinem Textfeld werden im Beispielprogramm auf einem JPanel plaziert. JPanel ist
das Swing-Pendant zu Panel und dient wie dieses dazu, eine Reihe von Dialogelementen unter Kontrolle eines eigenen
Layoutmanagers anzuordnen. Anders als bei den Hauptfenstern gibt es hier keine ContentPane, sondern die
Dialogelemente und der Layoutmanager werden dem Panel direkt zugewiesen. Die Bedienung eines JPanel entspricht
fast vollstndig der eines Panel.
Die Vaterklasse von JPanel ist JComponent. Sie wird - mit Ausnahme der Hauptfenster - als Basisklasse fr Hinweis
praktisch alle Swing-Komponenten verwendet. JComponent ist direkt aus Container abgeleitet und besitzt
daher alle Eigenschaften der Klassen Component und Container. Die technische Unterscheidung zwischen
Dialogelementen mit bzw. ohne Unterelemente wurde damit in Swing fallengelassen. Dennoch ist ein Button im
praktischen Sinne natrlich nach wie vor ein elementares Dialogelement, und es gibt keine sinnvolle
Mglichkeit, weitere Dialogelemente darauf zu plazieren.
JLabel
Ein JLabel ist ein Dialogelement zur Anzeige eines (meist unvernderlichen) Textes. Es wird im Beispielprogramm
verwendet, um das Textfeld im oberen Teil des Fensters zu beschriften. Im Gegensatz zu einem Label kann ein JLabel
zustzlich ein Icon enthalten. Im Beispielprogramm wird es in Zeile 024 direkt an den Konstruktor der Klasse bergeben.
Auf diese Weise ist es sehr einfach mglich, zustzliche grafische Elemente in Swing-Dialogen zu plazieren.
JTextField
Ein JTextField reprsentiert ein einzeiliges Eingabefeld fr Textdaten. Fr einfache Anwendungsflle entspricht seine
Bedienung der Klasse TextField. Unser Beispiel verwendet es zur Eingabe eines Namens. Zur verdeckten Eingabe von
Pawrtern eignet sich die aus JTextField abgeleitete Klasse JPasswordField. Sie entspricht einem TextField
nach Aufruf von setEchoCharacter.
Tooltips und Umrandungen
Einige der Dialogelemente im Beispielprogramm enthalten einen Tooltip-Text. Dieser wird angezeigt, wenn die Maus ber
das Dialogelement bewegt und dort gehalten wird. Ein Tooltip sollte zustzliche Informationen geben, die dem
unerfahrenen Anwender eine Hilfe bei der Bedienung des Programms sind. Tooltips werden mit der Methode
setToolTipText definiert, die alle aus JComponent abgeleiteten Komponenten besitzen.
Eine andere Fhigkeit, die ebenfalls bereits in der Klasse JComponent realisiert wurde, besteht darin, beliebigen
Komponenten eine Umrandung zuweisen zu knnen. Das Beispielprogramm versieht seine beiden Panels mit einem
eingelassenen Rahmen (Zeile 031 und Zeile 051). Dazu ist lediglich ein Aufruf der Methode setBorder mit bergabe
einer Instanz der Klasse Border erforderlich. Diese wird meist mit Hilfe einer der create-Methoden der Klasse
BorderFactory erzeugt.
JList und JScrollPane
Eine JList ist eine Liste von Werten, von denen einer oder mehrere ausgewhlt werden knnen. Ihre Bedienung hnelt
der aus dem AWT bekannten Klasse List; ihre Fhigkeiten gehen aber ber diese hinaus. JList ist ein gutes Beispiel
fr die Aufteilung der Swing-Komponenten in Benutzerschnittstelle und Model. Anstatt - wie in unserem Beispiel - einfach
ein Array von festen Werten an den Konstruktor zu bergeben, knnen mit Hilfe eines Listenmodells (reprsentiert durch
eine Instanz der Klasse ListModel) sehr komplexe und dynamische Datenstrukturen in der Liste prsentiert werden.
Auch die Benutzeroberflche ist vernderbar. Mit Hilfe eigener "Renderer" knnen neben einfachen Texten beispielsweise
auch Grafiken oder andere Objekte angezeigt werden.
In Zeile 036 kann man noch eine Besonderheit sehen. Eine JList bietet standardmig keine Mglichkeit zum Scrollen
ihres Inhalts. Wird sie ohne weitere Manahmen auf dem Panel plaziert, knnen nur Elemente aus dem sichtbaren Bereich
ausgewhlt werden. Dieses Eigenschaft teilt sie mit anderen komplexen Swing-Elementen. Glcklicherweise gibt es eine
einfache Lsung fr dieses Problem. Die Liste ist nmlich lediglich in eine JScrollPane einzubetten, die an ihrer Stelle
auf dem Dialog plaziert wird. Dieses vielseitige Dialogelement versieht Dialogelemente, die mehr Platz bentigen, als auf
dem Bildschirm zur Verfgung steht, mit Schiebereglern und kmmert sich um alle Details des Scrollens der Daten.
JButton
Als letztes Dialogelement in unserem Beispiel wird die Klasse JButton verwendet. Sie dient zur Darstellung von
Schaltflchen und erbt die meisten Eigenschaften aus ihrer Vaterklasse AbstractButton (die ihre Eigenschaften
brigens auch an Meneintrge vererbt). Wie im Programm zu sehen, entsprechen die Grundlagen der Bedienung von
JButton denen der Klasse Button.
Umschalten des Look-and-Feel
Wie in Abschnitt 35.1.2 erwhnt, kann das Look-and-Feel von Swing-Programmen zur Laufzeit umgeschaltet werden. Im
Beispielprogramm ist das ab Zeile 061 zu sehen. Abhngig davon, welcher Button gedrckt wurde, wird der Variablen
plaf der Klassenname einer Look-and-Feel-Implementierung zugewiesen. Durch Aufruf der Methode
setLookAndFeel wird der UIManager angewiesen, das Look-and-Feel umzuschalten. Damit die nderungen auch
bei bereits auf dem Bildschirm befindlichen Dialogelementen sichtbar werden, wird anschlieend durch Aufruf von
updateComponentTreeUI der Klasse SwingUtilities der gesamte Dialog neu dargestellt.
Schlgt der Aufruf von setLookAndFeel fehl (was je nach Plattform durchaus passieren kann), lst die
Methode eine Ausnahme des Typs UnsupportedLookAndFeelException oder eine der
Instanzierungsausnahmen aus. Das Programm sollte diese Ausnahme abfangen, das bestehende Look-and-Feel
aktiviert lassen und den Anwender darber in Kenntnis setzen.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Hinweis
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
36.1 Hauptfenster
36.1 Hauptfenster
36.1.1 JFrame
RootPane, LayeredPane und ContentPane
Dialogelemente plazieren
36.1.2 JWindow
36.1.3 JDialog
36.1.4 JOptionPane
Anzeigen einer Nachricht
Eingabe einer Besttigung
Eingabe von Daten
36.1.5 JApplet
36.1.6 JInternalFrame
Der Desktop
Die Kindfenster
Beispielprogramm
36.1.1 JFrame
Die Klasse JFrame ist die wichtigste Hauptfensterklasse in Swing. Sie ist aus java.awt.Frame abgeleitet und stellt ein
Hauptfenster mit Rahmen, Systemmen und Standardschaltflchen zur Verfgung. Ein JFrame kann ebenso einfach instanziert
werden wie ein Frame:
public JFrame()
public JFrame(String title)
javax.swing.JFrame
Der parameterlose Konstruktor erzeugt ein Hauptfenster mit einer leeren Titelzeile, der andere schreibt den bergebenen String
hinein. Da JFrame aus Frame abgeleitet ist, stehen alle Methoden aus Window, Container und Component zur Verfgung.
Ein einfaches Beispielprogramm mit einem leeren Hauptfenster, das sich ber den Schlieen-Button beenden lt, knnen wir
(analog zu den Beispielen in Kapitel 31) wie folgt realisieren:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
/* Listing3601.java */
import javax.swing.*;
import java.awt.event.*;
public class Listing3601
extends JFrame
{
public Listing3601()
{
super("Ein einfacher JFrame");
addWindowListener(new WindowClosingAdapter(true));
}
public static void main(String[] args)
{
Listing3601 wnd = new Listing3601();
wnd.setLocation(100, 100);
wnd.setSize(300, 200);
Listing3601.java
020
wnd.setVisible(true);
021
}
022 }
Listing 36.1: Verwendung der Klasse JFrame
Das Programm erzeugt ein Hauptfenster der Gre 300 * 200 an der Bildschirmposition (100, 100). In der Titelzeile steht "Ein
einfacher JFrame", und das Programm lt sich durch einen Klick auf den Schlieen-Button beenden:
weitergegeben werden.
Die LayeredPane enthlt das Men und die Dialogelemente der Anwendung. Als Instanz der Klasse JLayeredPane verfgt sie
ber die Fhigkeit, Dialogelemente nicht nur neben-, sondern in kontrollierter Weise auch bereinander anzuordnen. Das ist
beispielsweise wichtig, um Mens oder interne Dialoge ber den Komponenten anzuzeigen, die sie verdecken. Tatschlich verdeckt
das in der LayeredPane gehaltene Men die in seiner ContentPane plazierten Dialogelemente der Anwendung.
Das hrt sich alles sehr kompliziert an, und mancher wird sich fragen, ob soviel Aufwand wirklich ntig war. Glcklicherweise
braucht eine Swing-Anwendung sich um die Details gar nicht zu kmmern. Einerseits wird die RootPane, und mit ihr die darin
enthaltene GlassPane, LayeredPane und ContentPane, beim Anlegen des Fensters automatisch erzeugt (einzig die Menleiste bleibt
standardmig leer). Zweitens implementieren alle Hauptfenster das Interface RootPaneContainer, das den Zugriff auf die
RootPane vereinfacht. Einige seiner Methoden sind:
public
public
public
public
JRootPane getRootPane()
Container getContentPane()
JLayeredPane getLayeredPane()
Component getGlassPane()
javax.swing.RootPaneContainer
Um auf einem Hauptfenster Komponenten zu plazieren, ist es also nicht ntig, zunchst mit getRootPane die RootPane, dann
mit getLayeredPane die LayeredPane und schlielich mit getContentPane die ContentPane zu beschaffen, sondern es kann
direkt getContentPane aufgerufen werden. Neben den getter-Methoden gibt es auch setter-Methoden, mit denen der
strukturelle Aufbau der RootPane vollstndig verndert werden kann. Darauf wollen wir aber nicht weiter eingehen.
Dialogelemente plazieren
Das Einfgen und Anordnen von Dialogelementen auf einem Hauptfenster erfolgt also ber dessen ContentPane. Die Aufrufe von
add und setLayout werden damit nicht direkt auf dem Fenster ausgefhrt, sondern auf dessen ContentPane, die ber einen
Aufruf von getContentPane beschafft werden kann.
Um das zu demonstrieren, wollen wir das vorige Beispiel um drei Buttons erweitern, die mit Hilfe eines GridLayout der Gre 3
* 1 angeordnet werden:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
/* Listing3602.java */
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Listing3602
extends JFrame
{
public Listing3602()
{
super("Ein einfacher JFrame");
//WindowListener hinzufgen
addWindowListener(new WindowClosingAdapter(true));
//Layout setzen und Buttons hinzufgen
Container contentPane = getContentPane();
contentPane.setLayout(new GridLayout(3, 1));
contentPane.add(new JButton("Button 1"));
contentPane.add(new JButton("Button 2"));
contentPane.add(new JButton("Button 3"));
}
public static void main(String[] args)
{
Listing3602 wnd = new Listing3602();
wnd.setLocation(100, 100);
wnd.setSize(300, 200);
wnd.setVisible(true);
}
}
Listing3602.java
Warnung
36.1.2 JWindow
Die zweite Hauptfensterklasse, die wir in diesem Kapitel vorstellen, ist JWindow. Sie ist aus Window abgeleitet und dient wie
diese dazu, ein rahmenloses Fenster zu erzeugen, das an beliebiger Stelle und in beliebiger Gre auf dem Bildschirm plaziert
werden kann. JWindow besitzt drei Konstruktoren:
public JWindow()
public JWindow(Frame owner)
public JWindow(Window owner)
javax.swing.JWindow
Ebenso wie JFrame besitzt auch JWindow eine RootPane mit der im vorigen Abschnitt beschriebenen Struktur. Dialogelemente
und Layoutmanager werden also nicht direkt auf dem Fenster, sondern auf der ContentPane plaziert. Zur einfacheren Verwendung
implementiert auch JWindow das Interface RootPaneContainer.
Als Beispiel fr die Anwendung von JWindow wollen wir einen einfachen SplashScreen konstruieren, also ein Fenster, das nach
dem Start eines Programmes angezeigt wird und nach dessen Initialisierung wieder entfernt wird. Dazu leiten wir eine Klasse
SplashScreen von JWindow ab und plazieren darin ein Icon und einen Text. Der Name der Icondatei und der Text knnen an
den Konstruktor bergeben werden. Das Programm soll jeweils ein Drittel der verfgbaren Hhe und Breite des Bildschirms
belegen und das Icon und den Text zentriert darin anzeigen.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
/* SplashScreen.java */
import
import
import
import
javax.swing.*;
javax.swing.border.*;
java.awt.*;
java.awt.event.*;
SplashScreen.java
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050 }
);
Border bd2 = BorderFactory.createEtchedBorder();
Border bd3 = BorderFactory.createCompoundBorder(bd1, bd2);
((JPanel)contentPane).setBorder(bd3);
ImageIcon icon = new ImageIcon(image);
contentPane.add(new JLabel(" ", JLabel.CENTER), BorderLayout.NORTH);
contentPane.add(new JLabel(icon, JLabel.CENTER), BorderLayout.CENTER);
contentPane.add(new JLabel(text, JLabel.CENTER), BorderLayout.SOUTH);
setContentPane(contentPane);
}
public void showFor(int millis)
{
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
setLocation(dim.width / 3, dim.height / 3);
setSize(dim.width / 3, dim.height / 3);
setVisible(true);
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
}
setVisible(false);
}
public static void main(String[] args)
{
SplashScreen intro = new SplashScreen(
"mine.gif",
"(C) Copyright 2000, J. Krger, All Rights Reserved"
);
intro.showFor(3000);
System.exit(0);
}
der Methoden createBevelBorder und createEtchedBorder der Klasse BorderFactory zwei unabhngige
Umrandungen konstruiert. Durch Aufruf von createCompoundBorder werden sie zu einer neuen Umrandung zusammengefat
und anschlieend an den ContentPane bergeben.
36.1.3 JDialog
Neben einem oder mehreren langlebigen Hauptfenstern besitzt eine Anwendung meist auch Dialogfenster. Sie werden oft nur
vorbergehend aufgerufen, um eine temporre Kommunikation zwischen Anwender und Programm zu etablieren. Dialogfenster
unterscheiden sich meist dadurch von Hauptfenstern, da sie kein Men und nur eingeschrnkte Systemfunktionen besitzen. Zudem
ist ihre Gre oft nicht vernderbar, und sie halten (als modale Fenster) whrend der eigenen Ausfhrung den Programmflu im
brigen Programm an.
Mit der aus Dialog abgeleiteten Klasse JDialog stehen auch in Swing Dialogfenster zur Verfgung. Sie besitzen denselben
strukturellen Aufbau wie JFrame und JWindow und implementieren ebenfalls das Interface RootPaneContainer. Auch hier
erfolgt also das Hinzufgen und Anordnen von Komponenten nicht auf dem Fenster selbst, sondern auf seiner ContentPane.
JDialog besitzt eine Vielzahl von Konstruktoren. Die wichtigsten sind:
public
public
public
public
JDialog(Frame
JDialog(Frame
JDialog(Frame
JDialog(Frame
owner)
owner, boolean modal)
owner, String title)
owner, String title, boolean modal)
javax.swing.JDialog
Als owner sollte der Aufrufer dabei das Fenster bergeben, zu dem der Dialog logisch gehrt. Alle Konstruktoren gibt es auch in
einer Form, bei der der owner vom Typ Dialog ist. Wahlweise kann ein JDialog auch ohne owner konstruiert werden (mit
dem parameterlosen Konstruktor), doch dann kann es unter Umstnden Fokusprobleme beim Wechsel zwischen mehreren
Anwendungen geben.
Die brigen Parameter geben den Titel des Dialogs an und legen fest, ob er modal oder nicht-modal sein soll. Bei einem modalen
Dialog wird der Aufruf von show (bzw. setVisible(true)) erst dann beendet, wenn der Dialog geschlossen wurde. Bei
einem nicht-modalen Dialog fhrt das Programm dagegen unmittelbar mit der nchsten Anweisung hinter show fort.
36.1.4 JOptionPane
Eine weitere (und noch dazu sehr bequeme) Mglichkeit, Swing-Dialoge zu erzeugen, steht mit der Klasse JOptionPane zur
Verfgung. Diese ist in der Lage, einfache Dialoge, die lediglich ein Icon und einen Text oder ein Eingabefeld und eine Auswahl
der Buttons "Yes", "No" und "Cancel" enthalten, mit einem einzigen Aufruf einer statischen Methode zu erzeugen. JOptionPane
ist sehr vielseitig, wir wollen uns an dieser Stelle allerdings auf ihre wichtigsten Anwendungen beschrnken.
Anzeigen einer Nachricht
In seiner einfachsten Form kann JOptionPane dazu verwendet werden, ein Dialogfenster mit Titel, Icon und Hinweistext
anzuzeigen, das mit Hilfe eines "OK"-Buttons beendet werden kann. Dazu stehen verschiedene Varianten der Methode
showMessageDialog zur Verfgung:
public static void showMessageDialog(
Component parentComponent,
Object message
)
public static void showMessageDialog(
Component parentComponent,
Object message,
String title,
int messageType
)
public static void showMessageDialog(
Component parentComponent,
Object message,
String title,
int messageType,
javax.swing.JOptionPane
Icon icon
)
Aufrufe von showMessageDialog sind grundstzlich modal. Erst wenn der Dialog vom Anwender beendet wurde, wird die
nchste Anweisung ausgefhrt. Der Parameter parentComponent bezeichnet die Vaterkomponente, aus der heraus der Dialog
aufgerufen wurde, und message ist der anzuzeigende Text. Soll eine eigene Titelzeile angegeben werden, kann dazu title
verwendet werden, andernfalls wird "Message" angezeigt. Wahlweise kann mit dem Parameter icon ein Icon angegeben werden,
das neben dem Text angezeigt wird. Alternativ kann auch durch bergabe einer der Konstanten ERROR_MESSAGE,
INFORMATION_MESSAGE, WARNING_MESSAGE, QUESTION_MESSAGE oder PLAIN_MESSAGE an den Parameter
messageType ein Standard-Icon erzeugt werden:
javax.swing.JOptionPane
Icon icon
)
Die Parameter entsprechen im wesentlichen denen der Methode showMessageDialog. Zustzlich kann mit optionType eine
der Konstanten YES_NO_OPTION, OK_CANCEL_OPTION oder YES_NO_CANCEL_OPTION angegeben werden, um zu
entscheiden, welche Kombination von Buttons der Dialog anzeigen soll:
Bedeutung
YES_OPTION
NO_OPTION
CANCEL_OPTION
OK_OPTION
CLOSED_OPTION
javax.swing.JOptionPane
Icon icon,
Object[] selectionValues,
Object initialSelectionValue
)
In der einfachsten Form werden lediglich ein Dialog mit dem Titel "Input", die angegebene Nachricht mit einem Icon und das
Textfeld zur Eingabe der Daten angezeigt. Die zweite Variante erlaubt zustzlich die Angabe der Titelzeile und die Auswahl des
Icontyps. In der letzten Variante wird anstelle des Textfelds eine Combobox zur Dateneingabe verwendet. Der Parameter
selectionValues gibt die in der Combobox anzuzeigenden Elemente an (sie werden mit toString in Strings konvertiert),
und initialSelectionValue gibt an, welches von ihnen standardmig selektiert werden soll.
Bei allen Methoden ist der Rckgabewert ein String mit den vom Anwender eingegebenen Daten. Falls eine Combobox zur
Auswahl verwendet wurde, wird der String-Wert des ausgewhlten Elements zurckgegeben. Ein Rckgabewert von null zeigt
an, da der Anwender den Dialog abgebrochen hat.
Das folgende Programm zeigt eine einfache Anwendung von JOptionPane, bei der der Anwender einen Wert aus einer
vordefinierten Liste auswhlen kann:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
/* Listing3604.java */
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Listing3604
extends JFrame
{
private static final String[] QUARTALE = {
"1. Quartal", "2. Quartal", "3. Quartal", "4. Quartal"
};
public Listing3604()
{
super("Test von JOptionPane");
addWindowListener(new WindowClosingAdapter(true));
}
public static void main(String[] args)
{
Listing3604 wnd = new Listing3604();
wnd.setLocation(100, 100);
wnd.setSize(300, 200);
wnd.setVisible(true);
String ret = (String)JOptionPane.showInputDialog(
wnd,
"Whlen Sie das Quartal aus",
"JOptionPane.showInputDialog",
JOptionPane.QUESTION_MESSAGE,
null,
QUARTALE,
QUARTALE[2]
);
System.out.println("Ausgewhlt wurde " + ret);
}
}
Listing3604.java
36.1.5 JApplet
Die Klasse JApplet ist eine einfache Erweiterung von java.applet.Applet. Sie dient zur Entwicklung von Applets, die
Swing-Dialogelemente zur Gestaltung der Oberflche verwenden. Die Unterschiede zwischen beiden Klassen sind nicht sehr
umfangreich; insbesondere werden die Methoden init, start, stop und destroy in derselben Weise verwendet wie bei der
Klasse Applet.
Wie bei den anderen Hauptfenstern ist auch hier der wichtigste Unterschied, da JApplet die in Abschnitt 36.1.1 beschriebene
RootPane-Struktur realisiert. Die Klasse implementiert ebenfalls das RootPaneContainer-Interface, und alle Komponenten
mssen an die ContentPane bergeben werden. Da die Applet-Programmierung ab Kapitel 39 ausfhrlich beschrieben wird, wollen
wir uns an dieser Stelle auf ein einfaches Beispiel beschrnken:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
/* JAppletTest.java */
JAppletTest.java
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class JAppletTest
extends JApplet
{
public void init()
{
Container contentPane = getContentPane();
contentPane.setLayout(new GridLayout(3, 1));
contentPane.add(new JButton("Button 1"));
contentPane.add(new JButton("Button 2"));
contentPane.add(new JButton("Button 3"));
}
}
<html>
<head><title>JAppletTest</title></head>
<body>
<h1>JAppletTest</h1>
<applet code="JAppletTest.class" width=300 height=200>
Hier steht das JAppletTest
</applet>
</body>
</html>
japplet.html
appletviewer japplet.html
36.1.6 JInternalFrame
Bei vielen Programmen ist es blich, da sie ein einziges Hauptfenster besitzen und ihre zahlreichen, gleichzeitig geffneten
Kindfenster innerhalb dieses Hauptfensters anordnen. Diese unter Windows als MDI (Multiple Document Interface) bezeichnete
Technik ist bei bestimmten Typen von Anwendungen mittlerweile weitverbreitet (z.B. bei Textverarbeitungen, Grafikprogrammen
oder Entwicklungsumgebungen).
Whrend im AWT keine Mglichkeit vorgesehen war, MDI-Anwendungen zu entwickeln, ist es in Swing recht einfach. Dazu
werden lediglich zwei Arten von Komponenten bentigt:
Der Desktop
Als Desktop ist prinzipiell jede beliebige Hauptfensterklasse geeignet, meist wird aber die Klasse JFrame verwendet. Um die
Kindfenster zu verwalten, wird die vordefinierte ContentPane durch eine Instanz der Klasse JDesktopPane ersetzt. Diese von
JLayeredPane abgeleitete Klasse besitzt einen DesktopManager, der fr die Verwaltung der Kindfenster zustndig ist. Der
DesktopManager wird beispielsweise benachrichtigt (und fhrt alle dazu erforderlichen Aktionen aus), wenn ein Kindfenster
verkleinert, vergrert oder verschoben werden soll.
DesktopManager ist ein Interface, das eine Vielzahl von Methoden enthlt. Mit der Klasse
DefaultDesktopManager gibt es eine Standardimplementierung, die fr viele Zwecke ausreichend ist.
Hinweis
Die Kindfenster
Die Kindfenster werden aus JInternalFrame abgeleitet; einem Dialogelement, das recht berzeugend vortuscht, ein
Hauptfenster zu sein. Tatschlich ist JInternalFrame direkt aus JComponent abgeleitet und nicht aus JFrame, wie man es
vielleicht vermuten knnte. Der Grund liegt darin, da JFrame als Frame stets eine betriebssystemspezifische Fensterressource
besitzt und damit fr die Verwendung innerhalb der Grenzen eines anderen Fensters ungeeignet ist.
Eine betriebssystemspezifische Ressource fr MDI-Kindfenster steht aber nicht auf allen grafischen Oberflchen zur Verfgung,
und so haben sich die Swing-Entwickler entschlossen, JInternalFrame als leichtgewichtige Komponente selbst zu realisieren.
Das hat einige Konsequenzen:
Die blichen Ausstattungsmerkmale und Bedienelemente eines Fensters mssen nachgebildet werden (Rahmen, Titelleiste,
Systemmen, Buttons, das Verschieben des Fensters und das Verndern seiner Gre usw.).
Die Programmierschnittstelle der Klasse Frame mu nachgebildet werden.
Da JInternalFrame nicht aus Frame abgeleitet ist, kann ein JInternalFrame-Objekt nicht verwendet werden,
wenn eine Variable oder ein Parameter vom Typ Frame gefordert ist.
Mu eine Anwendung sowohl in einem Frame als auch in einem JInternalFrame laufen knnen, so mu der Code fr
die Implementierung des Fensters in beiden Klassen verfgbar gemacht werden. Vererbung kommt wegen der
unterschiedlichen Vererbungslinien beider Klassen nicht in Frage. Als Lsung fr dieses Problem knnte das DelegatePattern aus Abschnitt 10.3.6 angewendet werden.
Um die Kindfenster zu aktivieren, mssen sie durch Aufruf von setVisible sichtbar gemacht und mit add auf
dem Desktop plaziert werden. Bis zum JDK 1.2 war der Aufruf von setVisible nicht unbedingt erforderlich,
denn ein JInternalFrame war standardmig sichtbar. Zur Angleichung an die anderen Hauptfensterklassen
wurde dies mit dem JDK 1.3 gendert.
JDK1.1-1.4
JInternalFrame besitzt diverse Konstruktoren. Das nachfolgende Syntaxdiagramm zeigt nur den umfangreichsten von ihnen.
Alle anderen ergeben sich, indem man auf der rechten Seite nicht bentigte Argumente wegfallen lt:
public JInternalFrame(
String title,
boolean resizable,
boolean closable,
boolean maximizable,
boolean iconifiable
)
javax.Swing.JInternalFrame
JInternalFrame implementiert das schon erluterte Interface RootPaneContainer und stellt zustzlich verschiedene
Methoden zur Verfgung. Einige von ihnen sind:
public
public
public
public
void
void
void
void
setClosable(boolean b)
setResizable(boolean b)
setIconifiable(boolean b)
setMaximizable(boolean b)
javax.Swing.JInternalFrame
setClosable entscheidet, ob das Fenster geschlossen, und setResizable, ob seine Gre verndert werden kann.
setIconifiable legt fest, ob das Fenster in ein Symbol verkleinert, und setMaximizable, ob es maximiert werden kann.
Mit setTitle kann der Rahmentitel angegeben werden, und mit setDefaultCloseOperation wird angegeben, wie das
Fenster sich beim Schlieen verhalten soll. Hier kann eine der Konstanten DO_NOTHING_ON_CLOSE, HIDE_ON_CLOSE oder
DISPOSE_ON_CLOSE angegeben werden.
Beispielprogramm
Nach diesen Vorbemerkungen wollen wir ein einfaches Beispielprogramm erstellen. Es soll einen JFrame als Desktop mit zwei
Kindfenstern enthalten. Diese sollen alle Standardbedienelemente enthalten, vergrer- und verkleinerbar sein und innerhalb des
Desktops verschoben werden knnen.
Wir erstellen dazu eine Klasse DesktopFrame, die mit einem DefaultDesktopManager ein Desktop aufbaut. Mit ihrer
Methode addChild kann ein Kindfenster an einer bestimmten Position auf dem Desktop plaziert werden. Weiterhin definieren wir
eine aus JInternalFrame abgeleitete Klasse ChildFrame. Sie legt im Konstruktor die Eigenschaften des Fensters fest, hat
darber hinaus aber keine Funktionalitten.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
/* Listing3607.java */
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class DesktopFrame
extends JFrame
{
private JDesktopPane desk;
public DesktopFrame()
{
super("DesktopFrame");
this.desk = new JDesktopPane();
desk.setDesktopManager(new DefaultDesktopManager());
setContentPane(desk);
addWindowListener(new WindowClosingAdapter(true));
}
public void addChild(JInternalFrame child, int x, int y)
{
child.setLocation(x, y);
child.setSize(200, 150);
Listing3607.java
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
child.setDefaultCloseOperation(
JInternalFrame.DISPOSE_ON_CLOSE
);
desk.add(child);
child.setVisible(true);
}
}
class ChildFrame
extends JInternalFrame
{
public ChildFrame(String title)
{
super("Child " + title, true, true);
setIconifiable(true);
setMaximizable(true);
setBackground(Color.lightGray);
}
}
public class Listing3607
{
public static void main(String[] args)
{
//Desktop erzeugen
DesktopFrame desktop = new DesktopFrame();
desktop.setLocation(100, 100);
desktop.setSize(400, 300);
desktop.setVisible(true);
//Zwei ChildFrames hinzufgen
desktop.addChild(new ChildFrame("1"), 10, 10);
desktop.addChild(new ChildFrame("2"), 20, 20);
}
}
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
36.2 Mens
36.2 Mens
36.2.1 Einfache Mens
36.2.2 Grundlagen von Swing-Mens
JMenuBar
JMenu
JMenuItem
36.2.3 Weitere Mglichkeiten
Untermens
Icons in Meneintrgen
Checkboxes und Radiobuttons in Meneintrgen
36.2.4 Kontextmens
javax.swing.JMenuBar
Swing-Mens und ihre Bestandteile haben starke hnlichkeit mit den korrespondierenden Konzepten im AWT.
Viele der in Kapitel 30 erluterten Eigenschaften gelten damit analog fr Swing-Mens.
Hinweis
JMenu
Die einzelnen Mens einer Menleiste sind Instanzen der Klasse JMenu, die aus JMenuItem abgeleitet ist. Ihre
wichtigsten Methoden sind:
public JMenu(String s)
javax.swing.JMenu
Der Konstruktor erzeugt ein neues Men mit dem angegebenen Namen. Mit add werden Meneintrge hinzugefgt. Im
allgemeinen Fall sind das Instanzen der Klasse JMenuItem, die auf vielfltige Weise konfiguriert werden knnen. Es ist
aber auch mglich, lediglich Strings an add zu bergeben. Dadurch wird ein einfacher Meneintrag dieses Namens
angefgt. Mit addSeparator wird eine Trennlinie hinter dem letzten Menpunkt angefgt.
Fr OO-Neulinge ist es schwer zu verstehen, warum ein Men die Ableitung eines Meneintrags ist. Eine
Hinweis
einfache Erklrung ist, da es mglich sein soll, Mens ineinander zu schachteln. Da also ein Element eines
Mens, das ja per Definition ein Meneintrag ist, wahlweise auch ein Men enthalten soll. Das kann nur erreicht
werden, wenn Men und Meneintrag zuweisungskompatibel, also voneinander abgeleitet, sind. Eine
ausfhrlichere Erklrung findet sich bei der Besprechung des Composite-Patterns in Abschnitt 10.3.7.
JMenuItem
Die Klasse JMenuItem reprsentiert Meneintrge, also Elemente, die sich in einem Men befinden. Dabei handelt es
sich um Texte, die wahlweise mit einem Icon oder einem Hkchen versehen werden knnen. Da JMenu aus JMenuItem
abgeleitet ist, kann ein Men wiederum Mens als Eintrge enthalten. Auf diese Weise lassen sich Mens schachteln. Die
wichtigsten Eigenschaften von JMenuItem sind:
public JMenuItem(String text)
public JMenuItem(String text, Icon icon)
public JMenuItem(String text, int mnemonic)
javax.swing.JMenuItem
Der an den Konstruktor bergebene String text legt den Mentext fest. Wahlweise kann zustzlich ein Icon bergeben
werden, das neben dem Mentext angezeigt wird. Mit setMnemonic wird das mnemonische Krzel des Meneintrags
festgelegt. Das ist ein (unterstrichen dargestellter) Buchstabe innerhalb des Mentexts, der bei geffnetem Men gedrckt
werden kann, um den Meneintrag per Tastatur aufzurufen. Wahlweise kann das Krzel auch schon an den Konstruktor
bergeben werden. Mit getMnemonic kann es abgefragt werden.
Neben den mnemonischen Krzeln gibt es eine weitere Mglichkeit, Meneintrge ber Tastenkrzel aufzurufen. Diese als
Acceleratoren oder Beschleunigertasten bezeichneten Tasten knnen auch dann verwendet werden, wenn das entsprechende
Men nicht geffnet ist. Beschleuniger werden mit setAccelerator zugewiesen. Sie bestehen aus einer Kombination
von normalen Tasten und Umschalttasten, die durch ein KeyStroke-Objekt beschrieben werden.
Instanzen der Klasse KeyStroke werden ausschlielich mit ihrer Factory-Methode getKeyStroke erzeugt:
public static KeyStroke getKeyStroke(char keyChar)
public static KeyStroke getKeyStroke(int keyCode, int modifiers)
javax.swing.KeyStroke
In der einfachsten Form wird lediglich ein einzelnes Zeichen bergeben. In diesem Fall reprsentiert der erzeugte
KeyStroke genau die entsprechende Taste. Soll diese zusammen mit Umschalttasten gedrckt werden, mu die
gewnschte Kombination an den Parameter modifiers bergeben werden:
Konstante
Bedeutung
SHIFT_MASK
[UMSCHALT]
CTRL_MASK
[STRG]
META_MASK
ALT_MASK
[ALT]
/* Listing3608.java */
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Listing3608
extends JFrame
implements ActionListener
{
public Listing3608()
{
super("Swing-Mentest");
addWindowListener(new WindowClosingAdapter(true));
JMenuBar menubar = new JMenuBar();
menubar.add(createFileMenu());
setJMenuBar(menubar);
}
public void actionPerformed(ActionEvent event)
{
System.out.println(event.getActionCommand());
}
//---Private Methoden--------------private JMenu createFileMenu()
{
JMenu ret = new JMenu("Datei");
ret.setMnemonic('D');
JMenuItem mi;
//ffnen
mi = new JMenuItem("ffnen", 'f');
Listing3608.java
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065 }
setCtrlAccelerator(mi, 'O');
mi.addActionListener(this);
ret.add(mi);
//Speichern
mi = new JMenuItem("Speichern", 'p');
setCtrlAccelerator(mi, 'S');
mi.addActionListener(this);
ret.add(mi);
//Separator
ret.addSeparator();
//Beenden
mi = new JMenuItem("Beenden", 'e');
mi.addActionListener(this);
ret.add(mi);
return ret;
}
private void setCtrlAccelerator(JMenuItem mi, char acc)
{
KeyStroke ks = KeyStroke.getKeyStroke(
acc, Event.CTRL_MASK
);
mi.setAccelerator(ks);
}
public static void main(String[] args)
{
Listing3608 frame = new Listing3608();
frame.setLocation(100, 100);
frame.setSize(300, 200);
frame.setVisible(true);
}
Icons in Meneintrgen
Einem Meneintrag kann auch ein Icon zugeordnet werden. Dazu kann ein Icon-Objekt entweder direkt an den
Konstruktor von MenuItem bergeben werden, oder es kann spter durch Aufruf von setIcon zugewiesen werden.
Icon ist ein Interface, das die abstrakten Eigenschaften eines Icons definiert. Es besitzt eine Implementierung
ImageIcon, mit der sehr einfach aus einer gif- oder jpeg-Datei ein Icon erzeugt werden kann:
public ImageIcon(String filename)
javax.swing.ImageIcon
Standardmig wird das Icon eines Meneintrags links von seiner Beschriftung plaziert. Um es anders anzuordnen,
kann auf dem Meneintrag die Methode setHorizontalTextPosition aufgerufen und eine der Konstanten
RIGHT, LEFT, CENTER, LEADING oder TRAILING aus dem Interface SwingConstants bergeben werden.
SwingConstants ist ein Interface im Paket javax.swing, das eine Reihe von Konstanten definiert, mit
denen die Position und Orientierung von Swing-Komponenten beschrieben werden kann. Wir werden die
Konstanten nher beschreiben, wenn sie als Parameter einer Methode auftauchen. Tabelle 36.3 listet alle
Elemente berblicksartig auf:
Konstante
Bedeutung
BOTTOM
Unten
CENTER
An zentraler Position
EAST
Im Osten (rechts)
Tip
Hinweis
LEFT
Links
NORTH
Im Norden (oben)
Rechts
SOUTH
Im Sden (unten)
Oben
TRAILING
VERTICAL
Vertikal ausgerichtet
WEST
Im Westen (links)
public
public
public
public
JCheckBoxMenuItem(String
JCheckBoxMenuItem(String
JCheckBoxMenuItem(String
JCheckBoxMenuItem(String
text)
text, Icon icon)
text, boolean b)
text, Icon icon, boolean b)
javax.swing.JCheckBoxMenuItem
Im Gegensatz zu einem JCheckBoxMenuItem wird ein JRadioButtonMenuItem immer dann verwendet, wenn von
mehreren Buttons nur einer zur Zeit aktiviert werden soll. Die wichtigsten Konstruktoren beider Klassen sind identisch:
public
public
public
public
JRadioButtonMenuItem(String
JRadioButtonMenuItem(String
JRadioButtonMenuItem(String
JRadioButtonMenuItem(String
javax.swing.JRadioButtonMenuItem
text)
text, Icon icon)
text, boolean b)
text, Icon icon, boolean b)
Die Kontrolle des Zustands der Buttons erfolgt mit einem ButtonGroup-Objekt. Es wird vor dem Erzeugen der
Meneintrge angelegt, und jeder JRadioButtonMenuItem wird mit add hinzugefgt:
public void add(AbstractButton b)
javax.swing.ButtonGroup
Das ButtonGroup-Objekt sorgt automatisch dafr, da zu jedem Zeitpunkt genau ein Eintrag selektiert ist. Auf den
aktuellen Zustand jedes Meneintrags kann mit Hilfe der Methoden isSelected und setSelected zugegriffen
werden:
public boolean isSelected()
public void setSelected(boolean b)
javax.swing.JRadioButtonMenuItem
Das folgende Programm zeigt alle weiterfhrenden Mglichkeiten im berblick. Es definiert ein Men "Extras", dessen
oberster Eintrag "Tools" ein Untermen mit sechs weiteren Eintrgen ist. Darunter befinden sich zwei Checkbox- und drei
RadioButton-Meneintrge, die durch Anklicken aktiviert werden knnen. Der letzte Meneintrag "Sicherheit" enthlt
zustzlich ein Icon, das ein geffnetes Vorhngeschlo zeigt.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
/* Listing3609.java */
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Listing3609
extends JFrame
{
public Listing3609()
{
super("Swing-Mentest II");
addWindowListener(new WindowClosingAdapter(true));
JMenuBar menubar = new JMenuBar();
menubar.add(createExtrasMenu());
setJMenuBar(menubar);
}
//---Private Methoden--------------private JMenu createExtrasMenu()
{
Listing3609.java
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
'R'));
'E'));
'B'));
'Z'));
'S'));
'V'));
083
frame.setSize(300, 200);
084
frame.setVisible(true);
085
}
086 }
Listing 36.9: Weitere Mglichkeiten von Swing-Mens
Mit geffneten Mens stellt sich das Programm so dar:
36.2.4 Kontextmens
Kontextmens fr AWT-Programme wurden bereits in Abschnitt 30.6 ausfhrlich besprochen. In Swing-Programmen
werden Kontextmens mit Hilfe der Klasse JPopupMenu erzeugt. Sie kann parameterlos instanziert werden und stellt
Methoden hnlich JMenu zur Verfgung:
public JMenuItem add(JMenuItem menuItem)
public JMenuItem add(String s)
javax.swing.JPopupMenu
Mit add werden Meneintrge hinzugefgt, mit addSeparator eine Trennlinie. Kontextmens knnen prinzipiell
ebenso komplex aufgebaut sein wie normale Mens, also inbesondere Icons, Untermens, Checkboxen oder Radiobuttons
enthalten. Um das Men anzuzeigen, ist show aufzurufen. Die dazu erforderlichen Koordinaten gewinnt man am besten aus
der aktuellen Position des Mauszeigers, die im Mausereignis mitgeliefert wird.
Die zum Aktivieren eines Kontextmens erforderliche Mausaktion kann von Plattform zu Plattform unterschiedlich sein.
Die portabelste Lsung besteht darin, einen MouseListener auf der Komponente zu registrieren, und bei jedem
Mausereignis mit isPopupTrigger abzufragen, ob es sich um eine Aktion zum Aufrufen eines Kontextmens handelte.
Das folgende Programm zeigt ein Hauptfenster, auf dem ein Kontextmen mit den Eintrgen "Rueckgaengig",
"Ausschneiden", "Kopieren" und "Einfuegen" aufgerufen werden kann. Der Aufruf des Kontextmens erfolgt durch
Abfrage der Mausereignisse. Beim Auswhlen einer Menaktion wird der Kommandoname auf der Konsole ausgegeben.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
/* Listing3610.java */
import
import
import
import
java.awt.*;
java.awt.event.*;
javax.swing.*;
javax.swing.border.*;
Listing3610.java
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085 }
popup.add(mi);
//Men anzeigen
popup.show(
event.getComponent(),
event.getX(),
event.getY()
);
}
}
//ActionListener
public void actionPerformed(ActionEvent event)
{
System.out.println(event.getActionCommand());
}
public static void main(String[] args)
{
Listing3610 frame = new Listing3610();
frame.setLocation(100, 100);
frame.setSize(300, 200);
frame.setVisible(true);
}
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
36.3.1 JComponent
Viele der Swing-Komponenten sind direkt oder indirekt aus der Klasse JComponent abgeleitet. Sie stellt eine Reihe allgemeiner
Hilfsmittel zur Verfgung, die fr daraus abgeleitete Komponentenklassen ntzlich sind. Als Ableitung von
java.awt.Container (und damit von java.awt.Component) besitzt JComponent bereits einen Groteil der
Funktionalitt von AWT-Komponenten. Insbesondere bietet sie als Container die Mglichkeit, andere Komponenten
aufzunehmen, und sie kann einen Layout-Manager besitzen, der fr die Gre und Anordnung der enthaltenen Komponenten
zustndig ist.
Die mit dem AWT eingefhrte grundstzliche Unterscheidung zwischen elementaren Dialogelementen, und solchen, die
Unterkomponenten aufnehmen knnen, wurde mit der Einfhrung von Swing also weitgehend fallengelassen. In der Praxis ist das
jedoch nur selten bedeutsam. Ein JButton beispielsweise stellt sich im praktischen Gebrauch stets als elementare Komponente
dar - obwohl er als Konkretisierung von JComponent auch Unterkomponenten enthalten knnte.
Eine der aufflligeren Konsequenzen besteht darin, da es in Swing keine zu Canvas korrespondierende Klasse gibt
(siehe Kapitel 33. Elementare Dialogelemente mit selbstdefinierter Oberflche werden in Swing direkt aus
JComponent abgeleitet.
Hinweis
Umrandungen
JComponent bietet die Mglichkeit, ihren Instanzen eine Umrandung zu geben. Dazu gibt es die Methode setBorder, mit der
der Komponente ein Objekt des Typs Border zugewiesen werden kann:
public void setBorder(Border border)
javax.swing.JComponent
Border ist ein Interface, zu dem es verschiedene Implementierungen gibt. Die wichtigsten von ihnen zeigt folgende Tabelle:
Klassenname
Beschreibung
EmptyBorder
LineBorder
BevelBorder
EtchedBorder
CompoundBorder
TitledBorder
javax.swing.JComponent
Tooltips knnen nicht nur einfache Texte enthalten, sondern prinzipiell beliebig komplex aufgebaut sein. Sie werden durch Tip
die aus JComponent abgleitete Klasse JToolTip reprsentiert. Aus ihr knnen anwendungsspezifische Tooltip-Klassen
abgeleitet werden, die nahezu beliebige GUI-Funktionalitten zur Verfgung stellen. Um einer Komponente einen solchen
Tooltip zuzuordnen, mu die Methode createToolTip von JComponent berlagert werden und auf Anfrage ihr
eigenes JToolTip-Objekt zurckgeben.
Bildschirmdarstellung der Komponente
Normalerweise braucht eine GUI-Anwendung sich um die konkrete Darstellung seiner Dialogelemente keine Gedanken zu
machen. Die elementaren Komponenten erledigen dies selbst, und die zusammengesetzten Komponenten bedienen sich ihrer
Layout-Manager und der Ausgabemethoden der elementaren Komponenten. Dies ndert sich, wenn eine eigene Komponente
entwickelt werden soll. Bei AWT-Anwendungen wurde diese aus Canvas abgeleitet, und in berlagerten Varianten von paint
oder update wurde die ntige Bildschirmausgabe zur Verfgung gestellt.
In elementaren Swing-Komponenten, die aus JComponent abgeleitet wurden, liegen die Dinge etwas komplizierter. Die
Methode paint hat bereits in JComponent eine recht aufwendige Implementierung und wird normalerweise nicht mehr
berlagert. Im Prinzip ruft sie nacheinander ihre Methoden paintComponent, paintBorder und paintChildren auf. Die
letzten beiden sind fr das Zeichnen der Umrandung und der enthaltenen Dialogelemente zustndig und brauchen normalerweise
in eigenen Komponenten nicht berlagert zu werden. Fr die Darstellung der eigenen Komponente ist dagegen
paintComponent zustndig:
protected void paintComponent(Graphics g)
javax.swing.JComponent
In JComponent wird jeder Aufruf von paintComponent an das ComponentUI der Komponente delegiert. Instanzen dieser
im Paket javax.swing.plaf liegenden Klasse spielen in dem von jeder Swing-Komponente implementierten Model-ViewController-Konzept die Rolle des Views, sind also fr die grafische Darstellung der Komponente zustndig. Jede SwingKomponente besitzt ein ComponentUI, das je nach Look-and-Feel unterschiedlich sein kann. Eine selbstdefinierte Komponente
mu also entweder fr jedes untersttzte Look-and-Feel ein passendes ComponentUI zur Verfgung stellen oder die
Bildschirmdarstellung durch berlagern von paintComponent selbst erledigen.
Debug-Grafik
Eine interessante Hilfe zum Testen eigener Komponenten kann durch Aufruf der Methode setDebugGraphicsOptions
aktiviert werden:
public void setDebugGraphicsOptions(int debugOptions)
javax.swing.JComponent
Dadurch wird die Komponente mit Hilfe eines DebugGraphics-Objekts gezeichnet. Es ist in der Lage, die verwendeten
Grafikoperationen auf der Console zu protokollieren oder zur besseren Kontrolle verzgert auszugeben. Als Argument kann an
setDebugGraphicsOptions eine der folgenden Konstanten aus der Klasse DebugGraphics bergeben werden:
Konstante
Bedeutung
NONE_OPTION
Normale Ausgabe
LOG_OPTION
FLASH_OPTION
Die Grafikoperationen erfolgen verzgert und werden whrend der Ausgabe blinkend
dargestellt. Bei dieser Option mu die Doppelpufferung fr die Komponente ausgeschaltet
werden.
BUFFERED_OPTION
Gepufferte Ausgaben werden in einem separaten Frame angezeigt (hat im Test nicht
funktioniert).
/* Listing3611.java */
import java.awt.*;
import javax.swing.*;
public class Listing3611
extends JFrame
{
public Listing3611()
{
super("Debug-Grafik");
addWindowListener(new WindowClosingAdapter(true));
Container cp = getContentPane();
DebugGraphics.setFlashTime(100);
DebugGraphics.setFlashCount(3);
JButton button = new JButton("DEBUG-Button");
RepaintManager repaintManager = RepaintManager.currentManager(button);
repaintManager.setDoubleBufferingEnabled(false);
Listing3611.java
019
020
021
022
023
024
025
026
027
028
029
030 }
button.setDebugGraphicsOptions(DebugGraphics.FLASH_OPTION);
cp.add(button);
}
public static void main(String[] args)
{
Listing3611 frame = new Listing3611();
frame.setLocation(100, 100);
frame.setSize(300, 200);
frame.setVisible(true);
}
/* Listing3612.java */
import java.awt.*;
import javax.swing.*;
public class Listing3612
extends JFrame
{
public Listing3612()
{
super("Transparenz");
addWindowListener(new WindowClosingAdapter(true));
Container cp = getContentPane();
//SimpleGridComponent erzeugen
SimpleGridComponent grid = new SimpleGridComponent();
grid.setLayout(new FlowLayout(FlowLayout.CENTER));
//Transparenten Button hinzufgen
JButton button = new JButton("Transparent");
button.setOpaque(false);
grid.add(button);
//Undurchsichtigen Button hinzufgen
button = new JButton("Opaque");
grid.add(button);
//SimpleGridComponent hinzufgen
cp.add(grid, BorderLayout.CENTER);
}
public static void main(String[] args)
{
try {
String plaf = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
UIManager.setLookAndFeel(plaf);
Listing3612 frame = new Listing3612();
frame.setLocation(100, 100);
frame.setSize(300, 100);
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
Listing3612.java
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
}
}
class SimpleGridComponent
extends JComponent
{
protected void paintComponent(Graphics g)
{
int width = getSize().width;
int height = getSize().height;
g.setColor(Color.gray);
for (int i = 0; i < width; i += 10) {
g.drawLine(i, 0, i, height);
}
for (int i = 0; i < height; i += 10) {
g.drawLine(0, i, width, i);
}
}
}
javax.swing.JComponent
Grenvorgaben
Bereits in der Klasse Component sind die Methoden getMinimumSize, getPreferredSize und getMaximumSize
definiert. Sie werden in abgeleiteten Klassen berlagert, um dem Layoutmanager die minimale, optimale und maximale Gre der
Komponenten mitzuteilen (siehe z.B. Abschnitt 33.2.2). In JComponent gibt es zustzlich die Methoden setMinimumSize,
setPreferredSize und setMaximumSize. Damit knnen die Grenvorgaben bestehender Komponenten verndert
werden, ohne eine neue Klasse daraus ableiten zu mssen.
Invalidierung/Validierung
Sowohl in Swing- als auch in AWT-Programmen kann der Aufbau eines Dialogs gendert werden, wenn er bereits auf dem
Bildschirm sichtbar ist. Dialogelemente knnen hinzugefgt oder entfernt oder in ihrem Aussehen gendert werden. Die dadurch
implizierten Layoutnderungen werden zwar oft, aber nicht immer automatisch erkannt, und es kann sein, da die
Bildschirmdarstellung die nderungen nicht angemessen wiedergibt.
Wird beispielsweise die Beschriftung oder der Font eines Swing-Buttons verndert, so wird seine Gre den neuen Erfordernissen
angepat und der Dialog neu aufgebaut. Bei AWT-Buttons ist das nicht der Fall. Wenn sich der Platzbedarf fr die Beschriftung
ndert, ist der Button nach der nderung entweder zu klein oder zu gro. Auch wenn neue Dialogelemente hinzugefgt werden,
sind diese weder im AWT noch in Swing unmittelbar sichtbar.
Um einem Container mitzuteilen, da das Layout seiner Komponenten komplett neu aufgebaut werden soll, ist dessen validateMethode aufzurufen:
public void validate()
java.awt.Container
Um einen unntigen Neuaufbau des Bildschirms zu vermeiden, wird validate allerdings nur dann wirklich aktiv, wenn der
Container, auf dem der Aufruf erfolgte, zuvor mit invalidate als ungltig deklariert wurde:
public void invalidate()
java.awt.Component
Wird invalidate auf einer elementaren Komponente aufgerufen, die in einen Container eingebettet ist, wird der Aufruf an den
Container weitergegeben und invalidiert auch diesen. Soll also nach der nderung einer Komponente der zugehrige Container
neu dargestellt werden, ist auf der Komponente invalidate und anschlieend auf dem Container validate aufzurufen. In
JComponent gibt es zustzlich die Methode revalidate, die beide Schritte nacheinander durchfhrt.
Die Fokusreihenfolge
Innerhalb eines Dialogs kann immer nur eine Komponente zur Zeit den Fokus haben, also Maus- und Tastaturereignisse erhalten.
Durch Anklicken mit der Maus kann dieser direkt einer bestimmten Komponente zugewiesen werden. Alternativ kann (meist mit
Hilfe der Tasten [TAB] und [UMSCHALT]+[TAB]) der Fokus auch per Tastendruck verndert werden. Die Komponenten eines
Dialogs durchlaufen dabei einen Zyklus, der sich an der Einfge-Reihenfolge der Elemente orientiert. Das zuerst mit add
hinzugefgte Element enthlt nach dem Aufrufen des Dialogs den Fokus zuerst.
Mitunter ist es sinnvoll, lokale Fokuszyklen zu bilden, um Gruppen von Komponenten zusammenzufassen und einfacher bedienen
zu knnen. Gruppierte Radiobuttons erledigen das beispielsweise automatisch. Mit Hilfe der Methode
setNextFocusableComponent der Klasse JComponent kann dies aber auch manuell erfolgen:
public void setNextFocusableComponent(Component aComponent)
javax.swing.JComponent
Wird diese Methode auf einer beliebigen Komponente aufgerufen, erhlt das als Argument bergebene Element den nchsten
Fokus. Handelt es sich dabei um ein Element, das in der Fokusreihenfolge vor dem aktuellen Element liegt, wird auf diese Weise
ein lokaler Zyklus gebildet, der mit der Tastatur nicht mehr verlassen werden kann (mit der Maus natrlich schon). Auch beim
umgekehrten Durchlauf wird automatisch die neue Reihenfolge eingehalten; eine Methode
setPreviousFocusableComponent wird dazu nicht bentigt.
Mit dem JDK 1.4 wurde das Fokus-Subsystem vollstndig neu geschrieben. Hauptziel dieser nderung war es, eine JDK1.1-1.4
groe Zahl von existierenden Fehlern zu beheben, sowie ein wohldefiniertes und plattformunabhngiges FokusVerhalten zu implementieren. In der JDK-Doku findet sich im Guide To Features im Abschnitt Abstract
Window Toolkit eine ausfhrliche Beschreibung mit dem Titel Focus Model Specification.
Registrieren von Tastaturkommandos
Als letzte Eigenschaft der Klasse JComponent wollen wir uns das Registrieren von Tastaturkommandos ansehen. Dabei wird
eine bestimmte Tastenkombination bei einer Komponente angemeldet und lst bei jeder Anwendung ein Action-Event aus.
Tastaturkommandos knnen sowohl bei elementaren Komponenten als auch bei Containern angemeldet werden, und es gibt
verschiedene Mglichkeiten, sie vom Fokus der Komponente abhngig zu machen. Das Registrieren erfolgt mit der Methode
registerKeyboardAction:
public void registerKeyboardAction(
ActionListener anAction,
String aCommand,
KeyStroke aKeyStroke,
int aCondition
)
javax.swing.JComponent
Deren erstes Argument ist der ActionListener, der im Falle des Tastendrucks (mit dem Kommando aCommand) aufgerufen
werden soll. aKeyStroke definiert die zu registrierende Tastenkombination (wie man KeyStroke-Objekte erzeugt, wurde in
Listing 36.8 gezeigt). Das letzte Argument gibt an, in welcher Relation der Fokus zur Komponente stehen mu, damit das
Kommando aktiv wird. Er kann einen der folgenden Werte annehmen:
Konstante
Bedeutung
WHEN_FOCUSED
WHEN_IN_FOCUSED_WINDOW
WHEN_ANCESTOR_OF_FOCUSED_COMPONENT Das Tastaturkommando wird ausgelst, wenn die Komponente oder eines
der darin enthaltenen Elemente den Fokus hat.
Tabelle 36.6: Bedingungen zur Registrierung von Tastaturkommandos
Bei der Verwendung der zweiten und dritten Variante werden die Tastendrcke immer dann ausgefhrt, wenn der Container selbst
oder eine der darin enthaltenen Komponenten den Fokus hat. Der Unterschied besteht in der Sichtweise.
WHEN_IN_FOCUSED_WINDOW wird auf die elementaren Komponenten angewendet, whrend
WHEN_ANCESTOR_OF_FOCUSED_COMPONENT fr den Container selbst vorgesehen ist.
Das folgende Programm zeigt eine beispielhafte Anwendung fr das Registrieren von Tastaturkommandos:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
/* Listing3613.java */
import
import
import
import
java.awt.*;
java.awt.event.*;
javax.swing.*;
javax.swing.border.*;
Listing3613.java
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071 }
"tf2",
ctrlShift(KeyEvent.VK_F8),
JComponent.WHEN_FOCUSED
);
//Textfelder hinzufgen
cp.add(tf1);
cp.add(tf2);
cp.add(tf3);
}
public void actionPerformed(ActionEvent event)
{
String cmd = event.getActionCommand();
System.out.println(cmd);
}
private KeyStroke ctrlShift(int vkey)
{
return KeyStroke.getKeyStroke(
vkey,
Event.SHIFT_MASK + Event.CTRL_MASK
);
}
public static void main(String[] args)
{
Listing3613 frame = new Listing3613();
frame.setLocation(100, 100);
frame.setSize(300, 200);
frame.setVisible(true);
}
JPanel()
JPanel(boolean isDoubleBuffered)
JPanel(LayoutManager layout)
JPanel(LayoutManager layout, boolean isDoubleBuffered)
javax.swing.JPanel
Der parameterlose Konstruktor erzeugt ein JPanel mit Doppelpufferung und einem FlowLayout. Wahlweise knnen beide
Eigenschaften durch Setzen der Argumente isDoubleBuffered und layout gendert werden. In der weiteren Anwendung
ist JPanel mit JComponent identisch.
JLayeredPane
Eine weitere Container-Klasse, die direkt aus JComponent abgeleitet wurde, ist JLayeredPane. Sie fgt den Dialogen eine
dritte Dimension hinzu und ermglicht mit Hilfe eines Layerkonzepts die kontrollierte Anordnung von Komponenten
bereinander. JLayeredPane ist Vaterklasse von JDesktopPane (siehe Abschnitt 36.1.6) und wichtiger Bestandteil der
Struktur von Hauptfenstern (siehe Abschnitt 36.1.1). In der praktischen Anwendung wird sie nur selten direkt gebraucht, und wir
wollen nicht weiter darauf eingehen.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
37.1.1 JLabel
Ein JLabel ist ein Dialogelement zur Anzeige einer Beschriftung innerhalb eines GUI-Containers. Es besitzt einen
Text und ein Icon, die in beliebiger Anordnung dargestellt werden knnen. Beide Elemente sind optional, ein JLabel
kann also auch nur ein Icon enthalten. Auf Benutzereingaben reagiert ein JLabel nicht. Seine wichtigsten
Konstruktoren sind:
public JLabel(String text)
public JLabel(Icon image)
public JLabel(String text, Icon icon, int horizontalAlignment)
javax.swing.JLabel
Wird ein Icon bergeben, steht es links neben dem Text. Wie es erzeugt werden kann, wurde in Abschnitt 36.2.3
gezeigt. Der Parameter horizontalAlignment gibt an, wie das Label horizontal plaziert werden soll, falls links
und rechts mehr Platz als erforderlich zur Verfgung steht. Hier kann eine der Konstanten LEFT, CENTER oder RIGHT
aus dem Interface SwingConstants angegeben werden.
Auf die relative Anordnung von Text und Icon kann mit den Methoden setHorizontalTextPosition und
getHorizontalTextPosition zugegriffen werden (standardmig steht der Text rechts neben dem Icon).
Mgliche Werte sind LEFT und RIGHT. :
public void setHorizontalTextPosition(int textPosition)
public int getHorizontalTextPosition()
javax.swing.JLabel
JLabel stellt noch einige Methoden zur Verfgung, mit denen die Anordnung seines Inhalts beeinflut werden kann:
public void setHorizontalAlignment(int alignment)
public int getHorizontalAlignment()
javax.swing.JLabel
Mit setHorizontalAlignment wird die horizontale Ausrichtung festgelegt, mgliche Parameter sind LEFT,
CENTER oder RIGHT. Mit setVerticalAlignment wird die vertikale Ausrichtung festgelegt, hier kann eine der
Konstanten TOP, CENTER oder BOTTOM aus dem Interface SwingConstants bergeben werden. Mit
/* Listing3701.java */
import java.awt.*;
import javax.swing.*;
public class Listing3701
extends JFrame
{
public Listing3701()
{
super("JLabel");
addWindowListener(new WindowClosingAdapter(true));
Container cp = getContentPane();
cp.setLayout(new GridLayout(5, 1));
JLabel label;
//Standardlabel
label = new JLabel("Standard-Label");
cp.add(label);
//Label mit Icon
label = new JLabel(
"Label mit Icon",
new ImageIcon("lock.gif"),
JLabel.CENTER
);
cp.add(label);
//Nur-Icon
label = new JLabel(new ImageIcon("lock.gif"));
cp.add(label);
//Icon auf der rechten Seite
label = new JLabel(
"Label mit Icon rechts",
new ImageIcon("lock.gif"),
JLabel.CENTER
);
label.setHorizontalTextPosition(JLabel.LEFT);
cp.add(label);
//Label rechts unten
label = new JLabel("Label rechts unten");
label.setHorizontalAlignment(JLabel.RIGHT);
label.setVerticalAlignment(JLabel.BOTTOM);
cp.add(label);
}
public static void main(String[] args)
{
Listing3701 frame = new Listing3701();
frame.setLocation(100, 100);
frame.setSize(300, 200);
frame.setVisible(true);
}
}
Listing3701.java
37.1.2 JTextField
Die Klasse JTextField ist das Swing-Pendant zur AWT-Klasse TextField (siehe Abschnitt 32.6) und stellt ein
einzeiliges Textfeld zur Eingabe von Daten dar. In ihren grundlegenden Mglichkeiten ist die Programmierschnittstelle
beider Klassen hnlich, JTextField bietet aber zustzliche Mglichkeiten, die in TextField nicht zu finden sind.
Ein Groteil der Fhigkeiten von JTextField ist in der Basisklasse JTextComponent des Pakets
javax.swing.text implementiert.
Die wichtigsten Konstruktoren von JTextField sind:
public JTextField(int columns)
public JTextField(String text)
public JTextField(String text, int columns)
javax.swing.JTextField
Der erste von ihnen erzeugt ein leeres Textfeld mit der angegebenen Anzahl Spalten, der zweite ein Textfeld mit dem
angegebenen Text. Beim dritten Konstruktor knnen sowohl Spaltenzahl als auch Text vorgegeben werden. Die
Spaltenzahl wird zur Berechnung der Breite des Textfeldes verwendet (Anzahl Spalten mal Breite eines "m"), sie dient
nicht dazu, die Anzahl der Eingabezeichen zu begrenzen. Ist die Spaltenzahl 0 wie im zweiten Konstruktor, wird die
initiale Textlnge zur Berechnung der Spaltenbreite verwendet.
JTextField bietet hnliche Methoden wie TextField:
public String getText()
public void setText(String t)
javax.swing.JTextField
Mit den parameterlosen Versionen von getText und setText kann auf den kompletten Inhalt des Textfelds
zugegriffen werden. Die parametrisierte Variante von getText liefert den Textausschnitt der angegebenen Lnge und
Position. getSelectedText liefert dagegen den selektierten Text (bzw. null, wenn kein Text selektiert ist), und
mit getSelectionStart und getSelectionEnd kann Anfang und Ende des selektierten Bereichs ermittelt
werden. Mit setSelectionStart und setSelectionEnd kann dieser sogar verndert werden.
getCaretPosition liefert die Position der Eingabemarke, und mit setCaretPosition kann sie verndert
werden. moveCaretPosition verndert die Position der Einfgemarke und markiert dabei den Bereich zwischen
alter und neuer Position. Fr alle Positionsangaben gilt, da der Platz vor dem ersten Zeichen die Position 0 und der
nach dem letzten Textzeichen die Position Lnge des Textes hat.
Die wichtigsten registrierbaren Listener sind ActionListener und CaretListener:
public void addActionListener(ActionListener l)
public void addCaretListener(CaretListener listener)
javax.swing.JTextField
Ein ActionListener wird immer dann aufgerufen, wenn im Eingabefeld [ENTER] gedrckt wird, ein
CaretListener, wenn sich die Position der Einfgemarke gendert hat.
Einen TextListener wie bei der Klasse TextField gibt es bei den aus JTextComponent abgeleiteten
Klassen nicht. Will die Anwendung ber jede nderung in einem Textfeld unterrichtet werden, mu sie einen
DocumentListener auf dessen Modell registrieren. Das Modell eines Textfeldes ist eine Instanz der Klasse
Document und kann durch Aufruf von getDocument beschafft werden.
Tip
Das folgende Beispiel zeigt ein Programm mit drei Textfeldern. Auf dem untersten sind ein ActionListener und
ein CaretListener registriert, die bei jedem Aufruf einige Parameter auf der Konsole ausgeben:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
/* Listing3702.java */
import
import
import
import
java.awt.*;
java.awt.event.*;
javax.swing.*;
javax.swing.event.*;
Listing3702.java
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057 }
{
JTextField tf = (JTextField)event.getSource();
System.out.println("---ActionEvent---");
System.out.println(tf.getText());
System.out.println(tf.getSelectedText());
System.out.println(tf.getSelectionStart());
System.out.println(tf.getSelectionEnd());
System.out.println(tf.getCaretPosition());
}
public void caretUpdate(CaretEvent event)
{
System.out.println("---CaretEvent---");
System.out.println(event.getDot());
System.out.println(event.getMark());
}
public static void main(String[] args)
{
Listing3702 frame = new Listing3702();
frame.setLocation(100, 100);
frame.setSize(300, 150);
frame.setVisible(true);
}
37.1.3 JPasswordField
JPasswordField ist eine Spezialisierung von JTextField zur Eingabe von Passwrtern. Der Hauptunterschied zu
JTextField besteht darin, da der eingegebene Text nicht angezeigt, sondern statt dessen fr jedes Zeichen ein
Sternchen ausgegeben wird. Durch Aufruf von setEchoChar kann auch ein anderes Ausgabezeichen ausgewhlt
werden. Des weiteren sind die beiden Zwischenablagefunktionen Kopieren und Ausschneiden deaktiviert, um zu
verhindern, da der Text aus einem Pawortfeld in ein anderes Feld kopiert oder an ein anderes Programm bergeben
werden kann.
37.1.4 JTextArea
JTextArea ist eine Komponente zur Anzeige und Eingabe von mehrzeiligen Texten. Wie die AWT-Klasse
TextArea dient sie dazu, unformatierte Texte zu bearbeiten. Diese knnen zwar Zeilenumbrche und Tabulatoren,
nicht aber unterschiedliche Schriften, Farben oder grafische Elemente enthalten (fr diesen Zweck gibt es die Klassen
JEditorPane und JTextPane, die hier nicht behandelt werden sollen). Die wichtigsten Konstruktoren von
JTextArea sind:
public JTextArea(String text)
public JTextArea(int rows, int columns)
public JTextArea(String text, int rows, int columns)
javax.swing.JTextArea
Sie entsprechen im Prinzip denen von JTextField, zustzlich kann jedoch die Anzahl der Zeilen angegeben werden.
Neben den in Abschnitt 37.1.2 vorgestellten Methoden der Klasse JTextField stehen zustzliche Methoden zum
zeilenweisen Zugriff auf den dargestellten Text zur Verfgung:
public int getLineCount()
javax.swing.JTextArea
getLineCount liefert die Gesamtzahl der Zeilen. Mit getLineStartOffset und getLineEndOffset kann
zu einer beliebigen Zeile ihr Anfangs- und Endeoffset bestimmt werden. Mit diesen Informationen kann beispielsweise
getText aufgerufen werden, um den Inhalt einer bestimmten Zeile zu lesen. Die Methode getLineOfOffset dient
dazu, die Nummer einer Zeile zu ermitteln, wenn der Offset eines darin enthaltenen Zeichens bekannt ist.
JTextArea stellt einige Methoden zur Verfgung, mit denen die Formatierung des Textes beeinflut werden kann:
public void setTabSize(int size)
public int getTabSize()
javax.swing.JTextArea
Mit setTabSize und getTabSize kann auf die Tabulatorweite zugegriffen werden. Sie hat standardmig den
Wert 8 (zur Umrechnung in Bildschirmpixel wird er mit der Breite des breitesten Buchstabens im aktuellen Font
multipliziert). Mit setLineWrap kann festgelegt werden, ob zu breite Spalten automatisch umbrochen werden sollen.
setWrapStyleWord definiert, ob dies an Wortgrenzen oder mitten im Wort geschehen soll. Mit getLineWrap und
getWrapStyleWord knnen die beiden Eigenschaften auch abgefragt werden.
Anders als TextArea ist JTextArea nicht in der Lage, Text automatisch zu scrollen, wenn er zu lang
oder zu breit fr den verfgbaren Bildschirmausschnitt ist. Wird das gewnscht, mu das Textfeld in eine
Komponente des Typs JScrollPane eingebettet werden. An Stelle der JTextArea ist dem Dialog dann
die JScrollPane hinzuzufgen. Weitere Informationen zu JScrollPane finden sich in Abschnitt
38.1.1.
Hinweis
Das folgende Programm enthlt eine JTextArea mit 30 Spalten und 20 Zeilen. Ihre Tabulatorweite wurde auf 4
reduziert, zu lange Zeilen werden automatisch an Wortgrenzen umbrochen. Die JTextArea wurde in eine
JScrollPane eingebettet und erhlt - wenn ntig - automatisch einen vertikalen Schieber. Ein horizontaler Schieber
wird nicht verwendet, da die Zeilen wegen des automatischen Umbruchs nicht zu lang werden knnen.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
/* Listing3703.java */
import java.awt.*;
import javax.swing.*;
public class Listing3703
extends JFrame
{
public Listing3703()
{
super("JTextArea");
addWindowListener(new WindowClosingAdapter(true));
Container cp = getContentPane();
JTextArea ta = new JTextArea("Hello, world", 20, 30);
ta.setTabSize(4);
ta.setLineWrap(true);
ta.setWrapStyleWord(true);
cp.add(new JScrollPane(ta));
}
public static void main(String[] args)
{
Listing3703 frame = new Listing3703();
frame.setLocation(100, 100);
frame.setSize(300, 200);
frame.setVisible(true);
}
}
37.1.5 JSpinner
Listing3703.java
Seit dem JDK 1.4 gibt es die Klasse JSpinner, mit der Textfelder erzeugt werden knnen, deren Inhalt JDK1.1-1.4
einer vordefinierten, geordneten Liste von Werten entstammt. Diese Werte knnen vom Anwender nicht
nur manuell eingegeben werden, sondern auch auf- oder absteigend durchlaufen werden. Dazu besitzt der
JSpinner zwei kleine Buttons, mit denen die Wertefolge abgerufen werden kann. Alternativ knnen
meist auch die Tasten [CURSORHOCH] oder [CURSORRUNTER] verwendet werden, wenn der
JSpinner den Fokus hat.
Konzeptionell kann ein Spinner als Mischung aus Texfeldern und den in Abschnitt 37.3 vorgestellten Listen oder
Comboboxen angesehen werden. Im Gegensatz zu Listen und Comboboxen mu der Wertevorrat eines Spinners
allerdings nicht notwendigerweise endlich sein. Soll beispielsweise ein Datum eingegeben werden, kann es sinnvoll
sein, den Wertebereich nach unten oder oben offen zu lassen, um beliebige Datumswerte eingeben zu knnen.
Gegenber einer Combobox hat ein Spinner zudem den Vorteil, da er bei der Darstellung auf dem Bildschirm keine
darunterliegenden Dialogelemente verdecken kann. Nachteilig ist allerdings, da es keine Mglichkeit der Vorschau auf
die Menge der insgesamt verfgbaren Elemente gibt.
Die Klasse JSpinner besitzt zwei Konstruktoren:
public JSpinner()
javax.swing.JSpinner
Der erste erzeugt einen JSpinner fr Ganzzahlen, die in Einerschritten beliebig weit in jede Richtung durchlaufen
werden knnen. Der zweite Konstruktor erwartet ein SpinnerModel, mit dem der Typ der darzustellenden Daten
festgelegt wird. SpinnerModel ist ein Interface im Paket javax.swing, das die Wertemenge eines Spinners ber
dessen aktuelles Element sowie den jeweiligen Vorgnger und Nachfolger dieses Elements definiert. Zu diesem
Interface gibt es eine abstrakte Implementierung AbstractSpinnerModel und drei Konkretisierungen
SpinnerDateModel, SpinnerNumberModel und SpinnerListModel. Sie reprsentieren Folgen von
Datumswerten, Ganz- oder Fliekommazahlen oder fest vorgegebene Listen von Strings.
Um einen JSpinner zu erzeugen, wird das gewnschte Modell instanziert und an den Konstruktor der Klasse
bergeben. Das folgende Beispiel zeigt ein einfaches Fenster mit vier Spinnern. Der erste Spinner reprsentiert eine
nach oben und unten offene Liste von Ganzzahlen der Schrittweite 1. Beim zweiten ist ihr Wertebereich begrenzt, und
die Folge hat die Schrittweite 7. Der dritte Spinner erlaubt die Auswahl von Datumswerten und der vierte die von
Wochentagen.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
/* Listing3704.java */
import
import
import
import
java.awt.*;
java.awt.event.*;
javax.swing.*;
javax.swing.event.*;
Listing3704.java
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043 }
//Default-Spinner fr Ganzzahlen
JSpinner spinner = new JSpinner();
cp.add(spinner);
//Spinner fr einige Vielfache von 7
spinner = new JSpinner(new SpinnerNumberModel(91, 49, 126, 7));
cp.add(spinner);
//Spinner fr Datum/Uhrzeit
spinner = new JSpinner(new SpinnerDateModel());
cp.add(spinner);
//Spinner fr Wochentage
spinner = new JSpinner(new SpinnerListModel(WDAYS));
cp.add(spinner);
}
public static void main(String[] args)
{
Listing3704 frame = new Listing3704();
frame.setLocation(100, 100);
frame.setSize(300, 150);
frame.setVisible(true);
}
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
37.2 Buttons
37.2 Buttons
37.2.1 JButton
37.2.2 JCheckBox
37.2.3 JRadioButton
37.2.1 JButton
Swing-Buttons sind uns in den vorangegangenen Abschnitten schon mehrfach begegnet. Sie sind Instanzen der Klasse
JButton und dienen dazu, Schaltflchen zu erzeugen. Die wichtigsten Konstruktoren von JButton sind:
public JButton(String text)
public JButton(Icon icon)
public JButton(String text, Icon icon)
javax.swing.JButton
Ein Button kann also wahlweise mit Beschriftung, mit Icon oder mit beidem instanziert werden. Auf die Anordnung eines
Buttons und seiner Bestandteile kann mit Hilfe der schon aus Abschnitt 37.1.1 bekannten Methoden zugegriffen werden:
public void setHorizontalTextPosition(int textPosition)
public int getHorizontalTextPosition()
javax.swing.JButton
JButton ist wie JMenuItem (siehe Abschnitt 36.2.1) aus AbstractButton abgeleitet und stellt ebenfalls die
Methoden setMnemonic, getMnemonic, setEnabled und isEnabled zur Verfgung. Wird ein JButton per
Mausklick bettigt, sendet er ein ActionEvent an alle registrierten Listener. Durch Aufruf von doClick kann ein
Buttonklick auch programmgesteuert ausgelst werden.
Das Beispielprogramm in Listing 37.7 erzeugt drei Buttons und ordnet sie nebeneinander in einem JFrame an:
Der "OK"-Button ist eine Instanz unserer eigenen (aus JButton abgeleiteten) Klasse DefaultButton. Seine
Besonderheit besteht darin, da er sich durch Aufruf von setDefaultButton bei der an den Konstruktor
bergebenen RootPane registriert. Dadurch kann er - unabhngig davon, welches Dialogelement gerade den Fokus
hat - auch durch Drcken von [ENTER] bettigt werden.
Der "Abbrechen"-Button ist eine Instanz der ebenfalls selbstdefinierten und aus JButton abgeleiteten Klasse
CancelButton. Seine Besonderheit besteht darin, da er beim Drcken von [ESCAPE] die Methode
doClick aufruft und dadurch einen Buttonklick simuliert.
Der "Hilfe"-Button ist eine direkte Instanzierung von JButton. Er definiert ein Mnemonik fr die
Tastenkombination [ALT]+[H], registriert den ActionListener und wird dann der ContentPane
hinzugefgt.
Wir wollen uns zunchst den Code fr die Klasse DefaultButton ansehen:
001
002
003
004
005
006
007
008
009
010
011
012
013
/* DefaultButton.java */
DefaultButton.java
import javax.swing.*;
public class DefaultButton
extends JButton
{
public DefaultButton(String title, JRootPane rootpane)
{
super(title);
rootpane.setDefaultButton(this);
}
}
/* CancelButton.java */
import java.awt.event.*;
import javax.swing.*;
public class CancelButton
extends JButton
{
public CancelButton(String title)
{
super(title);
ActionListener al = new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
String cmd = event.getActionCommand();
if (cmd.equals("PressedESCAPE")) {
doClick();
}
}
};
registerKeyboardAction(
al,
"PressedESCAPE",
KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
JButton.WHEN_IN_FOCUSED_WINDOW
);
}
}
CancelButton.java
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
/* Listing3707.java */
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Listing3707
extends JFrame
implements ActionListener
{
public Listing3707()
{
super("JButton");
addWindowListener(new WindowClosingAdapter(true));
Container cp = getContentPane();
cp.setLayout(new FlowLayout());
JPanel panel = new JPanel();
//OK-Button
JButton okButton = new DefaultButton("OK", getRootPane());
okButton.addActionListener(this);
panel.add(okButton);
//Abbrechen-Button
JButton cancelButton = new CancelButton("Abbrechen");
cancelButton.addActionListener(this);
panel.add(cancelButton);
//Hilfe-Button
JButton helpButton = new JButton("Hilfe");
helpButton.setMnemonic('H');
helpButton.addActionListener(this);
panel.add(helpButton);
//Panel hinzufgen
cp.add(panel);
}
public void actionPerformed(ActionEvent event)
{
System.out.println(event.getActionCommand());
}
public static void main(String[] args)
{
Listing3707 frame = new Listing3707();
frame.setLocation(100, 100);
frame.setSize(300, 100);
frame.setVisible(true);
}
}
Listing3707.java
37.2.2 JCheckBox
Die Klasse JCheckBox stellt einen Button dar, der vom Anwender wahlweise an- oder ausgeschaltet werden kann. Er
wird meist verwendet, um boolesche Werte auf einer GUI-Oberflche darzustellen. JCheckBox ist von der Klasse
JToggleButton abgeleitet, die als Abstraktion von Buttons, die ihren Zustand ndern knnen, auch Basisklasse von
JRadioButton ist.
JCheckBox kann eine textuelle Beschriftung oder ein Icon oder beides enthalten. Die wichtigsten Konstruktoren sind:
public JCheckBox(String text)
public JCheckBox(String text, boolean selected)
javax.swing.JCheckBox
Mit dem Parameter selected kann bereits bei der Instanzierung angegeben werden, ob die Checkbox aktiviert oder
deaktiviert sein soll. Spter kann der Aktivierungszustand mit setSelected gesetzt und mit isSelected abgefragt
werden:
public void setSelected(boolean b)
public boolean isSelected()
javax.swing.JCheckBox
Bei der Instanzierung einer JCheckBox kann auch ein Icon bergeben werden. Das ist allerdings nur in
Ausnahmefllen sinnvoll, denn es wird nicht zustzlich zum Ankreuzfeld angezeigt, sondern statt dessen. Das
Ankreuzfeld einer JCheckBox ist also gerade ihr (automatisch angezeigtes) Icon. Durch Aufruf von
setHorizontalTextPosition und bergabe der Konstante LEFT knnen Ankreuzfeld und Text
vertauscht werden.
Hinweis
Wie ein JButton sendet eine JCheckBox bei jeder Bettigung ein ActionEvent an registrierte Listener. Zudem
wird bei Zustandsnderungen ein ItemEvent versendet, auf das ein ItemListener reagieren kann.
Das folgende Programm erzeugt drei Checkboxen in einem Panel und gibt alle Zustandsnderungen auf der Konsole aus:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
/* Listing3708.java */
import
import
import
import
java.awt.*;
java.awt.event.*;
javax.swing.*;
javax.swing.event.*;
Listing3708.java
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044 }
37.2.3 JRadioButton
Die Klasse JRadioButton hnelt der Klasse JCheckBox. Auch sie stellt einen Button dar, der wahlweise an- oder
ausgeschaltet werden kann. Anders als bei JCheckBox ist in einer Gruppe von Radiobuttons allerdings immer nur ein
Button zur Zeit aktiviert, alle anderen sind deaktiviert. Die wichtigsten Konstruktoren von JRadioButton sind:
public JRadioButton(String text)
public JRadioButton(String text, boolean selected)
javax.swing.JRadioButton
Die brigen Methoden knnen so verwendet werden, wie es bei JCheckBox beschrieben wurde. Um die Buttons zu
gruppieren, ist eine ButtonGroup zu instanzieren, und die Buttons sind durch Aufruf von add hinzuzufgen. Mit
getSelection kann auf das ButtonModel des selektierten Elements zugegriffen werden, getElements liefert
alle Buttons der Gruppe:
public void add(AbstractButton b)
public ButtonModel getSelection()
public Enumeration getElements()
javax.swing.ButtonGroup
Das folgende Programm enthlt drei Radiobuttons, die ber eine ButtonGroup gruppiert werden. Der zustzliche
Button "Selektion" ermittelt das selektierte Element und gibt dessen Kommandonamen auf der Konsole aus:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
/* Listing3709.java */
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Listing3709
extends JFrame
implements ActionListener
{
private ButtonGroup group = new ButtonGroup();
public Listing3709()
{
super("JRadioButton");
addWindowListener(new WindowClosingAdapter(true));
//RadioButton-Panel
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(3, 1));
for (int i = 1; i <= 3; ++i) {
JRadioButton rb = new JRadioButton("RadioButton" + i, i == 2);
rb.setActionCommand(rb.getText());
panel.add(rb);
group.add(rb);
}
getContentPane().add(panel, BorderLayout.CENTER);
//Selektion-Button
JButton button = new JButton("Selektion");
button.addActionListener(this);
getContentPane().add(button, BorderLayout.SOUTH);
}
public void actionPerformed(ActionEvent event)
{
String cmd = event.getActionCommand();
if (cmd.equals("Selektion")) {
ButtonModel selected = group.getSelection();
System.out.print("Selektiert: ");
if (selected != null) {
System.out.println(selected.getActionCommand());
}
}
}
public static void main(String[] args)
{
Listing3709 frame = new Listing3709();
frame.setLocation(100, 100);
frame.setSize(300, 120);
frame.setVisible(true);
}
}
Listing3709.java
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
37.3.1 JList
Die Klasse JList dient dazu, Listen von Werten darzustellen, aus denen der Anwender einen oder mehrere Eintrge
auswhlen kann. Im Gegensatz zur AWT-Klasse List kann sie nicht nur Strings, sondern beliebige Objekte enthalten. Auch
die Darstellung der Listenelemente auf dem Bildschirm kann weitgehend frei gestaltet werden. Wir wollen uns die wichtigsten
Eigenschaften von JList ansehen und dazu zunchst mit den Konstruktoren beginnen:
public
public
public
public
JList()
JList(Object[] listData)
JList(Vector listData)
JList(ListModel dataModel)
javax.swing.JList
Der parameterlose Konstruktor erzeugt eine leere Liste. Wird ein Array oder Vector bergeben, erzeugt JList aus dessen
Daten ein Listenmodell und benutzt es zur Darstellung. Schlielich kann auch direkt eine Instanz der Klasse ListModel
bergeben werden, um den Inhalt der Liste zu definieren.
hnlich wie JTextArea und andere Swing-Komponenten besitzt JList keine eigene Funktionalitt zum
Scrollen der Daten, falls diese nicht vollstndig auf den Bildschirm passen. Eine JList wird daher meist in eine
JScrollPane eingebettet, bevor sie zu einem GUI-Container hinzugefgt wird.
Hinweis
javax.swing.JList
Mit setSelectionMode wird der Selektionsmodus verndert. Als Argument kann eine der folgenden Konstanten der
Klasse ListSelectionModel bergeben werden:
getSelectionMode liefert den aktuellen Selektionsmodus. Es gibt eine Reihe von Methoden, um Informationen ber die
javax.swing.JList
getSelectedIndex liefert das selektierte Element, falls der Selektionsmodus SINGLE_SELECTION ist. Knnen
mehrere Elemente ausgewhlt werden, liefert getSelectedIndices ein Array mit den Indizes aller selektierten
Elemente. getSelectedValue und getSelectedValues arbeiten in analoger Weise, liefern aber statt der Indizes die
selektierten Elemente zurck. Mit isSelectedIndex kann geprft werden, ob das Element mit dem angegebenen Index
gerade selektiert ist, und isSelectionEmpty prft, ob mindestens ein Element selektiert wurde.
Als "Anchor" und "Lead" bezeichnet Swing in einem zusammenhngend markierten Bereich das jeweils zuerst und zuletzt
markierte Element. Das zuletzt markierte Element ist gleichzeitig aktuelles Element. Mit getAnchorSelectionIndex
und getLeadSelectionIndex kann auf "Anchor" und "Lead" zugegriffen werden, wenn der Selektionsmodus
SINGLE_INTERVAL_SELECTION ist.
Zustzlich gibt es Methoden, um die Selektion programmgesteuert zu verndern:
public void clearSelection()
javax.swing.JList
Mit clearSelection wird die Selektion gelscht. Mit setSelectedIndex kann ein einzelnes Element selektiert
werden, mit setSelectedIndices eine Menge von Elementen. Mit setSelectionInterval,
addSelectionInterval und removeSelectionInterval knnen Selektionen auch bereichsweise hinzugefgt und
gelscht werden.
Wird die Selektion gendert, versendet eine JList ein ListSelectionEvent an alle registrierten Listener. Um im
Programm auf nderungen zu reagieren, ist also lediglich das Interface ListSelectionListener des Pakets
javax.swing.event zu implementieren und durch Aufruf von addListSelectionListener bei der JList zu
registrieren. Jede Selektionsnderung fhrt dann zum Aufruf der Methode valueChanged.
Den Listeninhalt dynamisch verndern
Etwas mehr Aufwand als beim AWT-Pendant mu getrieben werden, wenn der Inhalt einer JList nach der Instanzierung
modifiziert werden soll. In diesem Fall kann nicht mehr mit dem automatisch erzeugten Listenmodel gearbeitet werden,
sondern es mu selbst eines erzeugt werden. Das Modell einer JList mu stets das Interface ListModel implementieren
und der Liste durch Versenden eines ListDataEvent jede Datennderung mitteilen. Eine fr viele Zwecke ausreichende
Implementierung steht mit der Klasse DefaultListModel zur Verfgung. Ihre Schnittstelle entspricht der Klasse
Vector (siehe Abschnitt 14.2), und alle erforderlichen nderungsbenachrichtigungen werden automatisch verschickt. Die
wichtigsten Methoden von DefaultListModel sind:
javax.swing.DefaultListModel
Soll eine Liste mit einem benutzerdefinierten Modell arbeiten, wird dieses einfach manuell erzeugt und an den Konstruktor
bergeben. Alle Einfgungen, Lschungen und nderungen von Daten werden dann an diesem Modell vorgenommen. Durch
Aufruf von getModel kann auf einfache Weise auf das Modell einer JList zugegriffen werden.
Beispiel
Zum Abschlu wollen wir uns ein Beispiel ansehen. Das folgende Programm instanziert eine JList durch bergabe eines
String-Arrays. Bei jedem Drcken des Buttons "Ausgabe" gibt es die Liste der selektierten Elemente auf der Konsole aus:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
/* Listing3710.java */
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Listing3710
extends JFrame
implements ActionListener
{
static final String[] DATA = {
"Hund", "Katze", "Meerschweinchen", "Tiger", "Maus",
"Fisch", "Leopard", "Schimpanse", "Kuh", "Pferd",
"Reh", "Huhn", "Marder", "Adler", "Nilpferd"
};
private JList list;
public Listing3710()
{
super("JList");
addWindowListener(new WindowClosingAdapter(true));
Container cp = getContentPane();
//Liste
list = new JList(DATA);
list.setSelectionMode(
ListSelectionModel.MULTIPLE_INTERVAL_SELECTION
);
list.setSelectedIndex(2);
cp.add(new JScrollPane(list), BorderLayout.CENTER);
//Ausgabe-Button
JButton button = new JButton("Ausgabe");
button.addActionListener(this);
cp.add(button, BorderLayout.SOUTH);
}
public void actionPerformed(ActionEvent event)
{
String cmd = event.getActionCommand();
if (cmd.equals("Ausgabe")) {
System.out.println("---");
ListModel lm = list.getModel();
int[] sel = list.getSelectedIndices();
for (int i = 0; i < sel.length; ++i) {
String value = (String)lm.getElementAt(sel[i]);
Listing3710.java
046
047
048
049
050
051
052
053
054
055
056
057
058 }
System.out.println("
" + value);
}
}
}
public static void main(String[] args)
{
Listing3710 frame = new Listing3710();
frame.setLocation(100, 100);
frame.setSize(200, 200);
frame.setVisible(true);
}
37.3.2 JComboBox
Eine JComboBox ist das Swing-Pendant zur AWT-Klasse Choice. Es stellt eine Kombination aus Textfeld und Liste dar.
Die Liste ist normalerweise unsichtbar und wird vom Anwender nur dann geffnet, wenn er einen Wert daraus auswhlen
will. Sie erlaubt grundstzlich nur einfache Selektion. Die wichtigsten Konstruktoren von JComboBox sind:
public JComboBox(Object[] items)
public JComboBox(Vector items)
public JComboBox(ComboBoxModel aModel)
javax.swing.JComboBox
Sie entsprechen den korrespondierenden Konstruktoren der Klasse JList. Ein wichtiges Merkmal einer JComboBox ist die
Mglichkeit, das Textfeld editieren zu knnen oder nicht. Ist das der Fall, kann der Anwender auch Werte eingeben, die nicht
in der Liste stehen; andernfalls ist er auf Listenwerte beschrnkt. Mit den Methoden setEditable und isEditable kann
auf diese Eigenschaft zugegriffen werden:
public void setEditable(boolean aFlag)
public boolean isEditable()
Zur Abfrage oder Auswahl des selektierten Elements stehen folgende Methoden zur Verfgung:
javax.swing.JComboBox
javax.swing.JComboBox
Mit getSelectedItem kann das selektierte Element abgefragt werden, mit setSelectedItem kann es gesetzt werden.
Wurde bei einer editierbaren JComboBox vom Anwender ein Wert eingegeben, das nicht in der Liste steht, liefert
getSelectedItem diesen Wert. Mit getSelectedIndex und setSelectedIndex kann auch ber die Indizes der
Liste auf deren Elemente zugegriffen werden. getSelectedIndex liefert -1, wenn vom Anwender ein Wert eingegeben
wurde, der nicht in der Liste steht.
Im Gegensatz zu JList stellt JComboBox auch einige Methoden zur Verfgung, mit denen die Elemente der Liste
dynamisch verndert werden knnen:
public
public
public
public
public
void
void
void
void
void
addItem(Object anObject)
insertItemAt(Object anObject, int index)
removeItem(Object anObject)
removeItemAt(int anIndex)
removeAllItems()
javax.swing.JComboBox
Mit addItem wird ein neues Element an das Ende der Liste angehngt, mit insertItemAt wird es einer beliebigen
Position eingefgt. removeItem entfernt das angegebene Element, removeItemAt das Element mit dem angegebenen
Index. removeAllItems entfernt alle Elemente aus der Liste.
Jedesmal, wenn ein anderes Element selektiert wird, sendet eine JComboBox ein ItemEvent an registrierte
ItemListener (und zwar sowohl fr das deselektierte als auch fr das selektierte Element). Nach Abschlu der Selektion
oder wenn der Anwender in einer editierbaren JComboBox einen Wert per Hand eingegeben hat, wird zustzlich ein
ActionEvent an registrierte ActionListener versendet.
Das folgende Programm enthlt zwei Comboboxen, von denen die erste editierbar ist und die zweite nicht. Ihre Elemente
stammen aus einem String-Array mit Farbwerten, das direkt an den Konstruktor bergeben wird:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
/* Listing3711.java */
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Listing3711
extends JFrame
{
private static final String[] COLORS = {
"rot", "grn", "blau", "gelb"
};
public Listing3711()
{
super("JComboBox");
addWindowListener(new WindowClosingAdapter(true));
Container cp = getContentPane();
for (int i = 1; i <= 2; ++i) {
JPanel panel = new JPanel();
panel.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 2));
panel.add(new JLabel("Farbe " + i + ":"));
JComboBox combo = new JComboBox(COLORS);
combo.setEditable(i == 1);
panel.add(combo);
Listing3711.java
026
027
028
029
030
031
032
033
034
035
036
037 }
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
37.4.1 JScrollBar
JScrollBar ist die leichtgewichtige Swing-Variante der AWT-Klasse Scrollbar (siehe Abschnitt 32.10). Sie
dient dazu, mit Hilfe eines Schiebereglers einen Wert kontinuierlich innerhalb vorgegebener Grenzen einzustellen. Der
wichtigste Konstruktor der Klasse JScrollBar ist:
public JScrollBar(
int orientation,
int value,
int extent,
int min,
int max
)
javax.swing.JScrollBar
Mit orientation wird die Ausrichtung des Schiebereglers festgelegt. Sie kann entweder HORIZONTAL oder
VERTICAL sein. min gibt den kleinsten, max den grten mglichen Wert an. Mit extent wird die Ausdehnung des
Schiebers festgelegt. Sie mu mindestens eins, kann aber auch grer sein. value ist der Anfangswert des Schiebers.
Er mu zwischen min und max - extent liegen.
JScrollBar stellt einige Methoden zur Verfgung, mit denen nach der Instanzierung auf die numerischen
Eigenschaften des Schiebereglers zugegriffen werden kann:
public int getMinimum()
public void setMinimum(int minimum)
javax.swing.JScrollBar
Mit getMinimum, getMaximum, setMinimum und setMaximum kann auf das Minimum und Maximum des
definierten Wertebereichs zugegriffen werden. getVisibleAmount liefert die Ausdehnung des Schiebers, und mit
setVisibleAmount kann diese abgefragt werden. Mit getValue und setValue kann auf den aktuellen Wert des
Schiebereglers zugegriffen werden.
javax.swing.JScrollBar
getUnitIncrement gibt an, um welchen Betrag der Wert des Schiebereglers verndert wird, wenn der Anwender
einen der Pfeilbuttons bettigt. getBlockIncrement ermittelt analog dazu den Betrag der nderung, wenn
zwischen Schieber und Pfeilbuttons geklickt wird. Mit setUnitIncrement und setBlockIncrement knnen
beide Werte auch verndert werden.
Wird der Wert einer JScrollBar verndert, sendet sie ein AdjustmentEvent an registrierte Listener. Diese
mssen das Interface AdjustmentListener implementieren und werden durch Aufruf von
addAdjustmentListener registriert. Interessant ist in diesem Zusammenhang die Methode
getValueIsAdjusting(), mit der festgestellt werden kann, auf welche Weise der Wert verndert wird. Sie gibt
genau dann true zurck, wenn die nderung Bestandteil einer Kette von nderungen ist, wenn also der Anwender den
Schieber bettigt. Wurde die nderung dagegen durch einen Mausklick auf einen der Buttons oder auf die Flche
zwischen Buttons und Schieber ausgelst, liefert getValueIsAdjusting() den Wert false.
Das folgende Programm zeigt eine einfache Anwendung der Klasse JScrollBar. Es stellt zwei Schieberegler zur
Verfgung, mit deren Hilfe die Hintergrundfarbe des in der Mitte angezeigten Panels verndert werden kann. Alle
nderungen werden durch einen AdjustmentListener registriert und fhren beim Panel zum Aufruf von
setBackground. Am Ende einer nderungssequenz wird der aktuelle Farbwert auf der Konsole ausgegeben.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
/* Listing3712.java */
Listing3712.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Listing3712
extends JFrame
implements AdjustmentListener
{
private JPanel coloredPanel;
private JScrollBar sbEast;
private JScrollBar sbSouth;
private int
blue = 0;
private int
red = 0;
public Listing3712()
{
super("JScrollBar");
addWindowListener(new WindowClosingAdapter(true));
Container cp = getContentPane();
//Vertikaler Schieberegler
sbEast = new JScrollBar(JScrollBar.VERTICAL, 0, 10, 0, 255);
sbEast.addAdjustmentListener(this);
cp.add(sbEast, BorderLayout.EAST);
//Horizontaler Schieberegler
sbSouth = new JScrollBar(JScrollBar.HORIZONTAL, 0, 10, 0, 255);
sbSouth.addAdjustmentListener(this);
cp.add(sbSouth, BorderLayout.SOUTH);
//Farbiges Panel
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057 }
37.4.2 JSlider
Mit der Klasse JSlider werden ebenso wie mit JScrollBar Schieberegler erzeugt. Abgesehen von den
unterschiedlichen Oberflchen gibt es zwischen beiden Klassen zwei wichtige konzeptionelle Unterschiede:
1. Ein JSlider kann eine Anzeigeskala mit grober und feiner Einteilung und Beschriftung haben.
2. Ein JSlider kennt keine unterschiedlichen Schiebergren. Die Ausdehnung der Schieber ist immer 1.
Welche der beiden Klassen in der Praxis eingesetzt werden soll, lt sich nicht eindeutig festlegen. Soll ein
Bildschirminhalt verschoben werden, ist es sicher sinnvoll, bei JScrollBar zu bleiben. Soll dagegen ein Wert
verndert werden, der fr den Anwender den Charakter einer Zahl hat, kann durchaus ein JSlider verwendet werden.
javax.swing.JSlider
Der Parameter orientation gibt die Orientierung an. Hier knnen die Konstanten HORIZONTAL und VERTICAL
bergeben werden. min und max legen die Grenzen des Wertebereichs fest, und mit value wird der Anfangswert des
Schiebers festgelegt. hnlich wie JScrollBar stellt auch JSlider Methoden zum Zugriff auf die numerischen
Eigenschaften des Sliders zur Verfgung:
public int getMinimum()
public void setMinimum(int minimum)
javax.swing.JSlider
Sie haben dieselbe Bedeutung wie bei JScrollBar. Zustzlich gibt es Methoden zum Zugriff auf die Anzeigeskala
und die Beschriftung:
public int getMajorTickSpacing()
public void setMajorTickSpacing(int n)
javax.swing.JSlider
Die Anzeigeskala eines JSlider hat groe Markierungen, die das Grobraster vorgeben, und dazwischenstehende
kleine, die das Feinraster vorgeben. Mit setMajorTickSpacing wird der Abstand der groen Markierungen
vorgegeben, mit getMajorTickSpacing der Abstand der kleinen. Mit getMinorTickSpacing und
setMinorTickSpacing knnen beide Werte auch abgefragt werden.
Damit die Anzeigeskala tatschlich angezeigt wird, mu setPaintTicks aufgerufen und true bergeben werden.
Soll auch die Beschriftung angezeigt werden, mu zustzlich setPaintLabels mit true als Argument aufgerufen
werden:
public void setPaintTicks(boolean b)
public void setPaintLabels(boolean b)
javax.swing.JSlider
Ein Aufruf von setSnapToTicks (mit bergabe von true als Argument) sorgt dafr, da der Schieber stets auf den
Skalenmarkierungen einrastet. Zwischenpositionen knnen dann nicht mehr angewhlt werden.
Im Gegensatz zu JScrollPane sendet ein JSlider kein AdjustmentEvent, wenn sein Wert verndert wird,
sondern ein ChangeEvent (diese Klasse liegt im Paket javax.swing.event). Um auf dieses Ereignis zu
reagieren, ist das Interface ChangeListener zu implementieren und das implementierende Objekt durch Aufruf von
addChangeListener zu registrieren. Wie bei JScrollPane kann mit getValueIsAdjusting festgestellt
werden, ob die nderung Bestandteil einer Kette von Wertnderungen ist oder ob sie einzeln aufgetreten ist.
Das folgende Programm zeigt ein zu Listing 37.12 quivalentes Beispiel, das zwei JSlider anstelle der Scrollbars
verwendet.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
/* Listing3713.java */
import
import
import
import
import
java.awt.*;
java.awt.event.*;
javax.swing.*;
javax.swing.border.*;
javax.swing.event.*;
Listing3713.java
058
059
060
061
062
063
064
065
066
067
068 }
}
}
public static void main(String[] args)
{
Listing3713 frame = new Listing3713();
frame.setLocation(100, 100);
frame.setSize(300, 250);
frame.setVisible(true);
}
37.4.3 JProgressBar
Ein weiteres Hilfsmittel zur Darstellung von kontinuierlichen Werten ist die Klasse JProgressBar. Sie stellt eine
Fortschrittsanzeige dar, wie sie auch das Betriebssystem oder ein Installationsprogramm bei lngeren Kopiervorgngen
anzeigt. Die Fortschrittsanzeige hat einen aktuellen Wert, der grafisch mit einer Fllstandsanzeige dargestellt wird und
sich schrittweise vom Minimal- zum Maximalwert fortentwickelt.
Die wichtigsten Konstruktoren von JProgressBar sind:
public JProgressBar(int orient)
public JProgressBar(int min, int max)
public JProgressBar(int orient, int min, int max)
javax.swing.JProgressBar
Der Parameter orient gibt die Orientierung der Fortschrittsanzeige an, sie kann wahlweise HORIZONTAL oder
VERTICAL sein. Wird sie ausgelassen, erzeugt Swing eine horizontale Darstellung. min und max geben die untere und
obere Grenze des Wertebereichs an. Wenn sie ausgelassen werden, wird eine Voreinstellung von 0 bzw. 100 verwendet.
Standardmig wird die Fortschrittsanzeige ohne Beschriftung dargestellt. Durch Aufruf von setStringPainted
und bergabe von true kann ein prozentualer Fortschrittswert angezeigt werden:
public void setStringPainted(boolean b)
javax.swing.JProgressBar
Der Wert (und mit ihm die grafische Darstellung des Fortschritts) kann durch Aufruf von setValue verndert werden.
Mit getValue kann dieser auch abgefragt werden:
public void setValue(int n)
public int getValue()
javax.swing.JProgressBar
Das folgende Programm zeigt eine einfache Anwendung von JProgressBar. Es enthlt eine Fortschrittsanzeige mit
einem Wertebereich von 0 bis 100 und einem initialen Wert von 0. Durch Bettigen des Buttons "Weiter" wird der Wert
um fnf erhht. Hat der Wert hundert erreicht, wird er wieder auf null gesetzt.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
/* Listing3714.java */
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Listing3714
extends JFrame
implements ActionListener
{
private JProgressBar pb;
private int
value = 0;
public Listing3714()
{
super("JProgressBar");
addWindowListener(new WindowClosingAdapter(true));
Container cp = getContentPane();
//Fortschrittsanzeige
pb = new JProgressBar(JProgressBar.HORIZONTAL, 0, 100);
pb.setStringPainted(true);
cp.add(pb, BorderLayout.NORTH);
//Weiter-Button
JButton button = new JButton("Weiter");
button.addActionListener(this);
cp.add(button, BorderLayout.SOUTH);
}
public void actionPerformed(ActionEvent event)
{
value = (value >= 100 ? 0 : value + 5);
pb.setValue(value);
}
public static void main(String[] args)
{
Listing3714 frame = new Listing3714();
frame.setLocation(100, 100);
frame.setSize(300, 150);
frame.setVisible(true);
Listing3714.java
041
}
042 }
Listing 37.14: Die Klasse JProgressBar
Die Programmausgabe ist:
JDK1.1-1.4
javax.swing.JProgressBar
Nach Aufruf von setIndeterminate und bergabe von true wird die Fortschrittsanzeige in den kontinuierlichen
Modus versetzt. Dann ist sie dauerhaft animiert, und der aktuelle Wert sowie das eingestellte Minimum und Maximum
werden ignoriert. Die Anwendung selbst braucht sich um die Animation nicht zu kmmern, sie luft automatisch. Durch
Aufruf von setIndeterminate mit false als Argument kann der JProgressBar in den Standardmodus
zurckgesetzt werden. isIndeterminate liefert den aktuellen Wert dieser Eigenschaft.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
38.1.1 JScrollPane
Die Klasse JScrollPane wurde in den vorigen Abschnitten bereits mehrfach verwendet. In Verbindung mit den
Klassen JTextArea und JList bestand ihre Aufgabe darin, Dialogelemente, die zu gro fr den zur Verfgung
stehenden Platz waren, mit Hilfe eines verschiebbaren Fensters ausschnittsweise sichtbar zu machen. Dabei war es
ausreichend, das betreffende Dialogelement an den Konstruktor von JScrollPane zu bergeben und anstelle des
Dialogelements selbst die JScrollPane-Instanz an den Container zu bergeben.
JScrollPane besitzt aber noch weitere Fhigkeiten, die hier kurz vorgestellt werden sollen. Zunchst wollen wir
uns ihre wichtigsten Konstruktoren ansehen:
javax.swing.JScrollPane
public JScrollPane(Component view)
public JScrollPane(Component view, int vsbPolicy, int hsbPolicy)
Die Argumente vsbPolicy und hsbPolicy geben an, wann ein horizontaler bzw. vertikaler Schieberegler
eingeblendet wird. Hier knnen folgende Werte angegeben werden:
Konstante
Bedeutung
VERTICAL_SCROLLBAR_NEVER
VERTICAL_SCROLLBAR_ALWAYS
VERTICAL_SCROLLBAR_AS_NEEDED
HORIZONTAL_SCROLLBAR_NEVER
Der horizontale
Schieberegler wird nie
angezeigt.
HORIZONTAL_SCROLLBAR_ALWAYS
Der horizontale
Schieberegler wird immer
angezeigt.
javax.swing.JScrollPane
Mit setColumnHeaderView kann eine Komponente fr den Spaltenkopf angegeben werden. Sie wird ber dem
eigentlichen Dialogelement angezeigt und bei horizontalen Bewegungen zusammen mit diesem verschoben. Bei
vertikalen Schieberbewegungen bleibt sie dagegen an ihrem Platz. Analog dazu kann mit setRowHeaderView
ein Zeilenkopf angegeben werden, der links neben der eigentlichen Komponente plaziert wird. Er wird bei
vertikalen Bewegungen verschoben und behlt bei horizontalen Bewegungen seinen Platz bei.
Mit setCorner kann in einer beliebigen der vier ungenutzten Ecken einer JScrollPane ein Dialogelement
plaziert werden. Der Parameter key gibt dabei an, welche Ecke belegt werden soll. Als Argument kann eine der
Konstanten LOWER_LEFT_CORNER, LOWER_RIGHT_CORNER , UPPER_LEFT_CORNER oder
UPPER_RIGHT_CORNER der Klasse JScrollPane angegeben werden.
Zu beachten ist allerdings, da die Eckflchen unter Umstnden gar nicht zur Verfgung stehen. Die
Hinweis
beiden Ecken auf der linken Seite sind beispielsweise nur dann vorhanden, wenn ein Zeilenkopf
eingeblendet wurde. Die rechte obere ist nur vorhanden, wenn ein vertikaler Schieberegler eingeblendet
wurde, und die rechte untere erfordert sogar die Anwesenheit beider Schieberegler. Auch kann die
Anwendung praktisch keinen Einflu auf die Gre der Ecken nehmen. Diese wird ausschlielich die
Ausdehnung der Schieberegler und des Zeilenkopfes bestimmt. Summa sumarum ist die Mglichkeit, die
Ecken belegen zu knnen, eine nur in Ausnahmefllen ntzliche Eigenschaft.
/* Listing3801.java */
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Listing3801
extends JFrame
{
public Listing3801()
{
super("JScrollPane");
addWindowListener(new WindowClosingAdapter(true));
//Dialogpanel erzeugen
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(10, 10));
for (int i = 1; i <= 100; ++i) {
panel.add(new JCheckBox("Frage " + i));
}
//JScrollPane erzeugen
JScrollPane scroll = new JScrollPane(panel);
scroll.setCorner(
JScrollPane.UPPER_RIGHT_CORNER,
new JLabel("1", JLabel.CENTER)
);
scroll.setCorner(
JScrollPane.LOWER_RIGHT_CORNER,
new JLabel("2", JLabel.CENTER)
);
scroll.setColumnHeaderView(new ColumnHeader(panel, 10));
//JScrollPane zur ContentPane hinzufgen
getContentPane().add(scroll, BorderLayout.CENTER);
}
public static void main(String[] args)
{
Listing3801 frame = new Listing3801();
frame.setLocation(100, 100);
frame.setSize(300, 150);
frame.setVisible(true);
}
}
class ColumnHeader
extends JComponent
{
JComponent component;
int
columns;
public ColumnHeader(JComponent component, int columns)
{
this.component = component;
this.columns
= columns;
}
public void paintComponent(Graphics g)
Listing3801.java
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073 }
{
int width = component.getSize().width;
int height = getSize().height;
int colwid = width / columns;
for (int i = 0; i < columns; ++i) {
g.setColor(i % 2 == 0 ? Color.yellow : Color.gray);
g.fillRect(i * colwid, 0, colwid, height);
}
g.setColor(Color.black);
g.drawLine(0, height - 1, width, height - 1);
}
public Dimension getPreferredSize()
{
return new Dimension(component.getSize().width, 20);
}
38.1.2 JSplitPane
JSplitPane ist ein Panel, mit dem zwei Komponenten neben- oder bereinander plaziert werden knnen. Die
Komponenten werden dabei durch einen sichtbaren Separator voneinander getrennt. Der Anwender kann den
Separator verschieben und so den Platz, der beiden Komponenten zur Verfgung steht, variieren. Ein JSplitPane
ist beispielsweise ntzlich, wenn Komponenten dargestellt werden sollen, deren Breite oder Hhe von den darin
enthaltenen Daten abhngig ist. Das Programm braucht dann den bentigten Platz nur grob vorzugeben, und der
Anwender kann durch Verschieben des Separators zur Laufzeit festlegen, wieviel Platz er jeder Komponente zur
Verfgung stellen will.
Die Konstruktoren von JSplitPane sind:
javax.swing.JSplitPane
public JSplitPane(
int orientation,
boolean continuousLayout
)
public JSplitPane(
int orientation,
Component leftComponent,
Component rightComponent
)
public JSplitPane(
int orientation,
boolean continuousLayout,
Component leftComponent,
Component rightComponent
)
Der Parameter orientation gibt an, wie die Elemente zueinander angeordnet werden sollen. Hat er den Wert
HORIZONTAL_SPLIT, werden sie nebeneinander, bei VERTICAL_SPLIT bereinander angeordnet. Hat
continuousLayout den Wert true, so wird schon beim Verschieben des Separators der Bildschirminhalt
aktualisiert. Andernfalls erfolgt das erst nach Ende des Verschiebevorgangs. In den Parametern leftComponent
und rightComponent werden die beiden einzubettenden Komponenten bergeben. Alle Eigenschaften knnen
auch nach der Instanzierung verndert bzw. abgefragt werden:
public
public
public
public
public
public
void
void
void
void
void
void
setOrientation(int orientation)
setContinuousLayout(boolean continuousLayout)
setLeftComponent(Component comp)
setRightComponent(Component comp)
setTopComponent(Component comp)
setBottomComponent(Component comp)
public
public
public
public
public
public
int getOrientation()
boolean isContinuousLayout()
Component getLeftComponent()
Component getTopComponent()
Component getRightComponent()
Component getBottomComponent()
javax.swing.JSplitPane
Die Gre der beiden Komponenten richtet sich nach ihren minimalen und gewnschten Abmessungen.
JSplitPane ermittelt diese Werte durch Aufruf von getPreferredSize und getMinimumSize auf den
Komponentenobjekten. Es gilt:
Bei der initialen Darstellung der Komponenten wird zunchst geprft, ob beide in ihrer gewnschten Gre
angezeigt werden knnen (es wird also getPreferredSize aufgerufen). Ist das der Fall, wird die linke
(obere) Komponente in der gewnschten Gre angezeigt, und die rechte (untere) erhlt den verbleibenden
Platz.
Ist das nicht der Fall, wird die linke (untere) Komponente in ihrer minimalen Gre angezeigt, und die rechte
erhlt den verbleibenden Platz.
Reicht der Platz nicht einmal aus, um beide Komponenten in ihrer minimalen Ausdehnung anzuzeigen, ist
das Verhalten undefiniert. Typischerweise steht der Separator danach komplett auf einer Seite, und es ist nur
noch eine der beiden Komponenten sichtbar.
Sind die Komponenten auf dem Bildschirm sichtbar, kann der Separator soweit in jede Richtung verschoben
werden, bis die minimale Gre der jeweiligen Komponente erreicht ist.
Manche Komponenten liefern beim Aufruf von getMinimumSize die Gre, die sie beim ersten
Tip
Sichtbarwerden auf dem Bildschirm hatten. In diesem Fall ist der Separator berhaupt nicht vernderbar, denn
jede Verschiebung wrde die minimale Gre einer der beiden Komponenten unterschreiten. Falls ein solches
Problem auftritt, kann Abhilfe geschaffen werden, indem durch Aufruf von setMinimumSize die
minimale Gre der Komponenten vermindert wird.
Eine interessante Methode zur Modifikation des Separators ist setOneTouchExpandable:
public void setOneTouchExpandable(boolean newValue)
javax.swing.JSplitPane
Wird sie mit true als Argument aufgerufen, erhlt der Separator zwei kleine Pfeile, die jeweils auf eine der beiden
Komponenten zeigen. Wird einer von ihnen angeklickt, so wird der Separator komplett auf die angezeigte Seite
verschoben. Die auf dieser Seite liegende Komponente wird verdeckt und die andere erhlt den vollen zur
Verfgung stehenden Platz (die von getMinimumSize definierten Grenzen werden dabei ignoriert). Ein weiterer
Klick auf den Separator gibt der verdeckten Komponente wieder ihre Minimalgre.
Das folgende Programm zeigt einen JFrame mit einem horizontal geteilten JSplitPane. Als Komponenten
werden zwei Instanzen der weiter unten definierten Klasse GridComponent verwendet, die ein Gitternetz anzeigt,
dessen Maschengre proportional zur Ausdehnung der Komponente ist:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
/* Listing3802.java */
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Listing3802
extends JFrame
{
public Listing3802()
{
super("JSplitPane");
addWindowListener(new WindowClosingAdapter(true));
//Linkes Element erzeugen
GridComponent grid1 = new GridComponent();
grid1.setMinimumSize(new Dimension(50, 100));
grid1.setPreferredSize(new Dimension(180, 100));
//Rechtes Element erzeugen
GridComponent grid2 = new GridComponent();
grid2.setMinimumSize(new Dimension(100, 100));
grid2.setPreferredSize(new Dimension(80, 100));
//JSplitPane erzeugen
JSplitPane sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
sp.setLeftComponent(grid1);
sp.setRightComponent(grid2);
sp.setOneTouchExpandable(true);
sp.setContinuousLayout(true);
getContentPane().add(sp, BorderLayout.CENTER);
}
public static void main(String[] args)
{
Listing3802 frame = new Listing3802();
Listing3802.java
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
frame.setLocation(100, 100);
frame.setSize(300, 200);
frame.setVisible(true);
}
}
class GridComponent
extends JComponent
{
public void paintComponent(Graphics g)
{
g.setColor(Color.gray);
int width = getSize().width;
int height = getSize().height;
for (int i = 0; i < 10; ++i) {
g.drawLine(i * width / 10, 0, i * width / 10, height);
}
for (int i = 0; i < 10; ++i) {
g.drawLine(0, i * height / 10, width, i * height / 10);
}
g.setColor(Color.black);
g.drawString("" + width, 5, 15);
}
}
38.1.3 JTabbedPane
Als letztes der speziellen Panels wollen wir uns die Klasse JTabbedPane ansehen. Mit ihr ist es mglich, Dialoge
zu erstellen, die eine Reihe von Registerkarten enthalten. Das sind Unterdialoge, die ber ein am Rand befindliches
Register einzeln ausgewhlt werden knnen. Derartige Registerkarten werden beispielsweise in
Konfigurationsdialogen verwendet, wenn die zu bearbeitenden Optionen nicht alle auf eine Seite passen.
JTabbedPane stellt zwei Konstruktoren zur Verfgung:
public JTabbedPane()
public JTabbedPane(int tabPlacement)
javax.swing.JTabbedPane
Der erste von beiden erzeugt ein JTabbedPane mit oben liegenden Registern. Beim zweiten kann explizit
angegeben werden, auf welcher Seite die Register angeordnet werden sollen. Hier knnen die Konstanten TOP,
BOTTOM, LEFT oder RIGHT aus dem Interface SwingConstants bergeben werden. blich ist es, die Register
links oder oben anzuordnen. Mit den Methoden getTabPlacement und setTabPlacement kann auch
nachtrglich auf die Anordnung zugegriffen werden.
Nach der Instanzierung ist der Registerdialog zunchst leer. Um Registerkarten hinzuzufgen, kann eine der
Methoden addTab oder insertTab aufgerufen werden:
public void addTab(
String title,
Icon icon,
Component component,
String tip
)
javax.swing.JTabbedPane
Der Parameter title gibt die Beschriftung des Registereintrags an. Mit icon kann zustzlich ein Icon und mit
tip ein Tooltiptext hinzugefgt werden. Der Parameter component gibt das darzustellende Dialogelement an.
Meist wird hier eine Containerklasse bergeben (z.B. ein JPanel), die den entsprechenden Unterdialog enthlt.
Die Parameter icon und tip sind optional, d.h. es gibt die beiden Methoden auch ohne sie. addTab fgt die
Registerkarte am Ende ein, bei insertTab kann die Einfgeposition mit dem Parameter index selbst angegeben
werden.
Bereits definierte Registerkarten knnen auch wieder entfernt werden:
public void removeTabAt(int index)
public void removeAll()
javax.swing.JTabbedPane
removeTabAt entfernt die Karte mit dem angegebenen Index, removeAll entfernt alle Karten.
Mit getTabCount kann die Anzahl der Registerkarten ermittelt werden, und mit getComponentAt kann die
Komponente einer beliebigen Registerkarte ermittelt werden. Mit setComponentAt kann der Inhalt einer
Registerkarte sogar nachtrglich ausgetauscht werden:
public int getTabCount()
javax.swing.JTabbedPane
In einem Registerdialog ist immer genau eine der Karten selektiert. Mit getSelectedIndex kann deren Index
ermittelt werden, mit getSelectedComponent sogar direkt auf ihren Inhalt zugegriffen werden. Die Methode
setSelectedIndex erlaubt es, programmgesteuert eine beliebige Registerkarte auszuwhlen:
javax.swing.JTabbedPane
Registerkarten besitzen eine Reihe von Eigenschaften, die einzeln verndert werden knnen. Die wichtigste von
ihnen ist die Mglichkeit, sie zu aktivieren oder zu deaktivieren. Nur aktivierte Karten knnen ausgewhlt werden,
deaktivierte dagegen nicht. Der Zugriff auf den Aktivierungsstatus erfolgt mit den Methoden setEnabledAt und
isEnabledAt:
public boolean isEnabledAt(int index)
public void setEnabledAt(int index, boolean enabled)
javax.swing.JTabbedPane
Bei jeder nderung der Selektion versendet ein JTabbedPane ein ChangeEvent an registrierte
Tip
ChangeListener. Auf diese Weise ist es mglich, Programmcode zu schreiben, der beim Anwhlen oder
Verlassen einzelner Registerkarten ausgefhrt wird. Eine der Anwendungen hierfr besteht darin, die Dialoge
auf den Registerkarten erst dann zu erzeugen, wenn sie wirklich gebraucht werden. Dazu wird an alle
Registerkarten zunchst ein leeres Panel bergeben und erst in der Methode stateChanged durch den
eigentlichen Dialog ersetzt. Auf diese Weise spart das Programm beim Initialisieren des Registerdialogs
Rechenzeit und Speicher, was sich vor allem bei komplexen Dialogen positiv bemerkbar machen knnte.
Das folgende Programm zeigt ein einfaches Beispiel eines Registerdialogs. Es erstellt ein JTabbedPane mit fnf
Registerkarten, die jeweils ein JPanel mit einem Label und einem Button enthalten. Das Label zeigt den Namen
der Karte an, der Button dient dazu, die jeweils nchste Karte auszuwhlen. Dessen Aktivitt ist in der Klasse
NextTabActionListener gekapselt. Als Besonderheit wird nach jedem Seitenwechsel
requestDefaultFocus auf der neuen Seite aufgerufen, um automatisch dem ersten Dialogelement den Fokus
zu geben.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
/* Listing3803.java */
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Listing3803
extends JFrame
{
JTabbedPane tp;
public Listing3803()
{
super("JTabbedPane");
addWindowListener(new WindowClosingAdapter(true));
tp = new JTabbedPane();
for (int i = 0; i < 5; ++i) {
JPanel panel = new JPanel();
panel.add(new JLabel("Karte " + i));
JButton next = new JButton("Weiter");
next.addActionListener(new NextTabActionListener());
panel.add(next);
tp.addTab("Tab" + i, panel);
}
getContentPane().add(tp, BorderLayout.CENTER);
}
Listing3803.java
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047 }
class NextTabActionListener
implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
int tab = tp.getSelectedIndex();
tab = (tab >= tp.getTabCount() - 1 ? 0 : tab + 1);
tp.setSelectedIndex(tab);
((JPanel)tp.getSelectedComponent()).requestDefaultFocus();
}
}
public static void main(String[] args)
{
Listing3803 frame = new Listing3803();
frame.setLocation(100, 100);
frame.setSize(300, 200);
frame.setVisible(true);
}
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
38.2 JTable
38.2 JTable
38.2.1 Erzeugen von Tabellen
38.2.2 Konfiguration der Tabelle
38.2.3 Selektieren von Elementen
Selektionsmodi
Abfragen der Selektion
Verndern der Selektion
38.2.4 Zugriff auf den Inhalt der Tabelle
Die Daten in der Tabelle
Editieren von Tabellenelementen
38.2.5 Das Tabellenmodell
Beispiel
38.2.6 Das Spaltenmodell
38.2.7 Rendering der Zellen
38.2.8 Reaktion auf Ereignisse
javax.swing.JTable
An den ersten Konstruktor werden die darzustellenden Daten in Form eines zweidimensionalen Arrays bergeben. Dessen erste
Dimension enthlt die Zeilen, die zweite die Spalten. Zur Darstellung in der Tabelle werden die Array-Elemente mit toString in
Strings umgewandelt. Das zweite Argument enthlt ein Array mit Strings, die als Spaltenkpfe angezeigt werden.
Statt der bergabe von Arrays kann auch der zweite Konstruktor verwendet und die Daten und Spaltenkpfe in einem Vector
bergeben werden. In diesem Fall mu der Datenvektor rowData fr jede Zeile einen Untervektor mit den Datenelementen dieser
Zeile enthalten.
Der dritte Konstruktor stellt die allgemeinste Mglichkeit dar, eine JTable zu konstruieren. Hierbei werden alle drei Modelle der
Tabelle explizit an den Konstruktor bergeben. Das TableModel stellt dabei die Daten zur Verfgung, das
TableColumnModel definiert die Spalten, und das ListSelectionModel ist fr die Selektion von Tabellenelementen
zustndig. Werden alle drei Modelle separat instanziert und bergeben, hat das Programm die volle Kontrolle ber alle Aspekte der
Tabellendarstellung und -verarbeitung.
Eine einfache Tabelle lt sich also sehr schnell erzeugen:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
/* Listing3804.java */
Listing3804.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Listing3804
extends JFrame
implements TableData
{
public Listing3804()
{
super("JTable 1");
addWindowListener(new WindowClosingAdapter(true));
JTable table = new JTable(DATA, COLHEADS);
Container cp = getContentPane();
cp.add(new JLabel("Alte c\'t-Ausgaben:"), BorderLayout.NORTH);
cp.add(new JScrollPane(table), BorderLayout.CENTER);
}
public static void main(String[] args)
{
Listing3804 frame = new Listing3804();
frame.setLocation(100, 100);
frame.setSize(300, 200);
frame.setVisible(true);
}
}
Warnung
In Listing 38.4 wurden zwei Konstanten DATA und COLHEADS verwendet. Sie dienen als Beispieldaten fr die Programme dieses
Abschnitts und wurden als Konstanten in dem Interface TableData definiert:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
/* TableData.java */
TableData.java
javax.swing.JTable
Standardmig werden die Zellen einer JTable mit senkrechten und waagerechten Begrenzungslinien voneinander getrennt. Mit
setShowGrid knnen beide Linienarten zugleich an- oder ausgeschaltet werden. Sollen die horizontalen oder vertikalen Linien
separat aktiviert oder deaktiviert werden, knnen die Methoden setShowHorizontalLines und
setShowVerticalLines verwendet werden:
public void setShowGrid(boolean b)
public void setShowHorizontalLines(boolean b)
public void setShowVerticalLines(boolean b)
Das Verndern der Farben der Zellen ist in begrenzter Weise mit folgenden Methoden mglich:
javax.swing.JTable
javax.swing.JTable
setGridColor verndert die Farbe, in der die Gitterlinien angezeigt werden. Mit setSelectionForeground und
setSelectionBackground wird die Vorder- und Hintergrundfarbe des selektierten Bereichs festgelegt.
Als letzte der Konfigurationsmethoden wollen wir uns setAutoResizeMode ansehen:
public void setAutoResizeMode(int mode)
javax.swing.JTable
Sie bestimmt das Verhalten der Tabelle, nachdem die Breite einer einzelnen Spalte verndert wurde. Der dadurch freiwerdende
oder zustzlich bentigte Platz kann nmlich auf unterschiedliche Weise den brigen Spalten zugeordnet werden. Der Parameter
mode kann folgende Werte annehmen:
Modus
Bedeutung
AUTO_RESIZE_OFF
AUTO_RESIZE_LAST_COLUMN
AUTO_RESIZE_NEXT_COLUMN
void
void
void
void
setRowSelectionAllowed(boolean flag)
setColumnSelectionAllowed(boolean flag)
setSelectionMode(int selectionMode)
setCellSelectionEnabled(boolean flag)
javax.swing.JTable
Soll zeilenweise selektiert werden, ist setRowSelectionAllowed mit true als Argument aufzurufen. Soll spaltenweise
selektiert werden, ist analog setColumnSelectionAllowed aufzurufen. Durch bergabe von false knnen beide
Selektionsarten ausgeschaltet werden und nur noch einzelne Zellen selektiert werden. Standardmig kann zeilen-, aber nicht
spaltenweise selektiert werden.
Mit setSelectionMode wird festgelegt, ob ein einzelnes Element, ein zusammenhngender Bereich oder mehrere Bereiche
selektiert werden knnen. Hier ist eine der in Abschnitt 37.3.1 beschriebenen Konstanten SINGLE_SELECTION,
javax.swing.JTable
getSelectedRow und getSelectedColumn liefern die selektierte Zeile bzw. Spalte, wenn der Selektionsmodus
SINGLE_SELECTION ist. Die erste Zeile und Spalte haben dabei jeweils den Index 0. Erlaubt der aktuelle Selektionsmodus das
Selektieren ganzer Zeilen oder Spalten, impliziert das Ergebnis, da alle Elemente dieser Zeile bzw. Spalte selektiert sind. Ist einer
der Mehrfachselektionsmodi aktiviert, knnen mit getSelectedRows und getSelectedColumns Arrays mit allen
selektierten Zeilen und Spalten beschafft werden.
Falls keine Elemente selektiert sind, geben getSelectedRow und getSelectedColumn -1 und
getSelectedRows und getSelectedColumns ein leeres Array zurck.
Hinweis
javax.swing.JTable
Mit selectAll kann die komplette Tabelle markiert werden, mit clearSelection wird die Selektion entfernt. Mit
setRowSelectionInterval kann ein zusammenhngender Bereich von Zeilen markiert werden. Mit
addRowSelectionInterval wird ein solcher zur aktuellen Selektion hinzugefgt und mit
removeRowSelectionInterval daraus entfernt. Fr die Selektion von Spalten stehen die analogen Methoden
setColumnSelectionInterval, addColumnSelectionInterval und removeColumnSelectionInterval zur
Verfgung.
Damit die beschriebenen Methoden korrekt funktionieren, sollte ihr Aufruf in Einklang mit den aktuell gewhlten
Selektionsmodi stehen. Im RC1 des JDK 1.3 gab es beispielsweise Probleme, wenn selectAll auf einer Tabelle
aufgerufen wurde, die nur Einfachselektion erlaubte. Nach dem Aufruf wurde zwar (korrekterweise) keine Selektion
mehr angezeigt, die Methoden zur Abfrage der selektierten Elemente verhielten sich aber dennoch so, als wren alle
Elemente selektiert. Bei Verwendung von addRowSelectionInterval fiel dagegen auf, da die Methode nur
dann korrekt funktionierte, wenn der Selektionsmodus MULTIPLE_INTERVAL_SELECTION aktiviert war.
Warnung
javax.swing.JTable
getRowCount und getColumnCount liefern die aktuelle Zeilen- bzw. Spaltenzahl der Tabelle. Mit getValueAt kann auf
das Element an der Position (row, column) zugegriffen werden. Beide Indices beginnen bei 0, ein Zugriff auerhalb der Grenzen
wird mit einer ArrayIndexOutOfBoundsException quittiert. Mit setValueAt kann ein Zellenelement sogar verndert
werden.
Bei der Verwendung der Methoden getValueAt und setValueAt ist es wichtig zu wissen, da die angegebenen
Zeilen- und Spaltenwerte sich auf die aktuelle Ansicht der Tabelle beziehen, nicht auf ihr Modell. Hat der Anwender
beispielsweise die Spalten eins und zwei vertauscht, wrde ein Zugriff auf ein Element in Spalte eins den Modellwert
in Spalte zwei verndern und umgekehrt. Whrend dieses Verhalten erwartungskonform ist, wenn der Wert durch den
Anwender editiert wird, wrde es bei programmgesteuertem Aufruf zu einem logischen Fehler kommen, denn das
Programm hat natrlich zunchst einmal keine Kenntnis davon, da der Anwender die Spaltenreihenfolge verndert
hat. In aller Regel werden programmgesteuerte Zugriffe auf einzelne Zellen daher nicht mit getValueAt und
setValueAt ausgefhrt, sondern an die gleichnamigen Methoden des TableModel delegiert.
Warnung
javax.swing.JTable
isEditing gibt genau dann true zurck, wenn gerade ein Element der Tabelle gendert wird. Mit getEditingRow und
getEditingColumn kann das Programm herausfinden, welches Element betroffen ist. Wird keine Zelle editiert, geben die
Methoden -1 zurck. Zudem kann das Programm durch Aufruf von editCellAt selbst das Editieren eines Tabellenelements
einleiten:
public boolean editCellAt(int row, int column)
javax.swing.JTable
Unabhngig von seinem bisherigen Typ wird der genderte Wert nach Abschlu des nderungsvorgangs als String in Hinweis
das Modell zurckgeschrieben. Wird - wie im letzten Beispiel - ein Object-Array als Modell verwendet, ist diese
Typkonvertierung zwar korrekt, kann aber bei der Weiterverarbeitung des Modells zu berraschungen fhren. Besser ist
es, Eingaben des Anwenders direkt nach der Eingabe zu prfen und vor der Speicherung in den passenden Typ
umzuwandeln. Wir werden im nchsten Abschnitt zeigen, wie man das mit Hilfe eines eigenen Tabellenmodells
erreichen kann.
javax.swing.JTable
javax.swing.table.TableModel
Die meisten von ihnen sind Service-Methoden. Sie werden von JTable aufgerufen, um Informationen zur Darstellung der Tabelle
zu erhalten. getRowCount und getColumnCount liefern die Anzahl der Zeilen und Spalten, getColumnName die
Spaltenberschrift und getColumnClass den Typ der Elemente einer Spalte. Mit isCellEditable wird abgefragt, ob eine
bestimmte Zelle editiert werden darf oder nicht. Mit getValueAt fragt die Tabelle beim Modell nach dem Wert einer
bestimmten Zelle, und mit setValueAt wird ein genderter Wert in das Modell zurckgeschrieben.
Mit den Methoden addTableModelListener und removeTableModelListener kann ein TableModelListener
registriert bzw. deregistriert werden. Er wird ber alle nderungen des Modells unterrichtet und damit insbesondere aufgerufen,
wenn eine Zeile oder Spalte eingefgt oder gelscht wurde, wenn der Inhalt einer Zelle modifiziert wurde oder wenn die
Gesamtstruktur des Modells sich gendert hat. Typischerweise registriert sich die JTable bei ihrem Modell, um auf
Modellnderungen mit entsprechenden nderungen der Benutzeroberflche reagieren zu knnen.
Beispiel
Als Beispiel wollen wir ein Modell konstruieren, das eine sehr groe Tabelle reprsentieren kann (z.B. mit 1000 mal 1000
Elementen), von denen aber nur sehr wenige tatschlich einen Wert enthalten und alle anderen leer sind. Statt einer
speicherintensiven Darstellung mittels eines entsprechend dimensionierten Arrays sollen nur die tatschlich belegten Elemente
gespeichert werden. Wir wollen dazu eine Hashtable verwenden, deren Elemente die tatschlich vorhandenen Werte sind. Als
Schlssel verwenden wir eine String-Darstellung der Koordinaten des Elements. Der Zugriff auf ein Element erfolgt dann, indem
dessen Koordinatenschlssel in der Hashtable gesucht und der zugehrige Wert zurckgegeben bzw. gespeichert wird.
Um das Tabellenmodell nicht von Grund auf neu entwickeln zu mssen, leiten wir es aus der Klasse AbstractTableModel des
Pakets javax.swing.table ab. Diese bietet fr fast alle erforderlichen Methoden Standardimplementierungen und stellt
darber hinaus einige ntzliche Hilfsmethoden zur Verfgung:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
/* SparseTableModel.java */
import java.util.*;
import javax.swing.*;
import javax.swing.table.*;
public class SparseTableModel
extends AbstractTableModel
{
private int size;
private Hashtable data;
//Konstruktor
public SparseTableModel(int size)
{
this.size = size;
this.data = new Hashtable();
}
//Methoden fr das TableModel-Interface
public int getRowCount()
{
return size;
SparseTableModel.java
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075 }
}
public int getColumnCount()
{
return size;
}
public String getColumnName(int columnIndex)
{
return "C" + columnIndex;
}
public Class getColumnClass(int columnIndex)
{
return String.class;
}
public boolean isCellEditable(int rowIndex, int columnIndex)
{
return rowIndex < size && columnIndex < size;
}
public Object getValueAt(int rowIndex, int columnIndex)
{
String key = "[" + rowIndex + "," + columnIndex + "]";
String value = (String)data.get(key);
return value == null ? "-" : value;
}
public void setValueAt(Object aValue, int rowIndex, int columnIndex)
{
String key = "[" + rowIndex + "," + columnIndex + "]";
String value = (String)aValue;
if (value.length() <= 0) {
data.remove(key);
} else {
data.put(key, value);
}
}
//Zustzliche Methoden
public void printData()
{
Enumeration e = data.keys();
while (e.hasMoreElements()) {
String key = (String)e.nextElement();
System.out.println(
"At " + key + ": " + (String)data.get(key)
);
}
}
Mit Hilfe dieses Modells Tabellen zu bauen, die auch bei groen Abmessungen noch effizient arbeiten, ist nicht mehr schwer. Das
folgende Programm zeigt das am Beispiel einer Tabelle mit einer Million Zellen. Neben der Tabelle enthlt es einen Button
"Drucken", mit dem die aktuelle Belegung der Tabelle ausgegeben werden kann.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
/* Listing3807.java */
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Listing3807
extends JFrame
implements ActionListener
{
JTable table;
SparseTableModel tableModel;
public Listing3807()
{
super("JTable 2");
addWindowListener(new WindowClosingAdapter(true));
tableModel = new SparseTableModel(1000);
table = new JTable(tableModel, null);
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
table.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
table.setCellSelectionEnabled(true);
Container cp = getContentPane();
cp.add(new JScrollPane(table), BorderLayout.CENTER);
JButton button = new JButton("Drucken");
button.addActionListener(this);
cp.add(button, BorderLayout.SOUTH);
}
public void actionPerformed(ActionEvent event)
{
tableModel.printData();
}
public static void main(String[] args)
{
Listing3807 frame = new Listing3807();
frame.setLocation(100, 100);
frame.setSize(320, 200);
frame.setVisible(true);
}
}
Listing3807.java
[997,998]:
[994,997]:
[999,999]:
[996,999]:
groe
Hallo
Welt
Tabellen-
javax.swing.table.DefaultTableColumnModel
Jede an das Modell bergebene Instanz der Klasse TableColumn reprsentiert dabei die Eigenschaften einer einzelnen
Tabellenspalte. Mit einer TableColumn knnen praktisch alle visuellen Eigenschaften der Spalte kontrolliert werden. So kann
die Breite ebenso wie die Spaltenposition festgelegt werden, und es knnen beliebige Komponenten zur Darstellung und zum
Editieren der Zellen definiert werden. Wie wollen uns auf ein einfaches Beispiel beschrnken und lediglich zeigen, wie die
anfngliche Breite der Spalten explizit festgelegt werden kann.
Dazu instanzieren wir ein DefaultTableColumnModel und fgen drei TableColumn-Objekte hinzu. Sie werden jeweils
mit folgendem Konstruktor initialisiert:
public TableColumn(int modelIndex, int width)
javax.swing.table.TableColumn
Der erste Parameter gibt den Modellindex an, also die Spalte im Tabellenmodell, zu der die visuelle Spalte korrespondiert. Der
zweite Parameter gibt die initiale Breite der Spalte an. Anschlieend rufen wir die Methode setHeaderValue auf, um die
Spaltenbeschriftung zu definieren, und fgen die Spalte in das Spaltenmodell ein. Das wiederholen wir fr alle drei Spalten und
bergeben das Spaltenmodell an den Konstruktor der Tabelle. Da bei bergabe eines Spaltenmodells auch das Tabellenmodell
explizit bergeben werden mu, definieren wir es aus unserem vorhandenen Datenarray durch eine lokale Ableitung der Klasse
AbstractTableModel:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
/* Listing3808.java */
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
public class Listing3808
extends JFrame
implements TableData
{
public Listing3808()
{
super("JTable 3");
addWindowListener(new WindowClosingAdapter(true));
//Spaltenmodell erzeugen
DefaultTableColumnModel cm = new DefaultTableColumnModel();
for (int i = 0; i < COLHEADS.length; ++i) {
Listing3808.java
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051 }
Sofern nicht in den Tabellenspalten ein eigener Renderer bestimmt wird, ist der Standard-Renderer fr die Darstellung aller
Tabellenelemente zustndig. Er mu das Interface TableCellRenderer implementieren. Es enthlt nur eine einzige Methode:
public Component getTableCellRendererComponent(
JTable table,
Object value,
boolean isSelected,
boolean hasFocus,
int row,
int column
)
javax.swing.table.TableCellRenderer
Diese arbeitet als Factory-Methode und wird immer dann aufgerufen, wenn zur Darstellung einer Zelle ein Renderer bentigt wird.
Mit Hilfe der bergebenen Argumente kann der Renderer bestimmen, fr welche Zelle er aktiv werden soll, welchen Inhalt diese
hat, und ob sie gerade selektiert ist oder den Fokus hat. Zustzlich wird die Tabelle selbst bergeben, so da der Renderer Zugriff
auf deren Eigenschaften und Modelle hat.
Standardmig wird als Renderer eine Instanz der Klasse DefaultTableCellRenderer verwendet. Sie ist eine Ableitung
von JLabel, mit deren Hilfe Farbe, Font und Hintergrund an das Look-and-Feel der Tabelle und die Erfordernisse der jeweiligen
Zelle anpat werden. Interessanterweise wird pro Tabelle lediglich eine einzige Instanz erzeugt und zur Darstellung aller Zellen
verwendet. Dazu wird das Label jeweils an die Position der darzustellenden Tabelle verschoben und dann mit den erforderlichen
visuellen Eigenschaften versehen.
Da ein JLabel fr diese Art von Anwendung eigentlich nicht vorgesehen wurde, mu DefaultTableCellRenderer aus
Performancegrnden (insbesondere im JDK 1.3) einige der Standardmechanismen von Swing-Komponenten deaktivieren oder
umdefinieren. Aus diesem Grunde ist das Ableiten einer eigenen Klasse aus DefaultTableCellRenderer problematisch.
Auch das Verwenden des Standard-Renderers in einer eigenen Renderer-Implementierung (mit dem Ziel, nur die wirklich
unterschiedlichen Eigenschaften zu modifizieren) funktioniert nicht ohne weiteres.
Das folgende Beispiel zeigt einen Renderer, dessen Aufgabe darin besteht, die Zellen unserer schon bekannten Tabelle in
unterschiedlichen Farben darzustellen. Die Klasse DefaultTableCellRenderer wird dazu weder per Ableitung noch per
Delegation verwendet.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
/* ColoredTableCellRenderer.java */
import
import
import
import
java.awt.*;
javax.swing.*;
javax.swing.border.*;
javax.swing.table.*;
ColoredTableCellRenderer.java
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
}
052 }
label.setForeground(table.getForeground());
label.setBackground(table.getBackground());
if (hasFocus) {
label.setBackground(darkBlue);
label.setForeground(Color.white);
} else if (isSelected) {
label.setBackground(lightBlue);
} else {
//Angezeigte Spalte in Modellspalte umwandeln
column = table.convertColumnIndexToModel(column);
if (column == 1) {
int numpages = Integer.parseInt((String)value);
if (numpages >= 250) {
label.setBackground(Color.red);
} else if (numpages >= 200) {
label.setBackground(Color.orange);
} else {
label.setBackground(Color.yellow);
}
}
}
return label;
/* Listing3810.java */
import java.awt.*;
import javax.swing.*;
public class Listing3810
extends JFrame
implements TableData
{
public Listing3810()
{
super("JTable 4");
addWindowListener(new WindowClosingAdapter(true));
JTable table = new JTable(DATA, COLHEADS);
table.setDefaultRenderer(
Object.class,
new ColoredTableCellRenderer()
);
Container cp = getContentPane();
cp.add(new JLabel("Alte c\'t-Ausgaben:"), BorderLayout.NORTH);
cp.add(new JScrollPane(table), BorderLayout.CENTER);
}
public static void main(String[] args)
Listing3810.java
025
{
026
Listing3810 frame = new Listing3810();
027
frame.setLocation(100, 100);
028
frame.setSize(350, 200);
029
frame.setVisible(true);
030
}
031 }
Listing 38.10: Eine Tabelle mit einem eigenen Zellrenderer
Die Ausgabe des Programmes sieht nun so aus:
Titel
Inhalt
Suchen
Index
DOC
javax.swing.JTable
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
38.3 JTree
38.3 JTree
38.3.1 Erzeugen eines Baums
38.3.2 Selektieren von Knoten
Konfiguration der Selektionsmglichkeit
Abfragen der Selektion
Verndern der Selektion
38.3.3 ffnen und Schlieen der Knoten
38.3.4 Verndern der Baumstruktur
Der eigentliche Aufwand beim Erzeugen eines Baums liegt im Aufbau eines passenden Datenmodells, das seiner Struktur nach
meist ebenfalls hierarchisch ist. Das Instanzieren des JTree ist dann vergleichsweise einfach. Die beiden wichtigsten
Konstruktoren der Klasse JTree sind:
public JTree(TreeModel newModel)
public JTree(TreeNode root)
javax.swing.JTree
Der erste von beiden erwartet ein vordefiniertes TreeModel zur Darstellung der Elemente des Baums. Ein TreeModel kapselt
alle relevanten Informationen ber die Struktur des Baums. Es liefert auf Anfrage dessen Wurzel, stellt Informationen ber einen
bestimmten Knoten zur Verfgung oder liefert dessen Unterknoten.
An den zweiten Konstruktor wird lediglich die Wurzel des Baums bergeben. Sie wird vom Konstruktor automatisch in ein
geeignetes TreeModel eingebettet. Beide Varianten sind prinzipiell gleichwertig. Zwar erfragt der JTree die zur Darstellung
und Navigation erforderlichen Daten immer beim TreeModel. Aber das mit der Baumwurzel des zweiten Konstruktors
instanzierte DefaultTreeModel ist in der Lage, diese Informationen aus den Knoten und den darin gespeicherten Verweisen
auf ihre Unterknoten zu entnehmen (alle ntigen Informationen werden in den TreeNodes selbst gehalten). Wir werden spter
auf beide Arten, Bume zu konstruieren, noch genauer eingehen.
Ein JTree besitzt nicht so viele Konfigurationsoptionen wie eine JTable. Die wichtigste von ihnen regelt, ob die Wurzel des
Baums bei seiner Darstellung angezeigt oder unterdrckt werden soll. Auf sie kann mit den Methoden setRootVisible und
isRootVisible zugegriffen werden:
public void setRootVisible(boolean rootVisible)
public boolean isRootVisible()
javax.swing.JTree
Wir wollen uns zunchst ein einfaches Beispiel ansehen. Das folgende Programm erzeugt eine rekursive Baumstruktur mit
Wurzel und zwei Unterebenen, deren Knoten aus Objekten des Typs DefaultMutableTreeNode bestehen. Diese im Paket
javax.swing.tree gelegene Klasse ist eine Standardimplementierung des TreeNode-Interfaces, das beschreibt, wie ein
Knoten Informationen ber seine Unter- und Vaterknoten zur Verfgung stellen kann. Die vier wichtigsten Methoden von
TreeNode sind:
public int getChildCount()
public TreeNode getChildAt(int childIndex)
javax.swing.tree.TreeNode
Mit getChildCount kann die Anzahl der Unterknoten ermittelt werden. Sie werden von 0 an durchnumeriert, getChildAt
liefert einen beliebigen Unterknoten. Ein Knoten kennt seinen Vaterknoten, der mit getParent ermittelt werden kann. Mit
isLeaf kann zudem abgefragt werden, ob ein Knoten ein Blatt ist oder weitere Unterknoten enthlt. Zur Beschriftung des
Knotens bei der visuellen Darstellung verwendet ein JTree die Methode toString der Knotenklasse.
Mit DefaultMutableTreeNode steht eine recht flexible Implementierung von TreeNode zur Verfgung, die auch
Methoden zum Einfgen und Lschen von Knoten bietet (sie implementiert brigens das aus TreeNode abgeleitete Interface
MutableTreeNode):
public DefaultMutableTreeNode(Object userObject)
public
public
public
public
void
void
void
void
javax.swing.tree.DefaultMutableTreeNode
add(MutableTreeNode newChild)
insert(MutableTreeNode newChild, int childIndex)
remove(int childIndex)
removeAllChildren()
Mit add wird ein neuer Kindknoten an das Ende der Liste der Unterknoten angefgt, mit insert kann dies an einer beliebigen
Stelle erfolgen. remove entfernt einen beliebigen und removeAllChildren alle Kindknoten. Anwendungsbezogene
Informationen werden in einem UserObject gehalten, das direkt an den Konstruktor bergeben werden kann. Mit
setUserObject und getUserObject kann auch nach der Konstruktion noch darauf zugegriffen werden. Das UserObject
ist auch der Lieferant fr die Knotenbeschriftung: jeder Aufruf von toString wird an das UserObject weitergeleitet.
DefaultMutableTreeNode stellt noch weitaus mehr als die hier beschriebenen Methoden zur Verfgung. Die Klasse Tip
ist sehr vielseitig und kann auch unabhngig von der Verwendung in einem JTree zum Aufbau und zur Verarbeitung
baumartiger Datenstrukturen verwendet werden.
Das folgende Programm erzeugt eine Wurzel mit fnf Unterknoten, die jeweils drei weitere Unterknoten enthalten. Anschlieend
wird der Wurzelknoten an den Konstruktor eines JTree bergeben und dieser durch Einbetten in eine JScrollPane (um
automatisches Scrollen zu ermglichen) in einem JFrame plaziert.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
/* Listing3811.java */
import
import
import
import
Listing3811.java
java.awt.*;
java.awt.event.*;
javax.swing.*;
javax.swing.tree.*;
javax.swing.JTree
Standardmig erlaubt ein JTree das Selektieren mehrerer Knoten. Soll die Selektionsmglichkeit auf einen einzelnen Knoten
beschrnkt werden, mu ein eigenes TreeSelectionModel an setSelectionModel bergeben werden. Dazu kann eine
Instanz der Klasse DefaultTreeSelectionModel erzeugt und durch Aufruf von setSelectionMode und bergabe
einer der Konstanten SINGLE_TREE_SELECTION, CONTIGUOUS_TREE_SELECTION oder
DISCONTIGUOUS_TREE_SELECTION konfiguriert werden:
public void setSelectionMode(int mode)
javax.swing.tree.DefaultTreeSelectionModel
javax.swing.JTree
Mit getSelectionPath wird das selektierte Element ermittelt. Bei aktivierter Mehrfachselektion liefert die Methode das
erste aller selektierten Elemente. Ist kein Knoten selektiert, wird null zurckgegeben. getSelectionPaths gibt ein Array
mit allen selektierten Knoten zurck. getLeadSelectionPath liefert das markierte Element.
Alle beschriebenen Methoden liefern Objekte des Typs TreePath. Diese Klasse beschreibt einen Knoten im Baum ber den
Pfad, der von der Wurzel aus beschritten werden mu, um zu dem Knoten zu gelangen. Mit getLastPathComponent kann
das letzte Element dieses Pfads bestimmt werden. In unserem Fall ist das gerade der selektierte Knoten. Mit getPath kann der
komplette Pfad ermittelt werden. An erster Stelle liegt dabei die Wurzel des Baums, an letzter Stelle das selektierte Element:
public Object getLastPathComponent()
public Object[] getPath()
javax.swing.tree.TreePath
Soll ermittelt werden, ob und welche Elemente im Baum selektiert sind, knnen die Methoden isSelectionEmpty und
isPathSelected aufgerufen werden:
public boolean isSelectionEmpty()
public boolean isPathSelected(TreePath path)
javax.swing.JTree
Alternativ zum TreePath kann auf die selektierten Elemente auch mit Hilfe ihrer internen Zeilennummer zugriffen Hinweis
werden. Dazu besitzt jedes angezeigte Element im Baum eine fortlaufende Nummer, die mit 0 bei der Wurzel beginnt
und sich dann zeilenweise bis zum letzten Element fortsetzt. Die zugehrigen Methoden heien
getSelectionRows und getLeadSelectionRow. Abhngig davon, wie viele Knoten oberhalb eines
bestimmten Knotens sichtbar oder verdeckt sind, kann sich die Zeilennummer whrend der Lebensdauer des Baums
durchaus verndern, und es gibt Methoden, um zwischen Knotenpfaden und Zeilennummern zu konvertieren. Wir
wollen auf dieses Konzept nicht weiter eingehen.
Dient der JTree zur Steuerung anderer Komponenten (etwa in explorerartigen Oberflchen), mu das Programm meist
unmittelbar auf nderungen der Selektion durch den Anwender reagieren. Dazu kann es einen TreeSelectionListener
instanzieren und ihn mit addTreeSelectionListener beim JTree registrieren. Bei jeder Selektionsnderung wird dann
die Methode valueChanged aufgerufen und bekommt ein TreeSelectionEvent als Argument bergeben:
public void valueChanged(TreeSelectionEvent event)
javax.swing.event.TreeSelectionListener
Dieses stellt unter anderem die Methoden getOldLeadSelectionPath und getNewLeadSelectionPath zur
Verfgung, um auf den vorherigen oder aktuellen Selektionspfad zuzugreifen:
public TreePath getOldLeadSelectionPath()
public TreePath getNewLeadSelectionPath()
javax.swing.event.TreeSelectionEvent
Das folgende Programm erweitert Listing 38.11 um die Fhigkeit, das selektierte Element auf der Konsole auszugeben. Dazu
definiert es ein TreeSelectionModel fr Einfachselektion und fgt einen TreeSelectionListener hinzu, der jede
Selektionsnderung dokumentiert:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
/* Listing3812.java */
import
import
import
import
import
java.awt.*;
java.awt.event.*;
javax.swing.*;
javax.swing.event.*;
javax.swing.tree.*;
Listing3812.java
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064 }
if (tp != null) {
System.out.println("
} else {
System.out.println("
}
}
}
);
}
public static void main(String[] args)
{
try {
Listing3812 frame = new Listing3812();
frame.setLocation(100, 100);
frame.setSize(250, 200);
frame.setVisible(true);
} catch (Exception e) {
}
}
javax.swing.JTree
Mit clearSelection wird die Selektion vollstndig gelscht. Mit addSelectionPath und addSelectionPaths
kann die Selektion um ein einzelnes oder eine Menge von Knoten erweitert werden. Mit setSelectionPath und
setSelectionPaths werden - unabhngig von der bisherigen Selektion - die als Argument bergebenen Knoten selektiert.
javax.swing.JTree
isExpanded liefert true, wenn der Knoten geffnet ist, isCollapsed, wenn er geschlossen ist. hasBeenExpanded gibt
an, ob der Knoten berhaupt schon einmal geffnet wurde. isVisible gibt genau dann true zurck, wenn der Knoten
sichtbar ist, d.h. wenn alle seine Elternknoten geffnet sind. Mit makeVisible kann ein Knoten sichtbar gemacht werden. Mit
javax.swing.tree.DefaultTreeModel
Mit insertNodeInto wird ein neuer Kindknoten an einer beliebigen Position zu einem Elternknoten hinzugefgt. Mit
removeNodeFromParent wird ein beliebiger Knoten aus dem Baum entfernt (er darf auch Unterknoten enthalten), und
nodeChanged sollte aufgerufen werden, wenn der Inhalt eines Knotens sich so gendert hat, da seine Bildschirmdarstellung
erneuert werden mu. getPathToRoot schlielich ist eine ntzliche Hilfsmethode, mit der das zur Konstruktion eines
TreePath-Objekts erforderliche Knoten-Array auf einfache Weise erstellt werden kann.
Das folgende Programm zeigt einen Baum, der zunchst nur den Wurzelknoten enthlt. Dieser ist vom Typ
DefaultMutableTreeNode und wird in ein explizit erzeugtes DefaultTreeModel eingebettet, da an den Konstruktor
des JTree bergeben wird. Neben dem JTree enthlt das Programm drei Buttons, mit denen ein neuer Knoten eingefgt sowie
ein vorhandener gelscht oder seine Beschriftung gendert werden kann.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
/* Listing3813.java */
import
import
import
import
import
java.awt.*;
java.awt.event.*;
javax.swing.*;
javax.swing.event.*;
javax.swing.tree.*;
Listing3813.java
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
public Listing3813()
{
super("JTree 3");
addWindowListener(new WindowClosingAdapter(true));
//JTree erzeugen und Einfachselektion aktivieren
root = new DefaultMutableTreeNode("Root");
treeModel = new DefaultTreeModel(root);
tree = new JTree(treeModel);
TreeSelectionModel tsm = new DefaultTreeSelectionModel();
tsm.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
tree.setSelectionModel(tsm);
tree.setRootVisible(true);
//JTree einfgen
Container cp = getContentPane();
cp.add(new JScrollPane(tree), BorderLayout.CENTER);
//ButtonPanel
JPanel panel = new JPanel(new FlowLayout());
String[] buttons = new String[]{"AddChild", "Delete", "Change"};
for (int i = 0; i < buttons.length; ++i) {
JButton button = new JButton(buttons[i]);
button.addActionListener(this);
panel.add(button);
}
cp.add(panel, BorderLayout.SOUTH);
}
public void actionPerformed(ActionEvent event)
{
String cmd = event.getActionCommand();
TreePath tp = tree.getLeadSelectionPath();
if (tp != null) {
DefaultMutableTreeNode node;
node = (DefaultMutableTreeNode)tp.getLastPathComponent();
if (cmd.equals("AddChild")) {
DefaultMutableTreeNode child;
child = new DefaultMutableTreeNode("child");
treeModel.insertNodeInto(child, node, node.getChildCount());
TreeNode[] path = treeModel.getPathToRoot(node);
tree.expandPath(new TreePath(path));
} else if (cmd.equals("Delete")) {
if (node != root) {
TreeNode parent = node.getParent();
TreeNode[] path = treeModel.getPathToRoot(parent);
treeModel.removeNodeFromParent(node);
tree.setSelectionPath(new TreePath(path));
}
} else if (cmd.equals("Change")) {
String name = node.toString();
node.setUserObject(name + "C");
treeModel.nodeChanged(node);
}
}
}
public static void main(String[] args)
{
try {
String plaf = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
UIManager.setLookAndFeel(plaf);
Listing3813 frame = new Listing3813();
frame.setLocation(100, 100);
frame.setSize(300, 300);
frame.setVisible(true);
} catch (Exception e) {
081
}
082
}
083 }
Listing 38.13: Einfgen, ndern und Lschen in einem Baum
Alle Button-Aktionen werden in actionPerformed ausgefhrt. Darin wird zunchst das Action-Kommando abgefragt und
dann in Zeile 046 der Pfad des selektierten Elements bestimmt. Ist dieser nicht leer, werden die Kommandos wie folgt
ausgefhrt:
Bei "AddChild" wird zunchst ein neuer Kindknoten erzeugt und in Zeile 053 als letztes Element an den selektierten
Knoten angehngt. Anschlieend wird der selektierte Knoten expandiert, um die nderung fr den Anwender sichtbar zu
machen.
Bei "Delete" wird zunchst geprft, ob der zu lschende Knoten die Wurzel ist. Ist das nicht der Fall, wird zunchst ein
Pfad zum Vaterknoten beschafft und dann in Zeile 060 der selektierte Knoten gelscht. Anschlieend wird der
Vaterknoten selektiert, um auch nach der Lschung eine definierte Selektion zu haben.
Bei "Change" wird lediglich das bisherige UserObject durch einen String mit einem angehngten "C" ersetzt und in
Zeile 066 nodeChanged aufgerufen, um dem JTree mitzuteilen, da der Knoten neu dargestellt werden soll.
Nach einigen Einfgungen, nderungen und Lschungen sieht die Programmausgabe beispielsweise so aus:
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
DOC
API
39.1.1 Grundlagen
Fr viele Leser, die Java lernen wollen, steht die Entwicklung von Applets im Vordergrund und
ist der Hauptgrund fr die Beschftigung mit der Sprache. In letzter Zeit ist allerdings ein Trend
zu beobachten, bei dem Java zunehmend auch als Sprache fr die Anwendungsentwicklung an
Bedeutung gewinnt. Da man mit Java auch Web-Pages verschnern kann, ist dabei ein
angenehmer Nebeneffekt.
Tatschlich unterscheiden sich Applets und Applikationen gar nicht so stark voneinander, wie
man vermuten knnte. Bis auf wenige Ausnahmen werden sie mit denselben Werkzeugen und
Techniken konstruiert. Vereinfacht kann man sagen, da Java-Applikationen eigenstndige
Programme sind, die zur Ausfhrung den Stand-Alone-Java-Interpreter bentigen, whrend JavaApplets aus HTML-Seiten heraus aufgerufen werden und zur Ausfhrung einen Web-Browser
bentigen.
Die wichtigsten Unterschiede kann man in einer kurzen Liste zusammenfassen:
Das Hauptprogramm eines Applets wird immer aus der Klasse Applet abgeleitet. Bei
einer Applikation ist es prinzipiell gleichgltig, woraus die Hauptklasse abgeleitet wird.
Eine Applikation wird gestartet, indem vom Java-Interpreter die Klassenmethode main
aufgerufen wird. Das Starten eines Applets wird dadurch erreicht, da der Web-Browser
die Applet-Klasse instanziert und die Methoden init und start aufruft.
Aus Sicherheitsgrnden darf ein Applet in der Regel weder auf Dateien des lokalen
Rechners zugreifen noch externe Programme auf diesem starten. Eine Ausnahme bilden
signierte Applets. Fr eine Applikation gelten diese Beschrnkungen nicht.
Ein Applet arbeitet immer grafik- und ereignisorientiert. Bei einer Applikation dagegen
ist es mglich, auf die Verwendung des AWT zu verzichten und alle Ein-/Ausgaben
textorientiert zu erledigen.
Applets bieten einige zustzliche Mglichkeiten im Bereich des BenutzerschnittstellenDesigns, die so bei Applikationen nicht ohne weiteres zu finden sind. Die Ausgabe von
Sound beispielsweise ist standardmig auf Applets beschrnkt.
Dieses Kapitel erklrt die Grundlagen der Applet-Programmierung und erlutert die Einbindung
von Applets in HTML-Dokumente. Es baut dabei auf vielen der in den Kapiteln 23 bis 34 des
Buches vermittelten AWT-Features auf, ohne deren Kenntnis die Applet-Programmierung kaum
mglich wre.
Ereignisse empfangen kann und in der Lage ist, grafische Ausgaben vorzunehmen. Tatschlich
kommt dies dem Anspruch eines Applets, ein eigenstndiges Programm zu sein, das innerhalb
eines fensterartigen Ausschnitts in einem grafikfhigen Web-Browser luft, sehr entgegen.
Durch die Vererbungshierarchie erbt ein Applet bereits von seinen Vaterklassen einen groen
Teil der Fhigkeiten, die es zur Ausfhrung bentigt. Die ber die Fhigkeiten von
Container, Component und Panel hinaus erforderliche Funktionalitt, die bentigt wird,
um ein Objekt der Klasse Applet als Hauptmodul eines eigenstndigen Programms laufen zu
lassen, wird von der Klasse Applet selbst zur Verfgung gestellt.
java.applet.Applet
blicherweise wird dieser in der abgeleiteten Klasse nicht berlagert, sondern von Applet
geerbt. Notwendige Initialisierungen von Membervariablen werden spter erledigt.
Initialisierung des Applets
Nach der Instanzierung ruft der Browser die Methode init auf, um dem Applet die Mglichkeit
zu geben, Initialisierungen vorzunehmen:
public void init()
java.applet.Applet
init wird whrend der Lebensdauer eines Applets genau einmal aufgerufen,
nachdem die Klassendatei geladen und das Applet instanziert wurde. Innerhalb von
init knnen Membervariablen initialisiert, Images oder Fonts geladen oder
Parameter ausgewertet werden.
Hinweis
java.applet.Applet
Im Gegensatz zur Initialisierung kann das Starten eines Applets mehrfach erfolgen.
Wenn der Browser eine andere Web-Seite ldt, wird das Applet nicht komplett
zerstrt, sondern lediglich gestoppt (siehe nchsten Abschnitt). Bei erneutem Aufruf
der Seite wird es dann wieder gestartet und die Methode start erneut aufgerufen.
Hinweis
java.applet.Applet
Wie erwhnt, geschieht dies immer dann, wenn eine andere Seite geladen wird. Whrend der
Lebensdauer eines Applets knnen die Methoden start und stop also mehrfach aufgerufen
werden. Keinesfalls darf ein Applet also innerhalb von stop irgendwelche endgltigen
Aufrumarbeiten durchfhren und Ressourcen entfernen, die es bei einem nachtrglichen
Neustart wieder bentigen wrde.
Zerstren des Applets
Wenn ein Applet ganz bestimmt nicht mehr gebraucht wird (z.B. weil der Browser beendet
wird), ruft der Browser die Methode destroy auf:
public void destroy()
java.applet.Applet
Diese kann berlagert werden, um Aufrumarbeiten zu erledigen, die erforderlich sind, wenn das
Applet nicht mehr verwendet wird. Eine typische Anwendung von destroy besteht
beispielsweise darin, einen Thread zu zerstren, der bei der Initialisierung eines Applets angelegt
wurde.
Component-Objekt versendet werden. Hierzu zhlen Mouse-, MouseMotion-, Key-, Focusund Component-Events ebenso wie die Aufforderung an das Applet, seine Client-Area neu zu
zeichnen. Bezglich Reaktion auf die Events und die Registrierung und Programmierung
geeigneter Listener-Klassen verhlt sich ein Applet genauso wie jedes andere Fenster. In Kapitel
28 und Kapitel 29 wurden die mglichen Ereignisse und die Reaktion des Programms darauf
vorgestellt.
showStatus
Mit der Methode showStatus kann das Applet einen Text in die Statuszeile des HTMLBrowsers schreiben, der das Applet ausfhrt:
public void showStatus(String msg)
java.applet.Applet
Das folgende Beispiel zeigt ein sehr einfaches Applet, das den Text Hello, world auf dem
Bildschirm ausgibt und in die Statuszeile des Browsers schreibt:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
/* Listing3901.java */
Listing3901.java
import java.awt.*;
import java.applet.*;
public class Listing3901
extends Applet
{
public void paint(Graphics g)
{
showStatus("Hello, world");
g.drawString("Hello, world",10,50);
}
}
java.applet.Applet
getParameterInfo kann vom Browser aufgerufen werden, um Informationen ber die vom
Applet akzeptierten Parameter zu erhalten. Der Rckgabewert von getParameterInfo ist
ein zweidimensionales Array von Strings. Jedes Element des Arrays beschreibt einen Parameter
des Applets durch ein Subarray mit drei Elementen. Das erste Element gibt den Namen des
Parameters an, das zweite seinen Typ und das dritte eine textuelle Beschreibung des Parameters.
Die Informationen sollten so gestaltet sein, da sie fr menschliche Benutzer verstndlich sind;
eine programmgesteuerte Verwendung ist eigentlich nicht vorgesehen.
Das nachfolgende Listing zeigt eine beispielhafte Implementierung der Methode
getParameterInfo fr das Applet in Listing 39.5:
001 public String[][] getParameterInfo()
002 {
003
String ret = {
004
{"redwidth","int","Breite eines roten Segments"},
005
{"whitewidth","int","Breite eines weissen Segments"}
006
};
007
return ret;
008 }
Listing 39.2: Verwendung von getParameterInfo
getAppletInfo
hnlich der Methode getParameterInfo gibt es eine Methode getAppletInfo, mit der
die Anwendung Informationen ber das Applet zur Verfgung stellen sollte:
public String getAppletInfo()
java.applet.Applet
Die Sprachspezifikation gibt an, da hier ein String zurckgegeben werden sollte, der Angaben
zum Applet selbst, zur aktuellen Version und zum Autor des Applets macht. Eine beispielhafte
Implementierung knnte so aussehen:
001 public String getAppletInfo()
002 {
003
return "AppletBeispiel Ver. 1.0 (C) 1997-99 Guido Krueger";
004 }
Listing 39.3: Die Methode getAppletInfo
Titel
<<
>
>>
DOC
API
Titel
<<
Eine Reihe von Parametern, die innerhalb von <APPLET> untergebracht werden und
die Gre und Anordnung des Applets bestimmen. CODE, WIDTH und HEIGHT
mssen dabei in jedem Fall angegeben werden, auerdem gibt es noch einige
optionale Parameter.
Eine Liste von PARAM-Tags, die zwischen <APPLET> und </APPLET> stehen
Alternativ kann die Klassendatei auch in einem der Verzeichnisse liegen, die in der
Umgebungsvariablen CLASSPATH angegeben wurden. CLASSPATH enthlt eine Liste von
durch Kommata getrennten Verzeichnissen, die in der Reihenfolge ihres Auftretens
durchsucht werden. CLASSPATH spielt auerdem beim Aufruf des Compilers eine Rolle:
Sie dient dazu, die importierten Pakete zu suchen.
Das Applet-Tag hat zwei weitere nichtoptionale Parameter WIDTH und HEIGHT, die die
Hhe und Breite des fr das Applet reservierten Bildschirmausschnitts angeben. Innerhalb
des Applets steht ein Rechteck dieser Gre als Ausgabeflche zur Verfgung.
Das Applet-Tag besitzt weitere optionale Parameter. Diese dienen zur Konfiguration des
Applets und zur Beeinflussung der Darstellung des Applets und des umgebenden Textes.
Tabelle 39.1 gibt einen berblick ber die verfgbaren Parameter:
Parameter
Bedeutung
CODEBASE
ARCHIVE
OBJECT
Name einer Datei, die den serialisierten Inhalt des Applets enthlt
ALT
NAME
ALIGN
Vertikale Anordnung des Applets in einer Textzeile. Hier kann einer der
Werte left, right, top, texttop, middle, absmiddle,
baseline, bottom oder absbottom angegeben werden.
VSPACE
HSPACE
java.applet.Applet
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
/* Schranke.java */
import java.awt.*;
import java.applet.*;
public class Schranke
extends Applet
{
private int[] dx;
private Color[] color;
public void init()
{
String tmp;
dx = new int[2];
try {
dx[0] = Integer.parseInt(
getParameter("redwidth")
);
dx[1] = Integer.parseInt(
getParameter("whitewidth")
);
} catch (NumberFormatException e) {
dx[0] = 10;
dx[1] = 10;
}
color = new Color[2];
color[0] = Color.red;
color[1] = Color.white;
}
public void paint(Graphics g)
{
int maxX = getSize().width;
int maxY = getSize().height;
int x = 0;
int flg = 0;
Polygon p;
while (x <= maxX+maxY/2) {
p = new Polygon();
p.addPoint(x,0);
p.addPoint(x+dx[flg],0);
p.addPoint(x+dx[flg]-maxY/2,maxY);
p.addPoint(x-maxY/2,maxY);
p.addPoint(x,0);
g.setColor(color[flg]);
Schranke.java
048
g.fillPolygon(p);
049
x += dx[flg];
050
flg = (flg==0) ? 1 : 0;
051
}
052
}
053 }
Listing 39.5: Ein parametrisiertes Applet
Das folgende HTML-Dokument zeigt die Einbindung eines Schranken-Applets mit einer
Hhe von 10 Pixeln und einer Breite von 400 Pixeln. Die roten Felder der Schranke sind 10
und die weien 7 Pixel breit:
001
002
003
004
005
006
007
008
009
010
011
012
013
Schranke.html
<html>
<head>
<title>Schranke</title>
</head>
<body>
<h1>Schranke</h1>
<applet code="Schranke.class" width=400 height=10>
<param name="redwidth" value=10>
<param name="whitewidth" value=7>
Hier steht das Applet Schranke.class
</applet>
</body>
</html>
Hinweis
appletviewer Schranke.html
Auch die echten Web-Browser knnen meist mit einer Datei als Argument aufgerufen
werden. Alternativ kann die HTML-Datei natrlich auch direkt aus dem laufenden Browser
geladen werden. Der Applet-Viewer ist kein vollwertiger Browser, sondern extrahiert
lediglich die APPLET-Tags und ihre Parameter, um die in der HTML-Datei angegebenen
Applets zu starten.
Titel
<<
>
>>
Titel
<<
Inhalt Suchen
<
>
Index
>>
DOC
API
JDK1.1-1.4
Die Ausgabe von Sound ist denkbar einfach und kann auf zwei unterschiedliche Arten erfolgen. Zum
einen stellt die Klasse Applet die Methode play zur Verfgung, mit der eine Sound-Datei geladen
und abgespielt werden kann:
public void play(URL url)
java.applet.Applet
Hierbei kann entweder der URL einer Sound-Datei (siehe nchster Abschnitt) oder die Kombination
von Verzeichnis-URL und Dateinamen angegeben werden. blicherweise wird zur bergabe des
Verzeichnis-URLs eine der Applet-Methoden getCodeBase oder getDocumentBase
verwendet. Diese liefern einen URL des Verzeichnisses, aus dem das Applet gestartet wurde bzw. in
dem die aktuelle HTML-Seite liegt:
java.applet.Applet
Der Nachteil dieser Vorgehensweise ist, da die Sound-Datei bei jedem Aufruf neu geladen werden
mu. In der zweiten Variante wird zunchst durch einen Aufruf von getAudioClip ein Objekt der
Klasse AudioClip beschafft, das dann beliebig oft abgespielt werden kann:
public getAudioClip(URL url, String name)
java.applet.Applet
AudioClip stellt die drei Methoden play, loop und stop zur Verfgung:
public void play()
java.applet.AudioClip
play startet die zuvor geladene Sound-Datei und spielt sie genau einmal ab. loop startet sie
ebenfalls, spielt den Sound in einer Endlosschleife aber immer wieder ab. Durch Aufruf von stop
kann diese Schleife beendet werden. Es ist auch mglich, mehr als einen Sound gleichzeitig
abzuspielen. So kann beispielsweise eine Hintergrundmelodie in einer Schleife immer wieder
abgespielt werden, ohne da die Ausgabe von zustzlichen Vordergrund-Sounds beeintrchtigt
wrde.
Das folgende Beispiel ist eine neue Variante des Hello, World-Programms. Anstatt der textuellen
Ausgabe stellt das Applet zwei Buttons zur Verfgung, mit denen die Worte Hello und World
abgespielt werden knnen:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
/* HWApplet.java */
HWApplet.java
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
public class HWApplet
extends Applet
implements ActionListener
{
Button
hello;
Button
world;
AudioClip helloClip;
AudioClip worldClip;
public void init()
{
super.init();
setLayout(new FlowLayout());
hello = new Button("Hello");
hello.addActionListener(this);
add(hello);
world = new Button("World");
world.addActionListener(this);
add(world);
helloClip = getAudioClip(getCodeBase(),"hello.au");
worldClip = getAudioClip(getCodeBase(),"world.au");
}
public void actionPerformed(ActionEvent event)
{
String cmd = event.getActionCommand();
if (cmd.equals("Hello")) {
helloClip.play();
} else if (cmd.equals("World")) {
worldClip.play();
}
}
}
Hinweis
Seit dem JDK 1.2 kann nicht nur in Applets, sondern auch in Applikationen Sound
ausgegeben werden. Dazu bietet die Klasse Applet eine statische Methode
newAudioClip:
JDK1.11.4
Da es sich um eine Klassenmethode handelt, kann sie auch auerhalb eines Applets aufgerufen
werden. Das folgende Beispiel zeigt ein einfaches Programm, das in der Kommandozeile den URL
einer Sounddatei erwartet und diese dann maximal 10 Sekunden lang abspielt:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
PlaySound.java
/* PlaySound.java */
import java.net.*;
import java.applet.*;
public class PlaySound
{
public static void main(String[] args)
{
if (args.length >= 1) {
try {
URL url = new URL(args[0]);
AudioClip clip = Applet.newAudioClip(url);
clip.play();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
}
} catch (MalformedURLException e) {
System.out.println(e.toString());
}
}
}
}
Titel
<<
Inhalt Suchen
<
>
Index
DOC
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Animation ist in Applets ebenso mglich wie in Applikationen. Alle Techniken, die in Kapitel 34 erklrt wurden, sind
grundstzlich auch auf Applets anwendbar. Aufgrund der Tatsache, da Applets in einem Browser laufen und ber
eine Netzwerk-Fernverbindung mit Daten versorgt werden mssen, sollten folgende Besonderheiten beachtet werden:
Ein animiertes Applet sollte in einem eigenen Thread laufen. Andernfalls wrde mglicherweise der Browser
lahmgelegt werden und nicht mehr auf Benutzereingaben reagieren.
Die Animation in Applets kann durch Sound untersttzt werden.
Das Laden von Sound- und Image-Dateien kann unter Umstndern sehr lange dauern und liegt in der Regel um
Grenordnungen ber den Ladezeiten im lokalen Betrieb. Mssen whrend der Applet-Initialisierung grere
Dateien geladen werden, kann das Starten des Applets fr Anwender mit langsamen Netzwerkverbindungen
schwierig werden.
Wir wollen uns diese Regeln zu eigen machen und in diesem Abschnitt ein einfaches animiertes Applet entwickeln.
Das Programm soll die Skyline einer Grostadt bei Nacht darstellen. Dabei gehen in den Wolkenkratzern die Lichter
an und aus, auf einigen Dchern gibt es rote Blinklichter, und von Zeit zu Zeit schlgt der Blitz mit Krachen in einen
der Wolkenkratzer ein. (Diese Aufgabenstellung erinnert nicht ganz zu Unrecht an einen bekannten
Bildschirmschoner.) Abbildung 39.4 zeigt einen Schnappschu des laufenden Programms.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
/* SkyscraperApplet.java */
import java.awt.*;
import java.util.*;
import java.applet.*;
class Skyscraper
{
public int x;
public int y;
public int width;
public int height;
int
wndcntx;
int
wndcnty;
boolean
blinkon = false;
Skyscraper(int x, int y)
{
this.x = x;
this.y = y;
this.width = (int)(30*(0.5+Math.random()));
this.height = (int)(100*(0.5+Math.random()));
wndcntx = (width-4)/5;
wndcnty = (height-4)/5;
}
void LightEvent(Graphics g)
{
double rnd = Math.random();
int
xwnd = (int)(Math.random()*wndcntx);
int
ywnd = (int)(Math.random()*wndcnty);
if (blinkon) {
g.setColor(Color.black);
g.fillRect(x+width/2,y-height-20,2,2);
blinkon = false;
}
if (rnd >= 0.9) {
blinkon = true;
g.setColor(Color.red);
g.fillRect(x+width/2,y-height-20,2,2);
} else if (rnd >= 0.7) {
g.setColor(Color.black);
g.fillRect(x+2+xwnd*5,y-height+2+ywnd*5,2,2);
} else {
g.setColor(Color.yellow);
g.fillRect(x+2+xwnd*5,y-height+2+ywnd*5,2,2);
}
}
}
public class SkyscraperApplet
extends Applet
implements Runnable
{
//Membervariablen
Thread th;
Vector v = new Vector();
AudioClip thunder;
SkyscraperApplet.java
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
boolean running;
//Parameter
int
DELAY;
float FLASH;
String THUNDER;
public void init()
{
Skyscraper house;
int x = 5;
//Huser erzeugen
while (this.getSize().width-x-1 >= 30) {
house = new Skyscraper(x,this.getSize().height-10);
v.addElement(house);
x += house.width + 5;
}
setBackground(Color.black);
//Parameter einlesen
try {
DELAY = Integer.parseInt(getParameter("delay"));
} catch (NumberFormatException e) {
DELAY = 75;
}
try {
FLASH = (new Float(getParameter("flash"))).floatValue();
} catch (NumberFormatException e) {
FLASH = 0.01F;
}
THUNDER = getParameter("thunder");
if (THUNDER != null) {
thunder = getAudioClip(getCodeBase(),THUNDER);
}
System.out.println("DELAY = "+DELAY);
System.out.println("FLASH = "+FLASH);
System.out.println("THUNDER = "+THUNDER);
}
public void start()
{
if (th == null) {
running = true;
th = new Thread(this);
th.start();
}
}
public void stop()
{
if (th != null) {
running = false;
th = null;
}
}
public void run()
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
{
while (running) {
repaint();
try {
Thread.sleep(DELAY);
} catch (InterruptedException e) {
//nothing
}
}
}
public void update(Graphics g)
{
paint(g);
}
public void paint(Graphics g)
{
int i;
Skyscraper house;
i = (int)Math.floor(Math.random()*v.size());
house = (Skyscraper)v.elementAt(i);
house.LightEvent(g);
if (Math.random() < FLASH) {
Lightning(g,house.x+10,house.y-house.height);
}
}
public void Lightning(Graphics g, int x, int y)
{
Vector poly = new Vector();
int dx, dy, i, polysize;
thunder.play();
//Blitzpolygon berechnen
poly.addElement(new Point(x,y));
polysize = 1;
while (y > 10) {
dx = 10 - (int)(Math.floor(Math.random()*20));
dy = - (int)(Math.floor(Math.random()*20));
x += dx;
y += dy;
poly.addElement(new Point(x,y));
++polysize;
}
//Blitzvector in Koordinaten-Arrays umwandeln
int[] xpoints = new int[poly.size()];
int[] ypoints = new int[poly.size()];
for (i = 0; i < polysize; ++i) {
Point p = (Point)poly.elementAt(i);
xpoints[i] = p.x;
ypoints[i] = p.y;
}
//Blitz zeichnen
for (i = 0; i <= 1; ++i) {
g.setColor(Color.white);
g.drawPolyline(xpoints, ypoints, polysize);
175
176
177
178
179
180
181
182
183
}
184
}
185 }
try {
Thread.sleep(20);
} catch (InterruptedException e) {}
g.setColor(Color.black);
g.drawPolyline(xpoints, ypoints, polysize);
try {
Thread.sleep(20);
} catch (InterruptedException e) {}
<html>
<head>
<title>Skyscraper</title>
</head>
<body>
<h1>Skyscraper</h1>
<applet code=SkyscraperApplet.class width=500 height=300>
<param name="delay"
value=75>
<param name="flash"
value=0.01>
<param name="thunder" value="thunder.au">
Hier steht das Applet Skyscraper.class
</applet>
</body>
</html>
SkyscraperApplet.html
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Dem Host-Namen (z.B. java.sun.com), der vom Dienst durch die Zeichenkette "://" getrennt wird.
Einer optionalen Port-Nummer (z.B. 80 fr einen http-Dmon), die vom Host-Namen durch einen
":" abgetrennt wird.
Einem Dateinamen (z.B. bugsandfeatures.html), der vom Host-Namen bzw. der Port-Nummer
durch "/" abgetrennt wird. Diesem Dateinamen kann auch die Rolle eines Kommandos oder
Parameterstrings zukommen, beispielsweise, wenn ein CGI-Script aufgerufen wird.
sich im Paket java.net befindet. Diese dient dazu, die Syntax von URLs zu kapseln und die einzelnen
Bestandteile voneinander zu trennen. Die Klasse URL besitzt vier Konstruktoren, die es ermglichen,
eine Adresse auf verschiedene Art zusammenzubauen. Wir werden hier nur die Variante verwenden, die
einen String als Argument akzeptiert:
public URL(String url)
throws MalformedURLException
java.net.URL
Bei der Anwendung dieses Konstruktors mu ein syntaktisch einwandfreier URL als String
bergeben werden. Enthlt der String einen Syntaxfehler, lst der Konstruktor eine Ausnahme des Typs
MalformedURLException aus.
An dieser Stelle wird natrlich noch nicht berprft, ob die angegebene Adresse wirklich existiert. Dazu
wre eine funktionsfhige Netzwerkverbindung ntig, und es wrde ein in der Regel nicht akzeptabler
Aufwand bei der Konstruktion von URL-Objekten entstehen. Die im Konstruktor durchgefhrten
berprfungen sind lediglich syntaktischer Natur, um sicherzustellen, da ein URL ein gltiges
Adressenformat hat. Nachdem ein gltiges URL-Objekt erzeugt wurde, kann es zur Adressenbergabe
verwendet werden.
Weitere Hinweise zur Anwendung der Klasse URL finden sich in Abschnitt 45.4.
Hinweis
java.applet.Applet
In der Klasse AppletContext gibt es eine Reihe von Methoden, die dem Applet Funktionalitten des
Browsers zur Verfgung stellen:
Wir wollen auf die ersten beiden Methoden nicht weiter eingehen, sondern uns hier lediglich mit
showDocument beschftigen.
fhrt dazu, da das aktive Applet angehalten wird und die Kontrolle ber die Web-Seite verliert.
Befindet sich auf der neuen Web-Seite ein anderes Applet, so wird dieses geladen bzw. - falls es bereits
geladen war - reaktiviert.
Die Methode showDocument steht in zwei unterschiedlichen Varianten zur Verfgung:
public void showDocument(URL url)
java.applet.AppletContext
In seiner einfachsten Form erwartet showDocument lediglich einen einzigen Parameter, nmlich die
Adresse der Zielseite. Hier mu ein gltiges URL-Objekt angegeben werden, also eine komplette
Adresse mit Dienst, Host-Name, Verzeichnis und Datei. In der zweiten Variante kann zustzlich ein
target angegeben werden, also das Sprungziel innerhalb der ausgewhlten Datei. Hier knnen die
Standard-HTML-Targets _self, _parent, _top und _blank sowie alle benutzerdefinierten
Sprungmarken verwendet werden.
Soll statt einer absoluten eine relative Adresse angegeben werden , also beispielsweise eine andere Tip
Web-Seite aus demselben Verzeichnis oder aus einem seiner Unterverzeichnisse, mu das URLObjekt mit einem erweiterten Konstruktor instanziiert werden. Zustzlich zur Angabe des
relativen URL-Strings ist als erstes Argument ein Kontext-URL anzugeben, der das
Basisverzeichnis fr das zweite Argument vorgibt. Mit Hilfe der Methoden getCodeBase und
getDocumentBase der Klasse Applet kann beispielsweise das Stammverzeichnis des
Applets bzw. der HTML-Seite ermittelt werden. Als zweites Argument mu dann lediglich der
Dateiname relativ zu diesem Verzeichnis angegeben werden.
Das folgende Applet demonstriert die Anwendung von showDocument. Es erstellt eine Reihe von
Buttons und zeigt sie auf dem Bildschirm an. Wird einer der Buttons gedrckt, verzweigt das Programm
auf die durch den Button spezifizierte Seite. Whrend der Initialisierung sucht das Applet nach
Parametern mit den Namen button1, button2 usw. Jeder dieser Parameter sollte den Namen des
Buttons und die Zieladresse enthalten. Die beiden Teile mssen durch ein Komma getrennt sein. Eine
Besonderheit besteht darin, da auch Adressen angegeben werden knnen, die mit = anfangen. In
diesem Fall wird das = entfernt und der interne URL nicht absolut, sondern unter Verwendung des
erweiterten Konstruktors relativ zur aktuellen Web-Seite angelegt.
Um die Buttons sowohl auf dem Bildschirm anzeigen zu knnen als auch den zugehrigen URL zu
speichern, wurde eine neue Klasse URLButton definiert. URLButton erweitert die Klasse Button
um die Fhigkeit, einen URL mitzuspeichern und bei Bedarf abzufragen.
Hier ist das Listing:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
/* URLLaden.java */
import
import
import
import
import
java.applet.*;
java.awt.*;
java.awt.event.*;
java.util.*;
java.net.*;
class URLButton
extends Button
{
private URL url;
public URLButton(String label, URL url)
{
super(label);
this.url = url;
}
public URL getURL()
{
return url;
}
}
public class URLLaden
extends Applet
implements ActionListener
{
Vector buttons;
public void init()
{
super.init();
setLayout(new FlowLayout());
addNotify();
buttons = new Vector();
for (int i=1; ; ++i) {
String s = getParameter("button"+i);
if (s == null) {
break;
}
try {
StringTokenizer st = new StringTokenizer(s,",");
String label = st.nextToken();
String urlstring = st.nextToken();
URL url;
if (urlstring.charAt(0) == '=') {
urlstring = urlstring.substring(1);
url = new URL(getDocumentBase(),urlstring);
} else {
url = new URL(urlstring);
URLLaden.java
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082 }
}
URLButton button = new URLButton(label,url);
button.addActionListener(this);
add(button);
buttons.addElement(button);
} catch (NoSuchElementException e) {
System.out.println("Button"+i+": "+e.toString());
break;
} catch (MalformedURLException e) {
System.out.println("Button"+i+": "+e.toString());
break;
}
}
}
public void actionPerformed(ActionEvent event)
{
URLButton source = (URLButton)event.getSource();
Enumeration en = buttons.elements();
while (en.hasMoreElements()) {
URLButton button = (URLButton)en.nextElement();
if (button == source) {
System.out.println(
"showDocument("+button.getURL().toString()+")"
);
getAppletContext().showDocument(button.getURL());
}
}
}
<html>
<head>
<title>URLLaden</title>
</head>
<body>
<h1>URLLaden</h1>
<applet code=URLLaden.class width=400 height=200>
<param
name="button1"
value="JAVA-Home,http://java.sun.com/">
<param
name="button2"
value="Guido-Home,http://www.gkrueger.com">
<param
name="button3"
value="Schranke,=Schranke.html">
<param
URLLaden.html
018
019
020
021
022
023
name="button4"
value="Jana30,=images/jana30.gif">
Hier steht das Applet URLLaden.class
</applet>
</body>
</html>
Hinweis
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Wenn mehr als ein Applet auf einer Web-Seite plaziert wurde, ist es mitunter wnschenswert, da die Applets
miteinander kommunizieren. Da Applets gewhnliche Java-Objekte sind, ist es prinzipiell nicht schwer, aus einem
Applet heraus ffentliche Methoden oder Membervariablen eines anderen Applets aufzurufen. Dazu ist es allerdings
erforderlich, da ein Applet in den Besitz einer Instanz eines anderen Applets gelangt. Der Applet-Kontext stellt dazu
zwei Methoden zur Verfgung:
public Applet getApplet(String name)
java.applet.AppletContext
getApplets liefert eine Enumeration mit allen Applets, die sich auf derselben Seite befinden. Sofern sie fr den
Aufrufer unterscheidbar sind, kann daraus das gewnschte Applet ausgewhlt werden. getApplet liefert dagegen
genau das Applet mit dem als Parameter angegebenen Namen. Dieser mu dem NAME-Parameter des gewnschten
Applets entsprechen (siehe Abschnitt 39.2.2).
Wir wollen uns ein einfaches Beispiel fr die Kommunikation von drei Applets, die sich auf derselben Web-Seite
befinden, ansehen. Dazu soll eine Klasse ChgNextApplet geschrieben werden, die den zur Verfgung stehenden
Platz in der aktuellen Hintergrundfarbe einfrbt. Nach einem Mausklick soll die Hintergrundfarbe eines anderen, mit
dem NEXT-Parameter spezifizierten, Applets verndert werden.
Der Code fr die Applet-Klasse kann wie folgt realisiert werden:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
/* ChgNextApplet.java */
import
import
import
import
java.awt.*;
java.awt.event.*;
java.applet.*;
java.util.*;
ChgNextApplet.java
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041 }
if (next != null) {
Applet applet = getAppletContext().getApplet(next);
if (applet != null) {
int red
= (int)(Math.random() * 256);
int green = (int)(Math.random() * 256);
int blue = (int)(Math.random() * 256);
applet.setBackground(new Color(red, green, blue));
applet.repaint();
}
}
}
}
);
}
public void paint(Graphics g)
{
g.drawString("Change " + next, 5, 20);
}
<html>
<head>
<title>ThreeApplets</title>
</head>
<body>
A1:
<applet code="ChgNextApplet.class" width=90 height=30 name="A1">
<param name="next" value="A2">
Applet A1
</applet>
<p>
A2:
<applet code="ChgNextApplet.class" width=90 height=30 name="A2">
<param name="next" value="A3">
Applet A2
</applet>
<p>
A3:
<applet code="ChgNextApplet.class" width=90 height=30 name="A3">
<param name="next" value="A1">
Applet A3
</applet>
ThreeApplets.html
026
027 </body>
028 </html>
Listing 40.4: Die HTML-Datei mit den drei kommunizierenden Applets
Die HTML-Seite kann nun im Browser aufgerufen werden. Nach einigen Mausklicks knnte die Darstellung so
aussehen:
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
In der Praxis steht man manchmal vor der Aufgabe, eine bestehende Applikation in ein Applet umzuwandeln, etwa
wenn ein vorhandendes Programm in eine HTML-Prsentation eingebunden werden soll. Da die Unterschiede
zwischen Applets und Applikationen nicht so gro sind, kann diese Konvertierung - etwas Glck vorausgesetzt mit berschaubarem Aufwand erledigt werden. Wir wollen in diesem Abschnitt die prinzipielle Vorgehensweise
erlutern und auf einige der dabei mglicherweise auftretenden Besonderheiten eingehen.
/* Calculator.java */
import java.awt.*;
import java.awt.event.*;
public class Calculator
extends Frame
implements ActionListener
{
private TextField tf;
public Calculator()
{
super("Calculator");
addWindowListener(new WindowClosingAdapter(true));
setBackground(Color.lightGray);
setLayout(new GridLayout(2, 1));
tf = new TextField("777");
add(tf);
Panel p = new Panel();
for (int i = 1; i <= 1000; i *= 10) {
Button b = new Button("+" + i);
b.addActionListener(this);
p.add(b);
}
add(p);
Calculator.java
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044 }
}
public void actionPerformed(ActionEvent event)
{
String cmd = event.getActionCommand();
int n1 = Integer.parseInt(tf.getText());
int n2 = Integer.parseInt(cmd.substring(1));
tf.setText("" + (n1 + n2));
}
public static void main(String[] args)
{
Calculator calc = new Calculator();
calc.setLocation(100, 100);
calc.setSize(200, 85);
calc.setVisible(true);
}
Hinweis
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
/* CalculatorApplet1.java */
CalculatorApplet1.java
import java.awt.*;
import java.applet.*;
public class CalculatorApplet1
extends Applet
{
Calculator calc;
public void init()
{
calc = new Calculator();
calc.setLocation(100, 100);
calc.setSize(200, 130);
}
public void start()
{
calc.setVisible(true);
}
public void stop()
{
calc.setVisible(false);
}
}
<html>
<head>
<title>CalculatorApplet1</title>
</head>
<body>
CalculatorApplet1.html
<h1>CalculatorApplet1</h1>
<applet code="CalculatorApplet1.class" width=200 height=100>
CalculatorApplet1
</applet>
</body>
</html>
Eigenstndige Hauptfenster werden im Browser mit einem zustzlichen Warnhinweis angezeigt (um das
Erstellen von Trojanern zu erschweren). Wir haben daher die Hhe des Fensters von 85 auf 130 Pixel
erhht.
Der in dem WindowClosingAdapter untergebrachte Aufruf zum Beenden des Programms beim
Bettigen des Schlieen-Buttons ist im Browser aus Sicherheitsgrnden nicht erlaubt und lst eine
AppletSecurityException aus. Auch dispose sollte nicht aufgerufen werden, da das Fenster unter
Umstnden noch einmal bentigt wird.
Die Klassen Frame und Applet haben mit Container eine gemeinsame Basisklasse. Die Funktionalitt
zum Hinzufgen von Dialogelementen und Anordnen derselben mit Hilfe eines Layoutmanagers steht also
auch in Applet zur Verfgung. Im ersten Schritt ndern wir daher einfach die Basisklasse und leiten unser
zuknftiges Applet von Applet ab. Zustzlich mu das Paket java.applet importiert werden.
Nun entfernen wir den Konstruktor und plazieren den dort befindlichen Code in der init-Methode. Der
Aufruf des Superklassenkonstruktors kann entfallen, weil ein Applet keine Titelzeile hat. Auch alle eventuell
vorhandenen Aufrufe von setTitle mssen entfernt werden. Die Beschriftung des Applets kann in der
HTML-Datei erfolgen.
Falls der Frame keinen expliziten Layoutmanager gesetzt hat, sondern mit dem vordefinierten
BorderLayout gearbeitet hat, mu ein Aufruf von setLayout erfolgen. Applet verwendet nmlich
standardmig ein FlowLayout.
Nun mssen die Aufrufe der Frame- bzw. Window-spezifischen Methoden entfernt werden. In unserem
Beispiel betrifft das lediglich setWindowAdapter, weitere Beispiele wren setMenuBar,
setIconImage oder setResizable. Am einfachsten ist es, das Programm in diesem Stadium zu
kompilieren und die Fehlermeldungen des Compilers auszuwerten.
Nun sollte noch die main-Methode entfernt werden. Das Instanzieren des Applets und der Aufruf von
setVisible knnen entfallen, denn sie werden vom Browser erledigt. Die Position des Applets ergibt
sich aus der HTML-Datei, und die Gre des Applets kann mit Hilfe der Parameter WIDTH und HEIGHT im
APPLET-Tag festgelegt werden.
Nachdem die Schritte beendet wurden, steht nun (wenigstens in diesem nicht sehr komplexen Beispiel) ein
gleichwertiges Applet zur Verfgung:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
/* CalculatorApplet2.java */
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
public class CalculatorApplet2
extends Applet
implements ActionListener
{
private TextField tf;
public void init()
{
setBackground(Color.lightGray);
setLayout(new GridLayout(2, 1));
tf = new TextField("777");
add(tf);
Panel p = new Panel();
for (int i = 1; i <= 1000; i *= 10) {
Button b = new Button("+" + i);
b.addActionListener(this);
p.add(b);
CalculatorApplet2.java
024
025
026
027
028
029
030
031
032
033
034
035 }
}
add(p);
}
public void actionPerformed(ActionEvent event)
{
String cmd = event.getActionCommand();
int n1 = Integer.parseInt(tf.getText());
int n2 = Integer.parseInt(cmd.substring(1));
tf.setText("" + (n1 + n2));
}
CalculatorApplet2.html
<html>
<head>
<title>CalculatorApplet2</title>
</head>
<body>
<h1>CalculatorApplet2</h1>
<applet code="CalculatorApplet2.class" width=200 height=85>
CalculatorApplet2
</applet>
</body>
</html>
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Mit den Grundlagen aus dem vorigen Abschnitt sind bereits die wichtigsten Prinzipien der Serialisierung in Java
erklrt. Beeindruckend ist dabei einerseits, wie das Konzept in die Klassenbibliothek eingebunden wurde.
ObjectOutputStream und ObjectInputStream passen in natrlicher Weise in die Stream-Hierarchie
und zeigen, wie man Streams konstruiert, die strukturierte Daten verarbeiten. Andererseits ist es eine groe
Hilfe, da Objekte ohne grere nderungen serialisiert werden knnen. Es ist lediglich erforderlich, das
Serializable-Interface zu implementieren, um ein einfaches Objekt persistent machen zu knnen.
Dennoch ist das API leistungsfhig genug, auch komplexe Klassen serialisierbar zu machen. Wir wollen in
diesem Abschnitt weiterfhrende Aspekte betrachten, die im Rahmen dieser Einfhrung noch verstndlich sind.
Daneben gibt es weitere Mglichkeiten, mit denen das Serialisieren und Deserialisieren von Klassen komplett an
die speziellen Anforderungen einer Applikation angepat werden kann. Auf diese Details wollen wir hier aber
nicht eingehen. Als vertiefende Lektre empfiehlt sich die Java Object Serialization Specification, die seit der
Version 1.2 Bestandteil der Online-Dokumentation des JDK ist.
41.2.1 Versionierung
Applikationen, in denen Code und Daten getrennt gehalten werden, haben grundstzlich mit dem Problem der
Inkonsistenz beider Bestandteile zu kmpfen. Wie kann sichergestellt werden, da die Struktur der zu
verarbeitenden Daten tatschlich den vom Programm erwarteten Strukturen entspricht? Dieses Problem gibt es
bei praktisch allen Datenbankanwendungen, und es tritt immer dann verstrkt auf, wenn Code und Datenstruktur
getrennt gendert werden. Auch durch das Serialisieren von Objekten haben wir das Problem, denn die Datei mit
den serialisierten Objekten enthlt nur die Daten, der zugehrige Code kommt dagegen aus dem .class-File.
Das Serialisierungs-API versucht diesem Problem mit einem Versionierungsmechanismus zu begegnen. Dazu
enthlt das Interface Serializable eine long-Konstante serialVersionUID, in der eine
Versionskennung zur Klasse gespeichert wird. Sie wird beim Aufruf von writeObject automatisch berechnet
und stellt einen Hashcode ber die wichtigsten Eigenschaften der Klasse dar. So gehen beispielsweise Name und
Signatur der Klasse, implementierte Interfaces sowie Methoden und Konstruktoren in die Berechnung ein. Selbst
triviale nderungen wie das Umbenennen oder Hinzufgen einer ffentlichen Methode verndern die
serialVersionUID.
Die serialVersionUID einer Klasse kann mit Hilfe des Hilfsprogramms serialver ermittelt
werden. Dieses einfache Programm wird zusammen mit dem Namen der Klasse in der Kommandozeile
aufgerufen und liefert die Versionsnummer als Ausgabe. Alternativ kann es auch mit dem Argument show aufgerufen werden. Es hat dann eine einfache Oberflche, in der der Name der Klasse interaktiv
eingegeben werden kann (siehe Abbildung 41.1).
Tip
import java.io.*;
public class Time
implements Serializable
{
static final long serialVersionUID = -8717671986526504937L;
private int hour;
private int minute;
public Time(int hour, int minute)
{
this.hour = hour;
this.minute = minute;
}
public String toString()
{
return hour + ":" + minute;
}
}
Jetzt mu die Anwendung natrlich selbst darauf achten, da die durchgefhrten nderungen kompatibel sind,
da also durch das Laden der Daten aus dem lteren Objekt keine Inkonsistenzen verursacht werden. Dabei
mgen folgende Regeln als Anhaltspunkte dienen:
Solange die nderungen kompatibel bleiben, ist also durch eine feste serialVersionUID sichergestellt, da
serialisierte Objekte lesbar und deserialisierbar bleiben. Sind die nderungen dagegen inkompatibel, sollte die
Konstante entsprechend gendert werden, und die serialisierten Daten drfen nicht mehr verwendet werden
(bzw. mssen vor der weiteren Verwendung konvertiert werden).
41.2.3 Objektreferenzen
Eine wichtige Eigenschaft des Serialisierungs-APIs im JDK ist die, da auch Referenzen automatisch gesichert
und rekonstruiert werden. Besitzt ein Objekt selbst Strings, Arrays oder andere Objekte als Membervariablen, so
werden diese ebenso wie die primitiven Typen serialisiert und deserialisiert. Da eine Objektvariable lediglich
einen Verweis auf das im Hauptspeicher allozierte Objekt darstellt, ist es wichtig, da diese Verweise auch nach
dem Serialisieren/Deserialisieren erhalten bleiben. Insbesondere darf ein Objekt auch dann nur einmal angelegt
werden, wenn darauf von mehr als einer Variable verwiesen wird. Auch nach dem Deserialisieren darf das
Objekt nur einmal vorhanden sein, und die verschiedenen Objektvariablen mssen auf dieses Objekt zeigen.
Der ObjectOutputStream hlt zu diesem Zweck eine Hashtabelle, in der alle bereits serialisierten Objekte
verzeichnet werden. Bei jedem Aufruf von writeObject wird zunchst in der Tabelle nachgesehen, ob das
Objekt bereits serialisiert wurde. Ist das der Fall, wird in der Ausgabedatei lediglich ein Verweis auf das Objekt
gespeichert. Andernfalls wird das Objekt serialisiert und in der Hashtabelle eingetragen. Beim Deserialisieren
eines Verweises wird dieser durch einen Objektverweis auf das zuvor deserialisierte Objekt ersetzt. Auf diese
Weise werden Objekte nur einmal gespeichert, die Objektreferenzen werden konserviert, und das Problem von
Endlosschleifen durch zyklische Referenzen ist ebenfalls gelst.
Das folgende Programm zeigt das Speichern von Verweisen am Beispiel eines Graphen, der Eltern-KindBeziehungen darstellt. Zunchst bentigen wir dazu eine Klasse Person, die den Namen und die Eltern einer
Person speichern kann. Jeder Elternteil wird dabei durch einen Verweis auf eine weitere Person dargestellt:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
import java.io.*;
Person.java
/* Listing4108.java */
import java.io.*;
import java.util.*;
public class Listing4108
{
public static void main(String[] args)
{
//Erzeugen der Familie
Person opa = new Person("Eugen");
Person oma = new Person("Therese");
Person vater = new Person("Barny");
Person mutter = new Person("Wilma");
Person kind1 = new Person("Fritzchen");
Person kind2 = new Person("Kalli");
vater.father = opa;
vater.mother = oma;
kind1.father = kind2.father = vater;
Listing4108.java
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
}
068 }
speichern.
In Zeile 034 setzen wir die beiden Kindvariablen auf null, um zu beweisen, da sie ausschlielich durch das
nachfolgende Deserialisieren korrekt gesetzt werden. Nun werden kind1 und kind2 deserialisiert, und in den
Zeilen 041 bis 048 wird der komplette Verwandtschaftsgraph ausgegeben. An der Ausgabe des Programms
knnen wir erkennen, da tatschlich alle Objekte rekonstruiert und die Verweise darauf korrekt gesetzt wurden:
Fritzchen
Kalli
Barny
Wilma
Barny
Wilma
Eugen
Therese
--Fritzchen
Kalli
Fred
Wilma
Fred
Wilma
Eugen
Therese
Der zweite Block von Ausgabeanweisungen (in den Zeilen 052 bis 060) zeigt, da auch die Objekteindeutigkeit
gewahrt wurde. Dazu haben wir nmlich in Zeile 050 den Namen des Vaterobjekts von kind1 auf "Fred"
gendert. Wie im zweiten Teil der Ausgabe des Programms zu erkennen ist, wurde damit auch der Name des
Vaters des zweiten Kindes auf "Fred" gendert, und wir knnen sicher sein, da es sich um ein und dasselbe
Objekt handelt.
Obwohl (oder gerade weil) das Serialisieren von Objektgraphen in aller Regel sehr bequem und
vollautomatisch abluft, seien an dieser Stelle einige Warnungen ausgesprochen:
Warnung
Einerseits kann es passieren, da mehr Objekte als erwartet serialisiert werden. Insbesondere
bei komplexen Objektbeziehungen kann es sein, da an dem zu serialisierenden Objekt
indirekt viele weitere Objekte hngen und beim Serialisieren wesentlich mehr Objekte
gespeichert werden, als erwartet wurde. Das kostet unntig Zeit und Speicher.
Durch das Zwischenspeichern der bereits serialisierten Objekte in ObjectOutputStream
werden viele Verweise auf Objekte gehalten, die sonst mglicherweise fr das Programm
unerreichbar wren. Da der Garbage Collector diese Objekte nicht freigibt, kann es beim
Serialisieren einer groen Anzahl von Objekten zu Speicherproblemen kommen. Mit Hilfe
der Methode reset kann der ObjectOutputStream in den Anfangszustand versetzt
werden; alle bereits bekannten Objektreferenzen werden vergessen. Wird ein bereits
serialisiertes Objekt danach noch einmal gespeichert, wird kein Verweis, sondern das Objekt
selbst noch einmal geschrieben.
Wenn ein bereits serialisiertes Objekt verndert und anschlieend erneut serialisiert wird,
bleibt die Vernderung beim Deserialisieren unsichtbar, denn in der Ausgabedatei wird
lediglich ein Verweis auf das Originalobjekt gespeichert.
meisten Collection-Klassen. Um beispielsweise alle Daten eines Vektors oder einer Hashtable persistent zu
speichern, gengt ein einfaches Serialisieren nach obigem Muster. Voraussetzung ist allerdings, da auch die
Elemente der Collection serialisierbar sind, andernfalls gibt es eine NotSerializableException. Auch
die Wrapperklassen zu den Basistypen (siehe Abschnitt 10.2) sind standardmig serialisierbar und knnen
damit problemlos als Objekte in serialisierbaren Collections verwendet werden. Im nchsten Abschnitt stellen
wir eine kleine Anwendung fr das Serialisieren von Hashtabellen vor.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Typ
Bedeutung
did
INT
Primrschlssel
dname
CHAR(100)
Verzeichnisname
fatherdid
INT
Schlssel Vaterverzeichnis
entries
INT
Typ
Bedeutung
fid
INT
Primrschlssel
did
INT
Zugehriges Verzeichnis
fname
CHAR(100)
Dateiname
fsize
INT
Dateigre
fdate
DATE
nderungsdatum
ftime
TIME
nderungszeit
import
import
import
import
import
java.util.*;
java.io.*;
java.sql.*;
java.text.*;
gk.util.*;
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
findDir(args[2]);
066
close();
067
} else if (args[1].equalsIgnoreCase("biggestfiles")) {
068
open();
069
biggestFiles(Integer.parseInt(args[2]));
070
close();
071
} else if (args[1].equalsIgnoreCase("clustering")) {
072
open();
073
clustering(Integer.parseInt(args[2]));
074
close();
075
}
076
} catch (SQLException e) {
077
while (e != null) {
078
System.err.println(e.toString());
079
System.err.println("SQL-State: " + e.getSQLState());
080
System.err.println("ErrorCode: " + e.getErrorCode());
081
e = e.getNextException();
082
}
083
System.exit(1);
084
} catch (Exception e) {
085
System.err.println(e.toString());
086
System.exit(1);
087
}
088
}
089 }
Listing 42.2: Das Rahmenprogramm der DirDB-Datenbank
In main wird zunchst ein usage-Text definiert, der immer dann ausgegeben wird, wenn das Programm
ohne Argumente gestartet wird. Die korrekte Aufrufsyntax ist:
java DirDB [A|I|H] <command> [<options>]
Nach dem Programmnamen folgt zunchst der Buchstabe "A", "I" oder "H", um anzugeben, ob die
Access-, InstantDB- oder HSQLDB-Datenbank verwendet werden soll. Das nchste Argument gibt den
Namen des gewnschten Kommandos an. In der folgenden verschachtelten Verzweigung werden
gegebenenfalls weitere Argumente gelesen und die Methode zum Ausfhren des Programms aufgerufen.
Den Abschlu der main-Methode bildet die Fehlerbehandlung, bei der die Ausnahmen des Typs
SQLException und Exception getrennt behandelt werden.
Das vollstndige Programm findet sich auf der CD-ROM zum Buch unter dem Namen
Hinweis
DirDB.java. In diesem Abschnitt sind zwar auch alle Teile abgedruckt, sie finden sich jedoch
nicht zusammenhngend wieder, sondern sind ber die verschiedenen Unterabschnitte
verteilt. Das importierte Paket gk.util kann wie in Abschnitt 13.2.3 beschrieben installiert
werden.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
/**
* Schliet die Datenbank.
*/
public static void close()
throws SQLException
{
stmt.close();
stmt1.close();
con.close();
}
Soll auf eine ODBC-Datenquelle zugegriffen werden, mu diese unter WindowsSystemsteuerung mit Hilfe des Menpunkts ODBC-Datenquellen zunchst eingerichtet
werden. Im Falle von Microsoft Access ist eine Benutzer-DSN hinzuzufgen, in der als
wichtigste Eigenschaften der gewnschte Name der Datenquelle sowie der Pfad der .mdb-Datei
angegeben werden mssen. In unserem Beispiel wird DirDB als Datenquellenname verwendet.
Im Verzeichnis mit den Beispieldateien zum Buch befindet sich eine leere Datenbank
emptydb.mdb, die mit Access 2000 erstellt wurde und fr erste Experimente verwendet werden
kann. Sie sollte zu den Access-ODBC-Treibern, die aktuellen Windows-Betriebssystemen
beiliegen, kompatibel sein.
InstantDB und HSQLDB sind reine Java-Datenbanken, die ohne separat zu installierende Treiber
betrieben werden knnen. blicherweise mu bei ihnen lediglich das zugehrige jar-Archiv in
den CLASSPATH aufgenommen werden, damit ein Java-Programm darauf zugreifen kann.
InstantDB war frher frei verfgbar, gehrt aber mittlerweile zu Lutris Technologies. Privatleute
knnen es zu Forschungs- und Entwicklungszwecken immer noch frei downloaden. Die
Homepage von InstantDB ist http://www.lutris.com/products/instantDB/index.html. HSQLDB ist
der Nachfolger von Hypersonic SQL. Die Datenbank ist frei verfgbar und wird als SourceForgeProjekt weiterentwickelt. Ihre Homepage ist http://hsqldb.sourceforge.net/.
/**
* Legt die Tabellen an.
*/
public static void createTables()
throws SQLException
{
//Anlegen der Tabelle dir
try {
stmt.executeUpdate("DROP TABLE
} catch (SQLException e) {
//Nichts zu tun
}
stmt.executeUpdate("CREATE TABLE
"did
INT," +
"dname
CHAR(100)," +
"fatherdid INT," +
"entries
INT)"
);
stmt.executeUpdate("CREATE INDEX
stmt.executeUpdate("CREATE INDEX
//Anlegen der Tabelle file
try {
stmt.executeUpdate("DROP TABLE
} catch (SQLException e) {
//Nichts zu tun
}
stmt.executeUpdate("CREATE TABLE
"fid
INT ," +
"did
INT," +
"fname
CHAR(100)," +
"fsize
INT," +
"fdate
DATE," +
"ftime
CHAR(5))"
);
stmt.executeUpdate("CREATE INDEX
}
dir");
dir (" +
file");
file (" +
Hinweis
Nachdem die Tabellen angelegt wurden, knnen sie mit der Methode populate gefllt werden.
populate bekommt dazu vom Rahmenprogramm den Namen des Startverzeichnisses bergeben, das
rekursiv durchlaufen werden soll, und ruft addDirectory auf, um das erste Verzeichnis mit Hilfe des
Kommandos INSERT INTO (das ebenfalls an executeUpdate bergeben wird) in die Tabelle dir
einzutragen. Der Code sieht etwas unleserlich aus, weil einige Stringliterale einschlielich der
zugehrigen einfachen Anfhrungsstriche bergeben werden mssen. Sie dienen in SQL-Befehlen als
Begrenzungszeichen von Zeichenketten.
Fr das aktuelle Verzeichnis wird dann ein File-Objekt erzeugt und mit listFiles (seit dem JDK
1.2 verfgbar) eine Liste der Dateien und Verzeichnisse in diesem Verzeichnis erstellt. Jede Datei wird
mit einem weiteren INSERT INTO in die file-Tabelle eingetragen, fr jedes Unterverzeichnis ruft
addDirectory sich selbst rekursiv auf.
Am Ende wird mit einem UPDATE-Kommando die Anzahl der Eintrge im aktuellen
Verzeichnis in das Feld entries der Tabelle dir eingetragen. Der Grund fr diese etwas
umstndliche Vorgehensweise (wir htten das auch gleich beim Anlegen des dir-Satzes
erledigen knnen) liegt darin, da wir auch ein Beispiel fr die Anwendung der UPDATEAnweisung geben wollten.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
/**
* Durchluft den Verzeichnisbaum rekursiv und schreibt
* Verzeichnis- und Dateinamen in die Datenbank.
*/
public static void populate(String dir)
throws Exception
{
addDirectory(0, "", dir);
}
/**
* Fgt das angegebene Verzeichnis und alle
* Unterverzeichnisse mit allen darin enthaltenen
* Dateien zur Datenbank hinzu.
*/
public static void addDirectory(
int fatherdid, String parent, String name
)
throws Exception
{
String dirname = "";
if (parent.length() > 0) {
dirname = parent;
if (!parent.endsWith(FILESEP)) {
dirname += FILESEP;
}
}
dirname += name;
System.out.println("processing " + dirname);
File dir = new File(dirname);
if (!dir.isDirectory()) {
throw new Exception("not a directory: " + dirname);
}
//Verzeichnis anlegen
Hinweis
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078 }
/**
* Gibt die Anzahl der Dateien und Verzeichnisse aus.
*/
public static void countRecords()
throws SQLException
{
ResultSet rs = stmt.executeQuery(
"SELECT count(*) FROM dir"
);
if (!rs.next()) {
throw new SQLException("SELECT COUNT(*): no result");
}
System.out.println("Directories: " + rs.getInt(1));
rs = stmt.executeQuery("SELECT count(*) FROM file");
if (!rs.next()) {
throw new SQLException("SELECT COUNT(*): no result");
}
System.out.println("Files: " + rs.getInt(1));
rs.close();
}
Um eine bestimmte Datei oder Tabelle in unserer Datenbank zu suchen, verwenden wir ebenfalls ein
SELECT-Statement. Im Gegensatz zu vorher lassen wir uns nun mit der Spaltenangabe "*" alle Felder
der Tabelle geben. Zudem hngen wir an die Abfrageanweisung eine WHERE-Klausel an, um eine
Suchbedingung formulieren zu knnen. Mit Hilfe des LIKE-Operators fhren wir eine Mustersuche
durch, bei der die beiden SQL-Wildcards "%" (eine beliebige Anzahl Zeichen) und "_" (ein einzelnes
beliebiges Zeichen) verwendet werden knnen. Zustzlich bekommt InstantDB die (nicht SQL-92konforme) Option IGNORE CASE angehngt, um bei der Suche nicht zwischen Gro- und
Kleinschreibung zu unterscheiden (ist bei Access 7.0 nicht ntig).
Die von executeQuery zurckgegebene Ergebnismenge wird mit next Satz fr Satz durchlaufen
und auf dem Bildschirm ausgegeben. Mit Hilfe der Methode getDirPath wird zuvor der zugehrige
Verzeichnisname rekonstruiert und vor dem Dateinamen ausgegeben. Dazu wird in einer Schleife zur
angegebenen did (Verzeichnisschlssel) so lange das zugehrige Verzeichnis gesucht, bis dessen
fatherdid 0 ist, also das Startverzeichnis erreicht ist. Rckwrts zusammengebaut und mit
Trennzeichen versehen, ergibt diese Namenskette den kompletten Verzeichnisnamen.
Der in dieser Methode verwendete ResultSet wurde mit dem zweiten StatementObjekt stmt1 erzeugt. Htten wir dafr die zu diesem Zeitpunkt noch geffnete Variable
stmt verwendet, wre das Verhalten des Programmes undefiniert gewesen, weil die
bestehende Ergebnismenge durch das Erzeugen einer neuen Ergebnismenge auf demselben
Statement-Objekt ungltig geworden wre.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
/**
* Gibt eine Liste aller Files auf dem Bildschirm aus,
* die zu dem angegebenen Dateinamen passen. Darin drfen
* die blichen SQL-Wildcards % und _ enthalten sein.
*/
public static void findFile(String name)
throws SQLException
{
String query = "SELECT * FROM file " +
"WHERE fname LIKE \'" + name + "\'";
if (db == INSTANT185) {
query += " IGNORE CASE";
}
ResultSet rs = stmt.executeQuery(query);
while (rs.next()) {
String path = getDirPath(rs.getInt("did"));
System.out.println(
path + FILESEP +
rs.getString("fname").trim()
);
}
rs.close();
}
/**
* Liefert den Pfadnamen zu dem Verzeichnis mit dem
* angegebenen Schlssel.
*/
public static String getDirPath(int did)
Warnung
/**
* Gibt eine Liste aller Verzeichnisse auf dem Bildschirm
* aus, die zu dem angegebenen Verzeichnisnamen passen.
* Darin drfen die blichen SQL-Wildcards % und _
* enthalten sein.
*/
public static void findDir(String name)
throws SQLException
{
String query = "SELECT * FROM dir " +
"WHERE dname LIKE \'" + name + "\'";
if (db == INSTANT185) {
query += " IGNORE CASE";
}
ResultSet rs = stmt.executeQuery(query);
while (rs.next()) {
System.out.println(
getDirPath(rs.getInt("did")) +
" (" + rs.getInt("entries") + " entries)"
);
}
rs.close();
}
Hinweis
/**
* Gibt die howmany grten Dateien aus.
*/
public static void biggestFiles(int howmany)
throws SQLException
{
ResultSet rs = stmt.executeQuery(
"SELECT * FROM file ORDER BY fsize DESC"
);
for (int i = 0; i < howmany; ++i) {
if (rs.next()) {
System.out.print(
getDirPath(rs.getInt("did")) +
FILESEP + rs.getString("fname").trim()
);
System.out.println(
Str.getFormatted("%10d", rs.getInt("fsize"))
);
}
}
021
rs.close();
022 }
Listing 42.9: Sortieren der Ergebnismenge
/**
* Summiert einerseits die tatschliche Gre aller
* Dateien und andererseits die Gre, die sie durch
* das Clustering mit der angegebenen Clustergre
* belegen. Zustzlich wird der durch das Clustering
* "verschwendete" Speicherplatz ausgegeben.
*/
public static void clustering(int clustersize)
throws SQLException
{
int truesize = 0;
int clusteredsize = 0;
double wasted;
ResultSet rs = stmt.executeQuery(
"SELECT * FROM file"
);
while (rs.next()) {
int fsize = rs.getInt("fsize");
truesize += fsize;
if (fsize % clustersize == 0) {
clusteredsize += fsize;
} else {
clusteredsize += ((fsize / clustersize) + 1)*clustersize;
}
}
System.out.println("true size
= " + truesize);
System.out.println("clustered size = " + clusteredsize);
wasted = 100 * (1 - ((double)truesize / clusteredsize));
System.out.println("wasted space
= " + wasted + " %");
}
Um beispielsweise den Einflu der geclusterten Darstellung bei einer Clustergre von 8192 zu
ermitteln, kann das Programm wie folgt aufgerufen werden:
java DirDB I clustering 8192
Die Ausgabe des Programms knnte dann beispielsweise so aussehen:
InstantDB - Version 1.85
Copyright (c) 1997-1998 Instant Computer Solutions Ltd.
Connection URL: jdbc:idb:dirdb.prp
Driver Name:
InstantDB JDBC Driver
Driver Version: Version 1.85
true size
= 94475195
clustered size = 112861184
wasted space
= 16.290799323884464 %
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
DOC
API
Wir wollen uns in diesem Abschnitt mit dem Entwurf einer einfachen Bean beschftigen. Dazu
werden wir eine Klasse LightBulb entwerfen, die eine kleine Glhlampe grafisch darstellt. Sie
kann wahlweise an- oder ausgeschaltet werden. Diese Klasse wird alle notwendigen Eigenschaften
einer Bean aufweisen und kann im GUI-Designer und im laufenden Programm verwendet werden.
import
import
import
import
java.awt.*;
java.awt.event.*;
java.io.*;
java.beans.*;
offimage;
onimage;
createImage aufgelst.
Nach dem Aufruf von createImage sorgt ein MediaTracker dafr, das die
Bilddateien erst vollstndig geladen werden, bevor die Methode terminiert. Diese
Technik verhindert, da paint aufgerufen wird, whrend die Image-Objekte noch
nicht vollstndig initialisiert sind. Details dazu wurden in Abschnitt 34.1.1 vorgestellt.
Hinweis
44.2.3 Eigenschaften
Wie im einleitenden Abschnitt dargelegt, sind Eigenschaften ein wesentliches Designmerkmal von
Beans. Eine Eigenschaft ist eigentlich nur eine Membervariable, die ber ffentliche Methoden
gelesen und geschrieben werden kann. Eine Bean kann beliebig viele Eigenschaften haben, jede
von ihnen besitzt einen Namen und einen Datentyp. Die Bean-Designkonventionen schreiben vor,
da auf eine Eigenschaft mit dem Namen name und dem Datentyp typ ber folgende Methoden
zugegriffen werden soll:
public typ getName();
public void setName(typ newValue);
Diese beiden Methoden bezeichnet man auch als getter- und setter-Methoden.
Hinweis
Unsere Beispiel-Bean hat eine einzige Eigenschaft lightOn vom Typ boolean. Ihre getter-/setterMethoden haben demnach folgende Signatur (der erste Buchstabe des Eigenschaftsnamens wird
grogeschrieben, da er hinter dem "get" bzw. "set" steht):
public boolean getLightOn();
public void setLightOn(boolean newvalue);
Auf diese Weise knnen getter- und setter-Methoden fr alle primitiven Datentypen geschrieben
werden. Der GUI-Designer erkennt Eigenschaftennamen und -typen anhand der Signaturen und
stellt automatisch einen passenden Editor dafr zur Verfgung.
Objekte als Eigenschaften
Neben primitiven Typen ist auch die bergabe von Objekttypen erlaubt. Die Signaturkonventionen
entsprechen genau denen von primitiven Typen. Bei Objekteigenschaften kann allerdings nicht
unbedingt davon ausgegangen werden, da der GUI-Designer einen geeigneten Editor zur
Verfgung stellen kann. Zwar besitzt der GUI-Designer fr die hufig bentigten Objekttypen
Color und Font standardmig geeignete Editoren. Bei einem selbstdefinierten Objekttyp ist das
natrlich nicht der Fall. Hier mu der Entwickler ntigenfalls selbst einen Editor entwickeln und
dem Designer zur Verfgung stellen. Wir werden in Abschnitt 44.6.2 zeigen, wie das gemacht
wird.
Indizierte Eigenschaften
Anstelle eines Einzelwertes kann eine Eigenschaft auch durch ein Array von Werten reprsentiert
werden. Sie wird in diesem Fall als indizierte Eigenschaft bezeichnet. Die getter-/setter-Methoden
sehen dann so aus:
public typ getName(int index);
public void setName(int index, typ newValue);
Es werden also keine Arrays bergeben, sondern die Methoden erwarten jeweils den Index der
gewnschten Eigenschaft als zustzliches Argument. Fr das Einhalten der Arraygrenzen ist der
Aufrufer selbst verantwortlich. Ist die Arraygre variabel, knnte die Bean sie in einer zweiten
Eigenschaft festhalten und ber eigene getter-/setter-Methoden verfgbar machen.
Auch indizierte Eigenschaften werden vom GUI-Designer automatisch erkannt und
sollten zum Aufruf eines geeigneten Editors fhren. Die Beanbox selbst (also die von
SUN zur Verfgung gestellte Referenzimplementierung eines GUI-Designers), auf die
wir in Abschnitt 44.3 eingehen werden, kann allerdings nicht mit indizierten
Eigenschaften umgehen.
Hinweis
44.2.4 Implementierung
Nach diesen Vorbemerkungen ist die Implementierung der Bean zur Darstellung der Glhbirne
keine groe Hrde mehr:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
/* LightBulb.java */
import
import
import
import
java.awt.*;
java.awt.event.*;
java.io.*;
java.beans.*;
LightBulb.java
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099 }
//Private Methoden
private void initTransientState()
{
offimage = getImageResource("bulb1.gif");
onimage = getImageResource("bulb2.gif");
}
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException
{
stream.defaultReadObject();
initTransientState();
}
private Image getImageResource(String name)
{
Image img = null;
try {
java.net.URL url = getClass().getResource(name);
img = getToolkit().createImage(url);
} catch (Exception e) {
System.err.println(e.toString());
}
return img;
}
Zum Abschlu wollen wir uns ansehen, wie die erstellte Bean in ein einfaches Programm
eingebunden werden kann. Dazu bedienen wir uns exakt der Techniken, die bereits in den Kapiteln
31 bis 34 beschrieben wurden. Tatschlich unterscheidet sich die Verwendung einer
selbstentwickelten Bean nicht vom Einbinden einer vordefinierten Komponente.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
/* Listing4404.java */
import java.awt.*;
import java.awt.event.*;
public class Listing4404
extends Frame
{
public Listing4404()
{
super("Bean einbinden");
setLayout(new FlowLayout());
setBackground(Color.lightGray);
LightBulb bulb1 = new LightBulb();
bulb1.setLightOn(false);
add(bulb1);
LightBulb bulb2 = new LightBulb();
bulb2.setLightOn(true);
add(bulb2);
addWindowListener(
new WindowAdapter() {
public void windowClosing(WindowEvent event)
{
System.exit(0);
}
}
);
}
public static void main(String[] args)
{
Listing4404 frm = new Listing4404();
frm.setLocation(100, 100);
frm.pack();
frm.setVisible(true);
}
}
Listing4404.java
Titel
<<
>
>>
DOC
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Nach der Installation befindet sich eine Datei run.bat im Unterverzeichnis beanbox des BDK-Verzeichnisses
(analog dazu gibt es das Shell-Script run.sh zum Starten der Beanbox auf UNIX-Systemen). Diese Batchdatei
setzt den CLASSPATH auf die bentigten Klassen- und .jar-Dateien und startet die Beanbox. Sie ist leider etwas
archaisch. Einerseits wird der aktuelle CLASSPATH berschrieben. Zudem war sie im Test mitunter instabil, wenn
der HotSpot-Compiler aktiviert war. Eine modifizierte Version von run.bat knnte etwa so aussehen (analoge
Anpassungen knnen fr die UNIX-Version vorgenommen werden):
@echo off
set OLDCP=%CLASSPATH%
echo saving old CLASSPATH in OLDCP: %OLDCP%
set CLASSPATH=classes;..\lib\methodtracer.jar;..\infobus.jar
java -classic sun.beanbox.BeanBoxFrame
set CLASSPATH=%OLDCP%
set OLDCP=
echo CLASSPATH restored to %CLASSPATH%
Das linke Fenster zeigt die Toolbox an. Sie enthlt eine Liste mit allen verfgbaren Beans.
Das mittlere Fenster stellt den GUI-Designer dar, in dem ausgewhlte Beans plaziert werden. Hier knnen
die Beans verschoben, in der Gre verndert und zur Bearbeitung ausgewhlt werden.
Das rechte obere Fenster enthlt den Property-Editor, mit dessen Hilfe die Eigenschaften der ausgewhlten
Bean modifiziert werden knnen.
Der rechts unten angezeigte Method Tracer dient zur Untersttzung beim Debuggen und soll hier nicht
weiter behandelt werden. Er kann mit dem Menpunkt "Services.Hide_Method_Tracing" deaktiviert werden.
Um eine Bean zu bearbeiten, mu sie zunchst im GUI-Designer plaziert werden. Dazu ist sie in der Toolbox
anzuklicken und mit einem erneuten Klick im GUI-Designer zu plazieren. Der schraffierte Rand zeigt an, da sie
ausgewhlt wurde und ihre Eigenschaften im Property-Editor modifiziert werden knnen. Durch Ziehen an einem
der vier Rnder kann die Bean beliebig verschoben werden. Ist ihre Gre variabel, kann sie durch Ziehen an einer
der vier Ecken verndert werden. Soll eine Bean aus dem GUI-Designer entfernt werden, ist sie zu markieren und
mit dem Menpunkt "Edit.Cut" zu lschen.
Beachten Sie bitte, da das Verschieben der Bean und das ndern ihrer Gre ausschlielich durch
Mausklicks auf ihrem Rand ausgefhrt wird. Mausoperationen im Inneren der Bean werden als regulre
Ereignisse gewertet und direkt an das zu diesem Zeitpunkt bereits instanzierte Bean-Objekt weitergeleitet.
Tip
Als Beispiel wollen wir die vordefinierte Bean "OrangeButton" im GUI-Designer plazieren. Sie stellt sich zunchst
als oranges Rechteck dar, das nach einem Mausklick kurz die Farbe wechselt. Im Property-Editor knnen die
ffentlichen Eigenschaften der Bean verndert werden:
"background" verndert die Hintergrundfarbe. Sie ist standardmig orange und kann durch Klick auf das
farbige Panel verndert werden. Der angezeigte Farbauswahldialog ist allerdings etwas rudimentr und
erlaubt lediglich die direkte Eingabe eines RGB-Werts oder die Auswahl einer vordefinierten Farbkonstante.
"foreground" verndert die Farbe, in der der Rahmen des Buttons gezeichnet wird. Hier kommt derselbe
Farbdialog wie zuvor zum Einsatz.
Die Eigenschaft "label" definiert die Bezeichnung des Buttons. Wird der Wert in diesem Textfeld gendert,
pat sich die Beschriftung des Buttons entsprechend an.
Schlielich kann mit der Eigenschaft "font" die Schriftart und -gre gendert werden, in der die
Beschriftung des Buttons angezeigt wird. Auch hier wird der nderungsdialog durch Klicken auf den
angezeigten Wert aufgerufen und prsentiert sich hnlich spartanisch wie der Farbdialog. Immerhin knnen
die relevanten Font-Parameter verndert werden, und auch die Eigenschaftenanzeige verndert sich in
Abhngigkeit von der Fontauswahl.
Events
Die Beanbox bietet die Mglichkeit, eine Bean ber die von ihr generierten Events mit einer anderen Bean zu
verbinden. Um das beispielhaft zu demonstrieren, soll zustzlich eine Instanz der Bean vom Typ "EventMonitor" im
GUI-Designer plaziert werden. Dann klicken wir den Button an und rufen den Menpunkt
"edit.Events.buttonPushed.actionPerformed" auf. Wenn die Maus nun den Button verlt, bleibt sie ber eine rote
Linie mit dem Button verbunden. Wir ziehen diese Linie auf den "EventMonitor" und fhren einen weiteren
Mausklick aus. In der nun angezeigten Liste aufrufbarer Methoden whlen wir den (einzigen) Eintrag
initiateEventSourceMonitoring aus.
Die Beanbox generiert nun Java-Code fr eine Adapterklasse, kompiliert ihn und legt die resultierenden Dateien im
Unterverzeichnis tmp\sunw\beanbox ab. Gleichzeitig registriert sie die Adapterklasse fr das Action-Ereignis des
Buttons und sorgt durch den Aufruf der ausgewhlten Methode innerhalb von actionPerformed dafr, da der
Event-Listener bei jedem Buttonklick benachrichtigt wird. Seine Aufgabe besteht darin, die ausgelsten Ereignisse
zu speichern und in einer Listbox auf dem Bildschirm anzuzeigen.
Auf diese Weise lassen sich prinzipiell beliebige Beans miteinander verbinden. Die Einschrnkung der
Beanbox besteht lediglich darin, da sie nur Adapterklassen fr den Aufruf von parameterlosen
Methoden erzeugen kann. Fortgeschrittenere GUI-Designer wrden es erlauben, auch parametrisierte
Methoden aufzurufen. Sie wrden zustzlich einen Editor zur Verfgung stellen, mit dem der erzeugte
Code per Hand angepat werden kann.
Hinweis
eventuell eine etwas andere Vorgehensweise erforderlich sein. Dazu sollte die Bean-Dokumentation des jeweiligen
Entwicklungssystems konsultiert werden.
Entwickeln und bersetzen der Bean
Die ersten beiden Schritte, das Entwickeln und bersetzen der Bean, haben wird schon erledigt. Sie unterscheiden
sich in keiner Weise von der einer herkmmlichen Java-Klasse. Als Ergebnis ensteht eine Klasse
LightBulb.class mit dem Bytecode der Glhlampen-Bean.
Erzeugen einer Manifest-Datei
Um bei GUI-Designern mit einer groen Anzahl unterschiedlicher Beans ein schwer zu durchschauendes
Sammelsurium kleiner Klassendateien zu vermeiden, werden Beans in jar-Archive verpackt und so an den GUIDesigner bergeben. Eine jar-Datei darf beliebig viele Beans enthalten. Um dem GUI-Designer die Mglichkeit zu
geben, Klassen fr Beans von solchen mit reinen Hilfsfunktionen zu unterscheiden, wird dem jar-Archiv eine
Manifest-Datei hinzugefgt, die ihren Inhalt beschreibt. Es handelt sich um eine normale Textdatei, die fr jede
enthaltene Bean einen Eintrag der folgenden Form enthlt:
Name: BeanClassFile.class
Java-Bean: True
Der erste Eintrag gibt den Klassennamen an. Der zweite besagt, da es sich um eine Bean handelt (ManifestDateien knnen auch noch andere Informationen ber die im Archiv gespeicherten Dateien enthalten). Die
Informationen werden zweckmigerweise in eine Textdatei manifest.txt geschrieben und beim Erzeugen der jarDatei mit der Option "m" eingebunden. Zum Einbinden der Glhlampe mu diese Datei also folgenden Inhalt
haben:
Name: LightBulb.class
Java-Bean: True
Erzeugen des jar-Archivs
Nun kann das Kommando zum Erzeugen der jar-Datei abgesetzt werden (wir wollen sie hjp3beans.jar nennen):
jar cfm hjp3beans.jar manifest.txt LightBulb.class bulb*.gif
Die Option "c" steht fr "create" und besagt, da eine neue jar-Datei angelegt werden soll. Das "f" steht fr "file"
und gibt an, da als erstes Argument hinter den Kommandobuchstaben der Name der jar-Datei steht. Das "m" steht
fr "manifest" und besagt, da als nchstes Argument der Name der Manifest-Datei folgt. Ganz zum Schlu stehen
die Namen der einzubindenden Dateien. In diesem Fall sind das die Klassendatei und die zur Darstellung bentigten
gif-Dateien bulb1.gif und bulb2.gif.
Die jar-Datei mu nun in das Unterverzeichnis jars des BDK-Installationsverzeichnisses kopiert werden. Beim
nchsten Start der Beanbox wird sie automatisch gefunden, analysiert, und die darin enthaltenen Beans werden im
GUI-Designer zur Verfgung gestellt.
Verwenden der eigenen Bean
Unsere Glhlampe wird nun in der Toolbox angezeigt und kann wie jede andere Bean in den GUI-Designer
bernommen werden. Im Eigenschaftenfenster werden ihre Eigenschaften angezeigt und knnen interaktiv
verndert werden. Wird beispielsweise die Eigenschaft lightOn von false auf true gesetzt, ndert die Bean
ihren Zustand und die Glhlampe leuchtet auf (siehe Abbildung 44.3). Auch auf das Verndern der ererbten
Eigenschaften (z.B. der Hintergrundfarbe) reagiert unsere neue Komponente erwartungsgem mit einer
entsprechenden nderung der graphischen Darstellung.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
/* Listing4405.java */
import java.awt.*;
import java.awt.event.*;
import java.io.*;
public class Listing4405
extends Frame
{
public Listing4405()
{
super("LighBulbTest");
setLayout(new FlowLayout());
setBackground(Color.gray);
addWindowListener(
new WindowAdapter() {
public void windowClosing(WindowEvent event)
{
System.exit(0);
}
}
);
loadLightBulb();
}
private void loadLightBulb()
{
try {
ObjectInputStream is = new ObjectInputStream(
new FileInputStream("lb1.ser"));
LightBulb bulb = (LightBulb)is.readObject();
is.close();
add(bulb);
} catch (ClassNotFoundException e) {
System.err.println(e.toString());
} catch (IOException e) {
System.err.println(e.toString());
}
}
public static void main(String[] args)
{
Listing4405 frm = new Listing4405();
frm.setLocation(100, 100);
frm.pack();
frm.setVisible(true);
}
}
Listing4405.java
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
44.4 Bean-Ereignisse
44.4 Bean-Ereignisse
44.4.1 Bound Properties
44.4.2 Constrained Properties
44.4.3 Anwendungsbeispiel
Um die Kommunikation zwischen Beans zu vereinfachen und zu vereinheitlichen, sieht die Beans-Spezifikation einige
Techniken vor, mit denen Beans untereinander kommunizieren knnen. Die wichtigste von ihnen ist die Mglichkeit,
Nachrichten zu versenden, wenn sich eine bestimmte Eigenschaft der Bean gendert hat. Das dabei angewendete Schema
entspricht genau dem Delegation Based Event Handling, das in Kapitel 28 erlutert wurde und das auch alle anderen
Dialogelemente zur Kommunikation mit anderen Komponenten verwenden.
java.beans.PropertyChangeEvent
Mit getPropertyName kann der Name der vernderten Eigenschaft ermittelt werden. getNewValue liefert ihren neuen
Wert und getOldValue den Wert, den sie vor der nderung hatte. Die Werte werden stets als Objekttyp zurckgegeben.
Primitive Typen werden in ihre korrespondierenden Wrapper-Klassen verpackt (siehe Abschnitt 10.2).
Eine Bean erzeugt immer dann ein PropertyChangeEvent und versendet es an alle registrierten Listener, wenn sich der
Zustand einer gebundenen Eigenschaft verndert hat. Ein Listener mu das Interface PropertyChangeListener
implementieren. Es enthlt nur eine Methode:
void propertyChange(PropertyChangeEvent evt)
java.beans.PropertyChangeListener
Verndert sich die gebundene Eigenschaft, erzeugt die Bean ein PropertyChangeEvent mit dem neuen und dem alten
Wert und ruft bei allen registrierten Listenern propertyChange auf. Die Registrierung bzw. Deregistrierung von Listenern
erfolgt mit den Methoden addPropertyChangeListener und removePropertyChangeListener, die beide einen
PropertyChangeListener als Argument erwarten.
Damit ein GUI-Designer erkennen kann, da eine Bean gebundene Eigenschaften besitzt, mssen die Methoden zum Hinweis
Registrieren und Deregistrieren exakt die hier gezeigten Namen- und Parameterkonventionen einhalten. Sie sind
ebenso Bestandteil der Designkonventionen von Beans wie die in den vorigen Abschnitten erwhnten getter- und
setter-Methoden.
Um die Bean beim Umgang mit gebundenen Eigenschaften zu untersttzen, gibt es im Paket java.beans eine Hilfsklasse
PropertyChangeSupport. Sie stellt einige ntzliche Methoden zur Verfgung, mit denen eine Bean die bei der EventRegistrierung und -verteilung anfallenden Aufgaben delegieren kann:
public void addPropertyChangeListener(
PropertyChangeListener listener
)
public void removePropertyChangeListener(
PropertyChangeListener listener
)
java.beans.PropertyChangeSupport
Die Bean instanziert bei Bedarf ein Objekt dieser Klasse und gibt Aufrufe der eigenen Methoden
addPropertyChangeListener und removePropertyChangeListener einfach weiter. ndert sich eine gebundene
Eigenschaft, ruft die Bean eine der firePropertyChange-Methoden auf und sendet dadurch ein
PropertyChangeEvent an alle registrierten Listener. Diese Methode steht einerseits vordefiniert fr die Typen int,
boolean und Object zur Verfgung. Andererseits kann aber auch manuell ein PropertyChangeEvent erzeugt und
weitergegeben werden.
Wir wollen uns als erstes Beispiel eine Bean ansehen, die einen beleuchteten Taster implementiert. Der Taster kann wahlweise
an- oder ausgeschaltet sein. In angeschaltetem Zustand leuchtet er, in ausgeschaltetem Zustand ist seine Oberflche dunkel.
Jeder Tastendruck schaltet zwischen den beiden Zustnden um. Die wichtigste Eigenschaft des Tasters heit lightOn. Sie ist
gebunden und zeigt an, ob der Taster an- oder ausgeschaltet ist:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
/* LightedPushButton.java */
import
import
import
import
java.awt.*;
java.awt.event.*;
java.io.*;
java.beans.*;
LightedPushButton.java
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
//---Methoden-----------------------------------------------public LightedPushButton()
{
linecolor
= Color.black;
framecolor
= Color.darkGray;
lightoncolor = Color.red;
lightoffcolor = new Color(127, 0, 0); //dark red
lighton
= false;
initTransientState();
}
//---Zustandsumschaltung Licht an/aus--public void setLightOn(boolean on)
throws PropertyVetoException
{
boolean oldvalue = this.lighton;
vchglisteners.fireVetoableChange("lighton", oldvalue, on);
this.lighton = on;
if (oldvalue != on) {
repaint();
}
pchglisteners.firePropertyChange("lighton", oldvalue, on);
}
public boolean getLightOn()
{
return this.lighton;
}
//---Verwaltung der PropertyChangeListener--public void addPropertyChangeListener(PropertyChangeListener l)
{
pchglisteners.addPropertyChangeListener(l);
}
public void removePropertyChangeListener(PropertyChangeListener l)
{
pchglisteners.removePropertyChangeListener(l);
}
//---Verwaltung der VetoableChangeListener--public void addVetoableChangeListener(VetoableChangeListener l)
{
vchglisteners.addVetoableChangeListener(l);
}
public void removeVetoableChangeListener(VetoableChangeListener l)
{
vchglisteners.removeVetoableChangeListener(l);
}
//---Implementierung der Oberflche--public void paint(Graphics g)
{
int width = getSize().width;
int height = getSize().height;
//Rahmen
g.setColor(framecolor);
g.fillOval(0, 0, width, height);
//Beleuchtung
g.setColor(lighton ? lightoncolor : lightoffcolor);
g.fillOval(4, 4, width - 8, height - 8);
//Konturlinien
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134 }
g.setColor(linecolor);
g.drawOval(0, 0, width - 1, height - 1);
g.drawOval(3, 3, width - 7, height - 7);
}
public Dimension getPreferredSize()
{
return new Dimension(32, 32);
}
public Dimension getMinimumSize()
{
return new Dimension(16, 16);
}
//---Private Klassen--------------------------------------/**
* Initialisierung der nicht-persistenten Instanzvariablen.
*/
private void initTransientState()
{
pchglisteners = new PropertyChangeSupport(this);
vchglisteners = new VetoableChangeSupport(this);
addMouseListener(new MouseClickAdapter());
}
/**
* Wird berlagert, um nach dem Deserialisieren den transienten
* Zustand zu initialisieren.
*/
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException
{
stream.defaultReadObject();
initTransientState();
}
//---Lokale Klassen---------------------------------------class MouseClickAdapter
extends MouseAdapter
{
public void mouseClicked(MouseEvent event)
{
try {
setLightOn(!getLightOn());
} catch (PropertyVetoException e) {
//no change if vetoed
}
}
}
Abbildung 44.5 zeigt die Klasse LightedPushButton in ihren beiden mglichen Zustnden in der Beanbox.
java.beans.VetoableChangeListener
Immer wenn sich eine Constrained Property ndern soll, ruft die Bean vor der nderung bei den registrierten Listenern die
Methode vetoableChange auf. Der Empfnger prft daraufhin das bergebene PropertyChangeEvent und
entscheidet, ob er dem nderungswunsch zustimmen soll oder nicht. Ist das nicht der Fall, lst er eine
PropertyVetoException aus, die beim Empfnger dazu fhrt, da der nderungsvorgang abgebrochen wird. Stimmt er
dagegen zu, ist gar nichts zu tun. Der Listener terminiert einfach und die Bean fhrt mit der Befragung beim nchsten Listener
fort. Nur wenn alle Listener zugestimmt haben (also niemand eine PropertyVetoException ausgelst hat), wird die
nderung tatschlich durchgefhrt.
Die Methode setLightOn in Listing 44.6 macht "lightOn" also sowohl zu einer gebundenen als auch unterbindbaren
Eigenschaft. Zunchst prft sie, ob alle registrierten VetoableChangeListener eine mgliche Zustandsnderung nicht
ablehnen. Erst wenn das der Fall ist, wird die nderung tatschlich durchgefhrt, und die registrierten
PropertyChangeListener werden darber informiert.
Das Registrieren und Deregistrieren erfolgt mit den Methoden addVetoableChangeListener und
removeVetoableChangeListener. Ebenso wie die korrespondierenden Methoden zur Registrierung der
PropertyChangeListener mssen ihre Namen und Parameter genau der hier angegebenen Form entsprechen, damit sie
vom GUI-Designer gefunden werden.
Zur Untersttzung bei der Implementierung kann sich die Bean der Klasse VetoableChangeSupport bedienen, die
hnliche Methoden wie PropertyChangeSupport zur Verfgung stellt:
public void addVetoableChangeListener(
VetoableChangeListener listener
)
public void removeVetoableChangeListener(
VetoableChangeListener listener
)
public void fireVetoableChange(PropertyChangeEvent evt)
throws PropertyVetoException
public void fireVetoableChange(
String propertyName,
Object oldValue,
Object newValue
)
throws PropertyVetoException
public void fireVetoableChange(
String propertyName,
int oldValue,
int newValue
)
throws PropertyVetoException
java.beans.VetoableChangeSupport
Um im nchsten Abschnitt ein Beispiel fr die Anwendung von Constrained Properties geben zu knnen, wollen wir eine
Klasse vorstellen, die das Interface VetoableChangeListener implementiert. Sie wird durch eine Bean reprsentiert, die
einen einfachen Umschalter darstellt, der durch Mausklick geffnet oder geschlossen werden kann. Ist der Schalter geschlossen,
sind Eigenschaftsnderungen erlaubt. Ist er dagegen geffnet, wird bei jedem Aufruf von vetoableChange eine
PropertyVetoException ausgelst. Auf diese Weise werden bei geffnetem Schalter alle Eigenschaftsnderungen
verhindert:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
/* VetoSwitch.java */
import
import
import
import
java.awt.*;
java.awt.event.*;
java.io.*;
java.beans.*;
VetoSwitch.java
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
3 / 8, height / 2);
7 / 8, height / 2);
3, 3);
3, 3);
* 5 / 8, height / 4);
* 5 / 8, height / 2);
111
setVetoAllChanges(!getVetoAllChanges());
112
}
113
}
114 }
Listing 44.7: Ein Veto-Schalter
Abbildung 44.5 zeigt die Klasse VetoSwitch in ihren beiden mglichen Zustnden in der Beanbox:
44.4.3 Anwendungsbeispiel
Das folgende Listing zeigt die drei bisher definierten Beans im Zusammenspiel. Zunchst werden alle drei Elemente instanziert
und auf einem Frame plaziert. Da der LightedPushButton bei jedem Tastendruck den Zustand der LightBulb
umschalten soll, bekommt er einen PropertyChangeListener zugeordnet, der bei Zustandsnderungen der Eigenschaft
"lighton" die Lampe umschaltet.
Der VetoSwitch wird als VetoableChangeListener ebenfalls beim LightedPushButton registriert. Da er in
geffnetem Zustand alle unterbindbaren Eigenschaftennderungen verhindert, dient er dazu, die Funktion des Buttons zu
deaktivieren. Nur, wenn der VetoSwitch geschlossen ist, hat das Drcken des LightedPushButton eine nderung des
Lampenzustands zur Folge.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
/* Listing4408.java */
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
public class Listing4408
extends Frame
{
public Listing4408()
{
//Initialisierung
super("BeanPropertiesTest");
setLayout(new FlowLayout());
setBackground(Color.lightGray);
addWindowListener(
new WindowAdapter() {
public void windowClosing(WindowEvent event)
{
System.exit(0);
Listing4408.java
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053 }
}
}
);
//Dialogelemente hinzufgen
LightedPushButton button1 = new LightedPushButton();
VetoSwitch veto1 = new VetoSwitch();
final LightBulb bulb1 = new LightBulb();
add(button1);
add(veto1);
add(bulb1);
button1.addPropertyChangeListener(
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent e)
{
if (e.getPropertyName().equals("lighton")) {
Boolean on = (Boolean)e.getNewValue();
bulb1.setLightOn(on.booleanValue());
}
}
}
);
button1.addVetoableChangeListener(veto1);
}
//---main------------------------------------------------public static void main(String[] args)
{
Listing4408 frm = new Listing4408();
frm.setLocation(100, 100);
frm.pack();
frm.setVisible(true);
}
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
44.5 Panel-Beans
44.5 Panel-Beans
Die bisherigen Beans waren aus Canvas abgeleitet und reprsentierten damit elementare Komponenten. Ebenso
leicht ist es mglich, zusammengesetzte Komponenten als Beans zu definieren und so grere Programmeinheiten
wiederverwendbar zu machen.
Wird eine Bean beispielsweise aus der Klasse Panel abgeleitet, knnen darauf vordefinierte Dialogelemente plaziert
und mit Hilfe von setter-Methoden konfigurierbar gemacht werden. Im GUI-Designer erscheinen die
unterschiedlichen Dialogelemente dann wie ein einziges. Meist spielt die Bean auch noch die Rolle eines
Ereigniskonzentrators, d.h. sie registriert sich als Listener bei ihren eigenen Komponenten und verarbeitet diese so
weit wie mglich selbst. Nach auen hin definiert sie ein abstrakteres Event-Modell, das an die Bedrfnisse der
Gesamtkomponente angepat ist. Statt den Client mit den Elementarereignissen der einzelnen Komponenten zu
belasten, sendet sie nur noch Ereignisse, die das Verhalten der zusammengesetzten Komponente reprsentieren.
Die Konstruktion einer Panel-Bean ist prinzipiell nicht schwieriger als die einer einfachen Bean. Zustzlicher
Aufwand wird allerdings erforderlich, wenn die Eigenschaften der darin enthaltenen Komponenten zum
Designzeitpunkt nicht mit einfachen getter- und setter-Methoden erledigt werden kann. Die meisten Entwickler
erwarten heute beispielsweise von einem Splitpanel (eine typische Panel-Bean), da im GUI-Designer die einzelnen
Komponenten per Drag-and-Drop darauf plaziert werden knnen.
So weit wollen wir allerdings nicht in die Bean-Programmierung vordringen. Wir wollen nur die Grundprinzipien
erklren und eine Klasse ButtonPanel entwickeln, die eine Gruppe von Radiobuttons kapselt. Die Anzahl der
Radiobuttons, ihre Beschriftung und die Auswahl des selektierten Elements sollen frei definiert werden knnen.
ndert sich die Anzahl der Buttons oder wird ein anderer Button selektiert, soll die Bean ein
PropertyChangeEvent versenden.
Das folgende Listing zeigt den Code fr diese Klasse:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
/* ButtonPanel.java */
import
import
import
import
java.awt.*;
java.awt.event.*;
java.beans.*;
java.io.*;
ButtonPanel.java
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
public ButtonPanel()
{
cbg = new CheckboxGroup();
cb = new Checkbox[0];
initTransientState();
}
//---Anzahl der RadioButtons----------------------------public void setButtonCnt(int cnt)
{
if (cnt != cb.length) {
int oldvalue = cb.length;
//Bisherige Buttons entfernen
if (cb.length > 0) {
removeAll();
}
cb = new Checkbox[cnt];
setLayout(new GridLayout(cnt, 1));
for (int i = 0; i < cnt; ++i) {
cb[i] = new Checkbox(
"RadioButton " + i,
cbg,
(i == 0 ? true : false)
);
cb[i].addItemListener(this);
add(cb[i]);
}
//PropertyChangeEvents senden
pcs.firePropertyChange("buttonCnt", oldvalue, cnt);
if (cnt > 0) {
setSelected(0);
}
//Neu layouten
setSize(getPreferredSize());
invalidate();
doLayout();
Container owner = getParent();
if (owner != null) {
owner.invalidate();
owner.doLayout();
}
}
}
public int getButtonCnt()
{
return cb.length;
}
//---Beschriftung der Buttons---------------------------public void setLabel(int index, String label)
{
if (index >= 0 && index < cb.length) {
cb[index].setLabel(label);
}
}
public String getLabel(int index)
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
{
String ret = "***invalid index***";
if (index >= 0 && index < cb.length) {
ret = cb[index].getLabel();
}
return ret;
}
//---Selektiertes Element-------------------------------public void setSelected(int index)
{
if (index >= 0 && index < cb.length) {
int oldvalue = getSelected();
cb[index].setState(true);
pcs.firePropertyChange("selected", oldvalue, index);
}
}
public int getSelected()
{
int ret = -1;
for (int i = 0; i < cb.length; ++i) {
if (cb[i].getState()) {
ret = i;
break;
}
}
return ret;
}
//---Verwaltung der PropertyChangeListener--public void addPropertyChangeListener(
PropertyChangeListener l
)
{
pcs.addPropertyChangeListener(l);
}
public void removePropertyChangeListener(
PropertyChangeListener l
)
{
pcs.removePropertyChangeListener(l);
}
//---Reaktion auf Zustandsnderungen--------------------public void itemStateChanged(ItemEvent event)
{
Checkbox changedcb = (Checkbox) event.getItemSelectable();
if (changedcb.getState()) {
for (int i = 0; i < cb.length; ++i) {
if (cb[i] == changedcb) {
pcs.firePropertyChange("selected", -1, i);
break;
}
}
}
}
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154 }
//---Private Methoden-----------------------------------/**
* Initialisierung der nicht-persistenten Instanzvariablen.
*/
private void initTransientState()
{
pcs = new PropertyChangeSupport(this);
}
/**
* Wird berlagert, um nach dem Deserialisieren den transienten
* Zustand zu initialisieren.
*/
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException
{
stream.defaultReadObject();
initTransientState();
}
Das Listing treibt einigen Aufwand, um nach einer Grennderung korrekt dargestellt zu werden (im
Listing ab Zeile 050 zu sehen). Trotzdem war beispielsweise die Beanbox nicht dazu zu bewegen, bei
einer Verminderung der Buttonzahl den nicht mehr bentigten Platz wieder freizugeben. Ist das
dynamische ndern der Gre ein wichtiges Feature, mu unter Umstnden noch ein wenig
experimentiert werden, um bei allen Clients zu den gewnschten Resultaten zu kommen.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Warnung
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
44.6.1 BeanInfo-Klassen
Die bisher beschriebenen Fhigkeiten und Eigenschaften einer Bean wurden vom GUI-Designer automatisch erkannt, weil die Beans bestimmte
Design-Konventionen eingehalten haben. Der GUI-Designer verwendet eine Instanz der Klasse Introspector aus dem Paket java.beans,
um diese Fhigkeiten nach dem Laden der Bean zu bestimmen.
Neben getter-/setter-Methoden und Registrierungsmethoden fr Events knnen Beans aber noch weitere Eigenschaften besitzen. So kann
beispielsweise in der Toolbox ein Icon angezeigt werden. Auch Methoden, die nicht den getter-/setter-Konventionen entsprechen, knnen
bekanntgemacht werden. Zudem ist es mglich, fr Nicht-Standard-Eigenschaften spezielle Eigenschafteneditoren zur Verfgung zu stellen.
Diese Mglichkeiten stehen erst zur Verfgung, wenn man die Bean mit einer expliziten BeanInfo-Klasse ausstattet. Sie hat denselben Namen wie
die Bean, trgt aber zustzlich den Suffix "BeanInfo" am Ende ihres Namens. Zudem mu sie im selben Paket liegen wie die Bean-Klasse. Die
BeanInfo-Klasse mu das Interface BeanInfo implementieren oder aus SimpleBeanInfo abgeleitet sein (letzteres bietet den Vorteil, da
bereits alle erforderlichen Methoden vorhanden sind und bei Bedarf nur noch berlagert werden mssen).
BeanInfo definiert eine Reihe von Methoden, die der GUI-Designer abfragt. Sie liefern jeweils ein Array von Descriptor-Objekten, von denen
jedes Informationen ber eine zu verffentlichende Methode, Eigenschaft oder ein Icon zur Verfgung stellt. Wir wollen uns die drei wichigsten
von ihnen in den nachfolgenden Abschnitten ansehen.
Whrend der GUI-Designer die ihm bekannten Beans ldt, sucht er fr jede von ihnen zunchst nach einer passenden BeanInfoKlasse. Findet er eine solche, wird sie verwendet, und der Low-Level-Introspectionsvorgang wird gar nicht erst angestoen. Daraus
folgt insbesondere, da in diesem Fall nur die in der BeanInfo-Klasse angegebenen Merkmale zur Verfgung stehen.
Warnung
Ist es beispielsweise ntig, eine BeanInfo-Klasse zur Verfgung zu stellen, weil dem Designer eine besondere Methode
bekanntgemacht werden soll, die nicht den getter-/setter-Konventionen entspricht, mu die BeanInfo-Klasse auch alle anderen
Eigenschaften der Bean explizit bekanntmachen. Andernfalls wrde der GUI-Designer nur die spezielle Methode sehen, alle anderen
Merkmale aber ignorieren.
getIcon
public Image getIcon(int iconKind)
java.beans.BeanInfo
Der GUI-Designer ruft getIcon auf, um herauszufinden, ob die Bean ein Icon zur Darstellung in der Toolbox zur Verfgung stellen kann. Als
Parameter wird dabei eine der symbolischen Konstanten ICON_COLOR_16x16, ICON_MONO_16x16, ICON_COLOR_32x32 oder
ICON_MONO_32x32 bergeben. Kann die BeanInfo-Klasse ein Icon in der gewnschten Form zur Verfgung stellen, mu sie ein passendes
Image-Objekt erzeugen und an den Aufrufer zurckgeben. Ist das nicht der Fall, sollte null zurckgegeben werden.
Ist die BeanInfo-Klasse aus SimpleBeanInfo abgeleitet, ist das Erzeugen eines Image-Objekts einfach. SimpleBeanInfo besitzt
eine Methode loadImage, die bei bergabe eines Dateinamens die ensprechende Bilddatei ldt und daraus ein Image erzeugt.
getPropertyDescriptors
Tip
java.beans.BeanInfo
Die Methode getPropertyDescriptors wird aufgerufen, um ein Array von PropertyDescriptor-Objekten zur Verfgung zu stellen.
Es enthlt fr jede ffentliche Eigenschaft der Bean ein Element, das dessen Merkmale beschreibt. Ein PropertyDescriptor kann recht
einfach instanziert werden:
public PropertyDescriptor(String propertyName, Class beanClass)
throws IntrospectionException
java.beans.PropertyDescriptor
Lediglich der Name der Eigenschaft und das Klassenobjekt der zugehrigen Bean sind anzugeben. Weitere Merkmale knnen durch
Methodenaufrufe hinzugefgt werden. Uns interessiert an dieser Stelle lediglich die Methode setPropertyEditorClass, auf die wir in
Abschnitt 44.6.2 zurckkommen werden.
Fr jedes von getPropertyDescriptors zurckgegebene Element erwartet der GUI-Designer eine setter- und eine getterMethode, deren Signatur dem bekannten Schema entsprechen mu.
Hinweis
getMethodDescriptors
java.beans.BeanInfo
Zustzliche Methoden knnen dem GUI-Designer durch berlagern von getMethodDescriptors bekanntgemacht werden. Zurckgegeben
wird ein Array, das fr jede Methode ein Objekt des Typs MethodDescriptor enthlt. Darin wird im wesentlichen ein Method-Objekt
gekapselt (siehe Abschnitt 43.3):
public MethodDescriptor(Method method)
java.beans.MethodDescriptor
/* LightBulbBeanInfo.java */
import java.awt.*;
import java.beans.*;
import java.lang.reflect.*;
public class LightBulbBeanInfo
extends SimpleBeanInfo
{
public Image getIcon(int iconKind)
{
String imgname = "bulbico16.gif";
if (iconKind == BeanInfo.ICON_MONO_32x32 ||
iconKind == BeanInfo.ICON_COLOR_32x32) {
imgname = "bulbico32.gif";
}
return loadImage(imgname);
}
public PropertyDescriptor[] getPropertyDescriptors()
{
try {
PropertyDescriptor pd1 = new PropertyDescriptor(
"lightOn",
LightBulb.class
);
//pd1.setPropertyEditorClass(LightBulbLightOnEditor1.class);
PropertyDescriptor[] ret = {pd1};
return ret;
} catch (IntrospectionException e) {
System.err.println(e.toString());
return null;
}
}
LightBulbBeanInfo.java
036
public MethodDescriptor[] getMethodDescriptors()
037
{
038
MethodDescriptor[] ret = null;
039
try {
040
Class bulbclass = LightBulb.class;
041
Method meth1 = bulbclass.getMethod("toggleLight", null);
042
ret = new MethodDescriptor[1];
043
ret[0] = new MethodDescriptor(meth1);
044
} catch (NoSuchMethodException e) {
045
//ret bleibt null
046
}
047
return ret;
048
}
049 }
Listing 44.10: Die Klasse LightBulbBeanInfo
Wird die Klasse bersetzt und in die jar-Datei fr die Beanbox aufgenommen, verndert sich die Prsentation der LighBulb-Bean in der
Beanbox:
Whrend die ersten beiden Vernderungen nach dem Starten der Beanbox offensichtlich sind, knnen wir die letzte berprfen, indem wir den
Aufruf von toggleLight auf das Ereignis einer anderen Bean legen. Dazu plazieren wir im GUI-Designer zunchst eine LightBulb- und
eine LightedPushButton-Bean. Anschlieend markieren wir die LightedPushButton-Bean und rufen den Menpunkt
"Edit.Events.PropertyChange.propertyChange" auf. Die rote Verbindungslinie ziehen wir auf die LightBulb und fixieren sie dort mit einem
Mausklick.
Aus der Auswahlliste der verfgbaren parameterlosen Methoden knnen wir nun toggleLight auswhlen und "OK" drcken. Die Beanbox
generiert und bersetzt nun eine Adapterklasse, die bei jedem PropertyChangeEvent des LightedPushButton die Methode
toggleLight der LightBulb aufruft. Nach jedem Drcken des Buttons verndert die Lampe also ihren Zustand. Die Lampe kann natrlich
nach wie vor auch im Eigenschaftenfenster an- und ausgeschaltet werden.
44.6.2 Property-Editoren
Die Beanbox und andere GUI-Designer stellen fr einfache Eigenschaften vordefinierte Editoren zur Verfgung, mit denen ihr Wert verndert
werden kann. Bereits bei indizierten Eigenschaften mu die Beanbox aber passen. Auch fr Objekttypen kann ein GUI-Designer keinen StandardEditor zur Verfgung stellen, weil er die Konfigurationsschnittstelle des Objekts nicht kennt. Fr diesen Zweck bietet die Beans-Architektur die
Mglichkeit, eigene Editoren zu definieren und bestimmten Eigenschaften von Beans zuzuordnen.
In Zeile 027 von Listing 44.10 ist der Aufruf von setPropertyEditorClass auskommentiert, damit die Beanbox den eingebauten Editor
fr boolesche Werte verwendet. Durch Entfernen des Kommentars erhlt der PropertyDescriptor der Eigenschaft "lightOn" die
Information, da zum Editieren dieser Eigenschaft ein benutzerdefinierter Editor verwendet werden soll.
Benutzerdefinierte Eigenschafteneditoren werden blicherweise aus der Klasse PropertyEditorSupport des Pakets java.beans
abgeleitet. Sie besitzt eine Reihe von Methoden, die in eigenen Editoren berlagert werden:
public void setValue(Object value)
public Object getValue()
java.beans.PropertyEditorSupport
In den nchsten Abschnitten werden wir drei Editoren fr die Eigenschaft "lightOn" vorstellen. Sie machen in unterschiedlicher Weise von diesen
Methoden Gebrauch.
Eine weitere Variante zur Konfiguration von Beans (auf die wir hier nicht nher eingehen wollen) ist die Implementierung eines
Customizers. Dieser erhlt volle Kontrolle ber die Konfiguration der Bean. Er kann insbesondere auch mehrere Eigenschaften
gleichzeitig verndern und auf diese Weise aufwendige Editoren zur Verfgung stellen, die den bekannten Wizards oder Assistenten
moderner Entwicklungssysteme hneln.
Hinweis
LightBulbLightOnEditor1
Der erste Editor ist sehr einfach aufgebaut. Er stellt ein Textfeld zur Verfgung, in dem die Werte "an" und "aus" eingegeben werden knnen, um
den Zustand der Lampe umzuschalten.
Jeder der nachfolgend vorgestellten Editoren kapselt eine boolesche Membervariable currentvalue, die den aktuellen Zustand der Lampe
festhlt. Sie wird vom GUI-Designer durch Aufruf von getValue abgefragt und durch setValue gesetzt. Beide Methoden operieren mit
Objekttypen, d.h. es ist jeweils eine Konvertierung zwischen boolean und Boolean erforderlich. In unserem Beispiel werden zustzlich die
Methoden getAsText und setAsText berlagert, um auf die textuelle Reprsentation ("an" und "aus") des booleschen Werts zuzugreifen.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
/* LightBulbLightOnEditor1.java */
import java.awt.*;
import java.beans.*;
public class LightBulbLightOnEditor1
extends PropertyEditorSupport
{
boolean currentvalue;
public void setValue(Object value)
{
currentvalue = ((Boolean)value).booleanValue();
}
public Object getValue()
{
return new Boolean(currentvalue);
}
public String getAsText()
{
return "" + (currentvalue ? "an" : "aus");
}
public void setAsText(String text)
throws java.lang.IllegalArgumentException
{
if (text.equalsIgnoreCase("an")) {
currentvalue = true;
} else if (text.equalsIgnoreCase("aus")) {
currentvalue = false;
} else {
throw new IllegalArgumentException(text);
}
firePropertyChange();
}
}
LightBulbLightOnEditor1.java
Ist der Wertevorrat fr die zu editierende Eigenschaft endlich, kann es bequemer sein, dem Anwender alle mglichen Varianten in einer Combox
anzubieten. Dazu mu lediglich die Methode getTags berlagert und ein Array von Strings mit den mglichen Werten zurckgegeben werden:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
/* LightBulbLightOnEditor2.java */
LightBulbLightOnEditor2.java
import java.awt.*;
import java.beans.*;
public class LightBulbLightOnEditor2
extends PropertyEditorSupport
{
boolean currentvalue;
public void setValue(Object value)
{
currentvalue = ((Boolean)value).booleanValue();
}
public Object getValue()
{
return new Boolean(currentvalue);
}
public String getAsText()
{
return "" + (currentvalue ? "an" : "aus");
}
public void setAsText(String text)
throws java.lang.IllegalArgumentException
{
System.out.println("setAsText(" + text + ")");
if (text.equalsIgnoreCase("an")) {
currentvalue = true;
} else if (text.equalsIgnoreCase("aus")) {
currentvalue = false;
} else {
throw new IllegalArgumentException(text);
}
firePropertyChange();
}
public String[] getTags()
{
return new String[]{"aus", "an"};
}
}
isPaintable gibt true zurck, um anzuzeigen, da der Editor selbst fr die Darstellung in der Eigenschaftenliste verantwortlich ist
und dafr die Methode paintValue berlagert.
supportsCustomEditor gibt ebenfalls true zurck, um anzuzeigen, da ein eigener Eigenschafteneditor zur Verfgung steht und
durch Aufruf von getCustomEditor beschafft werden kann. Der eigene Editor mu aus der Klasse Component abgeleitet sein; er
wird vom GUI-Designer automatisch in einem Editorfenster plaziert.
Die folgende Klasse LightBulbLightOnEditor3 stellt einen sehr einfachen Editor zur Verfgung. Er besteht lediglich aus zwei
nebeneinanderliegenden Rechtecken in den Farben Blau und Gelb. Die blaue Farbe symbolisiert den ausgeschalteten Zustand, die gelbe den
eingeschalteten. Der aktuelle Zustand der Lampe wird durch einen schwarzen Rahmen um eines der beiden Rechtecke angezeigt. Durch einfaches
Anklicken eines der beiden Farbfelder kann der Zustand umgeschaltet werden.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
/* LightBulbLightOnEditor3.java */
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
public class LightBulbLightOnEditor3
extends PropertyEditorSupport
{
boolean currentvalue;
public void setValue(Object value)
{
currentvalue = ((Boolean)value).booleanValue();
}
public Object getValue()
{
return new Boolean(currentvalue);
}
public boolean isPaintable()
{
return true;
}
public boolean supportsCustomEditor()
{
return true;
}
public Component getCustomEditor()
{
return new LightOnCustomEditor();
}
public void paintValue(Graphics g, Rectangle box)
{
//Linke Box: blau, Lampe ausgeschaltet
g.setColor(Color.blue);
g.fillRect(box.x, box.y, box.width / 2, box.height);
//Rechte Box: blau, Lampe angeschaltet
g.setColor(Color.yellow);
g.fillRect(box.x + box.width / 2, box.y, box.width / 2, box.height);
//Rahmen
g.setColor(Color.black);
for (int i = 0; i < 2; ++i) {
g.drawRect(
box.x + (currentvalue ? box.width / 2 : 0) + i,
box.y + i,
box.width / 2 - 1 - (2 * i),
box.height - 1 - (2 * i)
);
}
}
//---Private Klassen---------------------------------------class LightOnCustomEditor
extends Canvas
{
public LightOnCustomEditor()
{
addMouseListener(
LightBulbLightOnEditor3.java
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
}
085 }
new MouseAdapter() {
public void mouseClicked(MouseEvent event)
{
currentvalue = (event.getX() > getSize().width / 2);
LightBulbLightOnEditor3.this.firePropertyChange();
repaint();
}
}
);
}
public void paint(Graphics g)
{
paintValue(g, new Rectangle(0, 0, getSize().width, getSize().height));
}
public Dimension getPreferredSize()
{
return new Dimension(120, 60);
}
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Ein Anwender mchte auf eine Datei zugreifen, die ein anderer Anwender
erstellt hat.
Mehrere Arbeitspltze sollen auf einen gemeinsamen Drucker oder ein
zentrales Faxgert zugreifen.
Beim Booten sollen sich PC-Arbeitspltze die aktuelle Uhrzeit von einem
Server im Netz holen.
Das Intranet eines Unternehmens gibt den Angestellten Zugriff auf oft
bentigte Informationen.
ber einen Internet-Zugang soll eine elektronische Mail an einen Bekannten
So vielfltig wie diese Anwendungen ist auch die dafr erforderliche Hard- und
Softwaretechnik. Das Thema Computernetze hat in den letzten Jahren stark an
Dynamik und Umfang zugenommen. Es gibt heute eigene Studiengnge, deren
Schwerpunkt auf der Vernetzung von Computersystemen liegt. Fast jedes grere
Unternehmen beschftigt Mitarbeiter, die sich ausschlielich um den Betrieb und die
Erweiterung der unternehmenseigenen Netze und ihrer Anbindung an ffentliche
Netze kmmern.
45.1.2 Protokolle
Um berhaupt Daten zwischen zwei oder mehr Partnern austauschen zu knnen,
mssen sich die Teilnehmer auf ein gemeinsames Protokoll geeinigt haben. Als
Protokoll bezeichnet man die Menge aller Regeln, die notwendig sind, um einen
kontrollierten und eindeutigen Verbindungsaufbau, Datenaustausch und
Verbindungsabbau gewhrleisten zu knnen. Es gibt sehr viele Protokolle mit sehr
unterschiedlichen Ansprchen. Ein weitverbreitetes Architekturmodell, das ISO/OSI7-Schichten-Modell, unterteilt sie in Abhngigkeit von ihrem Abstraktionsgrad in
sieben Schichten (siehe Abbildung 45.1).
Die unterste Ebene reprsentiert die physikalischen Gerte. Sie wird durch die
Netzwerkhardware und -leitungen und die unmittelbar darauf laufenden
Protokolle wie beispielsweise Ethernet, FDDI oder ATM reprsentiert.
Die zweite Ebene reprsentiert die Netzwerkschicht. Sie wird in TCP/IPNetzen durch das IP-Protokoll und dessen Kontrollprotokolle (z.B. ICMP)
implementiert.
Die dritte Ebene stellt die Transportschicht dar. Sie wird durch die Protokolle
TCP bzw. UDP reprsentiert.
Die oberste Ebene steht fr die groe Klasse der Anwendungsprotokolle.
Hierzu zhlen beispielsweise FTP zum Filetransfer, SMTP zum Mail-Versand
und HTTP zur bertragung von Web-Seiten.
TCP ist ein verbindungsorientiertes Protokoll, das auf der Basis von IP
Hinweis
eine sichere und fehlerfreie Punkt-zu-Punkt-Verbindung realisiert.
Daneben gibt es ein weiteres Protokoll oberhalb von IP, das als UDP (User
Datagram Protocol) bezeichnet wird. Im Gegensatz zu TCP ist UDP ein
verbindungsloses Protokoll, bei dem die Anwendung selbst dafr sorgen
mu, da Pakete fehlerfrei und in der richtigen Reihenfolge beim
Empfnger ankommen. Sein Vorteil ist die grere Geschwindigkeit
gegenber TCP. In Java wird UDP durch die Klassen DatagramPacket
und DatagramSocket abgebildet. Wir wollen uns in diesem Abschnitt
allerdings ausschlielich mit den populreren Protokollen auf der Basis
von TCP/IP beschftigen, TCP/UDP wird im folgenden nicht weiter
behandelt.
7 Bit
14 Bit
21 Bit
Der Server stellt einen Dienst zur Verfgung, der von anderen Rechnern
genutzt werden kann. Er luft im Hintergrund und wartet darauf, da ein
anderer Rechner eine Verbindung zu ihm aufbaut. Der Server definiert das
Protokoll, mit dessen Hilfe der Datenaustausch erfolgt.
Ein Client ist der Nutzer von Diensten eines oder mehrerer Server. Er kennt
die verfgbaren Server und ihre Adressen und baut bei Bedarf die Verbindung
zu ihnen auf. Der Client hlt sich an das vom Server vorgegebene Protokoll,
um die Daten auszutauschen.
Hinweis
Auf einem Host laufen meist unterschiedliche Serveranwendungen, die noch dazu
von mehreren Clients gleichzeitig benutzt werden knnen. Um die Server
voneinander unterscheiden zu knnen, gibt es ein weiteres Adressmerkmal, die PortNummer. Sie wird oberhalb von IP auf der Ebene des Transportprotokolls definiert
(also in TCP bzw. UDP) und gibt die Server-Anwendung an, mit der ein Client
kommunizieren will. Port-Nummern sind positive Ganzzahlen im Bereich von 0 bis
65535. Port-Nummern im Bereich von 0 bis 1023 sind fr Anwendungen mit
Superuser-Rechten reserviert. Jeder Servertyp hat seine eigene Port-Nummer, viele
davon sind zu Quasi-Standards geworden. So luft beispielsweise ein SMTP-Server
meist auf Port 25, ein FTP-Server auf Port 21 und ein HTTP-Server auf Port 80.
Tabelle 45.2 gibt eine bersicht der auf den meisten UNIX-Systemen verfgbaren
Server und ihrer Port-Nummern.
Name
Port
Transport Beschreibung
echo
tcp/udp
discard
tcp/udp
daytime
13
tcp/udp
chargen
19
tcp/udp
ftp
21
tcp
telnet
23
tcp
smtp
25
tcp
time
37
tcp/udp
whois
43
tcp
Einfacher Namensservice
tftp
69
udp
gopher
70
tcp/udp
finger
79
tcp
Liefert Benutzerinformationen
www
80
tcp/udp
Der Web-Server
pop3
110
tcp/udp
nntp
119
tcp
snmp
161
udp
Netzwerkmanagement
rmi
1099
tcp
RFC791, RFC1060
ICMP
RFC792
TCP
RFC793
UDP
RFC768
DNS
ARP / RARP
RFC826, RFC903
SMTP
RFC821, RFC822
MIME
RFC2045 - RFC2049
Content Types
RFC1049
POP3
RFC1939
NNTP
RFC977
HTML 3.2
Internal Draft
HTML 2.0
RFC1866
RFC1945, RFC2068
FTP
RFC959, RFC765
TFTP
TELNET
RFC854
SNMP
RFC1157
X11
RFC1013
NTP
RFC1305
FINGER
RFC1288
WHOIS
RFC954
GOPHER
RFC1436
ECHO
RFC862
DISCARD
RFC863
CHARGEN
RFC864
DAYTIME
RFC867
TIME
RFC868
Assigned Numbers
RFC1700
Internet Protocol
Standards
RFC2400
Server. Ist dies nicht der Fall, kann man sich mit einem Datenbankproxy auf dem
Web-Host behelfen, der alle entsprechenden Anfragen an die Datenbank weiterleitet.
<
>
>>
46.1 Einleitung
46.1 Einleitung
46.1.1 Prinzipielle Arbeitsweise
46.1.2 Einzelheiten der Kommunikation
Bei der Kommunikation mit RMI hat der Client den Eindruck, Methoden von
Objekten aufzurufen, die auf dem Server liegen. In Wirklichkeit liegen die Dinge
natrlich etwas komplizierter, denn der Client hat von der RMI-Registry lediglich ein
Stub-Objekt erhalten, und das Remote-Objekt hat den Server nicht wirklich
verlassen. Ein Stub ist eine Klasse, die - wie das implementierende Remote-Objekt das Remote-Interface implementiert und daher fr den Client als Platzhalter fr den
Zugriff auf das Remote-Objekt dient.
Der Stub kommuniziert ber eine TCP-Verbindung mit dem als Skeleton
bezeichneten Gegenstck auf der Server-Seite. Das Skeleton kennt das tatschliche
Applikationsobjekt, leitet die Anfragen des Stubs an dieses weiter und gibt den
Rckgabewert an ihn zurck. Stub und Skeleton werden whrend der Entwicklung
mit Hilfe eines Tools generiert und verbergen die komplizierten Details der
Kommunikation zwischen Server und Client.
<
>
>>
Warnung
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
46.2.1 Vorgehensweise
In diesem Abschnitt wollen wir eine einfache Client-Server-Anwendung entwickeln und dabei die wichtigsten
Eigenschaften von RMI vorstellen. Dazu soll ein Remote-Interface TimeService geschrieben werden, das zwei
Methoden getTime und storeTime definiert. Mit getTime kann ein Client die aktuelle Uhrzeit auf dem Server
ermitteln und sie sich als String zurckgeben lassen. storeTime erledigt prinzipiell dieselbe Aufgabe, speichert
die Uhrzeit aber in einem TimeStore-Objekt, an dem wir die Besonderheiten des Austauschs von Objekttypen
zeigen wollen.
Der Server, der auf einem Computer mit dem Namen "ph01" luft, wird ein einziges Remote-Objekt instanzieren und
bei der ebenfalls auf "ph01" laufenden RMI-Registry unter dem Namen "TimeService" registrieren. Ein einfaches
Client-Programm auf dem Rechner "ph02" wird bei der RMI-Registry eine Remote-Referenz auf das
TimeService-Objekt beschaffen, mit seiner Hilfe die Uhrzeit des Servers abfragen und die Ergebnisse auf seiner
eigenen Console ausgeben.
Da insbesondere das Setup der Systeme einigen Aufwand erfordert, wollen wir die einzelnen Schritte genau
erlutern:
Das Remote-Interface mu aus dem Interface Remote des Pakets java.rmi abgeleitet und als public
deklariert werden.
Jede Methode mu die Exception RemoteException (ebenfalls aus dem Paket java.rmi) deklarieren.
Hiermit werden alle Arten von Netzwerkproblemen oder Verbindungsstrungen zwischen Client und Server
angezeigt.
Nur die im Remote-Interface definierten Methoden stehen spter den Clients zur Verfgung. Werden spter bei der
Implementierung des Servers weitere Methoden hinzugefgt, so bleiben sie fr den Client unsichtbar. Das RemoteInterface fr unseren Uhrzeit-Service sieht so aus:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
/* TimeService.java */
TimeService.java
import java.rmi.*;
import java.util.*;
public interface TimeService
extends Remote
{
public String getTime()
throws RemoteException;
public TimeStore storeTime(TimeStore store)
throws RemoteException;
}
import java.io.Serializable;
TimeStore.java
Hinweis
Die Implementierungsklasse
Nach der Definition des Remote-Interfaces mu dessen Implementierung (also die Klasse fr die Remote-Objekte)
realisiert werden. Dazu erstellen wir eine Klasse TimeServiceImpl, die aus UnicastRemoteObject
abgeleitet ist und das Interface TimeService implementiert. UnicastRemoteObject stammt aus dem Paket
java.rmi.server und ist fr die Details der Kommunikation zwischen Client und Server verantwortlich.
Zustzlich berlagert sie die Methoden clone, equals, hashCode und toString der Klasse Object, um den
Remote-Referenzen die Semantik von Referenzen zu verleihen.
Der hier verwendete Suffix "Impl" ist lediglich eine Namenskonvention, die anzeigt, da das Objekt eine
Implementierung von "TimeService" ist. Wir htten auch jeden anderen Namen whlen knnen. Die
Klasse TimeServiceImpl wird spter nur bei der Instanzierung der zu registrierenden RemoteObjekte bentigt, auf Client-Seite kommt sie berhaupt nicht vor.
Hinweis
/* TimeServiceImpl.java */
TimeServiceImpl.java
import java.rmi.*;
import java.rmi.server.*;
import java.util.*;
public class TimeServiceImpl
extends UnicastRemoteObject
implements TimeService
{
public TimeServiceImpl()
throws RemoteException
{
}
public String getTime()
throws RemoteException
{
GregorianCalendar cal = new GregorianCalendar();
StringBuffer sb = new StringBuffer();
sb.append(cal.get(Calendar.HOUR_OF_DAY));
sb.append(":" + cal.get(Calendar.MINUTE));
sb.append(":" + cal.get(Calendar.SECOND));
return sb.toString();
}
public TimeStore storeTime(TimeStore store)
throws RemoteException
{
store.setTime(getTime());
return store;
}
}
Hinweis
In getTime wird ein GregorianCalendar-Objekt instanziert und mit der aktuellen Uhrzeit belegt. Aus den
Stunden-, Minuten- und Sekundenwerten wird ein StringBuffer erzeugt und nach Konvertierung in einen
String an den Aufrufer zurckgegeben. storeTime ist noch einfacher aufgebaut. Es erzeugt zunchst einen
Uhrzeitstring, speichert diesen in dem als Parameter bergebenen TimeStore-Objekt und gibt es an den Aufrufer
zurck.
Erzeugen von Stub und Skeleton
Nachdem die Implementierungsklasse angelegt wurde, mssen Stub und Skeleton erzeugt werden. Diese Arbeit
braucht glcklicherweise nicht per Hand erledigt zu werden, sondern kann mit Hilfe des Programms rmic aus dem
JDK ausgefhrt werden. rmic erwartet den Namen der Implementierungsklasse als Argument (falls erforderlich, mit
der vollen Paketbezeichnung) und erzeugt daraus die beiden Klassendateien fr Stub und Skeleton. Aus der Klasse
TimeServiceImpl werden die Klassen TimeServiceImpl_Stub und TimeServiceImpl_Skel erzeugt
und als .class-Dateien zur Verfgung gestellt.
rmic ist ein Kommandozeilenprogramm, das hnliche Optionen wie javac kennt. Im einfachsten Fall reicht es aus,
den Namen der Implementierungsklasse anzugeben:
rmic TimeServiceImpl
rmic analysiert den Bytecode der bergebenen Klasse und erzeugt daraus die angegebenen Klassendateien.
Falls die Implememtierungsklasse noch nicht bersetzt war, wird dies automatisch erledigt. Wer sich einmal
den Quellcode von Stub und Skeleton ansehen mchte, kann rmic mit der Option "-keep" anweisen, die
temporren .java-Dateien nach dem Erzeugen der Klassendateien nicht zu lschen.
Tip
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
/* TimeServiceRegistration.java */
TimeServiceRegistration.java
import java.rmi.*;
import java.util.*;
public class TimeServiceRegistration
{
public static void main(String[] args)
{
System.setSecurityManager(new RMISecurityManager());
try {
System.out.println("Registering TimeService");
TimeServiceImpl tsi = new TimeServiceImpl();
Naming.rebind("TimeService", tsi);
System.out.println(" Done.");
} catch (Exception e) {
System.err.println(e.toString());
System.exit(1);
}
}
}
java.rmi.Naming
Mit bind wird ein Remote-Objekt unter einem vorgegebenen Namen registriert. Gab es bereits ein Objekt dieses
Namens, wird eine Ausnahme ausgelst. rebind erledigt dieselbe Aufgabe, ersetzt jedoch ein eventuell
vorhandenes gleichnamiges Objekt. Mit unbind kann ein registriertes Objekt aus der RMI-Registry entfernt
werden. Die Methode lookup dient dazu, zu einem gegebenen Namen eine Remote-Referenz zu erhalten. Sie wird
uns beim Client wiederbegegnen. Mit list kann eine Liste der Namen von allen registrierten Remote-Referenzen
beschafft werden.
Die an Naming bergebenen Namen haben das Format von URLs (siehe Abschnitt 40.1.1). Die Dienstebezeichnung
ist "rmi", der Rest entspricht einer HTTP-URL. Eine gltige rmi-URL wre also beispielsweise:
rmi://ph01:1099/TimeService
Der Server heit hier "ph01" und wird auf Port 1099 angesprochen, der Name des Remote-Objekts ist
"TimeService". Servername und Portnummer sind optional. Fehlt der Server, wird "localhost" angenommen, fehlt die
Portnummer, erfolgt die Kommunikation auf TCP-Port 1099. Aus diesem Grund haben wir bei der Registrierung des
TimeServiceImpl-Objekts mit "TimeService" lediglich den Namen des Remote-Objekts angegeben.
Der Name, unter dem ein Remote-Objekt bei der RMI-Registry registriert werden soll, ist frei whlbar.
Tatschlich hat der in unserem Fall verwendete Begriff "TimeService" nichts mit dem Namen des
Interfaces TimeService zu tun. Er stellt lediglich eine Vereinbarung zwischen Server und Client dar
und htte ebenso gut "ts", "TimeService1" oder "Blablabla" lauten knnen.
Hinweis
weiter. Das liegt daran, da der Konstruktor von UnicastRemoteObject einen neuen Thread zur
Kommunikation mit der RMI-Registry aufgebaut hat, in dem er unter anderem das soeben erzeugte Objekt vorhlt.
Die RMI-Kommunikation zwischen Client und Server knnte auch vllig ohne SecurityManager,
Web-Server und nderungen an den Policy-Dateien durchgefhrt werden. In diesem Fall wre es aber
nicht mglich, zur Laufzeit Bytecode zwischen den beiden Maschinen zu bertragen. Alle bentigten
Klassendateien mten dann im lokalen Klassenpfad des Clients bzw. Servers liegen.
Hinweis
/* TimeServiceClient.java */
TimeServiceClient.java
import java.rmi.*;
public class TimeServiceClient
{
public static void main(String[] args)
{
try {
String host = "ph01";
String port = "1099";
String srv = "TimeService";
String url = "rmi://" + host + ":" + port + "/" + srv;
System.out.println("Looking-up TimeService " + url);
TimeService ts = (TimeService)Naming.lookup(url);
System.out.println(" Server time is " + ts.getTime());
System.out.print(" MyTimeStore contains ");
TimeStore tsd = new MyTimeStore();
tsd = ts.storeTime(tsd);
System.out.println(tsd.getTime());
} catch (Exception e) {
System.err.println(e.toString());
System.exit(1);
}
}
}
Objekts bergeben. Dort wird die aktuelle Uhrzeit des Servers eingetragen und das Objekt als Rckgabewert an den
Aufrufer zurckgegeben. Vor der Rckbertragung wird es nun ebenfalls serialisiert und landet nach der
Deserialisierung durch den Client in Zeile 019 in der Variablen tsd. Der darin enthaltene Uhrzeitstring wird dann
ebenfalls auf der Console ausgegeben.
Die im Client verwendete Klasse MyTimeStore ist sehr einfach aufgebaut:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
/* MyTimeStore.java */
MyTimeStore.java
import java.io.*;
public class MyTimeStore
implements TimeStore, Serializable
{
String time;
public void setTime(String time)
{
this.time = time;
}
public String getTime()
{
return this.time;
}
}
46.2.6 Ausblick
Mit dem vorliegenden Beispiel wurden die grundlegenden Mechanismen von RMI vorgestellt. In der Praxis wird
man meist etwas mehr Aufwand treiben mssen, um eine stabile und performante RMI-Applikation zu erstellen.
Nachfolgend seien einige der Aspekte genannt, die dabei von Bedeutung sind:
Zugriffe auf die RMI-Registry knnen je nach Konfiguration der Systeme recht kostspielig sein. Es ist daher
sinnvoll, nur eines oder wenige Bootstrap-Objekte ber die Registry zu laden und an sie das Beschaffen
weiterer Remote-Referenzen zu delegieren.
Die RMI-Registry bietet lediglich eine sehr einfache Abbildung von Namen auf Remote-Objekte.
Hierarchische Namensrume, dynamische Objektsuche, Lastverteilung oder hnliche Eigenschaften fehlen ihr
dagegen. Mglicherweise ist es besser, einen anderen Namens-Service zu verwenden. Mit JNDI (Java
Naming and Directory Interface) steht dabei seit dem JDK 1.3 bereits eine mgliche Alternative
standardmig zur Verfgung.
Das dynamische Laden von Bytecode wurde hier nur angedeutet. In der Praxis sollen mglicherweise auch
Stubs dynamisch geladen werden. Andererseits kann vielleicht nicht jeder Client einen eigenen Web-Server
zur Verfgung stellen. Die Verteilung des Bytecodes ist also ebenso zu organisieren wie die Verteilung der
Objekte.
Auch auf den konkurrierenden Zugriff mehrerer Clients auf ein und dasselbe Remote-Objekt sind wir nicht
eingegangen. Tatschlich ist es in der Regel so, da das RMI-Laufzeitsystem fr jeden Zugriff eines Clients
einen eigenen Thread erzeugt und somit parallel auf das Remote-Objekt zugegriffen wird. Die RMISpezifikation schreibt konsequenterweise vor, die Remote-Objekte thread-sicher zu implementieren.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
kann die Nachricht (und mit ihr alle anderen Nachrichten, die mit demselben Verfahren verschlsselt wurden) entziffert
werden.
Um den Schaden durch das Entdecken eines Verschlsselungsverfahrens gering zu halten, werden die
Verschlsselungsalgorithmen in aller Regel parametrisiert. Dazu wird beim Verschlsseln eine als Schlssel bezeichnete
Ziffern- oder Zeichenfolge angegeben, mit der die Nachricht verschlssel wird. Der Empfnger bentigt dann zustzlich zur
Kenntnis des Verfahrens noch den vom Sender verwendeten Schlssel, um die Nachricht entziffern zu knnen.
Die Wissenschaft, die sich mit dem Verschlsseln und Entschlsseln von Nachrichten und eng verwandten Themen
beschftigt, wird als Kryptographie bezeichnet. Liegt der Schwerpunkt mehr auf dem Entschlsseln, (insbesondere dem
Entziffern geheimer Botschaften), wird dies als Kryptoanalyse bezeichnet. Die Kryptologie schlielich bezeichnet den
Zweig der Mathematik, der sich mit den formal-mathematischen Aspekten der Kryptographie und Kryptoanalyse
beschftigt.
/* Listing4701.java */
Listing4701.java
Ein Verfahren, bei dem Ver- und Entschlsselung mit demselben Algorithmus und Schlssel durchgefhrt
werden, wird als symmetrische Verschlsselung bezeichnet.
Hinweis
/* Listing4702.java */
Listing4702.java
Warnung
Vorsicht!
Derart einfache Verschlsselungen wie die hier vorgestellten sind zwar weit verbreitet, denn sie sind einfach zu
implementieren. Leider bieten sie aber nicht die geringste Sicherheit gegen ernsthafte Krypto-Attacken. Einige der in letzter
Zeit bekannt gewordenen (und fr die betroffenen Unternehmen meist peinlichen, wenn nicht gar kostspieligen) Flle von
Einbrchen in Softwaressysteme waren darauf zurckzufhren, da zu einfache Sicherheitssysteme verwendet wurden.
Wir wollen diesen einfachen Verfahren nun den Rcken zuwenden, denn seit dem JDK 1.2 gibt es in Java Mglichkeiten,
professionelle Sicherheitskonzepte zu verwenden. Es ist ein groer Vorteil der Sprache, da auf verschiedenen Ebenen
Sicherheitsmechanismen fest eingebaut wurden, und eine mibruchliche Anwendung der Sprache erschwert wird. In den
folgenden Abschnitten werden wir die wichtigsten dieser Konzepte vorstellen.
Allerdings sollte dieser Abschnitt nicht als umfassende Einfhrung in die Grundlagen der Kryptographie
Hinweis
miverstanden werden. Das Thema ist ausgesprochen vielschichtig, mathematisch anspruchsvoll, und es
erfordert in seiner Detailflle weitaus mehr Raum als hier zur Verfgung steht. Wir werden neue Begriffe nur
soweit einfhren, wie sie fr das Verstndnis der entsprechenden Abschnitte erforderlich sind. Fr Details sei auf
weiterfhrende Literatur verwiesen. Ein sehr gelungenes Buch ist Applied Cryptography von Bruce Schneier.
Es bietet einen umfassenden und dennoch verstndlichen Einblick in die gesamte Materie und ist interessant zu
lesen. Gleichermaen unterhaltsam wie lehrreich ist auch die in Geheime Botschaften von Simon Singh
dargestellte Geschichte der Kryptographie.
Ein Message Digest wird daher auch als Einweg-Hashfunktion bezeichnet. Er ist meist 16 oder 20 Byte lang und kann als
eine Art komplizierte mathematische Zusammenfassung der Nachricht angesehen werden. Message Digests haben
Anwendungen im Bereich digitaler Unterschriften und bei der Authentifizierung. Allgemein gesprochen werden sie dazu
verwendet, sicherzustellen, da eine Nachricht nicht verndert wurde. Bevor wir auf diese Anwendungen in den nchsten
Abschnitten zurckkommen, wollen wir uns ihre Implementierung im JDK 1.2 ansehen.
Praktisch alle wichtigen Sicherheitsfunktionen sind im Paket java.security oder einem seiner Unterpakete
untergebracht. Ein Message Digest wird durch die Klasse MessageDigest implementiert. Deren Objekte werden nicht
direkt instanziert, sondern mit der Methode getInstance erstellt:
public static MessageDigest getInstance(String algorithm)
throws NoSuchAlgorithmException
java.security.MessageDigest
Als Argument wird dabei die Bezeichnung des gewnschten Algorithmus angegeben. Im JDK 1.2 sind beispielsweise
folgende Angaben mglich:
"SHA": Der 160-Bit lange "Secure Hash Algorithm" des amerikanischen National Institute of Standards and
Technology
"MD5": Der 128-Bit lange "Message Digest 5" von Ron L. Rivest
Nachdem ein MessageDigest-Objekt erzeugt wurde, bekommt es die Daten, zu denen die Prfziffer berechnet werden
soll, in einzelnen Bytes oder Byte-Arrays durch fortgesetzten Aufruf der Methode update bergeben:
public void update(byte input)
public void update(byte[] input)
public void update(byte[] input, int offset, int len)
java.security.MessageDigest
Wurden alle Daten bergeben, kann durch Aufruf von digest das Ergebnis ermittelt werden:
public byte[] digest()
java.security.MessageDigest
Zurckgegeben wird ein Array von 16 bzw. 20 Byte (im Falle anderer Algorithmen mglicherweise auch andere Lngen),
in dem der Message Digest untergebracht ist. Ein Aufruf fhrt zudem dazu, da der Message Digest zurckgesetzt, also auf
den Anfangszustand initialisiert, wird.
Das folgende Listing zeigt, wie ein Message Digest zu einer beliebigen Datei erstellt wird. Sowohl Algorithmus als auch
Dateiname werden als Kommandozeilenargumente bergeben:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
/* Listing4703.java */
Listing4703.java
import java.io.*;
import java.security.*;
public class Listing4703
{
/**
* Konvertiert ein Byte in einen Hex-String.
*/
public static String toHexString(byte b)
{
int value = (b & 0x7F) + (b < 0 ? 128 : 0);
String ret = (value < 16 ? "0" : "");
ret += Integer.toHexString(value).toUpperCase();
return ret;
}
public static void main(String[] args)
{
if (args.length < 2) {
System.out.println(
"Usage: java Listing4703 md-algorithm filename"
);
System.exit(0);
}
try {
//MessageDigest erstellen
MessageDigest md = MessageDigest.getInstance(args[0]);
FileInputStream in = new FileInputStream(args[1]);
int len;
byte[] data = new byte[1024];
while ((len = in.read(data)) > 0) {
//MessageDigest updaten
md.update(data, 0, len);
}
in.close();
//MessageDigest berechnen und ausgeben
byte[] result = md.digest();
for (int i = 0; i < result.length; ++i) {
System.out.print(toHexString(result[i]) + " ");
}
System.out.println();
} catch (Exception e) {
System.err.println(e.toString());
System.exit(1);
}
}
}
Hinweis
Ein wichtiges Anwendungsgebiet von Message Digests ist die Authentifizierung, d.h. die berprfung, ob die Person oder
Maschine, mit der kommuniziert werden soll, tatschlich "echt" ist (also die ist, die sie vorgibt zu sein). Eine Variante, bei
der ein Anwender sich mit einem Benutzernamen und Pawort autorisiert, kann mit Hilfe eines Message Digests in
folgender Weise realisiert werden:
Bemerkenswert daran ist, da das System nicht die Pawrter selbst speichert, auch nicht in verschlsselter Form. Ein
Angriff auf die Benutzerdatenbank mit dem Versuch, gespeicherte Pawrter zu entschlsseln, ist daher nicht mglich. Eine
bekannte (und leider schon oft erfolgreich praktizierte) Methode des Angriffs besteht allerdings darin, Message Digests zu
allen Eintrgen in groen Wrterbchern berechnen zu lassen, und sie mit den Eintrgen der Benutzerdatenbank zu
vergleichen. Das ist einer der Grnde dafr, weshalb als Pawrter niemals Allerweltsnamen oder einfache, in
Wrterbchern verzeichnete, Begriffe verwendet werden sollten.
"Unwissende" Beweise
Eine weitere Anwendung von Message Digests besteht darin, die Existenz von Geheimnissen oder den Nachweis der
Kenntnis bestimmter Sachverhalte nachzuweisen, ohne deren Inhalt preiszugeben - selbst nicht eigentlich
vertrauenswrdigen Personen. Dies wird in Bruce Schneier's Buch als Zero-Knowledge Proof bezeichnet und funktioniert
so:
A speichert das Geheimnis in elektronischer Form und berechnet den Message Digest dazu.
A deponiert das Geheimnis an einer Stelle, die fr Dritte unerreichbar ist.
A bergibt den Message Digest zum Zeitpunkt X einem Notar, der Inhalt und Eingangsdatum besttigt und
urkundlich festhlt. Alternativ kann A den Message Digest auch in der regionalen Tagespresse verffentlichen und
sich den Zeitungsausschnitt an die Wand hngen.
Das Geheimnis ist nicht verffentlicht, der Nachweis fr seine Existenz zum Zeitpunkt X aber erbracht. Mu A Jahre spter
die Existenz dieser Informationen nachweisen, holt es die Diskette mit dem Geheimnis aus dem Tresor, berechnet den
Message Digest erneut und zeigt dessen bereinstimmung mit dem seinerzeit in der Zeitung verffentlichten.
Fingerprints
Eine weitere Anwendung von Message Digests besteht im Erstellen von Fingerprints (also digitalen Fingerabdrcken) zu
ffentlichen Schlsseln (was das genau ist, wird in Abschnitt 47.1.5 erklrt). Um die Korrektheit eines ffentlichen
Schlssels nachzuweisen, wird daraus ein Message Digest berechnet und als digitaler Fingerabdruck an prominenter Stelle
verffentlicht (beispielsweise in den Signaturen der E-Mails des Schlsselinhabers).
Soll vor der Verwendung eines ffentlichen Schlssel berprft werden, ob dieser auch wirklich dem gewnschten Inhaber
gehrt, ist lediglich der (durch das Schlsselverwaltungsprogramm adhoc berechnete) Fingerprint des ffentlichen
Schlssels mit dem in der E-Mail verffentlichten zu vergleichen. Stimmen beide berein, erhht sich das Vertrauen in die
Authentizitt des ffentlichen Schlssels und er kann verwendet werden. Stimmen sie nicht berein, sollte der Schlssel auf
keinen Fall verwendet werden. Wir werden in Abschnitt 47.1.7 noch einmal auf diese Problematik zurckkommen.
Es ist bekannt, da sich mit deterministischen Maschinen (wie Computerprogramme es beispielsweise sind)
keine echten Zufallszahlen erzeugen lassen. Eigentlich mten wir daher von Pseudo-Zufallszahlen sprechen,
um darauf hinzuweisen, da unsere Zufallszahlengeneratoren stets deterministische Zahlenfolgen erzeugen. Mit
der zustzlichen Forderung kryptographischer Zufallszahlen, praktisch unvorhersagbare Zahlenfolgen zu
generieren, wird diese Unterscheidung an dieser Stelle unbedeutend. Tatschlich besteht der wichtigste
Unterschied zu "echten" Zufallsgeneratoren nur noch darin, da deren Folgen nicht zuverlssig reproduziert
werden knnen (was bei unseren Pseudo-Zufallszahlen sehr wohl der Fall ist). Wir werden im folgenden daher
den Begriff "Zufallszahl" auch dann verwenden, wenn eigentlich "Pseudo-Zufallszahl" gemeint ist.
Hinweis
Die Klasse SecureRandom des Pakets java.security implementiert einen Generator fr kryptographische
Zufallszahlen, der die oben genannten Eigenschaften besitzt. Er wird durch Aufruf der Methode getInstance hnlich
instanziert wie ein Message Digest:
public static SecureRandom getInstance(String algorithm)
throws NoSuchAlgorithmException
java.security.MessageDigest
Als Algorithmus ist beispielsweise "SHA1PRNG" im JDK 1.2 implementiert. Hierbei entstehen die Zufallszahlen aus der
Berechnung eines Message Digests fr eine Pseudonachricht, die aus einer Kombination aus Initialwert und fortlaufendem
Zhler besteht. Die Klasse SecureRandom stellt weiterhin die Methoden setSeed und nextBytes zur Verfgung:
public void setSeed(long seed)
java.security.MessageDigest
Mit setSeed wird der Zufallszahlengenerator initialisiert. Die Methode sollte nach der Konstruktion einmal aufgerufen
werden, um den Initialwert festzulegen (andernfalls macht es Generator selbst). Gleiche Initialwerte fhren auch zu
gleichen Folgen von Zufallszahlen. Mit nextBytes wird eine beliebig lange Folge von Zufallszahlen erzeugt und in dem
als Argument bergebenen Byte-Array zurckgegeben.
Das folgende Listing instanziert einen Zufallszahlengenerator und erzeugt zehn Folgen zu je acht Bytes Zufallszahlen, die
dann auf dem Bildschirm ausgegeben werden:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
/* Listing4704.java */
import java.security.*;
public class Listing4704
{
/**
* Konvertiert ein Byte in einen Hex-String.
*/
public static String toHexString(byte b)
{
int value = (b & 0x7F) + (b < 0 ? 128 : 0);
String ret = (value < 16 ? "0" : "");
ret += Integer.toHexString(value).toUpperCase();
return ret;
}
public static void main(String[] args)
{
try {
//Zufallszahlengenerator erstellen
SecureRandom rand = SecureRandom.getInstance("SHA1PRNG");
byte[] data = new byte[8];
//Startwert initialisieren
rand.setSeed(0x123456789ABCDEF0L);
for (int i = 0; i < 10; ++i) {
Listing4704.java
027
//Zufallszahlen berechnen
028
rand.nextBytes(data);
029
//Ausgeben
030
for (int j = 0; j < 8; ++j) {
031
System.out.print(toHexString(data[j]) + " ");
032
}
033
System.out.println();
034
}
035
} catch (Exception e) {
036
System.err.println(e.toString());
037
System.exit(1);
038
}
039
}
040 }
Listing 47.4: Erzeugen kryptographischer Zufallszahlen
47.1.5 Public-Key-Verschlsselung
Eines der Hauptprobleme bei der Anwendung symmetrischer Verschlsselungen ist das der Schlsselbertragung. Eine
verschlsselte Nachricht kann nmlich nur dann sicher bertragen werden, wenn der Schlssel auf einem sicheren Weg vom
Sender zum Empfnger gelangt. Je nach rumlicher, technischer oder organisatorischer Distanz zwischen beiden Parteien
kann das unter Umstnden sehr schwierig sein.
Mit der Erfindung der Public-Key-Kryptosysteme wurde dieses Problem Mitte der siebziger Jahre entscheidend entschrft.
Bei einem solchen System wird nicht ein einzelner Schlssel verwendet, sondern diese treten immer paarweise auf. Einer
der Schlssel ist ffentlich und dient dazu, Nachrichten zu verschlsseln. Der anderen Schlssel ist privat. Er dient dazu,
mit dem ffentlichen Schlssel verschlsselte Nachrichten zu entschlsseln.
Das Schlsselbertragungsproblem wird nun dadurch gelst, da ein potentieller Empfnger verschlsselter Nachrichten
seinen ffentlichen Schlssel an allgemein zugnglicher Stelle publiziert. Seinen privaten Schlssel hlt er dagegen geheim.
Will ein Sender eine geheime Nachricht an den Empfnger bermitteln, verwendet er dessen allgemein bekannten
ffentlichen Schlssel und bertrgt die verschlsselte Nachricht an den Empfnger. Nur mit Hilfe seines privaten
Schlssels kann dieser nun die Nachricht entziffern.
Das Verfahren funktioniert natrlich nur, wenn der ffentliche Schlssel nicht dazu taugt, die mit ihm verschlsselte
Nachricht zu entschlsseln. Auch darf es nicht mglich sein, mit vertretbarem Aufwand den privaten Schlssel aus dem
ffentlichen herzuleiten. Beide Probleme sind aber gelst, und es gibt sehr leistungsfhige und sichere
Verschlsselungsverfahren, die auf dem Prinzip der Public-Key-Kryptographie beruhen. Bekannte Beispiele fr solche
Systeme sind RSA (benannt nach ihren Erfindern Rivest, Shamir und Adleman) und DSA (Digital Signature Architecture).
Asymmetrische Kryptosysteme haben meist den Nachteil, sehr viel langsamer zu arbeiten als symmetrische. In der Praxis
kombiniert man daher beide Verfahren und kommt so zu hybriden Kryptosystemen . Um eine geheime Nachricht von A
nach B zu bertragen, wird dabei in folgenden Schritten vorgegangen:
Mit Hilfe eines kryptographischen Zufallszahlengenerators erzeugt A einen Einmalschlssel (der auch als Session
Key bezeichnet wird).
A verschlsselt die geheime Nachricht mit Hilfe des Session Keys und eines (schnellen) symmetrischen Verfahrens.
A verschlsselt den Session-Key mit dem ffentlichen Schlssel von B auf der Basis des vereinbarten Public-KeyKryptoverfahrens und sendet ihn zusammen mit der verschlsselten Nachricht an B.
B entschlsselt den Session-Key mit seinem privaten Schlssel.
B entschlsselt die geheime Nachricht mit dem Session Key.
Fast alle Public-Key-Kryptosysteme arbeiten in dieser Weise als Hybridsysteme. Andernfalls wrde das Ver- und
Entschlsseln bei groen Nachrichten viel zu lange dauern. Ein bekanntes Beispiel fr ein solches System ist PGP (Pretty
Good Privacy) von Phil Zimmermann. Es wird vorwiegend beim Versand von E-Mails verwendet und gilt als sehr sicher.
Freie Implementierungen stehen fr viele Plattformen zu Verfgung.
Das Ver- und Entschlsseln von Daten mit Hilfe von asymmetrischen Verfahren war bis zur Version 1.3 nicht im Hinweis
JDK enthalten. Zwar gab es als Erweiterung zum JDK die JCE (JAVA Cryptography Extension), doch diese
durfte nur in den USA und Kanada verwendet werden. Mit dem JDK 1.4 wurden die JCE, sowie die Java Secure
Socket Extension (JSSE) und der Java Authentication and Authorization Service (JAAS) fester Bestandteil des
JDK. Dennoch gibt es nach wie vor einige Einschrnkungen in der Leistungsfhigkeit der einzelnen Pakete, die
auf US-Exportbeschrnkungen zurckzufhren sind. Details knnen in der Dokumentation zum JDK 1.4
nachgelesen werden.
Sie stellt sicher, da eine Nachricht von einem ganz bestimmten und eindeutig identifizierbaren Absender stammt.
Sie stellt sicher, da die Nachricht intakt ist und nicht whrend der bertragung verndert wurde.
Beide Eigenschaften sind fr den elektronischen Datenverkehr so fundamental wie die Verschlsselung selbst. Technisch
basieren sie darauf, da die Funktionsweise eines Public-Key-Kryptosystems sich umkehren lt. Da es also mglich ist,
Nachrichten, die mit einem privaten Schlssel verschlsselt wurden, mit Hilfe des korrespondierenden ffentlichen
Schlssels zu entschlsseln.
Im Prinzip funktioniert eine digitale Unterschrift so:
Will A eine Nachricht signieren, so verschlsselt er sie mit seinem privaten Schlssel. Jeder, der im Besitz des ffentlichen
Schlssel von A ist, kann sie entschlsseln. Da nur A seinen eigenen privaten Schlssel kennt, mu die Nachricht von ihm
stammen. Da es keinem Dritten mglich ist, die entschlsselte Nachricht zu modifizieren und sie erneut mit dem privaten
Schlssel von A zu verschlsseln, ist auch die Integritt der Nachricht sichergestellt. Den Vorgang des berprfens der
Integritt und Authentizitt bezeichnet man als Verifizieren einer digitalen Unterschrift.
In der Praxis sind die Dinge wieder einmal etwas komplizierter, denn die Langsamkeit der asymmetrischen Verfahren
erfordert eine etwas aufwendigere Vorgehensweise. Statt die komplette Nachricht zu verschlsseln, berechnet A zunchst
einen Message Digest der Nachricht. Diesen verschlsselt A mit seinem privaten Schlssel und versendet ihn als Anhang
zusammen mit der Nachricht. Ein Empfnger wird die Nachricht lesen, ihren Message Digest bilden, und diesen dann mit
dem (mit Hilfe des ffentlichen Schlssels von A entschlsselten) Original-Message-Digest vergleichen. Stimmen beide
berein, ist die Signatur gltig. Die Nachricht stammt dann sicher von A und wurde nicht verndert. Stimmen sie nicht
berein, wurde sie ver- oder geflscht.
Das JDK stellt Klassen zum Erzeugen und Verifizieren digitaler Unterschriften zur Verfgung. Wir wollen uns beide
Verfahren in den folgenden Abschnitten ansehen. Zuvor wird allerdings ein Schlsselpaar bentigt, dessen Generierung im
nchsten Abschnitt besprochen wird.
Erzeugen und Verwalten von Schlsseln mit dem JDK
Um digitale Unterschriften erzeugen und verifizieren zu knnen, mssen Schlsselpaare erzeugt und verwaltet werden. Seit
dem JDK 1.2 wird dazu eine Schlsseldatenbank verwendet, auf die mit Hilfe des Hilfsprogramms keytool zugegriffen
werden kann. keytool kann Schlsselpaare erzeugen, in der Datenbank speichern und zur Bearbeitung wieder
herausgeben. Zudem besitzt es die Fhigkeit, Zertifikate (siehe Abschnitt 47.1.7) zu importieren und in der Datenbank zu
verwalten. Die Datenbank hat standardmig den Namen ".keystore" und liegt im Home-Verzeichnis des angemeldeten
Benutzers (bzw. im Verzeichnis \windows eines Windows-95/98-Einzelplatzsystems).
keytool ist ein kommandozeilenbasiertes Hilfsprogramm, das eine groe Anzahl an Funktionen bietet. Wir wollen hier
nur die fr den Umgang mit digitalen Unterschriften bentigten betrachten. Eine vollstndige Beschreibung findet sich in
der Tool-Dokumentation des JDK.
Im JDK 1.1 gab es keytool noch nicht. Statt dessen wurde das Programm javakey zur
Schlsselverwaltung verwendet. Hier soll nur das Security-API des JDK 1.2 und darber betrachtet werden.
Wir wollen daher auf javakey und andere Eigenschaften der Pr-1.2-JDKs nicht eingehen.
JDK1.1-1.4
Um ein neues Schlsselpaar zu erzeugen, ist keytool mit dem Kommando -genkey aufzurufen. Zustzlich mssen
weitere Parameter angegeben werden:
Mit -alias wird der Aliasname des neu zu generierenden Eintrags angegeben. Er dient als eindeutiger Bezeichner
und wird knftig immer dann gebraucht, wenn auf diesen Eintrag zugegriffen werden soll.
Mit -dname wird ein strukturierter Name fr den Schlsselbesitzer angegeben. Er besteht aus mehreren Teilen, die
durch Kommata voneinander getrennt sind. Jeder einzelne Teil besteht aus einer mnemonischen Bezeichnung,
gefolgt von einem Gleichheitszeichen und dem zugehrigen Wert. Einzelne Bestandteile knnen ausgelassen
werden. Der strukturierte Name kann folgende Teile enthalten :
CN: blicher Name (Common Name)
OU: Organisatorische Einheit (Organisational Unit)
O: Unternehmen/Organisation (Organisation)
L: Stadt (Location)
S: Bundesstaat (State)
C: Land (Countrycode)
Die Optionen fr den Schlssel- und Signaturtyp (-keyalg und -sigalg) sowie die Schlssellnge (-keysize) und
die Gltigkeitsdauer (-validity) sollen unspezifiziert bleiben (und daher gem den eingebauten Voreinstellungen
belegt werden). Zustzlich besitzt jede Schlsseldatenbank ein Zugriffspawort, das mit der Option -storepass (oder
alternativ in der Eingabezeile) angegeben wird. Schlielich besitzt jeder private Schlssel ein Schlsselpawort, das mit der
Option -keypass (oder ber die Eingabezeile) angegeben wird.
Wir wollen zunchst ein Schlsselpaar mit dem Aliasnamen "hjp3" erzeugen und mit dem Pawort "hjp3key" vor
unberechtigtem Zugriff schtzen. Die Schlsseldatenbank wird beim Anlegen des ersten Schlssel automatisch erzeugt und
bekommt das Pawort "hjp3ks" zugewiesen. Wir verwenden dazu folgendes Kommando (bitte haben Sie etwas Geduld, das
Programm bentigt eine ganze Weile):
c:\-->keytool -genkey -alias hjp3 -dname
"CN=Guido Krueger,O=Computer Books,C=de"
Enter keystore password: hjp3ks
Enter key password for <hjp3>
(RETURN if same as keystore password):
hjp3key
Nun wird ein DSA-Schlsselpaar der Lnge 1024 mit einer Gltigkeitsdauer von 90 Tagen erzeugt. Zur berprfung kann
das Kommando -list (in Kombination mit -v) angegeben werden:
C:\--->keytool -alias hjp3 -list -v
Enter keystore password: hjp3ks
Alias name: hjp3
Creation date: Sun Dec 26 17:11:36 GMT+01:00 1999
Entry type: keyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=Guido Krueger, O=Computer Books, C=de
Issuer: CN=Guido Krueger, O=Computer Books, C=de
Serial number: 38663e2d
Valid from: Sun Dec 26 17:11:25 GMT+01:00 1999 until: Sat Mar 25 17:11:25 GMT+01:00
2000
Certificate fingerprints:
MD5: D5:73:AB:06:25:16:7F:36:27:DF:CF:9D:C9:DE:AD:35
SHA1: E0:A4:39:65:60:06:48:61:82:5E:8C:47:8A:2B:04:A4:6D:43:56:05
Gleichzeitig wird ein Eigenzertifikat fr den gerade generierten ffentlichen Schlssel erstellt. Es kann dazu verwendet
werden, digitale Unterschriften zu verifizieren. Jedes Zertifikat in der Schlsseldatenbank (und damit jeder eingebettete
ffentliche Schlssel) gilt im JDK automatisch als vertrauenswrdig.
Erstellen einer digitalen Unterschrift
Wie erwhnt, entsteht eine digitale Unterschrift zu einer Nachricht durch das Verschlsseln des Message Digests der
Nachricht mit dem privaten Schlssel des Unterzeichnenden. Nachdem wir nun ein Schlsselpaar erstellt haben, knnen wir
java.security.Signature
Als Argument von getInstance wird der gewnschte Signier-Algorithmus bergeben. Auch hier wird - wie
an vielen Stellen im Security-API des JDK - eine Strategie verfolgt, nach der die verfgbaren Algorithmen
konfigurier- und austauschbar sind. Dazu wurde ein Provider-Konzept entwickelt, mit dessen Hilfe dem API
Klassenpakete zur Verfgung gestellt werden knnen, die Funktionalitten des Security-Pakets teilweise oder
ganz austauschen. Falls der Provider beim Aufruf von getInstance nicht angegeben wird, benutzt die
Methode den Standard-Provider "SUN", der zusammen mit dem JDK ausgeliefert wird. Der zu dem von uns
generierten Schlssel passende Algorithmus ist "SHA/DSA".
Hinweis
Die zum Aufruf der init-Methoden bentigten Schlssel knnen aus der Schlsseldatenbank beschafft werden. Auf sie kann
mit Hilfe der Klasse KeyStore des Pakets java.security zugegriffen werden. Dazu wird zunchst ein KeyStoreObjekt instanziert und durch Aufruf von load mit den Daten aus der Schlsseldatenbank gefllt. Mit getKey kann auf
einen privaten Schlssel zugegriffen werden, mit getCertificate auf einen ffentlichen:
public static KeyStore getInstance(String type)
throws KeyStoreException
java.security.KeyStore
Das von getCertificate zurckgegebene Objekt vom Typ Certificate stammt nicht aus dem Paket
java.security, sondern java.security.cert. Das in java.security vorhandene gleichnamige
Interface wurde bis zum JDK 1.1 verwendet, ab 1.2 aber als deprecated markiert. Wenn nicht mit
qualifizierten Klassennamen gearbeitet wird, mu daher die import-Anweisung fr
java.security.cert.Certificate im Quelltext vor der import-Anweisung von
java.security.Certificate stehen.
Warnung
Die Klasse Certificate besitzt eine Methode getPublicKey, mit der auf den im Zertifikat enthaltenen ffentlichen
Schlssel zugegriffen werden kann:
public PublicKey getPublicKey()
java.security.cert.Certificate
Ist das Signature-Objekt initialisiert, wird es durch Aufruf von update mit Daten versorgt. Nachdem alle Daten
bergeben wurden, kann mit sign die Signatur abgefragt werden. Wurde das Objekt zum Verifizieren initialisiert, kann
das Ergebnis durch Aufruf von verify abgefragt werden:
java.security.Signature
Nach diesen Vorberlegungen knnen wir uns nun das Programm zum Erstellen einer digitalen Unterschrift ansehen. Es
erwartet zwei Kommandozeilenargumente: den Namen der zu signierenden Datei und den Namen der Datei, in den die
digitale Unterschrift ausgegeben werden soll.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
DigitalSignature.java
/* DigitalSignature.java */
import java.io.*;
import java.security.cert.Certificate;
import java.security.*;
public class DigitalSignature
{
static final String KEYSTORE
static final char[] KSPASS
static final String ALIAS
static final char[] KEYPASS
=
=
=
=
"c:\\windows\\.keystore";
{'h','j','p','3','k','s'};
"hjp3";
{'h','j','p','3','k','e','y'};
046
}
047 }
Listing 47.5: Erstellen einer digitalen Unterschrift
Will beispielsweise der Benutzer, dessen privater Schlssel unter dem Aliasnamen "hjp3" in der Schlsseldatenbank
gespeichert wurde, die Datei DigitalSignature.java signieren und das Ergebnis in der Datei ds1.sign abspeichern, so ist das
Programm wie folgt aufzurufen:
C:\--->java DigitalSignature DigitalSignature.java ds1.sign
Die Laufzeit des Programms ist betrchtlich. Das Verschlsseln des Message Digest kann auf durchschnittlichen Hinweis
Rechnern durchaus etwa 30 Sekunden dauern. Glcklicherweise ist die Laufzeit nicht nennenswert von der
Dateilnge abhngig, denn das Berechnen des Message Digests erfolgt sehr schnell.
Verifizieren einer digitalen Unterschrift
Das Programm zum Verifizieren arbeitet hnlich wie das vorige. Statt mit initSign wird das Signature-Objekt nun
mit initVerify initialisiert und das Ergebnis wird nicht durch Aufruf von sign, sondern durch Aufruf von verify
ermittelt.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
/* VerifySignature.java */
import java.io.*;
import java.security.cert.Certificate;
import java.security.*;
public class VerifySignature
{
static final String KEYSTORE = "c:\\windows\\.keystore";
static final char[] KSPASS
= {'h','j','p','3','k','s'};
static final String ALIAS
= "hjp3";
public static void main(String[] args)
{
try {
//Laden der Schlsseldatenbank
KeyStore ks = KeyStore.getInstance("JKS");
FileInputStream ksin = new FileInputStream(KEYSTORE);
ks.load(ksin, KSPASS);
ksin.close();
//Zertifikat "gk22" lesen
Certificate cert = ks.getCertificate(ALIAS);
//Signature-Objekt erstellen
Signature signature = Signature.getInstance("SHA/DSA");
signature.initVerify(cert.getPublicKey());
//Eingabedatei lesen
FileInputStream in = new FileInputStream(args[0]);
int len;
byte[] data = new byte[1024];
while ((len = in.read(data)) > 0) {
//Signatur updaten
signature.update(data, 0, len);
}
in.close();
//Signaturdatei einlesen
in = new FileInputStream(args[1]);
len = in.read(data);
in.close();
byte[] sign = new byte[len];
VerifySignature.java
040
System.arraycopy(data, 0, sign, 0, len);
041
//Signatur ausgeben
042
boolean result = signature.verify(sign);
043
System.out.println("verification result: " + result);
044
} catch (Exception e) {
045
System.err.println(e.toString());
046
System.exit(1);
047
}
048
}
049 }
Listing 47.6: Verifizieren einer digitalen Unterschrift
Soll die Datei DigitalSignature.java mit der im vorigen Beispiel erstellten Signatur verifiziert werden, kann das durch
folgendes Kommando geschehen:
C:\--->java VerifySignature DigitalSignature.java ds1.signature
verification result: true
Wird nur ein einziges Byte in DigitalSignature.java verndert, ist die Verifikation negativ und das Programm gibt false
aus. Durch eine erfolgreich verifizierte digitale Unterschrift knnen wir sicher sein, da die Datei nicht verndert wurde.
Zudem knnen wir sicher sein, da sie mit dem privaten Schlssel von "hjp3" signiert wurde, denn wir haben sie mit dessen
ffentlichen Schlssel verifiziert.
47.1.7 Zertifikate
Ein groes Problem bei der Public-Key-Kryptographie besteht darin, die Authentizitt von ffentlichen Schlsseln
sicherzustellen. Wrde beispielsweise B einen ffentlichen Schlssel publizieren, der glaubhaft vorgibt, A zu gehren,
knnte dies zu verschiedenen Unannehmlichkeiten fhren:
Angenommen, C will eine verschlsselte Nachricht an A schicken und verwendet dazu versehentlich den
ffentlichen Schlssel von B (den es fr den von A hlt). B kann nun mit seinem eigenen privaten Schlssel die
vertrauliche Nachricht lesen. Um weiterhin unentdeckt zu bleiben, knnte B die Nachricht anschlieend sogar mit
dem echten ffentlichen Schlssel von A verschlsseln und an A weitersenden.
Noch folgenreicher ist die Umkehrung. B knnte (als A getarnt) Dokumente mit einer digitalen Unterschrift
versenden, die fr die von A gehalten wird und so im Namen von A beispielsweise geschftliche oder rechtlich
relevante Transaktionen auslsen. A wre anschlieend in der unangenehmen Sitution, nachweisen zu mssen, da
ihm der von B verwendete private Schlssel nie gehrte und bekannt war.
Einen Schutz gegen derartigen Mibrauch bringen Zertifikate. Ein Zertifikat ist eine Art Echtheitsbeweis fr einen
ffentlichen Schlssel, das damit hnliche Aufgaben erfllt wie ein Personalausweis oder Reisepass. Ein Zertifikat besteht
meist aus folgenden Teilen:
Die Glaubwrdigkeit des Zertifikats hngt von der Glaubwrdigkeit des Ausstellers ab. Wird dieser als vertrauenswrdig
angesehen, d.h. kann man seiner digitialen Unterschrift trauen, so wird man auch dem Zertifikat trauen und den darin
enthaltenen ffentlichen Schlssel akzeptieren.
Dieses Vertrauen kann einerseits darauf basieren, da der Aussteller eine anerkannte Zertifizierungsautoritt ist (auch
Certification Authority, kurz CA, genannt), deren ffentlicher Schlssel bekannt und deren Serisitt institutionell
manifestiert ist. Mit anderen Worten: dessen eigenes Zertifikat in der eigenen Schlsselverwaltung bekannt und als
vertrauenswrdig deklariert ist. Andererseits kann das Vertrauen in das Zertifikat daher stammen, da der Aussteller
persnlich bekannt ist, sein ffentlicher Schlssel eindeutig nachgewiesen ist, und seiner Unterschrift Glauben geschenkt
wird.
Der erste Ansatz wird beispielsweise bei X.509-Zertifikaten verfolgt. Institute, die derartige Zertifikate ausstellen, werden
meist staatlich authorisiert und geprft. Beispiele dafr sind VeriSign, Thawte oder das TA Trustcenter. Der zweite Ansatz
liegt beispielsweise den Zertifikaten in PGP zugrunde. Hier ist es sogar mglich, ffentliche Schlssel mit mehreren
digitalen Unterschriften unterschiedlicher Personen zu signieren und so die Glaubwrdigkeit (bzw. ihre Reichweite) zu
erhhen.
Zertifizierungsinstitute stellen meist auch Schlsseldatenbanken zur Verfgung, aus denen Zertifikate abgerufen werden
knnen. Diese dienen auch als Anlaufstelle, um ungltig gewordene oder unbrauchbare Zertifikate zu registrieren. Lokale
Schlsselverwaltungen knnen sich mit diesen Informationen synchronisieren, um ihren eigenen Schlsselbestand up-todate zu halten.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Falle von gesampeltem Sound dient dazu die Klasse AudioSystem aus dem Paket javax.sound.sampled. Sie besitzt eine
Reihe von statischen Methoden, mit denen die grundlegenden Hilfsmittel fr den Umgang mit gesampeltem Sound beschafft werden
knnen:
Die Klasse AudioFormat beschreibt das Format von gesampeltem Sound. Sie enthlt unter anderem Informationen ber das
Kodierungsverfahren, die Anzahl der Kanle, die Sampling Rate oder die Auflsung der einzelnen Samples.
Die Klasse AudioFileFormat beschreibt das Format von Dateien, die gesampelten Sound enthalten. Im wesentlichen wird
dazu ein eingebettetes AudioFormat-Objekt verwendet, das mit einigen Zusatzinformationen versehen ist. Die wichtigsten
Dateiformate, die seit dem JDK 1.3 standardmig untersttzt werden, sind wav, aiff und au.
Eine weitere wichtige Abstraktion ist die der Line. Sie reprsentiert die verschiedenen, miteinander verbundenen Elemente
der Audio-Pipeline, mit denen die Sound-Daten erzeugt, transportiert oder modifiziert werden. Die abstrakten Eigenschaften
einer Line sind, da sie entweder geffnet oder geschlossen ist und da sie eine Reihe von Control-Objekten haben kann,
um etwa die Lautstrke, das Stereo-Panorama oder den Hall zu verndern. Zudem sendet eine Line bei wichtigen
Statusnderungen Events an registrierte Listener.
Wichtige Subinterfaces von Line sind Port und Mixer. Ein Port ist ein Endpunkt der Audio-Pipeline, also etwa ein
Mikrophon oder CD-Laufwerk auf der Eingabeseite oder ein Kopfhrer- oder Verstrkerausgang auf der Ausgabeseite. Ein
Mixer ist ein Audio-Element mit mehreren Ein-/Ausgabeleitungen. Er dient typischerweise dazu, verschiedene
Eingangssignale getrennt aufzubereiten und zu einem Summensignal zusammenzumischen. Ein Mixer kann auch
Vernderungen an den Eingangssignalen vornehmen, etwa einen Halleffekt hinzufgen oder mit Hilfe eines Equalizers den
Klang verndern.
Mit DataLine kommt das dritte Subinterface von Line ins Spiel. Eine DataLine ist das Vaterinterface fr alle Lines, die
explizit mit Datenstrmen umgehen. Eine SourceDataLine versorgt einen Mixer mit Eingabedaten, und eine
TargetDataLine empfngt Daten von ihm. Ein Clip ist eine spezielle Datenquelle, die vor der Weitergabe alle Daten in
einem Stck liest und fortan im Hauptspeicher hlt. Ein Clip kann dadurch wahlfrei auf die Daten zugreifen und
beispielsweise nur einen Teil des Signals wiedergeben oder Ausschnitte beliebig oft wiederholen. Allerdings kommt er nur fr
relativ kleine Datenmengen in Betracht.
javax.sound.sampled.AudioSystem
Mit getAudioFileFormat kann das AudioFileFormat einer Sound-Datei ermittelt werden. Die Eingabedatei kann entweder
als File, InputStream oder URL bergeben werden. Mit getAudioInputStream kann ein AudioInputStream beschafft
werden, mit dem der Inhalt der Sound-Datei gelesen werden kann. Auch hier darf der Parameter wahlweise vom Typ File,
javax.sound.sampled.AudioSystem
Um herauszufinden, welche Mixer verfgbar sind, mu zunchst getMixerInfo aufgerufen werden. Der Rckgabewert ist ein
Array mit Mixer.Info-Objekten, das je vorhandenem Mixer ein Element enthlt. Durch bergabe eines Mixer.Info-Objekts
an getMixer kann der zugehrige Mixer beschafft werden. Man kann allerdings auch ohne explizite Verwendung eines Mixers
gesampelten Sound ausgegeben, wenn mit getLine direkt ein geeignetes Audio-Gert beschafft wird. Dazu mu ein Line.InfoObjekt bergeben werden, das dessen Eigenschaften beschreibt. Wir werden in Listing 48.1 zeigen, wie das Line.Info-Objekt
konstruiert werden mu, um einen Clip zu erzeugen.
Die Steuerelemente eines Audio-Gerts
Wir hatten eingangs erwhnt, da eine Line eine Reihe von Steuerlementen zur Verfgung stellt, mit denen Parameter wie
Lautstrke, Stereo-Panorama oder Hall eingestellt werden knnen. Auf diese kann mit folgenden Methoden zugegriffen werden:
public Control[] getControls()
javax.sound.sampled.Line
getControls liefert ein Array aller verfgbaren Steuerelemente, die durch Instanzen der Klasse Control reprsentiert werden.
Mit isControlSupported kann durch bergabe eines Control.Type-Objekts festgestellt werden, ob ein bestimmter Typ
vorhanden ist. Mit getControl kann dieser schlielich beschafft werden.
Control ist die Basisklasse einer ganzen Gruppe von Kontrollelementen, die sich durch den Datentyp des zu verndernden
Parameters unterscheiden. Es gibt die Unterklassen BooleanControl, EnumControl, FloatControl und
CompoundControl. Ein FloatControl beispielsweise dient zur Vernderung eines Fliekommawerts (wie etwa der
Lautstrke), whrend ein BooleanControl einen An-/Aus-Wert verndern kann (beispielsweise die Stummschaltung eines
Elements). Die wichtigsten Methoden der Klasse FloatControl sind:
public float getMaximum()
public float getMinimum()
javax.sound.sampled.FloatControl
Mit getMinimum und getMaximum knnen der kleinste und grte einstellbare Wert abgefragt werden, mit getValue der
aktuelle. Mit setValue kann der Wert des Controls verndert werden.
Die Type-Klassen der einzelnen Steuerelemente besitzen jeweils eine Reihe von vordefinierten Konstanten, die an getControl
bergeben werden knnen. Fr FloatControl.TYPE sind das beispielsweise MASTER_GAIN zur Einstellung der Lautstrke oder
PAN zur Vernderung des Stereo-Panoramas.
Ein Steuerelement des Typs MASTER_GAIN bestimmt die Gesamtverstrkung, die das Audio-Element dem Ursprungssignal
hinzufgt. Ihr Wert wird in Dezibel (dB) angegeben, wobei positive Werte eine Verstrkung und negative eine Abschwchung des
Eingangssignals anzeigen. Das Dezibel ist eine logarithmische Maeinheit, die den Verstrkungsfaktor durch die Formel 10dB/20
ausdrckt. 20 dB entsprechen also einer 10-fachen Verstrkung, -40 dB einer 100-fachen Abschwchung. 0 dB bedeutet, da die
Strke des Ausgangs- im Verhltnis zum Eingangssignal unverndert bleibt.
Ein Steuerelement des Typs PAN bestimmt die Lage des Audio-Signals im Stereo-Panorama. Es kann Werte von -1.0 (ganz links) bis
+1.0 (ganz rechts) annehmen. Ein Wert von 0 legt das Signal in die Mitte.
Der Clip
Ein Clip ist eine besondere Form einer DataLine, der alle Audiodaten komplett im Hauptspeicher hlt. Wie jede Line mu er vor
der Verwendung durch Aufruf von open geffnet werden und nach Gebrauch mit close geschlossen werden. Zum Abspielen stellt
er folgende Methoden zur Verfgung:
public void start()
public void stop()
javax.sound.sampled.Clip
Ein Aufruf von start startet das Abspielen des Clips, mit stop wird es angehalten. Mit isRunning kann berprft werden, ob die
Wiedergabe noch luft oder bereits beendet wurde.
Die Methode start ist nicht modal, d.h. sie wartet beim Aufruf nicht, bis der Clip vollstndig abgespielt ist. Sie initiiert
lediglich den Abspielvorgang und kehrt dann sofort zum Aufrufer zurck. Falls dieser auf das Ende warten will, mu er
entweder durch wiederholten Aufruf von isRunning den Status des Clips abfragen oder sich bei diesem als Listener
registrieren und auf das STOP-Signal warten.
Hinweis
/* Listing4801.java */
import java.io.*;
import javax.sound.sampled.*;
public class Listing4801
{
private static void playSampleFile(String name, float pan, float gain)
throws Exception
{
//AudioInputStream ffnen
AudioInputStream ais = AudioSystem.getAudioInputStream(
new File(name)
);
AudioFormat format = ais.getFormat();
//ALAW/ULAW samples in PCM konvertieren
if ((format.getEncoding() == AudioFormat.Encoding.ULAW) ||
(format.getEncoding() == AudioFormat.Encoding.ALAW))
{
AudioFormat tmp = new AudioFormat(
AudioFormat.Encoding.PCM_SIGNED,
format.getSampleRate(),
format.getSampleSizeInBits() * 2,
format.getChannels(),
format.getFrameSize() * 2,
format.getFrameRate(),
true
);
ais = AudioSystem.getAudioInputStream(tmp, ais);
format = tmp;
Listing4801.java
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080 }
}
//Clip erzeugen und ffnen
DataLine.Info info = new DataLine.Info(
Clip.class,
format,
((int) ais.getFrameLength() * format.getFrameSize())
);
Clip clip = (Clip)AudioSystem.getLine(info);
clip.open(ais);
//PAN einstellen
FloatControl panControl = (FloatControl)clip.getControl(
FloatControl.Type.PAN
);
panControl.setValue(pan);
//MASTER_GAIN einstellen
FloatControl gainControl = (FloatControl)clip.getControl(
FloatControl.Type.MASTER_GAIN
);
gainControl.setValue(gain);
//Clip abspielen
clip.start();
while (true) {
try {
Thread.sleep(100);
} catch (Exception e) {
//nothing
}
if (!clip.isRunning()) {
break;
}
}
clip.stop();
clip.close();
}
public static void main(String[] args)
{
try {
playSampleFile(
args[0],
Float.parseFloat(args[1]),
Float.parseFloat(args[2])
);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
System.exit(0);
}
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
43.5 Arrays
43.5 Arrays
43.5.1 Erzeugen von Arrays
43.5.2 Zugriff auf Array-Elemente
java.lang.reflect.Array
Beide Methoden erwarten als erstes Argument ein Klassenobjekt, das den Typ der Array-Elemente bestimmt. Dieses kann
mit der Methode getClass oder - wie in Abschnitt 43.3 gezeigt - mit der .TYPE- oder .class-Notation erzeugt werden. Als
zweites Element wird entweder ein einzelner Wert des Typs int angegeben, wenn ein eindimensionales Array erzeugt
werden soll. Er gibt die Anzahl der zu erzeugenden Array-Elemente an. Alternativ kann ein int-Array bergeben werden,
dessen Elementzahl die Anzahl der Dimensionen des zu erzeugenden Arrays bestimmt. Jedes Element definiert seinerseits,
wie viele Elemente die korrespondierende Dimension hat. Der Rckgabewert von newInstance ist das neu erzeugte
Array, typisiert als Object.
Wir wollen uns ein einfaches Beispiel ansehen:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
/* Listing4308.java */
import java.lang.reflect.*;
public class Listing4308
{
public static void createArray1()
{
//Erzeugt ein eindimensionales int-Array
Object ar = Array.newInstance(Integer.TYPE, 3);
int[] iar = (int[])ar;
for (int i = 0; i < iar.length; ++i) {
iar[i] = i;
System.out.println(iar[i]);
};
}
Listing4308.java
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038 }
(0,1)
(1,1)
(2,1)
(3,1)
(4,1)
(5,1)
(6,1)
(0,2)
(1,2)
(2,2)
(3,2)
(4,2)
(5,2)
(6,2)
(0,3)
(1,3)
(2,3)
(3,3)
(4,3)
(5,3)
(6,3)
java.lang.reflect.Array
Mit get kann auf das Element mit dem Index index des Arrays array zugegriffen werden. Es wird stets als Object
zurckgegeben, primitive Typen werden in ein Wrapper-Objekt verpackt. Die Methode set fhrt die umgekehrte Funktion
aus. Sie speichert den Wert value an der durch index bezeichneten Position im Array array. Auch hier mssen
primitive Werte vor der bergabe in ein Wrapper-Objekt verpackt werden. Beide Methoden lsen eine Ausnahme aus, wenn
das bergebene Objekt kein Array ist oder ein ungltiger Index angegeben wird. Schlielich gibt es die Methode
getLength, mit der die Anzahl der Elemente eines Arrays ermittelt werden kann.
Der Zugriff auf mehrdimensionale Arrays erfolgt analog dem statischen Fall. Auch hier gilt, da
mehrdimensionale Arrays als (ggfs. mehrfach) geschachtelte eindimensionale Arrays dargestellt werden (siehe
Abschnitt 4.4.3). Soll also beispielsweise ein Element eines zweidimensionalen Arrays gelesen werden, so sind
dazu zwei Aufrufe von get ntig. Der erste liefert das geschachtelte innere Array, der zweite wird auf eben
dieses Array angewendet und liefert das gewnschte Element. Bei mehr als zwei Dimensionen sind entsprechend
weitere get-Aufrufe ntig.
Hinweis
Neben den einfachen get- und set-Methoden gibt es weitere, die eine automatische Konvertierung zu primitiven
Datentypen durchfhren:
public static boolean getBoolean(Object array, int index)
throws IllegalArgumentException, ArrayIndexOutOfBoundsException
public static byte getByte(Object array, int index)
throws IllegalArgumentException, ArrayIndexOutOfBoundsException
public static char getChar(Object array, int index)
throws IllegalArgumentException, ArrayIndexOutOfBoundsException
public static short getShort(Object array, int index)
throws IllegalArgumentException, ArrayIndexOutOfBoundsException
public static int getInt(Object array, int index)
throws IllegalArgumentException, ArrayIndexOutOfBoundsException
public static long getLong(Object array, int index)
throws IllegalArgumentException, ArrayIndexOutOfBoundsException
public static float getFloat(Object array, int index)
throws IllegalArgumentException, ArrayIndexOutOfBoundsException
public static double getDouble(Object array, int index)
throws IllegalArgumentException, ArrayIndexOutOfBoundsException
public static void setBoolean(Object array, int index, boolean z)
throws IllegalArgumentException, ArrayIndexOutOfBoundsException
public static void setByte(Object array, int index, byte b)
throws IllegalArgumentException, ArrayIndexOutOfBoundsException
public static void setChar(Object array, int index, char c)
throws IllegalArgumentException, ArrayIndexOutOfBoundsException
public static void setShort(Object array, int index, short s)
throws IllegalArgumentException, ArrayIndexOutOfBoundsException
public static void setInt(Object array, int index, int i)
throws IllegalArgumentException, ArrayIndexOutOfBoundsException
public static void setLong(Object array, int index, long l)
throws IllegalArgumentException, ArrayIndexOutOfBoundsException
public static void setFloat(Object array, int index, float f)
throws IllegalArgumentException, ArrayIndexOutOfBoundsException
public static void setDouble(Object array, int index, double d)
java.lang.reflect.Array
Das folgende Beispiel zeigt die Anwendung der get- und set-Methoden auf ein dynamisch erzeugtes Array:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
/* Listing4309.java */
Listing4309.java
import java.lang.reflect.*;
public class Listing4309
{
public static void createArray1()
{
//Erzeugt ein eindimensionales int-Array
Object ar = Array.newInstance(Integer.TYPE, 3);
for (int i = 0; i < Array.getLength(ar); ++i) {
Array.set(ar, i, new Integer(i));
System.out.println(Array.getInt(ar, i));
};
}
public static void createArray2()
{
//Erzeugt ein zweidimensionales String-Array
Object ar = Array.newInstance(String.class, new int[]{7, 4});
for (int i = 0; i < Array.getLength(ar); ++i) {
Object subArray = Array.get(ar, i);
for (int j = 0; j < Array.getLength(subArray); ++j) {
String value = "(" + i + "," + j + ")";
Array.set(subArray, j, value);
System.out.print(Array.get(subArray, j) + " ");
}
System.out.println();
};
}
public static void main(String[] args)
{
createArray1();
System.out.println("--");
createArray2();
}
}
(0,0)
(1,0)
(2,0)
(3,0)
(4,0)
(5,0)
(6,0)
(0,1)
(1,1)
(2,1)
(3,1)
(4,1)
(5,1)
(6,1)
(0,2)
(1,2)
(2,2)
(3,2)
(4,2)
(5,2)
(6,2)
(0,3)
(1,3)
(2,3)
(3,3)
(4,3)
(5,3)
(6,3)
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Bedeutung
Seit dem JDK 1.4 gibt es in Java eine assert-Anweisung. Diese, ursprnglich schon JDK1.1-1.4
fr die erste JDK-Version vorgesehene, dann aber aus Zeitgrnden zurckgestellte
Anweisung wird C- oder C++-Programmierern wohlbekannt sein. Sie dient dazu, an
einer bestimmten Stelle im Programmablauf einen logischen Ausdruck zu plazieren,
von dem der Programmierer annimmt, da er stets wahr ist. Ist das der Fall, fhrt das
Programm mit der nchsten Anweisung fort, andernfalls wird eine Ausnahme des Typs
AssertionError ausgelst. Ein solcher Ausdruck wird auch als Invariante
bezeichnet, denn er bleibt unverndert true, solange das Programm korrekt
funktioniert.
Assertions, wie assert-Anweisungen vereinfachend genannt werden, dienen also dazu, bestimmte
Annahmen ber den Zustand des Programmes zu verifizieren und sicherzustellen, da diese
eingehalten werden. Soll im Programm beispielsweise berprft werden, ob eine Variable x nichtnegativ ist, knnte dazu die folgende assert-Anweisung verwendet werden:
assert x >= 0;
Das Programm berprft die Bedingung und fhrt fort, wenn sie erfllt ist. Andernfalls wird eine
Ausnahme ausgelst. Natrlich htte derselbe Test auch mit Hilfe einer einfachen if-Anweisung
ausgefhrt werden knnen. Die assert-Anweisung hat ihr gegenber jedoch einige Vorteile:
Der im Syntaxdiagramm angegebene ausdruck1 mu immer vom Typ boolean sein, andernfalls
gibt es einen Compilerfehler. Fehlt ausdruck2 (er darf von beliebigem Typ sein), wird im Falle des
Nichterflltseins der Bedingung ein AssertionError mit einer leeren Fehlermeldung erzeugt.
Wurde ausdruck2 dagegen angegeben, wird er im Fehlerfall an den Konstruktor von
AssertionError bergeben und dient als Meldungstext fr das Fehlerobjekt.
An- und Abschalten der Assertions
Aus Kompatibilitt zu lteren JDK-Versionen sind Assertions sowohl im Compiler als auch im
Interpreter standardmig deaktiviert. Um einen Quellcode zu bersetzen, der Assertions enthlt, kann
dem Java-Compiler ab der Version 1.4 die Option -source 1.4 bergeben werden. Wird dies nicht
gemacht, gibt es eine Fehlermeldung, nach der "assert" ab der Version 1.4 ein Schlsselwort ist und
nicht mehr als Bezeichner verwendet werden darf. ltere Compilerversionen melden einen
Syntaxfehler. Wir wollen uns ein einfaches Beispielprogramm ansehen:
001
002
003
004
005
006
007
008
009
010
011
012
013
/* AssertionTest.java */
AssertionTest.java
Kommandozeilenargumente bergeben werden und da das zweite von ihnen nicht 0 ist. Es kann mit
folgendem Kommando bersetzt werden, wenn der Compiler mindestens die Version 1.4 hat:
javac -source 1.4 AssertionTest.java
Um das Programm laufen zu lassen, kennt der Java-Interpreter ab der Version 1.4 die
Kommandozeilenoptionen -enableassertions und -disableassertions, die mit -ea
bzw. -da abgekrzt werden knnen. Ihre Syntax lautet:
java [ -enableassertions | -ea
java [ -disableassertions | -da
] [:PaketName... | :KlassenName ]
] [:PaketName... | :KlassenName ]
Werden die Optionen ohne nachfolgenden Paket- oder Klassennamen angegeben, werden die
Assertions fr alle Klassen mit Ausnahme der Systemklassen java.* an- bzw. ausgeschaltet. Wird,
durch einen Doppelpunkt getrennt, ein Klassenname angegeben, gilt die jeweilige Option nur fr diese
Klasse. Wird ein Paketname angegeben (von einem Klassennamen durch drei angehngte Punkte zu
unterscheiden), erstreckt sich die Option auf alle Klassen innerhalb des angegebenen Pakets. Die
Optionen knnen mehrfach angegeben werden, sie werden der Reihe nach ausgewertet. Wird keine
dieser Optionen angegeben, sind die Assertions deaktiviert.
Soll unser Beispielprogramm mit aktivierten Assertions ausgefhrt werden, kann also folgendes
Kommando verwendet werden:
java -ea AssertionTest
In diesem Fall gibt es sofort eine Fehlermeldung, denn die erste assert-Anweisung ist nicht erfllt.
Rufen wir das Programm mit zwei Zahlen als Argumente auf, wird erwartungsgem deren Quotient
berechnet:
java -ea AssertionTest 100 25
Die Ausgabe lautet:
100/25=4
Wenn das zweite Argument dagegen 0 ist, gibt es eine Fehlermeldung, weil die zweite Assertion nicht
erfllt ist. Auch in diesem Fall steigt das Programm mit einem AssertionError aus, der
zustzlich die Fehlermeldung Teilen durch 0 nicht moeglich enthlt, die wir nach dem Doppelpunkt
in Zeile 010 angegeben haben:
Exception in thread "main" java.lang.AssertionError:
Teilen durch 0 nicht moeglich
...
Wird das Programm mit deaktivierten Assertions aufgerufen, verhlt es sich, als wren die Zeilen 007
und 010 gar nicht vorhanden. In diesem Fall gibt es die blichen Laufzeitfehler, die bei einem Zugriff
auf ein nicht vorhandenes Array-Element oder die Division durch 0 entstehen.
Anwendungen
Genau genommen war das vorige Programm kein wirklich gutes Beispiel fr die Anwendung von
Assertions. Das berprfen der an ein Programm bergebenen Kommandozeilenparameter sollte
nmlich besser einer IllegalArgumentException berlassen werden:
001 public class Listing0608
002 {
003
public static void main(String[] args)
004
{
005
if (args.length < 2) {
006
throw new IllegalArgumentException();
007
}
008
...
009
}
010 }
Listing 6.8: Verwendung einer IllegalArgumentException
Genau wie bei der bergabe eines Arguments an eine ffentliche Methode sollte es nicht einfach
mglich sein, deren berprfung zur Laufzeit abzuschalten. Da ein Programm bei der bergabe von
Werten an ffentliche Schnittstellen keinerlei Annahmen darber machen kann, ob diese Werte
korrekt sind, sollte die berprfung dieser Werte immer aktiv sein. Die Verwendung von Assertions
empfiehlt sich also in diesem Fall nicht. Weitere Beispiele fr derartige ffentliche Schnittstellen sind
etwa die Daten, die ber eine grafische Benutzerschnittstelle in ein Programm gelangen oder die aus
Dateien oder ber Netzwerkverbindungen eingelesen werden. In all diesen Fllen sollten nichtabschaltbare Fehlerprfungen anstelle von Assertions verwendet werden.
Der Einsatz von Assertions ist dagegen immer dann sinnvoll, wenn Daten aus unbekannten Quellen
bereits verifiziert sind. Wenn also das Nichterflltsein einer Assertion einen Programmfehler anzeigt
und nicht fehlerhafte Eingabedaten. Beispiele sind:
Die berprfung von Parametern, die an nicht-ffentliche Methoden bergeben werden. Diese
knnen nur dann falsch sein, wenn das eigene Programm sie fehlerhaft bergibt. Kann nach
dem Test der Software davon ausgegangen werden, da ein bestimmter Codeteil fehlerfrei ist,
ist es legitim, die Assertions in diesem Bereich zu deaktivieren und so die Performance des
Programmes zu verbessern. Innerhalb der Test- und Debugphase dagegen sind die Assertions
eine groe Hilfe beim Aufdecken von verdeckten Fehlern.
Die Verwendung in Postconditions, also in Bedingungen, die am Ende einer Methode erfllt
sein mssen. Postconditions dienen dazu, sicherzustellen, da eine Methode bei korrekten
Eingabewerten (die ihrerseits durch Preconditions abgesichert werden) auch korrekte
Ergebnisse produziert. Beispielsweise knnte am Ende der push-Methode eines Stacks
sichergestellt werden, da der Stack nicht-leer ist.
Die berprfung von Schleifeninvarianten, also von Bedingungen, die am Anfang oder Ende
einer Schleife bei jedem Schleifendurchlauf erfllt sein mssen. Dies ist besonders bei
Schleifen sinnvoll, die so komplex sind, da beim Programmieren eine Art "Restunsicherheit"
bezglich ihrer Korrektheit bestehen bleibt.
Die Markierung von "toten Zweigen" in if- oder case-Anweisungen, die normalerweise
niemals erreicht werden sollten. Anstatt hier einen Kommentar der Art "kann niemals erreicht
werden" zu plazieren, knnte auch eine Assertion mit false als Argument gesetzt werden.
Wird dieser Zweig spter tatschlich einmal aufgrund eines Programmfehlers durchlaufen,
geschieht dies wenigstens nicht unerkannt, sondern das Programm liefert eine Fehlermeldung
und zeigt den Ort des Problems an.
Sowohl Pre- als auch Postconditions wurden mit der Programmiersprache Eiffel, die
Hinweis
Bertrand Meyer in seinem Buch Object-oriented Software Construction 1988 vorgestellt
hat, einer breiteren ffentlichkeit bekannt. Das im JDK 1.4 implementierte AssertionKonzept entspricht allerdings nicht in allen Aspekten Meyer's ausgefeiltem Programming
by Contract. Dort gibt es beispielsweise explizite Schlsselwrter fr Pre- und
Postconditions, und es ist mglich, in Postconditions auf den alten Wert eines Parameters
zuzugreifen (also auf den, den er beim Eintritt in die Methode hatte).
Dennoch stellen Assertions ein wichtiges Hilfsmittel dar, um Programme zuverlssiger und besser
lesbar zu machen. Das folgende Programm zeigt verschiedene Anwendungen von Assertions am
Beispiel einer einfachen Klasse zur Speicherung von Listen von Ganzzahlen:
001 public class SimpleIntList
002 {
003
private int[] data;
004
private int
len;
005
006
public SimpleIntList(int size)
007
{
008
this.data = new int[size];
009
this.len = 0;
010
}
011
012
public void add(int value)
013
{
014
//Precondition als RuntimeException
015
if (full()) {
016
throw new RuntimeException("Liste voll");
017
}
018
//Implementierung
019
data[len++] = value;
020
//Postcondition
021
assert !empty();
022
}
023
024
public void bubblesort()
025
{
026
if (!empty()) {
027
int cnt = 0;
028
while (true) {
029
//Schleifeninvariante
030
assert cnt++ < len: "Zu viele Iterationen";
031
//Implementierung...
032
boolean sorted = true;
033
for (int i = 1; i < len; ++i) {
034
if (sortTwoElements(i - 1, i)) {
SimpleIntList.java
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072 }
sorted = false;
}
}
if (sorted) {
break;
}
}
}
}
public boolean empty()
{
return len <= 0;
}
public boolean full()
{
return len >= data.length;
}
private boolean sortTwoElements(int pos1, int pos2)
{
//Private Preconditions
assert (pos1 >= 0 && pos1 < len);
assert (pos2 >= 0 && pos2 < len);
//Implementierung...
boolean ret = false;
if (data[pos1] > data[pos2]) {
int tmp = data[pos1];
data[pos1] = data[pos2];
data[pos2] = tmp;
ret = true;
}
//Postcondition
assert data[pos1] <= data[pos2] : "Sortierfehler";
return ret;
}
Ein Quelltext, der assert-Anweisungen enthlt, kann nur mit einem Compiler bersetzt
werden, der mindestens die Version 1.4 untersttzt. ltere Compiler halten eine assertAnweisung fr eine Schlsselwort und melden einen Syntaxfehler. Das Gleiche passiert, wenn
der JDK-1.4-Compiler ohne die Option -source 1.4 aufgerufen wird, nur ist dann die
Fehlermeldung klarer.
Bytecode von Klassen, die assert-Anweisungen enthalten, lt sich nicht auf lteren
virtuellen Maschinen ausfhren, denn die vom Compiler generierten Zugriffe auf Felder und
Klassen sind dort nicht vorhanden.
Was folgt daraus? Assertions lassen sich nur dann sinnvoll einsetzen, wenn der Entwickler einen JDK1.4.Compiler besitzt und davon ausgehen kann, da auf allen Zielsystemen ein 1.4-kompatibles
Laufzeitsystem vorhanden ist. Letzteres ist - zumindest zum Zeitpunkt, an dem diese Zeilen
geschrieben werden - leider nicht unbedingt der Fall. Der Reiz von Java liegt ja gerade in seiner
Plattformunabhngigkeit, und bis auf allen wichtigen Zielsystemen ein JDK-1.4-Laufzeitsystem zur
Verfgung stehen wird, knnte es noch einige Zeit dauern. Wegen der zur Installation eines neueren
JDKs erforderlichen Betriebssystempatches auf UNIX-Systemen sind heute - mehr als zwei Jahre
nach der Verffentlichung des JDK 1.3 - immer noch viele UNIX-Systeme lediglich mit einem 1.2er
JDK ausgestattet oder wurden gerade erst auf 1.3 umgestellt. Vermutlich wird es noch einige Zeit
dauern, bis das JDK 1.4 flchendeckend verfgbar sein wird.
Fazit: Assertions sind ein ausgezeichnetes Mittel, um Code lesbarer und robuster zu gestalten.
Whrend der Debugphase helfen sie bei der Fehlersuche. Sofern keine Kompatibilitt zu lteren JDKVersionen erforderlich ist, sollten sie daher unbedingt verwendet werden. Kann dagegen nicht
sichergestellt werden, da die Zielsysteme mindestens das JDK 1.4 untersttzen, knnen Assertions
nicht verwendet werden.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Seit dem JDK 1.1 gibt es das Paket java.math mit den beiden Klassen BigInteger und BigDecimal. Beide
implementieren beliebig groe bzw. beliebig genaue Zahlen und stellen Methoden zur Durchfhrung arithmetischer
Berechnungen zur Verfgung. Whrend BigInteger beliebig groe Ganzzahlen implementiert, dient BigDecimal
zur Darstellung sehr groer Fliekommazahlen. Objekte beider Klassen sind immutable, d.h. sie knnen nach der
Instanzierung nicht mehr verndert werden.
java.math.BigInteger
Wird das Zahlensystem nicht angegeben, erwartet der Konstruktor eine Zahl zur Basis 10. Der bergebene String darf
eine beliebig lange Folge von Ziffern sein. An erster Stelle kann ein Minuszeichen stehen, um eine negative Zahl
anzuzeigen.
Die Arithmetik auf BigInteger-Zahlen erfolgt durch Aufruf ihrer arithmetischen Methoden und bergabe der
erforderlichen Argumente, die meist ebenfalls vom Typ BigInteger sind. Der Methodenaufruf verndert dabei nicht
den Wert des Objekts, sondern gibt das Ergebnis als neue BigInteger-Zahl an den Aufrufer zurck. Wichtige
arithmetische Methoden sind:
public
public
public
public
public
public
BigInteger
BigInteger
BigInteger
BigInteger
BigInteger
BigInteger
add(BigInteger val)
subtract(BigInteger val)
multiply(BigInteger val)
divide(BigInteger val)
remainder(BigInteger val)
pow(int exponent)
java.math.BigInteger
Sie berechnen die Summe, Differenz, Produkt, Quotient, Restwert und Potenz zweier BigInteger-Zahlen. Neben den
Grundrechenarten gibt es weitere Funktionen:
public
public
public
public
BigInteger abs()
BigInteger negate()
int signum()
BigInteger gcd(BigInteger val)
java.math.BigInteger
Sie stellen den absoluten Betrag zur Verfgung, multiplizieren mit -1, liefern das Vorzeichen und ermitteln den grten
gemeinsamen Teiler zweier Zahlen. min und max liefern den kleineren bzw. greren Wert aus aktueller und als
Argument bergebener Zahl. Daneben gibt es logische und bitweise Operationen, die denen der primitiven ganzzahligen
Typen entsprechen.
Zum Vergleich zweier BigInteger-Zahlen kann compareTo und equals verwendet werden, die Konvertierung in
einen String wird mit toString erledigt:
public int compareTo(BigInteger val)
public boolean equals(Object x)
java.math.BigInteger
Die Arbeitsweise dieser Methoden entspricht der in der Klasse Object und dem Interface Comparable definierten
und kann in Abschnitt 8.1.2 und Abschnitt 9.2 nachgelesen werden. Schlielich gibt es noch einige Methoden, um
BigInteger-Objekte in primitive Typen zu verwandeln:
public
public
public
public
int intValue()
long longValue()
float floatValue()
double doubleValue()
java.math.BigInteger
Die Umwandlung folgt den in Abschnitt 4.6 beschriebenen Regeln fr einschrnkende Konvertierungen.
Als abschlieendes Beispiel wollen wir uns ein kleines Programm ansehen, das die Fakultten der Zahlen 30 bis 40
berechnet:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
/* Listing1702.java */
import java.math.*;
public class Listing1702
{
public static void printFaculty(int n)
{
BigInteger bi = new BigInteger("1");
for (int i = 2; i <= n; ++i) {
bi = bi.multiply(new BigInteger("" + i));
}
System.out.println(n + "! is " + bi.toString());
}
public static void main(String[] args)
{
for (int i = 30; i <= 40; ++i) {
printFaculty(i);
}
Listing1702.java
021
}
022 }
Listing 17.2: Anwendung der Klasse BigInteger
Die Ausgabe des Programms ist:
30!
31!
32!
33!
34!
35!
36!
37!
38!
39!
40!
is
is
is
is
is
is
is
is
is
is
is
265252859812191058636308480000000
8222838654177922817725562880000000
263130836933693530167218012160000000
8683317618811886495518194401280000000
295232799039604140847618609643520000000
10333147966386144929666651337523200000000
371993326789901217467999448150835200000000
13763753091226345046315979581580902400000000
523022617466601111760007224100074291200000000
20397882081197443358640281739902897356800000000
815915283247897734345611269596115894272000000000
java.math.BigDecimal
BigDecimal
BigDecimal
BigDecimal
BigDecimal
add(BigDecimal val)
subtract(BigDecimal val)
multiply(BigDecimal val)
divide(BigDecimal val, int roundingMode)
java.math.BigDecimal
Ihr Verhalten entspricht weitgehend dem bei BigInteger beschriebenen. Eine Ausnahme bildet die Methode
divide, denn sie bentigt zustzlich eine Konstante, die die Art der Rundung (falls erforderlich) bestimmt:
ROUND_CEILING
ROUND_DOWN
ROUND_FLOOR
ROUND_HALF_DOWN
ROUND_HALF_EVEN
ROUND_HALF_UP
ROUND_UNNECESSARY
ROUND_UP
Auch die Konvertierungs- und Vergleichsmethoden entsprechen denen der Klasse BigInteger:
public int compareTo(BigDecimal val)
public boolean equals(Object x)
java.math.BigDecimal
int intValue()
long longValue()
float floatValue()
double doubleValue()
Mit Hilfe der Methoden scale und setScale kann die Skalierung abgefragt bzw. ein neues Objekt mit genderter
Skalierung erzeugt werden:
public int scale()
java.math.BigDecimal
Das ndern der Skalierung lt den numerischen Wert des Objekts intakt und verndert lediglich die Anzahl der
darstellbaren Dezimalstellen. Soll diese verkleinert werden, mu die zweite Variante von setScale verwendet und
eine passende Rundungskonstante bergeben werden. Soll die Anzahl der Dezimalstellen vergrert werden, kann die
erste Variante verwendet werden.
Zustzlich gibt es zwei Methoden, mit denen der Dezimalpunkt um eine bestimmte Anzahl Stellen nach links oder
rechts verschoben werden kann, also eine Multiplikation bzw. Division mit einer Zehnerpotenz ausgefhrt wird:
public BigDecimal movePointLeft(int n)
public BigDecimal movePointRight(int n)
java.math.BigDecimal
Zum Abschlu wollen wir uns ein Beispielprogramm ansehen, das die Quadratwurzel der Zahl 2 in (theoretisch)
beliebiger Genauigkeit errechnen kann:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
/* Listing1703.java */
import java.math.*;
public class Listing1703
{
public static final BigDecimal ZERO = new BigDecimal("0");
public static final BigDecimal ONE = new BigDecimal("1");
public static final BigDecimal TWO = new BigDecimal("2");
public static BigDecimal sqrt(BigDecimal x, int digits)
{
BigDecimal zero = ZERO.setScale(digits + 10);
BigDecimal one = ONE.setScale(digits + 10);
Listing1703.java
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043 }
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Seit dem JDK 1.1 bietet Java Untersttzung fr das Internationalisieren von Anwendungen. Auf diese Weise
knnen Programme geschrieben werden, die nicht nur im eigenen, sondern auch in anderen Lndern der Erde
verwendbar sind. Verschiedene Lnder und Regionen unterscheiden sich nicht nur durch die verwendete
Sprache und die zugrunde liegenden Schrift- und Zahlzeichen, sondern auch durch unterschiedliche Kalender
und Zeitzonen, abweichende Zahlen-, Zeit- und Datumsformatierungen, eigene Whrungen, unterschiedliche
Telefonnummern- und Anschriftenkonventionen und so weiter.
Die Begriffe Internationalisierung und Lokalisierung werden hufig synonym gebraucht oder
Hinweis
miteinander verwechselt. Unter Internationalisierung wollen wir das Bndel technischer Manahmen
verstehen, mit denen ein Programm fr die Verwendung in verschiedenen Lndern und
Sprachrumen vorbereitet wird. Unter Lokalisierung verstehen wir dagegen die konkrete Anpassung
eines internationalisierten Programms an eine ganz bestimmte Sprache. Ein Teil der
Internationalisierung eines Programmes ist es also beispielsweise, wenn Textkonstanten nicht mehr
fest im Programm enthalten sind, sondern zur Laufzeit aus Ressourcendateien geladen werden. Die
Lokalisierung hingegen besteht darin, eben diese Textressourcen in die jeweilige Sprache zu
bersetzen.
Im angelschsischen Sprachraum werden die beiden unhandlichen Begriffe meist abgekrzt. Statt
Internationalisierung wird I18N gesagt, statt Lokalisierung L10N. Die Spielregel ist ganz
einfach: Zwischen den ersten und letzten Buchstaben des jeweiligen Wortes steht die Gesamtzahl der
Zeichen.
Internationalisierung ist ein nicht-triviales Thema, das viele Facetten hat. Einige Beispiele dafr, in welcher
Weise es von Java untersttzt wird, sind:
Mit Hilfe der Klasse Locale wird ein Land oder eine Region der Erde bezeichnet.
Lokalisierte Ressourcen sorgen dafr, da die statischen Textkonserven eines Programms dem jeweiligen
Zielland ohne nderung des eigentlichen Programmcodes angepat werden knnen.
Die Klassen Calendar und TimeZone abstrahieren Kalender und Zeitzonen.
Im Paket java.text befindet sich eine Vielzahl von Klassen zur Formatierung von Zahlen, Datumsund Uhrzeitwerten und Whrungen.
Ein- und Ausgabe-Streams untersttzen Konvertierungen von und zu lnderspezifischen Zeichenstzen.
Es gibt Untersttzung zur Eingabe von komplizierten Schriftzeichen, wie sie beispielsweise in der
japanischen, chinesischen oder koreanischen Sprache verwendet werden.
java.util.Locale
Das erste Argument ist ein String mit dem Code fr die Sprache, wie er im Standard ISO-639 definiert ist.
Der Sprachcode wird kleingeschrieben und lautet z.B. "en" fr englisch, "fr" fr franzsich oder "de" fr
deutsch. Das zweite Argument gibt das Land gem dem Standard ISO-3166 an, hier werden stets groe
Buchstaben verwendet. So stehen beispielsweise "US" fr die USA, "GB" fr England, "FR" fr Frankreich und
"DE" fr Deutschland.
Der Lnderteil ist optional. Wird an seiner Stelle ein Leerstring bergeben, reprsentiert die Locale lediglich
eine Sprache ohne spezifisches Land. "en" alleine steht dann beispielsweise fr die englische Sprache, wie sie
nicht nur in England, sondern auch in den USA oder Kanada verstndlich wre. Soll dagegen auf kanadische
Besonderheiten abgestellt werden, ist als Land "CA" zu ergnzen. Fr den franzsischsprachigen Teil von
Kanada wrde dagegen eine Locale aus "fr" und "CA" gebildet werden.
Neben Sprach- und Lnderkennung kann eine Locale einen dritten Parameter haben. Dieser wird
als Variante bezeichnet und kann dazu verwendet werden, Betriebssysteme oder Konfigurationen
voneinander zu unterscheiden. So werden lokalisierte Hilfetexte unter UNIX mglicherweise mit
anderen Zeichenstzen oder Hilfsprogrammen arbeiten als unter Windows oder auf dem Macintosh.
Wir wollen darauf allerdings nicht weiter eingehen.
Hinweis
Die Klasse Locale bietet mit der statischen Methode getDefault die Mglichkeit, eine Locale zu
beschaffen, die dem Land entspricht, in dem das laufende Programm ausgefhrt wird. Mit getCountry kann
der Lnder-, mit getLanguage der Sprachcode und mit getVariant die Variante ermittelt werden:
public static Locale getDefault()
java.util.Locale
Mit getDefault kann ein Programm zur Laufzeit ermitteln, in welchem Land es luft und welche Sprache
dort gesprochen wird. Weiterhin stellt die Klasse noch einige Konstanten mit vordefinierten Locale-Objekten
fr wichtige Lnder und Sprachen zur Verfgung.
Das folgende Beispielprogramm ermittelt die Standard-Locale des aktuellen Rechners und die vordefinierten
Locales fr Deutschland, England und die USA und gibt sie auf der Console aus:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
Listing1704.java
/* Listing1704.java */
import java.util.*;
public class Listing1704
{
public static void main(String[]
{
System.out.println("Default: "
System.out.println("GERMANY: "
System.out.println("UK
: "
System.out.println("US
: "
}
}
args)
+
+
+
+
Locale.getDefault());
Locale.GERMANY);
Locale.UK);
Locale.US);
de_DE
de_DE
en_GB
en_US
java.text.DecimalFormat
java.text.NumberFormat
Soll ein DecimalFormat-Objekt fr die aktuelle Locale erzeugt werden, kann es durch bergabe eines
Formatstrings direkt instanziert werden. Wird es dagegen fr eine andere Locale bentigt, mu es mit der
statischen Methode getNumberInstance der Klasse NumberFormat erzeugt werden. In diesem Fall mu
noch die Methode applyPattern aufgerufen werden, um den Formatstring zuzuweisen:
java.text.NumberFormat
getNumberInstance gibt einen Wert vom Typ NumberFormat zurck (der Basisklasse von
DecimalFormat). Das ist zwar in westlichen Lndern in der Regel eine Instanz von
DecimalFormat. Ganz sicher kann sich ein Programm aber nur sein, wenn es den Typ des
Rckgabewerts vor der Konvertierung nach DecimalFormat mit instanceof berprft.
Warnung
Der Formatstring definiert eine Maske zur Formatierung der Ausgabe. Er besteht aus zwei Teilen, die durch ein
Semikolon voneinander getrennt sind. Der erste gibt die Formatierungsregeln fr positive, der zweite fr
negative Zahlen an. Der zweite Teil kann auch ausgelassen werden. Dann werden negative Zahlen wie positive
Zahlen mit vorangestelltem Minuszeichen formatiert. Jedes Zeichen im Formatstring regelt die Darstellung der
korrespondierenden Ziffer. Der Formatstring kann folgende Zeichen enthalten:
Symbol
Bedeutung
Dezimaltrennzeichen
Tausendertrennzeichen
Aktiviert Exponentialdarstellung
java.text.DecimalFormat
Die Methode gibt es sowohl mit einem long-Parameter zur Ausgabe von Ganzzahlen als auch mit einem
double zur Ausgabe von Fliekommazahlen. Da der Formatstring bereits bei der Konstruktion des Objekts
bergeben wurde, kann format mehrfach aufgerufen werden, um nacheinander weitere Zahlen zu formatieren.
Das folgende Programm zeigt die beispielhafte Anwendung von DecimalFormat. Es gibt die Zahl 1768.3518
fr die aktuelle Locale in unterschiedlichen Lngen und Formatierungen auf der Console aus:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
/* Listing1705.java */
Listing1705.java
import java.text.*;
public class Listing1705
{
public static void print(double value, String format)
{
DecimalFormat df = new DecimalFormat(format);
System.out.println(df.format(value));
}
public static void main(String[] args)
{
double value = 1768.3518;
print(value, "#0.0");
print(value, "#0.000");
print(value, "000000.000");
print(value, "#.000000");
print(value, "#,###,##0.000");
print(value, "0.000E00");
}
}
Wenn das Programm mit englischer Locale aufgerufen worden wre, wren in der Ausgabe Punkte und
Kommata vertauscht (denn im englischen Sprachraum werden Kommata als Tausenderseparatoren und Punkte
als Dezimaltrennzeichen verwendet). Die Ausgabe wre dann wie folgt gewesen:
1768.4
1768.352
001768.352
1768.351800
1,768.352
1.768E03
12345678.909
Neben der Ausgabesteuerung mit Hilfe des Formatstrings stellt DecimalFormat noch weitere
Hinweis
Methoden zur Konfiguration der Ausgabe zur Verfgung. Beispiele sind etwa setGroupingSize
zur Einstellung der Gre der Tausendergruppen oder setDecimalSeparatorAlwaysShown
zur Festlegung, ob auch Ganzzahlen mit Dezimalzeichen ausgegeben werden sollen. Zudem bietet
DecimalFormat eine Methode parse, mit der lokalisierte Zahleneingaben eingelesen werden
knnen. Weitere Details zu diesen Methoden knnen in der API-Dokumentation nachgelesen werden.
java.text.DateFormat
Mit getDateInstance wird eine Instanz zur Ausgabe eines Datums erzeugt, getTimeInstance dient zur
Ausgabe der Uhrzeit und getDateTimeInstance zur kombinierten Ausgabe von Datum und Uhrzeit. Alle
Methoden erwarten ein Argument style, das mit einer der Konstanten SHORT, MEDIUM, LONG, FULL oder
DEFAULT belegt werden mu. Es gibt an, wie lang die jeweilige Ausgabe spter sein soll und welche Daten sie
enthalten soll. So werden beispielsweise bei der kurzen Uhrzeit lediglich Stunden und Minuten ausgegeben,
whrend in den brigen Stufen auch die Sekunden und andere Informationen enthalten sind. Als zweites
Argument wird die gewnschte Locale angegeben; fr die Standard-Locale kann es auch ausgelassen
werden.
Das folgende Programm formatiert das aktuelle Datum und die aktuelle Uhrzeit mit allen mglichen styleParametern und gibt die Ergebnisse auf der Console aus:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
/* Listing1706.java */
Listing1706.java
import java.util.*;
import java.text.*;
public class Listing1706
{
public static void print(Calendar cal, int style)
{
DateFormat df;
df = DateFormat.getDateInstance(style);
System.out.print(df.format(cal.getTime()) + " / ");
df = DateFormat.getTimeInstance(style);
System.out.println(df.format(cal.getTime()));
}
public static void main(String[] args)
{
GregorianCalendar cal = new GregorianCalendar();
print(cal, DateFormat.SHORT);
print(cal, DateFormat.MEDIUM);
print(cal, DateFormat.LONG);
print(cal, DateFormat.FULL);
print(cal, DateFormat.DEFAULT);
}
}
Hilfsmittel zur Verwendung derartiger Ressourcen zur Verfgung. Die Grundidee ist dabei einfach: Ein
ResourceBundle ist ein Java-Objekt, das eine Sammlung von Ressourcen zusammenfasst und auf Anfrage
einzeln zur Verfgung stellt. Jede Ressource hat einen eindeutigen und fr alle untersttzten Sprachvarianten
identischen Namen, mit dem auf sie zugegriffen werden kann. Ein konkretes ResourceBundle ist stets
sprachabhngig und enthlt nur die Ressourcen fr eine bestimmte Locale.
Damit ist ein ResourceBundle zunchst nicht viel mehr als eine Schlssel-Wert-Collection wie
beispielsweise Hashtable oder HashMap. Der Clou liegt in der Tatsache, da der Name des
ResourceBundle die Locale angibt, deren Daten er enthlt. Whrend der vordere Teil fest und fr alle
lokalisierten Varianten gleich ist, ist der hintere Teil Locale-spezifisch und gibt Sprache, Land und
gegebenenfalls Variante an. Lautet der feste Teil beispielsweise "ImageResources", wren "ImageResources_fr"
und "ImageResources_en_US" die Namen fr die franzsiche und US-englische Variante.
Die Methode getBundle
ResourceBundle stellt eine statische Methode getBundle zur Verfgung, mit der zu einem Basisnamen
und einer vorgegebenen Locale ein ResourceBundle beschafft werden kann. Diese gibt es in
unterschiedlichen Ausprgungen:
public static final ResourceBundle getBundle(String baseName)java.util.ResourceBundle
throws MissingResourceException
public static final ResourceBundle getBundle(
String baseName,
Locale locale
)
Die erste Variante besorgt ein ResourceBundle fr die aktuelle Default-Locale, die zweite fr die als
Argument angegebene. In beiden Fllen wird wie folgt vorgegangen:
Zunchst wird versucht, eine genau zur gewnschten Locale passende Ressourceklasse zu finden.
Ist eine solche nicht vorhanden, wird der Ressourcenname schrittweise verallgemeinert. Soweit
vorhanden, wird zunchst die Variante, dann der Lnder- und schlielich der Sprachteil entfernt bis nur
noch der Basisname brigbleibt.
Falls immer noch keine passende Ressourcenklasse gefunden wurde, wird derselbe Ablauf fr die DefaultLocale wiederholt.
Ist beispielsweise Deutschland die Default-Locale und soll die Resource "MyTextResource" fr Frankreich
beschafft werden, sucht getBundle nacheinander nach folgenden Klassen:
1.
2.
3.
4.
5.
MyTextResource_fr_FR
MyTextResource_fr
MyTextResource_de_DE
MyTextResource_de
MyTextResource
Die Suche bricht ab, wenn die erste passende Klasse gefunden wurde. Ist berhaupt keine passende Ressource
vorhanden, lst getBundle eine MissingResourceException aus. Dies ist auch der Fall, wenn die
gefundene Klasse nicht aus ResourceBundle abgeleitet ist.
Eigene Ressourcenklassen erstellen
Eigene Resourcenklassen mssen aus ResourceBundle abgeleitet werden und die beiden (in der Basisklasse
abstrakten) Methoden getKeys und handleGetObject berlagern:
public Enumeration getKeys()
java.util.ResourceBundle
handleGetObject liefert eine Ressource zu einem vorgegebenen Schlssel, getKeys eine Aufzhlung
aller vorhandenen Schlssel. Gibt es zu einem vorgegebenen Schlssel keine Ressource, mu
handleGetObject null zurckgeben.
Das folgende Listing zeigt eine Klasse SimpleTextResource, die zur Internationalisierung von einfachen
Texten verwendet werden kann. Die lokalisierten Varianten knnen dabei sehr einfach realisiert werden, indem
sie aus SimpleTextResource abgeleitet werden. Sie mssen dann lediglich im Konstruktor die
Hashtable mit den gewnschten Schlssel-/Textpaaren fllen.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
/* SimpleTextResource.java */
SimpleTextResource.java
import java.util.*;
public class SimpleTextResource
extends ResourceBundle
{
protected Hashtable data = new Hashtable();
public Enumeration getKeys()
{
return data.keys();
}
public Object handleGetObject(String key)
{
return data.get(key);
}
public ResourceBundle getParent()
{
return parent;
}
}
001
002
003
004
005
006
007
008
009
010
011
/* MyTextResource.java */
MyTextResource.java
/* MyTextResource_de.java */
MyTextResource_de.java
/* MyTextResource_de_CH.java */
MyTextResource_de_CH.java
java.util.ResourceBundle
getObject liefert die Ressource als Object, getString als String und getStringArray als
String-Array. Die letzten beiden Methoden dienen vornehmlich der Bequemlichkeit: sie rufen selber
getObject auf und nehmen dem Aufrufer die anschlieend erforderliche Typkonvertierung ab.
Das folgende Listing zeigt ein einfaches Testprogramm, das die Textressourcen fr verschiedene
Lokalisierungen ermittelt und auf der Console ausgibt. Das ResourceBundle wird beschafft, indem
getBundle mit "MyTextResource" und der jeweils gewnschten Locale als Argument aufgerufen wird.
Anschlieend wird auf die bersetzten Texte mit Aufrufen von getString zugegriffen:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
/* Listing1711.java */
import java.util.*;
public class Listing1711
{
public static void sayHello(Locale locale)
{
System.out.print(locale + ": ");
ResourceBundle textbundle = ResourceBundle.getBundle(
"MyTextResource",
locale
);
if (textbundle != null) {
System.out.print(textbundle.getString("Hi") + ", ");
System.out.println(textbundle.getString("To"));
}
}
public static void main(String[] args)
{
sayHello(Locale.getDefault());
sayHello(new Locale("de", "CH"));
sayHello(Locale.US);
sayHello(Locale.FRANCE);
}
}
Hallo, Welt
Grezi, Schweiz
Hallo, Welt
Hallo, Welt
Listing1711.java
Die Default-Locale war beim Testen "de_DE". Zwar wurde keine passende Klasse
MyTextResource_de_DE definiert, aber durch den oben beschriebenen Fallback-Mechanismus liefert
getBundle eine Instanz von MyTextResource_de. Bei der nchsten Lokalisierung wird die erforderliche
Klasse MyTextResource_de_CH direkt gefunden, und die Ausgabe erfolgt entsprechend. Zu den letzten
beiden Lokalisierungswnschen werden keine passenden Ressourcen gefunden. getBundle verwendet in
beiden Fllen die Datei MyTextResource_de zur Default-Locale, und die Ausgaben erfolgen in deutscher
Sprache.
Damit die Suche nach Ressourcen und die (im nchsten Abschnitt zu besprechende)
Warnung
Vaterverkettung richtig funktionieren, mssen zu einer Ressource auch stets alle allgemeineren
Ressourcen vorhanden sein. Gibt es etwa - wie in unserem Beispiel - eine Ressource mit der Endung
"_de_CH", so mssen auch die Ressourcen mit der Endung "_de" und die Basisressource ohne
lokale Endung vorhanden sein.
Um die bersichtlichkeit zu verbessern, sollten Ressourcendateien in eigenen Verzeichnissen gehalten
werden. Da sie mit Hilfe des Classloaders geladen werden, mssen sie in einem Verzeichnis abgelegt
werden, in dem auch eine Klasse gefunden werden wrde. Man knnte beispielsweise im Hauptpaket der
Anwendung ein Unterverzeichnis resources anlegen und alle Ressourcendateien dort plazieren. Der im
Programm zu verwendende Ressourcenname wird dann einfach vorne um "resources." ergnzt. Derartige
Ressourcen, die ber den Klassenpfad erreichbar sind, werden auch dann gefunden, wenn das Programm
in Form einer .jar-Datei ausgeliefert wird.
Tip
/* MyTextResource_en.java */
MyTextResource_en.java
de_DE:
de_CH:
en_US:
fr_FR:
Hallo, Welt
Grezi, Schweiz
Hello, World of English
Hallo, Welt
Die bersetzung von "Hi" kommt nun aus der Vater-Ressource MyTextResource.
Die Klasse PropertyResourceBundle
Die vorangegangenen Beispiele haben an Textkonserven exemplarisch gezeigt, wie man Ressourcen lokalisieren
kann. Sollen einfache Texte bersetzt werden, gibt es aber noch einen einfacheren Weg. Das oben beschriebene
Suchschema von getBundle war nmlich insofern unvollstndig, als nach jedem einzelnen Schritt zustzlich
eine Datei desselben Names wie die aktuelle Klasse, aber mit der Erweiterung .properties, gesucht wird. Wird
also beispielsweise in einem beliebigen Suchschritt die Klasse MyResource_de_DE nicht gefunden, so sucht
getBundle vor dem bergang zum nchsten Schritt nach einer Datei mit dem Namen
MyResource_de_DE.properties.
Ist eine solche vorhanden, wird eine Instanz der Klasse PropertyResourceBundle erzeugt und an den
Aufrufer zurckgegeben. PropertyResourceBundle liest die Eingabedatei (sie mu dasselbe Format wie
die in Abschnitt 14.4.4 beschriebenen Property-Dateien haben) und speichert alle darin gefundenen SchlsselWert-Paare ab. Anschlieend knnen diese als gewhnliche Text-Ressourcen vom Programm verwendet
werden.
Um beispielsweise die vorigen Beispiele um eine franzsische bersetzung zu erweitern, knnten wir einfach
eine Datei MyTextResource_fr.properties anlegen und ihr folgenden Inhalt geben:
Hi=Salut
To=monde francais
Listing 17.11 wrde nun folgende Ausgabe erzeugen:
de_DE:
de_CH:
en_US:
fr_FR:
Hallo, Welt
Grezi, Schweiz
Hello, World of English
Salut, monde francais
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Java kennt acht elementare Datentypen, die gem Sprachspezifikation als primitive
Datentypen bezeichnet werden. Daneben gibt es die Mglichkeit, Arrays zu
definieren (die eingeschrnkte Objekttypen sind), und als objektorientierte Sprache
erlaubt Java natrlich die Definition von Objekttypen.
Im Gegensatz zu C und C++ gibt es die folgenden Elemente in Java
jedoch nicht:
Hinweis
explizite Zeiger
Typdefinitionen (typedef)
Aufzhlungen (enum)
Recordtypen (struct und union)
Bitfelder
Was auf den ersten Blick wie eine Designschwche aussieht, entpuppt sich bei
nherem Hinsehen als Strke von Java. Der konsequente Verzicht auf zustzliche
Datentypen macht die Sprache leicht erlernbar und verstndlich. Die Vergangenheit
hat mehrfach gezeigt, da Programmiersprachen mit einem berladenen Typkonzept
(zum Beispiel PL/I oder ADA) auf Dauer keine Akzeptanz finden.
Tatschlich ist es ohne weiteres mglich, die unverzichtbaren Datentypen mit den in
Java eingebauten Hilfsmitteln nachzubilden. So lassen sich beispielsweise Zeiger zur
Konstruktion dynamischer Datenstrukturen mit Hilfe von Referenzvariablen
simulieren, und Recordtypen sind nichts anderes als Klassen ohne Methoden. Der
Verzicht auf Low-Level-Datenstrukturen, wie beispielsweise Zeigern zur
Manipulation von Speicherstellen oder Bitfeldern zur Reprsentation von
Hardwareelementen, ist dagegen gewollt.
Alle primitiven Datentypen in Java haben eine feste Lnge, die von den Designern
der Sprache ein fr allemal verbindlich festgelegt wurde. Ein sizeof-Operator, wie
er in C vorhanden ist, wird in Java daher nicht bentigt und ist auch nicht vorhanden.
Ein weiterer Unterschied zu C und den meisten anderen Programmiersprachen
besteht darin, da Variablen in Java immer einen definierten Wert haben. Bei
Membervariablen (also Variablen innerhalb von Klassen, siehe Kapitel 7) bekommt
eine Variable einen Standardwert zugewiesen, wenn dieser nicht durch eine explizite
Initialisierung gendert wird. Bei lokalen Variablen sorgt der Compiler durch eine
Datenfluanalyse dafr, da diese vor ihrer Verwendung explizit initialisiert werden.
Eine Erluterung dieses Konzepts, das unter dem Namen Definite Assignment in der
Sprachdefinition beschrieben wird, ist Bestandteil von Kapitel 5. Tabelle 4.1 listet die
in Java verfgbaren Basistypen und ihre Standardwerte auf:
Typname
Lnge
Wertebereich
Standardwert
boolean
true, false
false
char
byte
-27...27-1
short
-215...215-1
int
-231...231-1
long
-263...263-1
float
double
+/0.0
1.79769313486231570
* 10308
Hinweis
Literale
char-Literale werden grundstzlich in einfache Hochkommata gesetzt. Daneben
gibt es String-Literale, die in doppelten Hochkommata stehen. hnlich wie C stellt
Java eine ganze Reihe von Standard-Escape-Sequenzen zur Verfgung, die zur
Darstellung von Sonderzeichen verwendet werden knnen:
Zeichen
Bedeutung
\b
Rckschritt (Backspace)
\t
Horizontaler Tabulator
\n
Zeilenschaltung (Newline)
\f
Seitenumbruch (Formfeed)
\r
\"
Doppeltes Anfhrungszeichen
\'
Einfaches Anfhrungszeichen
\\
Backslash
\nnn
und nicht nur Modifier. Es ist daher nicht erlaubt, long int oder short int
anstelle von long bzw. short zu schreiben. Auch den Modifier unsigned gibt es
in Java nicht.
Literale
Ganzzahlige Literale knnen in Dezimal-, Oktal- oder Hexadezimalform geschrieben
werden. Ein oktaler Wert beginnt mit dem Prfix 0, ein hexadezimaler Wert mit 0x.
Dezimale Literale drfen nur aus den Ziffern 0 bis 9, oktale aus den Ziffern 0 bis 7
und hexadezimale aus den Ziffern 0 bis 9 und den Buchstaben a bis f und A bis F
bestehen.
Durch Voranstellen eines - knnen negative Zahlen dargestellt werden, positive
knnen wahlweise durch ein + eingeleitet werden. Ganzzahlige Literale sind
grundstzlich vom Typ int, wenn nicht der Suffix L oder l hinten angehngt wird.
In diesem Fall sind sie vom Typ long.
3.14
2f
1e1
.5f
6.
Neben diesen numerischen Literalen gibt es noch einige symbolische in den Klassen
Float und Double des Pakets java.lang. Tabelle 4.3 gibt eine bersicht dieser
vordefinierten Konstanten. NaN entsteht beispielsweise bei der Division durch 0,
POSITIVE_INFINITY bzw. NEGATIVE_INFINITY sind Zahlen, die grer
bzw. kleiner als der darstellbare Bereich sind.
Name
Verfgbar fr
Bedeutung
MAX_VALUE
Float, Double
Grter darstellbarer
positiver Wert
MIN_VALUE
Float, Double
Kleinster darstellbarer
positiver Wert
NaN
Float, Double
Not-A-Number
Negativ unendlich
Positiv unendlich
<
>
>>
Bezeichnung
Bedeutung
Positives Vorzeichen
Negatives Vorzeichen
Summe
Differenz
Produkt
Quotient
Restwert
++
Prinkrement
++
Postinkrement
--
Prdekrement
--
Postdekrement
<
>
>>
Bezeichnung
Bedeutung
==
Gleich
!=
Ungleich
<
Kleiner
<=
Kleiner gleich
>
Grer
>=
Grer gleich
<
>
>>
Hinweis
Bezeichnung
Bedeutung
Logisches NICHT
&&
||
&
Exklusiv-ODER
<
>
>>
Mit Hilfe der bitweisen Operatoren kann auf die Binrdarstellung von numerischen
Operanden zugegriffen werden. Ein numerischer Datentyp wird dabei als Folge von
Bits angesehen, die mit Hilfe der bitweisen Operatoren einzeln abgefragt und
manipuliert werden knnen.
Java hat dieselben bitweisen Operatoren wie C und C++ und stellt daher
Schiebeoperationen, logische Verknpfungen und das Einerkomplement zur
Verfgung. Da alle numerischen Typen in Java vorzeichenbehaftet sind, gibt es einen
zustzlichen Rechtsschiebeoperator >>>, der das hchstwertige Bit nach der
Verschiebung auf 0 setzt - und zwar auch dann, wenn es vorher auf 1 stand. Tabelle
5.4 gibt eine bersicht ber die bitweisen Operatoren in Java.
Operator
Bezeichnung
Bedeutung
Einerkomplement
Bitweises ODER
&
Bitweises UND
>>
Rechtsschieben mit
Vorzeichen
>>>
Rechtsschieben ohne
Vorzeichen
<<
Linksschieben
<
>
>>
5.6 Zuweisungsoperatoren
5.6 Zuweisungsoperatoren
Bezeichnung
Bedeutung
Einfache Zuweisung
+=
Additionszuweisung
-=
Subtraktionszuweisung
*=
/=
Divisionszuweisung
%=
Modulozuweisung
&=
UND-Zuweisung
|=
ODER-Zuweisung
^=
Exklusiv-ODERZuweisung
<<=
Linksschiebezuweisung
>>=
Rechtsschiebezuweisung
>>>=
Rechtsschiebezuweisung
mit Nullexpansion
<
>
>>
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
5.8 Operator-Vorrangregeln
5.8 Operator-Vorrangregeln
Tabelle 5.6 listet alle Operatoren in der Reihenfolge ihrer Vorrangregeln auf. Weiter oben stehende
Operatoren haben dabei Vorrang vor weiter unten stehenden Operatoren. Innerhalb derselben Gruppe
stehende Operatoren werden entsprechend ihrer Assoziativitt ausgewertet.
Die Spalte Typisierung gibt die mglichen Operandentypen an. Dabei steht N fr numerische, I
fr integrale (also ganzzahlig numerische), L fr logische, S fr String-, R fr Referenz- und
P fr primitive Typen. Ein A wird verwendet, wenn alle Typen in Frage kommen, und mit einem
V wird angezeigt, da eine Variable erforderlich ist.
Gruppe
Operator
++
Inkrement
--
Dekrement
Unres Plus
Unres Minus
Einerkomplement
Logisches NICHT
(type)
Type-Cast
N,N
Multiplikation
N,N
Division
N,N
Modulo
N,N
Addition
N,N
Subtraktion
S,A
String-Verkettung
<<
I,I
Linksschieben
>>
I,I
Rechtsschieben
>>>
I,I
<
N,N
Kleiner
<=
N,N
Kleiner gleich
>
N,N
Grer
>=
N,N
Grer gleich
instanceof
R,R
Klassenzugehrigkeit
==
P,P
Gleich
!=
P,P
Ungleich
==
R,R
Referenzgleichheit
!=
R,R
Referenzungleichheit
&
I,I
Bitweises UND
&
L,L
I,I
Bitweises Exklusiv-ODER
L,L
Logisches Exklusiv-ODER
I,I
Bitweises ODER
L,L
10
&&
L,L
11
||
L,L
12
?:
L,A,A
Bedingte Auswertung
13
V,A
Zuweisung
+=
V,N
Additionszuweisung
-=
V,N
Subtraktionszuweisung
*=
V,N
Multiplikationszuweisung
/=
V,N
Divisionszuweisung
%=
V,N
Restwertzuweisung
&=
N,N u. L,L
Bitweises-UND-Zuweisung und
Logisches-UND-Zuweisung
|=
N,N u. L,L
Bitweises-ODER-Zuweisung und
Logisches-ODER-Zuweisung
^=
N,N u. L,L
Bitweises-Exklusiv-ODER-Zuweisung
und Logisches-Exklusiv-ODERZuweisung
<<=
V,I
Linksschiebezuweisung
>>=
V,I
Rechtsschiebezuweisung
>>>=
V,I
Rechtsschiebezuweisung mit
Nullexpansion
8
9
Etwas unschn ist die Tatsache, da die bitweisen Operatoren schwcher binden als die
Warnung
relationalen Operatoren. Da sie auf einer Stufe mit den zugehrigen logischen Operatoren
stehen, gibt es beim bersetzen des folgenden Programms den Fehler Incompatible type
for &. Can't convert int to boolean:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
/* Listing0508.java */
public class Listing0508
{
public static void main(String[] args)
{
int i = 55;
int j = 97;
if (i & 15 < j & 15) {
System.out.println("LowByte(55) < LowByte(97)");
} else {
System.out.println("LowByte(55) >= LowByte(97)");
}
}
}
/* Listing0509.java */
public class Listing0509
{
public static void main(String[] args)
{
int i = 55;
int j = 97;
if ((i & 15) < (j & 15)) {
System.out.println("LowByte(55) < LowByte(97)");
} else {
System.out.println("LowByte(55) >= LowByte(97)");
}
}
}
Listing0509.java
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
10.2 Wrapper-Klassen
10.2 Wrapper-Klassen
10.2.1 Vordefinierte Wrapper-Klassen
Instanzierung
Rckgabe des Wertes
Parsen von Strings
Konstanten
10.2.2 Call by Reference
Das Paket java.util stellt eine Reihe von Verbundklassen zur Verfgung, die beliebige
Objekttypen speichern knnen. Um darin auch elementare Typen ablegen zu knnen, ist es
notwendig, anstelle der primitiven Typen ihre Wrapper-Klassen zu verwenden.
Da Objektreferenzen den Wert null haben knnen, kann die Verwendung der Wrapper-Klassen
beispielsweise bei der Datenbankprogrammierung ntzlich sein. Damit lassen sich primitive
Feldtypen darstellen, die NULL-Werte enthalten knnen.
Das Reflection-API (siehe Kapitel 43) verwendet Wrapper-Klassen zum Zugriff auf
Membervariablen oder Methodenargumente primitiver Typen.
Wrapper-Klassen existieren zu allen numerischen Typen und zu den Typen char und boolean:
Wrapper-Klasse
Primitiver Typ
Byte
byte
Short
short
Integer
int
Long
long
Double
double
Float
float
Boolean
boolean
Character
char
Void
void
Das in diesem Beispiel mehrfach verwendete Schlsselwort throws deklariert Ausnahmen, die Hinweis
whrend der Methodenausfhrung auftreten knnen. Sie entstehen durch Programmfehler,
undefinierte Zustnde oder treten auf, wenn unvorhergesehene Ereignisse eintreten (Datei nicht
verfgbar, Speicher erschpft oder hnliches). Wir werden uns in Kapitel 12 ausfhrlich mit
diesem Thema beschftigen.
Rckgabe des Wertes
Die meisten Wrapper-Klassen besitzen zwei Methoden, um den internen Wert abzufragen. Eine der beiden
liefert ihn passend zum korrespondierenden Grundtyp, die andere als String. Der Name von Methoden
der ersten Art setzt sich aus dem Namen des Basistyps und der Erweiterung Value zusammen,
beispielsweise charValue, booleanValue oder intValue. Die numerischen Methoden
intValue, longValue, floatValue und doubleValue stehen dabei fr alle numerischen
Wrapper-Klassen zur Verfgung.
public
public
public
public
public
public
boolean booleanValue()
char charValue()
int intValue()
long longValue()
float floatValue()
double doubleValue()
Der Name der Methode, die den internen Wert als String zurckgibt, ist toString. Diese Methode
steht in allen Wrapper-Klassen zur Verfgung:
public String toString()
Die Methoden parseFloat und parseDouble gibt es erst seit dem JDK 1.2. In lteren JDK1.1-1.4
JDK-Versionen und den meisten Web-Browsern stehen sie nicht zur Verfgung.
Konstanten
Die numerischen Wrapper-Klassen stellen Konstanten zur Bezeichnung spezieller Elemente zur
Verfgung. So gibt es in jeder der Klassen Byte, Short, Integer, Long, Float und Double die
Konstanten MIN_VALUE und MAX_VALUE, die das kleinste bzw. grte Element des Wertebereichs
darstellen. In den Klassen Float und Double gibt es zustzlich die Konstanten
NEGATIVE_INFINITY, POSITIVE_INFINITY und NaN. Sie stellen die Werte minus unendlich, plus
unendlich und undefiniert dar.
Der primitive Typ kann in einen eigenen Wrapper verpackt werden, der das ndern des Wertes
erlaubt.
Der primitive Typ kann als Element eines Array bergeben werden. Da Arrays Objekte sind und
somit stets per Referenz bergeben werden, sind nderungen an ihren Elementen auch fr den
Aufrufer sichtbar.
Beide Methoden sind nicht sehr elegant, werden aber in der Praxis mitunter bentigt. Das folgende Listing
zeigt, wie es gemacht wird:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
/* Listing1005.java */
class IntWrapper
{
public int value;
public IntWrapper(int value)
{
this.value = value;
}
}
public class Listing1005
{
public static void inc1(IntWrapper w)
{
++w.value;
}
public static void inc2(int[] i)
{
++i[0];
}
public static void main(String[] args)
{
//Variante 1: bergabe in einem vernderlichen Wrapper
IntWrapper i = new IntWrapper(10);
System.out.println("i = " + i.value);
inc1(i);
Listing1005.java
031
032
033
034
035
036
037
}
038 }
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
13.2 Pakete
13.2 Pakete
13.2.1 Verwendung von Paketen
Der Import von java.lang
Die vordefinierten Pakete im JDK
13.2.2 Die Bedeutung der Paketnamen
Laden von Klassen im JDK 1.0 und 1.1
Laden von Klassen seit dem JDK 1.2
Die Dateien classes.zip und rt.jar
Umgekehrte Domain-Namen
13.2.3 Einbinden zustzlicher Pakete
13.2.4 Erstellen eigener Pakete
Benannte Pakete
Das Default-Paket
Das public-Attribut
Am Anfang des Programms werden die gewnschten Klassen mit Hilfe einer
import-Anweisung eingebunden:
import java.util.*;
...
Date d = new Date();
Die Verwendung voll qualifizierter Namen hat den Nachteil, da die Klassennamen sehr
lang und unhandlich werden. Bequemer ist daher die Anwendung der zweiten Variante,
bei der die bentigten Klassen mit Hilfe einer import-Anweisung dem Compiler
bekanntgemacht werden.
Die import-Anweisung gibt es in zwei unterschiedlichen Ausprgungen:
Mit der Syntax import Paket.Klasse; wird genau eine Klasse importiert.
Alle anderen Klassen des Pakets bleiben unsichtbar:
import java.util.Date;
...
Date d = new Date();
java.util.Vector = new java.util.Vector();
Unter Verwendung der Syntax import Paket.*; knnen alle Klassen des
angegebenen Pakets auf einmal importiert werden:
import java.util.*;
...
Date d = new Date();
Vector v = new Vector();
Warnung
Bedeutung
java.applet
Applets
java.awt
java.beans
Java Beans
java.io
java.lang
Elementare Sprachuntersttzung
java.lang.ref
Referenz-Objekte
java.lang.reflect
Reflection-API
java.math
Fliekomma-Arithmetik
java.net
Netzwerkuntersttzung
java.nio
java.rmi
java.security
Security-Dienste
java.sql
Datenbankzugriff (JDBC)
java.text
Internationalisierung
java.util
Bedeutung
Kryptographische Erweiterungen
javax.imageio
javax.naming
javax.print
Das Sound-API
javax.swing
Das SWING-Toolkit
javax.xml
[bin] steht hier fr den Pfad des bin-Verzeichnisses. Nur wenn die Klassendateien in
einem anderen Verzeichnis liegen oder Klassendateien in weiteren Verzeichnissen
eingebunden werden sollen, mu die CLASSPATH-Variable manuell gendert werden.
Existiert zum Zeitpunkt des Compiler-Aufrufs die Umgebungsvariable CLASSPATH,
beginnt der Compiler die Suche nach den eingebundenen Klassen in allen im
CLASSPATH angegebenen Verzeichnissen. Die einzelnen Verzeichnisse werden durch
ein Semikolon (bzw. einen Doppelpunkt unter UNIX) voneinander getrennt.
Beim Aufruf des Compilers kann der Schalter -classpath, gefolgt von einer Liste
von Verzeichnisnamen, bergeben werden. Er hat dieselbe Bedeutung wie die
Umgebungsvariable CLASSPATH und definiert die Verzeichnisse, in denen der
Compiler nach den .class-Dateien sucht. Der Compiler-Schalter hat dabei Vorrang
gegenber einer mglichen Umgebungsvariablen.
Laden von Klassen seit dem JDK 1.2
Die Verwendung der CLASSPATH-Variable hat immer wieder zu Schwierigkeiten und
Miverstndnissen gefhrt. Es ist insbesondere hufig passiert, da durch falsches Setzen
der Umgebungsvariable die Systemklassen selbst nicht mehr gefunden werden konnten
und auf diese Weise das gesamte Laufzeitsystem unbenutzbar wurde.
Seit dem JDK 1.2 wurde daher die Bedeutung der CLASSPATH-Umgebungsvariable
dahingehend verndert, da sie nur noch zur Suche der benutzerspezifischen Klassen
verwendet wird. Alle Standardpakete und Standarderweiterungen (beide zusammen
werden seit dem JDK 1.2 Bootstrap Classes genannt) werden dagegen unabhngig vom
CLASSPATH mit Hilfe der auf das Installationsverzeichnis verweisenden
Systemeigenschaft sun.boot.class.path gefunden. Sie wird bei der JDKInstallation automatisch gesetzt und sollte spter nicht mehr verndert werden. Der
CLASSPATH braucht also nur noch dann explizit gesetzt zu werden, wenn
benutzerspezifische Klassen vorhanden sind, die nicht im aktuellen Verzeichnis liegen
(letzteres wird ebenfalls automatisch durchsucht).
Falls Sie zuvor eine ltere Versionen des Java Development Kit auf Ihrem
Rechner installiert hatten, berprfen Sie bitte nach der Installation die
Umgebungsvariablen. Sorgen Sie dafr, da nicht ein veralteter
CLASSPATH auf Verzeichnisse oder Dateien verweist, aus denen das
Laufzeitsystem versehentlich unbrauchbare Klassendateien laden wrde.
Warnung
Datei classes.zip anstelle der erwhnten Unterverzeichnisse mit den Klassendateien. Aus
Grnden der Performance beim bersetzen haben sich die Entwickler entschlossen, alle
Standardklassendateien in diesem Archiv abzulegen, um einen schnelleren Lesezugriff
auf sie zu ermglichen.
Die Datei classes.zip sollte nicht ausgepackt werden, denn der Compiler
Warnung
verwendet sie in archivierter Form. Obwohl beim Einpacken der Klassen auf
die Komprimierung verzichtet wurde, bietet diese Form der Archivierung
den Vorteil, da viele kleine Dateien in einer einzigen groen
zusammengefat werden. Zeitaufwendige Verzeichniszugriffe und -wechsel
knnen so entfallen, wenn der Compiler nach Klassennamen suchen mu
oder den Inhalt einer Klassendatei lesen will.
Um dem Compiler diese Art der Speicherung der Klassendateien bekanntzumachen, mu
in der CLASSPATH-Umgebungsvariable nicht nur das Verzeichnis, sondern auch der
Name der .zip-Datei angegeben werden, z.B.:
CLASSPATH=.;c:\java\LIB\CLASSES.ZIP
Seit dem JDK 1.2 gibt es die Datei classes.zip nicht mehr. Die
JDK1.1-1.4
Klassenbibliotheken liegen nun als .jar-Dateien (z.B. rt.jar ) vor und
befinden sich im Unterverzeichnis jre\lib der JDK-Installation. Wie zuvor
erwhnt, werden sie unabhngig vom Inhalt der CLASSPATHUmgebungsvariable gefunden. Weitere Informationen zu jar-Dateien
finden Sie in Kapitel 50.
Umgekehrte Domain-Namen
Die Entwickler von Java haben sich einen Mechanismus ausgedacht, um auch bei sehr
groen Projekten, an denen mglicherweise viele Entwickler beteiligt sind,
Namenskollisionen zwischen den beteiligten Klassen und Paketen zu vermeiden. Auch
die Verwendung einer groen Anzahl unterschiedlicher Klassenbibliotheken von
verschiedenen Herstellern sollte mglich sein, ohne da Namensberschneidungen dies
schon im Keim ersticken.
Um diese Probleme zu lsen, hat sich das Java-Team eine Vorgehensweise zur Vergabe
von Paketnamen berlegt, die an das Domain-Namen-System bei Internet-Adressen
angelehnt ist. Danach sollte jeder Anbieter seine Pakete entsprechend dem eigenen
Domain-Namen benennen, dabei allerdings die Namensbestandteile in umgekehrter
Reihenfolge verwenden.
So sollten beispielsweise die Klassen der Firma SUN, deren Domain-Name sun.com
ist, in einem Paket com.sun oder in darunter befindlichen Subpaketen liegen. Da die
Alle Libraries, die sich an diese Konventionen halten, knnen in der beschriebenen
Weise installiert werden. Probleme gibt es nur, wenn ein Anbieter seine Klassendateien
nicht in Paketen ablegt. In diesem Fall mten die Klassendateien in das aktuelle
Verzeichnis oder nach c:\classes kopiert werden. Das wrde bei Klassendateien von
mehr als einem Anbieter natrlich schnell zum Chaos fhren, lt sich aber nicht so
einfach ndern. Es bieten sich zwei Handlungsalternativen an:
Beide Varianten sind unbefriedigend, und es bleibt zu hoffen, da die Anbieter von JavaKlassenbibliotheken sich verstrkt an die Namenskonventionen des Java-Teams halten
werden.
exakt dem der import-Anweisung. Der Compiler lst ebenso wie beim import den
dort angegebenen hierarchischen Namen in eine Kette von Unterverzeichnissen auf, an
deren Ende die Quelldatei steht. Neben der Quelldatei wird auch die Klassendatei in
diesem Unterverzeichnis erstellt.
Da der Java-Compiler eingebundene Quelldateien, die noch nicht bersetzt sind,
whrend der bersetzung einer anderen Klasse automatisch mit bersetzt, ist das
Erstellen eines neuen Pakets sehr einfach. Wir wollen uns ein Beispiel ansehen, bei dem
zwei Pakete demo und demo.tools angelegt und die darin enthaltenen Klassen A, B
und C in einer Klasse PackageDemo verwendet werden. Am einfachsten ist es, wie
nachfolgend beschrieben vorzugehen.
Wir gehen davon aus, da der CLASSPATH das aktuelle Verzeichnis enthlt (also
beispielsweise den Inhalt ".;c:\classes" hat), und legen im aktuellen Verzeichnis die
Unterverzeichnisse demo und demo\tools an.
package demo;
demo/A.java
public class A
{
public void hello()
{
System.out.println("Hier ist A");
}
}
001
002
003
004
005
006
007
008
009
package demo;
demo/B.java
public class B
{
public void hello()
{
System.out.println("Hier ist B");
}
}
package demo.tools;
demo/tools/C.java
public class C
{
public void hello()
{
System.out.println("Hier ist C");
}
}
import demo.*;
import demo.tools.*;
public class PackageDemo
{
public static void main(String[] args)
{
(new A()).hello();
(new B()).hello();
(new C()).hello();
}
}
PackageDemo.java
>
>>
API
Warnung
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Bis zum JDK 1.0.2 war die Klasse Date zur Darstellung und Manipulation von Datumswerten vorgesehen.
Leider war sie nicht ganz fehlerfrei und aufgrund diverser Einschrnkungen nur sehr bedingt zu gebrauchen.
Ab der Version 1.1 des JDK gibt es neben Date die Klasse Calendar zur Verarbeitung von Datumswerten.
Obgleich der Name den Anschein erwecken mag, da ein Objekt vom Typ Calendar ein visueller Kalender
ist, der als Komponente in GUI-basierten Programmen verwendet werden kann, ist dies nicht richtig. Statt
dessen stellt Calendar eine Kapselung fr Date dar, deren Aufgabe es ist, ein Datum-/Uhrzeitobjekt zu
realisieren und Methoden zur Konstruktion, zum Verndern und Auslesen von Datums-/Uhrzeitbestandteilen
und fr die Zeit- und Datumsarithmetik zur Verfgung zu stellen.
Zunchst ist Calendar nichts weiter als eine abstrakte Basisklasse. Sie enthlt die Methoden, mit denen auf
die einzelnen Elemente konkreter Kalenderklassen zugegriffen werden kann bzw. mit denen diese Klassen
manipuliert werden knnen. Als einzige konkrete Ableitung von Calendar steht die Klasse
GregorianCalendar zur Verfgung, die ein Datum nach dem hierzulande verwendeten gregorianischen
Kalender implementiert. Die Komplexitt der Klassen Calendar und GregorianCalendar kommt vor
allem durch folgende Ursachen zustande:
In der Tat ist die Implementierung der Kalenderklassen des JDK komplex und war lange Zeit fehlerbehaftet.
Sie wird insbesondere dadurch erschwert, da ein Datumsobjekt nicht nur aus den einzelnen Feldern fr Tag,
Monat, Jahr usw. besteht, sondern zustzlich einen ganzzahligen Wert des Typs long enthlt, der das Datum
als Anzahl der Millisekunden seit dem 1.1.1970 speichert. Beide Werte mssen auch nach Vernderungen
einzelner Bestandteile des Datumsobjekts konsistent gehalten werden.
Auch die Bedienung der Kalenderklassen ist nicht so eingngig wie in vielen anderen Programmiersprachen.
Hinderlich ist dabei oft die Tatsache, da neben Datum und Uhrzeit grundstzlich auch die Zeitzone mit
betrachtet wird. Wir wollen in diesem Abschnitt einen pragmatischen Ansatz whlen und nur die wesentlichen
Eigenschaften der beiden Klassen vorstellen. Fortgeschrittenere Themen wie Zeitzonenkalkulation oder
Lokalisierung werden auen vor bleiben.
16.2.1 Konstruktoren
Da die Klasse Calendar abstrakt ist, mssen konkrete Datumsobjekte aus der Klasse
GregorianCalendar erzeugt werden. Dazu stehen folgende Konstruktoren zur Verfgung:
java.util.GregorianCalendar
public GregorianCalendar()
public GregorianCalendar(int year, int month, int date)
public GregorianCalendar(
int year, int month, int date,
int hour, int minute
)
public GregorianCalendar(
int year, int month, int date,
int hour, int minute, int second
)
Der parameterlose Konstruktor initialisiert das Datumsobjekt mit dem aktuellen Datum und der aktuellen
Uhrzeit. Die brigen Konstruktoren weisen die als Parameter bergebenen Werte zu. Neben den hier
vorgestellten Konstruktoren gibt es noch weitere, die es erlauben, die Zeitzone und Lokalisierung zu verndern.
Standardmig werden die lokale Zeitzone und die aktuelle Lokalisierung verwendet.
get und set erwarten als erstes Argument einen Feldbezeichner, der angibt, auf welches der diversen Datums/Zeitfelder des Objektes zugegriffen werden soll. Als Rckgabewert liefert get den Inhalt des angegebenen
Feldes; set schreibt den als zweiten Parameter value bergebenen Wert in das Feld hinein. Tabelle 16.1 gibt
eine bersicht der in Calendar vorgesehenen Feldbezeichner, ihrer Wertegrenzen und Bedeutung:
Feldbezeichner
Minimalwert
Maximalwert
Bedeutung
Calendar.ERA
ra
Calendar.YEAR
5,000,000
Jahr
Calendar.MONTH
11
Monat - 1
Calendar.WEEK_OF_YEAR
54
Woche im Jahr
Calendar.WEEK_OF_MONTH
Woche im Monat
Calendar.DAY_OF_MONTH
31
Tag im Monat
Calendar.DAY_OF_YEAR
366
Tag im Jahr
Calendar.DAY_OF_WEEK
Wochentag
Wochentagswiederholung im
Monat
Calendar.DAY_OF_WEEK_IN_MONTH -1
Calendar.AM_PM
Vor-/nachmittags
Calendar.HOUR
12
Amerik. Stunde
Calendar.HOUR_OF_DAY
23
Stunde
Calendar.MINUTE
59
Minute
Calendar.SECOND
59
Sekunde
Calendar.MILLISECOND
999
Millisekunde
Calendar.ZONE_OFFSET
12*60*60*1000 Zeitzonenoffset
12*60*60*1000
Calendar.DST_OFFSET
1*60*60*1000 Sommerzeitoffset
/* Listing1602.java */
import java.util.*;
public class Listing1602
{
public static void main(String[] args)
{
//Zuerst Ausgabe des aktuellen Datums
GregorianCalendar cal = new GregorianCalendar();
//cal.setTimeZone(TimeZone.getTimeZone("ECT"));
printCalendarInfo(cal);
System.out.println("---");
//Nun Ausgabe der Informationen zum 22.6.1910,
//dem Geburtstag von Konrad Zuse
cal.set(Calendar.DATE, 22);
cal.set(Calendar.MONTH, 6 - 1);
cal.set(Calendar.YEAR, 1910);
printCalendarInfo(cal);
//cal.setTime(cal.getTime());
}
Listing1602.java
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
}
118 }
Anno Domini
28.4.2000
17:55:0 (+590 ms)
5:55:0 PM
119. im Jahr
28. im Monat
Woche......: 17. im Jahr
4. im Monat
Wochentag..:
Zeitzone...:
Sommerzeit.:
--Aera.......:
Datum......:
Zeit.......:
Am.Zeit....:
Tag........:
Woche......:
Wochentag..:
Zeitzone...:
Sommerzeit.:
4. Freitag im Monat
1 Stunden
1 Stunden
Anno Domini
22.6.1910
17:55:0 (+590 ms)
5:55:0 PM
173. im Jahr
22. im Monat
25. im Jahr
4. im Monat
4. Mittwoch im Monat
1 Stunden
1 Stunden
Wie man sieht, werden sowohl Datums- als auch Zeitwerte korrekt ausgegeben. Damit dieses
und vergleichbare Programme auch in den JDK-1.1-Versionen korrekt laufen, sind einige
Besonderheiten zu beachten:
JDK1.1-1.4
java.util.Calendar
Mit Hilfe der Methode add kann zu einem beliebigen Feld eines Calendar- oder GregorianCalendarObjekts ein beliebiger positiver oder negativer Wert hinzugezhlt werden:
java.util.Calendar
Dabei ist es auch erlaubt, da die Summe den fr dieses Feld erlaubten Grenzwert ber- bzw. unterschreitet. In
diesem Fall wird der nchsthherwertige Datums- bzw. Zeitbestandteil entsprechend angepat.
Das folgende Programm konstruiert zunchst ein Datum fr den 30.10.1908 und gibt es aus. Anschlieend wird
zunchst der Tag, dann der Monat und schlielich das Jahr je zweimal um 1 erhht. Nach erfolgter Ausgabe
wird die nderung schrittweise wieder rckgngig gemacht und der ursprngliche Wert wieder erzeugt:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
/* Listing1603.java */
import java.util.*;
public class Listing1603
{
public static void main(String[] args)
{
GregorianCalendar cal
= new GregorianCalendar();
cal.set(Calendar.DATE, 30);
cal.set(Calendar.MONTH, 10 - 1);
cal.set(Calendar.YEAR, 1908);
showDate(cal);
addOne(cal, Calendar.DATE);
addOne(cal, Calendar.DATE);
addOne(cal, Calendar.MONTH);
addOne(cal, Calendar.MONTH);
addOne(cal, Calendar.YEAR);
addOne(cal, Calendar.YEAR);
cal.add(Calendar.DATE, -2);
cal.add(Calendar.MONTH, -2);
cal.add(Calendar.YEAR, -2);
showDate(cal);
}
public static void addOne(Calendar cal, int field)
{
cal.add(field,1);
showDate(cal);
}
public static void showDate(Calendar cal)
{
String ret = "";
int
value = cal.get(Calendar.DAY_OF_WEEK);
switch (value) {
case Calendar.SUNDAY:
ret += "Sonntag";
break;
case Calendar.MONDAY:
ret += "Montag";
break;
case Calendar.TUESDAY:
Listing1603.java
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
}
067 }
ret += "Dienstag";
break;
case Calendar.WEDNESDAY:
ret += "Mittwoch";
break;
case Calendar.THURSDAY:
ret += "Donnerstag";
break;
case Calendar.FRIDAY:
ret += "Freitag";
break;
case Calendar.SATURDAY:
ret += "Samstag";
break;
}
ret += ", den ";
ret += cal.get(Calendar.DATE) + ".";
ret += (cal.get(Calendar.MONTH)+1) + ".";
ret += cal.get(Calendar.YEAR);
System.out.println(ret);
java.util.Calendar
Ein Aufruf von getTime liefert das Datum als Objekt des Typs Date. Soll das Datum aus einem
vorhandenen Date-Objekt in ein Calendar-Objekt bertragen werden, kann dazu die Methode setTime
aufgerufen werden. Die Klasse Date kann weiterhin dazu verwendet werden, auf die Anzahl der
Millisekunden seit dem 1.1.1970 zuzugreifen:
java.util.Date
Der Konstruktor erzeugt aus dem long ein Date-Objekt, und die Methode getTime kann dazu verwendet
werden, zu einem gegebenen Date-Objekt die Anzahl der Millisekunden seit dem 1.1.1970 zu ermitteln.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Neben den bisher vorgestellten Datenstrukturen des Pakets java.util gibt es in java.lang eine Klasse System, die
eine Reihe ntzlicher Hilfsmittel zur Verfgung stellt. Die wichtigsten von ihnen sollen in den folgenden Abschnitten
besprochen werden.
16.3.1 System-Properties
In Java gibt es keine Mglichkeit, direkt auf die Umgebungsvariablen eines Programms zuzugreifen. Ein solcher Zugriff
wurde von den Java-Designern als nichtportabel angesehen und statt dessen durch das Konzept der Properties ersetzt.
Properties sind Listen von Eigenschaften, die dem Programm vom Java-Laufzeitsystem zur Verfgung gestellt werden.
Jede Eigenschaft besitzt einen Namen, unter dem auf sie zugegriffen werden kann. Das Java-Laufzeitsystem stellt
standardmig die folgenden Properties zur Verfgung:
Property
Bedeutung
java.version
Java-Versionsnummer
java.vendor
Herstellerspezifische Zeichenkette
java.vendor.url
java.home
Installationsverzeichnis
java.class.version
java.class.path
Aktueller Klassenpfad
os.name
os.arch
Betriebssystem-Architektur
os.version
file.separator
path.separator
line.separator
Zeichenkette fr Zeilenschaltung
user.name
user.home
Home-Verzeichnis
user.dir
Aktuelles Arbeitsverzeichnis
java.vm.specification.name
java.vm.version
VM-Version
java.vm.vendor
Hersteller der VM
java.vm.name
java.specification.version
java.specification.vendor
java.specification.name
java.lang.System
Die erste Variante liefert die Eigenschaft mit dem Namen key in Form einer Zeichenkette. Falls keine Eigenschaft mit
diesem Namen gefunden wurde, wird null zurckgegeben. Die zweite Variante erlaubt die bergabe eines
Standardwertes. Der Unterschied zur ersten Variante besteht darin, da nicht null, sondern der Standardwert
zurckgegeben wird, wenn die gesuchte Eigenschaft nicht gefunden wurde.
Die Methode getProperties liefert das komplette Properties-Objekt mit den System-Properties:
public static Properties getProperties()
java.lang.System
Das folgende Programm gibt eine Liste aller System-Properties auf dem Bildschirm aus. Es verwendet dazu zunchst die
Methode getProperties, um das System-Properties-Objekt zu beschaffen. Anschlieend erzeugt es durch Aufruf
von propertyNames einen Enumerator, mit dem alle Schlsselwerte durchlaufen werden knnen und mit dem durch
Aufruf von getProperty der zugehrige Wert ermittelt werden kann. Auf diese Weise listet das Programm alle
verfgbaren System-Properties auf (das sind in der Regel sehr viel mehr als die plattformbergreifend spezifizierten):
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
/* Listing1604.java */
import java.util.*;
public class Listing1604
{
public static void main(String[] args)
{
Properties sysprops
= System.getProperties();
Enumeration propnames = sysprops.propertyNames();
while (propnames.hasMoreElements()) {
String propname = (String)propnames.nextElement();
System.out.println(
propname + "=" + System.getProperty(propname)
);
}
}
}
Listing1604.java
java.lang.System
Die Verwendung dieser Klassenvariablen ist im Grunde genommen nicht konform mit dem Dialogkonzept einer Hinweis
GUI-Anwendung. Ihr Einsatz kann aber immer dann sinnvoll sein, wenn Java-Programme geschrieben werden
sollen, die keine ausgefeilte Oberflche bentigen. In diesem Fall sind sie ein ntzliches Hilfsmittel, um
einfache Ein-/Ausgaben ohne groen Aufwand realisieren zu knnen. Bereits in Kapitel 3 wurde gezeigt, wie
man diese Routinen zur Ein- und Ausgabe verwenden kann. Weitere Informationen darber sind in Kapitel 19
zu finden, das sich mit Byte-Streams beschftigt.
16.3.3 exit
Auch Aufrufe der Methode System.exit sind uns schon begegnet:
public static void exit(int status)
java.lang.System
Mit System.exit wird das laufende Programm beendet. Der Aufrufparameter status dient als Fehleranzeige und
wird als Exitcode an den Aufrufer des Programms zurckgegeben. Gem Konvention zeigt dabei ein Wert grer oder
gleich 1 einen Fehler whrend der Programmausfhrung an, whrend 0 ein fehlerfreies Programmende signalisiert.
16.3.4 gc
public static void gc()
java.lang.System
Ein Aufruf der Methode gc fhrt einen expliziten Aufruf des Garbage Collectors durch. Dieser sucht dann nach freiem
Speicher und gibt diesen an das Laufzeitsystem zurck. Normalerweise ist ein Aufruf dieser Methode nicht erforderlich,
denn der Garbage Collector luft stndig als niedrig priorisierter Thread im Hintergrund. Der Aufruf von gc ist immer
dann sinnvoll, wenn eine explizite Kontrolle ber den Zeitpunkt der Speicherfreigabe gewnscht ist.
16.3.5 currentTimeMillis
Die Methode currentTimeMillis liefert die Anzahl der Millisekunden, die zum Zeitpunkt des Aufrufs seit
Mitternacht des 1.1.1970 vergangen sind:
Ob dabei tatschlich eine Auflsung von einer Millisekunde erreicht wird, ist von der konkreten JavaImplementierung abhngig. In PC-basierten Java-Systemen orientiert sie sich meist an der Auflsung des
System-Timers. Dieser wird 18,2 mal pro Sekunde aufgerufen, so da die Auflsung damit bei etwa 55 ms.
liegt.
java.lang.System
Hinweis
Mit dem folgenden Beispielprogramm kann die Auflsung des System-Timers ermittelt werden:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
Listing1605.java
/* Listing1605.java */
public class Listing1605
{
public static void main(String[] args)
{
long t1, t2;
int actres, sumres = 0, i = 0;
while (true) {
++i;
t1 = System.currentTimeMillis();
while (true) {
t2 = System.currentTimeMillis();
if (t2 != t1) {
actres = (int)(t2 - t1);
break;
}
}
sumres += actres;
System.out.print("it="+i+", ");
System.out.print("actres="+actres+" msec., ");
System.out.print("avgres="+(sumres/i)+" msec.");
System.out.println("");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
//nichts
}
}
}
}
actres=60
actres=50
actres=50
actres=50
actres=50
actres=50
msec.,
msec.,
msec.,
msec.,
msec.,
msec.,
avgres=60
avgres=55
avgres=53
avgres=52
avgres=52
avgres=51
msec.
msec.
msec.
msec.
msec.
msec.
...
it=65,
it=66,
it=67,
it=68,
it=69,
it=70,
actres=50
actres=50
actres=50
actres=60
actres=60
actres=60
msec.,
msec.,
msec.,
msec.,
msec.,
msec.,
avgres=55
avgres=55
avgres=55
avgres=55
avgres=55
avgres=55
msec.
msec.
msec.
msec.
msec.
msec.
Interessanterweise bietet die Methode sleep der Klasse Thread (sie wird in Abschnitt 22.2.4 beschrieben) auf
Tip
aktuellen SUN-JDKs unter Windows mit etwa 1 ms. eine wesentlich hhere Auflsung als currentTimeMillis.
Diese Eigenschaft ist allerdings nicht dokumentiert und kann von Interpreter zu Interpreter sehr unterschiedlich sein.
Selbst auf ein und derselben Java-Maschine kann es durch unterschiedliche Lastsituationen zu Abweichungen
kommen. Das folgende Programm ermittelt die Auflsung von sleep:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
/* Listing1606.java */
public class Listing1606
{
public static long testSleep(int millis)
{
final int MINDURATION = 3000;
int cnt = (millis >= MINDURATION ? 1 : MINDURATION/millis);
long start = System.currentTimeMillis();
for (int i = 0; i < cnt; ++i) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
}
}
long end = System.currentTimeMillis();
return (end - start) / cnt;
}
public static void main(String[] args)
{
final int DATA[] = {345, 27, 1, 1962, 2, 8111, 6, 89, 864};
for (int i = 0; i < DATA.length; ++i) {
System.out.println("Aufruf von sleep(" + DATA[i] + ")");
long result = testSleep(DATA[i]);
System.out.print(" Ergebnis: " + result);
double prec = ((double)result / DATA[i] - 1.0) * 100;
System.out.println(" (" + (prec > 0 ? "+": "") + prec + " %)");
}
}
}
Listing1606.java
16.3.6 arraycopy
Als letzte Methode der Klasse System soll arraycopy vorgestellt werden:
public static native void arraycopy(
Object src, int src_position,
Object dst, int dst_position,
int length
)
java.lang.System
arraycopy kann dazu verwendet werden, Arrays oder Teile davon zu kopieren. Dabei knnen die Elemente sowohl
innerhalb desselben Arrays als auch in ein anderes Array kopiert werden. Falls innerhalb desselben Arrays kopiert wird,
drfen sich Quell- und Zielbereich auch berlappen. Die Methode arbeitet sowohl mit elementaren als auch mit
Objekttypen, Ziel- und Quellarray mssen lediglich zuweisungskompatibel sein. Da die Methode in C bzw. Assembler
implementiert ist, arbeitet sie recht performant.
Als erste Argumente werden das Quellarray src und die Startposition src_position angegeben. Anschlieend folgen
das Zielarray dst und die Zielposition dst_position. Als letztes Argument wird die Lnge length des zu
kopierenden Bereichs angegeben. Falls die Argumente auf Elemente zeigen, die auerhalb des Arrays liegen, wird eine
Ausnahme des Typs ArrayIndexOutOfBoundsException ausgelst. Falls ein Element aufgrund eines Typfehlers
nicht gespeichert werden kann, gibt es eine Ausnahme des Typs ArrayStoreException.
Das folgende Programm zeigt die Verwendung von arraycopy an einem einfachen Beispiel:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
/* Listing1607.java */
Listing1607.java
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
18.2 Ausgabe-Streams
18.2 Ausgabe-Streams
18.2.1 Die abstrakte Klasse Writer
18.2.2 Auswahl des Ausgabegertes
OutputStreamWriter und FileWriter
StringWriter und CharArrayWriter
18.2.3 Schachteln von Ausgabe-Streams
BufferedWriter
PrintWriter
FilterWriter
java.io.Writer
Neben einem parameterlosen Konstruktor definiert sie die Methoden close und flush und eine Reihe
berladener write-Methoden. Die Bedeutung des Konstruktors liegt darin, den Ausgabestrom zu ffnen und fr
einen nachfolgenden Aufruf von write vorzubereiten, bevor er schlielich mit close wieder geschlossen wird.
In der Grundform erwartet write einen einzigen int-Parameter und schreibt diesen als Byte in den
Ausgabestrom. Daneben gibt es weitere Varianten, die ein Array von Bytes oder ein String-Objekt als
Parameter erwarten und dieses durch wiederholten Aufruf der primitiven write-Methode ausgeben. Durch
Aufruf von flush werden eventuell vorhandene Puffer geleert und die darin enthaltenen Daten an das
Ausgabegert weitergegeben.
Von abgeleiteten Klassen wird erwartet, da sie mindestens die Methoden flush und close sowie
Hinweis
write(char, int, int) berlagern. Aus Grnden der Performance ist es aber oftmals sinnvoll,
auch die brigen write-Methoden zu berlagern.
Als abstrakte Basisklasse kann Writer nicht instanziert werden. Statt dessen gibt es eine Reihe konkreter
Klassen, die aus Writer abgeleitet wurden und deren Aufgabe es ist, die Verbindung zu einem konkreten
Ausgabegert herzustellen oder Filterfunktionen zu bernehmen. Tabelle 18.1 gibt eine bersicht dieser
abgeleiteten Klassen.
Klasse
Bedeutung
FilterWriter
PrintWriter
BufferedWriter
StringWriter
CharArrayWriter
PipedWriter
Hinweis
java.io.FileWriter
Am einfachsten kann eine Datei gffnet werden, indem der Dateiname als String-Parameter fileName
bergeben wird. Falls fileName eine bereits vorhandene Datei bezeichnet, wird sie geffnet und ihr bisheriger
Inhalt gelscht, andernfalls wird eine neue Datei mit diesem Namen angelegt. Sukzessive Aufrufe von write
schreiben weitere Bytes in diese Datei. Wird zustzlich der Parameter append mit dem Wert true an den
Konstruktor bergeben, so werden die Ausgabezeichen an die Datei angehngt, falls sie bereits existiert.
Das folgende Programm erstellt eine Datei hallo.txt und schreibt die Zeile Hallo JAVA in die Datei:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
/* Listing1801.java */
Listing1801.java
import java.io.*;
public class Listing1801
{
public static void main(String[] args)
{
String hello = "Hallo JAVA\r\n";
FileWriter f1;
try {
f1 = new FileWriter("hallo.txt");
f1.write(hello);
f1.close();
} catch (IOException e) {
System.out.println("Fehler beim Erstellen der Datei");
}
}
}
Hinweis
public StringWriter()
java.io.StringWriter
Der parameterlose Konstruktor legt einen StringWriter mit der Standardgre eines StringBufferObjekts an, whrend der zweite Konstruktor es erlaubt, die initiale Gre selbst festzulegen. Wie schon erwhnt,
wchst der interne Puffer automatisch, wenn fortgesetzte Aufrufe von write dies erforderlich machen. Die
schreibenden Zugriffe auf den Puffer erfolgen mit den von Writer bekannten write-Methoden.
Fr den Zugriff auf den Inhalt des Puffers stehen die Methoden getBuffer und toString zur Verfgung:
public StringBuffer getBuffer()
java.io.StringWriter
Die Methode getBuffer liefert den internen StringBuffer, whrend toString den Puffer in einen
String kopiert und diesen an den Aufrufer liefert.
Die Klasse CharArrayWriter arbeitet hnlich wie StringWriter, schreibt die Zeichen allerdings in ein
Character-Array. Analog zu StringWriter wchst dieses automatisch bei fortgesetzten Aufrufen von write.
Die Konstruktoren haben dieselbe Struktur wie bei StringWriter, und auch die dort verfgbaren writeMethoden stehen allesamt hier zur Verfgung:
public CharArrayWriter()
java.io.CharArrayWriter
Auf den aktuellen Inhalt des Arrays kann mit Hilfe der Methoden toString und toCharArray zugegriffen
werden:
public String toString()
java.io.CharArrayWriter
Zustzlich stehen die Methoden reset und size zur Verfgung, mit denen der interne Puffer geleert bzw. die
aktuelle Gre des Zeichen-Arrays ermittelt werden kann. Durch Aufruf von writeTo kann des weiteren der
komplette Inhalt des Arrays an einen anderen Writer bergeben und so beispielsweise mit einem einzigen
Funktionsaufruf in eine Datei geschrieben werden:
public void reset()
public int size()
public void writeTo(Writer out)
throws IOException
java.io.CharArrayWriter
Wie eingangs erwhnt, ist es in Java mglich, Streams zu schachteln. Dazu gibt es die aus Writer abgeleiteten
Klassen BufferedWriter, PrintWriter und FilterWriter, deren wesentlicher Unterschied zu
Writer darin besteht, da sie als Membervariable einen zustzlichen Writer besitzen, der im Konstruktor
bergeben wird. Wenn nun die write-Methode eines derart geschachtelten Writers aufgerufen wird, gibt sie die
Daten nicht direkt an den internen Writer weiter, sondern fhrt zuvor erst die erforderlichen Filterfunktionen
aus. Damit lassen sich beliebige Filterfunktionen transparent realisieren.
BufferedWriter
Diese Klasse hat die Aufgabe, Stream-Ausgaben zu puffern. Dazu enthlt sie einen internen Puffer, in dem die
Ausgaben von write zwischengespeichert werden. Erst wenn der Puffer voll ist oder die Methode flush
aufgerufen wird, werden alle gepufferten Ausgaben in den echten Stream geschrieben. Das Puffern der Ausgabe
ist immer dann ntzlich, wenn die Ausgabe in eine Datei geschrieben werden soll. Durch die Verringerung der
write-Aufrufe reduziert sich die Anzahl der Zugriffe auf das externe Gert, und die Performance wird erhht.
BufferedWriter besitzt zwei Konstruktoren:
public BufferedWriter(Writer out)
java.io.BufferedWriter
In beiden Fllen wird ein bereits existierender Writer bergeben, an den die gepufferten Ausgaben
weitergereicht werden. Falls die Gre des Puffers nicht explizit angegeben wird, legt BufferedWriter einen
Standardpuffer an, dessen Gre fr die meisten Zwecke ausreichend ist. Die write-Methoden von
BufferedWriter entsprechen denen der Klasse Writer.
Zustzlich gibt es eine Methode newLine, mit der eine Zeilenschaltung in den Stream geschrieben werden kann.
Diese wird dem System-Property line.separator entnommen und entspricht damit den lokalen
Konventionen.
Das nachfolgende Beispiel demonstriert die gepufferte Ausgabe einer Reihe von Textzeilen in eine Datei
buffer.txt:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
/* Listing1802.java */
import java.io.*;
public class Listing1802
{
public static void main(String[] args)
{
Writer f1;
BufferedWriter f2;
String s;
try {
f1 = new FileWriter("buffer.txt");
f2 = new BufferedWriter(f1);
for (int i = 1; i <= 10000; ++i) {
s = "Dies ist die " + i + ". Zeile";
f2.write(s);
f2.newLine();
}
Listing1802.java
021
f2.close();
022
f1.close();
023
} catch (IOException e) {
024
System.out.println("Fehler beim Erstellen der Datei");
025
}
026
}
027 }
Listing 18.2: Gepufferte Ausgabe in eine Datei
Dieses Beispiel erzeugt zunchst einen neuen Writer f1, um die Datei buffer.txt anzulegen. Dieser wird
anschlieend als Parameter an den BufferedWriter f2 bergeben, der dann fr den Aufruf der
Ausgaberoutinen verwendet wird.
Die etwas umstndliche Verwendung zweier Stream-Variablen lt sich vereinfachen, indem die
Konstruktoren direkt beim Aufruf geschachtelt werden. Das ist die unter Java bliche Methode, die auch
zuknftig bevorzugt verwendet werden soll:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
/* Listing1803.java */
Tip
Listing1803.java
import java.io.*;
public class Listing1803
{
public static void main(String[] args)
{
BufferedWriter f;
String s;
try {
f = new BufferedWriter(
new FileWriter("buffer.txt"));
for (int i = 1; i <= 10000; ++i) {
s = "Dies ist die " + i + ". Zeile";
f.write(s);
f.newLine();
}
f.close();
} catch (IOException e) {
System.out.println("Fehler beim Erstellen der Datei");
}
}
}
java.io.PrintWriter
Der erste Konstruktor instanziert ein PrintWriter-Objekt durch bergabe eines Writer-Objekts, auf das die
Ausgabe umgeleitet werden soll. Beim zweiten Konstruktor gibt zustzlich der Parameter autoflush an, ob
nach der Ausgabe einer Zeilenschaltung automatisch die Methode flush aufgerufen werden soll.
Die Ausgabe von primitiven Datentypen wird durch eine Reihe berladener Methoden mit dem Namen print
realisiert. Zustzlich gibt es alle Methoden in einer Variante println, bei der automatisch an das Ende der
Ausgabe eine Zeilenschaltung angehngt wird. println existiert darber hinaus parameterlos, um lediglich eine
einzelne Zeilenschaltung auszugeben. Damit stehen folgende print-Methoden zur Verfgung (analog fr
println):
public
public
public
public
public
public
public
public
public
void
void
void
void
void
void
void
void
void
print(boolean b)
print(char c)
print(char[] s)
print(double d)
print(float f)
print(int i)
print(long l)
print(Object obj)
print(String s)
java.io.PrintWriter
Das folgende Beispiel berechnet die Zahlenfolge 1 + 1/2 + 1/4 + ... und gibt die Folge der Summanden und die
aktuelle Summe unter Verwendung mehrerer unterschiedlicher print-Routinen in die Datei zwei.txt aus:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
/* Listing1804.java */
import java.io.*;
public class Listing1804
{
public static void main(String[] args)
{
PrintWriter f;
double sum = 0.0;
int nenner;
try {
f = new PrintWriter(
new BufferedWriter(
new FileWriter("zwei.txt")));
for (nenner = 1; nenner <= 1024; nenner *= 2) {
sum += 1.0 / nenner;
f.print("Summand: 1/");
f.print(nenner);
f.print("
Summe: ");
f.println(sum);
}
f.close();
} catch (IOException e) {
Listing1804.java
027
System.out.println("Fehler beim Erstellen der Datei");
028
}
029
}
030 }
Listing 18.4: Die Klasse PrintWriter
Das Programm verwendet Methoden zur Ausgabe von Strings, Ganz- und Fliekommazahlen. Nach Ende des
Programms hat die Datei zwei.txt folgenden Inhalt:
Summand:
Summand:
Summand:
Summand:
Summand:
Summand:
Summand:
Summand:
Summand:
Summand:
Summand:
1/1
Summe: 1
1/2
Summe: 1.5
1/4
Summe: 1.75
1/8
Summe: 1.875
1/16
Summe: 1.9375
1/32
Summe: 1.96875
1/64
Summe: 1.98438
1/128
Summe: 1.99219
1/256
Summe: 1.99609
1/512
Summe: 1.99805
1/1024
Summe: 1.99902
Das vorliegende Beispiel realisiert sogar eine doppelte Schachtelung von Writer-Objekten. Das
Tip
PrintWriter-Objekt schreibt in einen BufferedWriter, der seinerseits in den FileWriter
schreibt. Auf diese Weise werden Datentypen im ASCII-Format gepuffert in eine Textdatei geschrieben.
Eine solche Schachtelung ist durchaus blich in Java und kann in einer beliebigen Tiefe ausgefhrt werden.
FilterWriter
Wie bereits mehrfach angedeutet, bietet die Architektur der Writer-Klassen die Mglichkeit, eigene Filter zu
konstruieren. Dies kann beispielsweise durch berlagern der Klasse Writer geschehen, so wie es etwa bei
PrintWriter oder BufferedWriter realisiert wurde. Der offizielle Weg besteht allerdings darin, die
abstrakte Klasse FilterWriter zu berlagern. FilterWriter besitzt ein internes Writer-Objekt out, das
bei der Instanzierung an den Konstruktor bergeben wird. Zustzlich berlagert es drei der vier writeMethoden, um die Ausgabe auf out umzuleiten. Die vierte write-Methode (write(String)) wird dagegen
nicht berlagert, sondern ruft gem ihrer Implementierung in Writer die Variante write(String, int,
int) auf.
Soll eine eigene Filterklasse konstruiert werden, so ist wie folgt vorzugehen:
Anschlieend kann die neue Filterklasse wie gewohnt in einer Kette von geschachtelten Writer-Objekten
verwendet werden.
Wir wollen uns die Konstruktion einer Filterklasse anhand der folgenden Beispielklasse UpCaseWriter
ansehen, deren Aufgabe es ist, innerhalb eines Streams alle Zeichen in Groschrift zu konvertieren:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
/* Listing1805.java */
import java.io.*;
class UpCaseWriter
extends FilterWriter
{
public UpCaseWriter(Writer out)
{
super(out);
}
public void write(int c)
throws IOException
{
super.write(Character.toUpperCase((char)c));
}
public void write(char[] cbuf, int off, int len)
throws IOException
{
for (int i = 0; i < len; ++i) {
write(cbuf[off + i]);
}
}
public void write(String str, int off, int len)
throws IOException
{
write(str.toCharArray(), off, len);
}
}
public class Listing1805
{
public static void main(String[] args)
{
PrintWriter f;
String s = "und dieser String auch";
try {
f = new PrintWriter(
new UpCaseWriter(
new FileWriter("upcase.txt")));
//Aufruf von auen
f.println("Diese Zeile wird schn gro geschrieben");
//Test von write(int)
f.write('a');
f.println();
//Test von write(String)
f.write(s);
f.println();
//Test von write(String, int, int)
f.write(s,0,17);
f.println();
//Test von write(char[], int, int)
f.write(s.toCharArray(),0,10);
Listing1805.java
058
059
060
061
062
063
064
065 }
f.println();
//--f.close();
} catch (IOException e) {
System.out.println("Fehler beim Erstellen der Datei");
}
}
Im Umfeld dieser Methode sollte man also mit der ntigen Umsicht agieren. Das Gegenstck lowerCase hat
diese Besonderheit brigens nicht.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
18.3 Eingabe-Streams
18.3 Eingabe-Streams
18.3.1 Die abstrakte Klasse Reader
18.3.2 Auswahl des Eingabegertes
InputStreamReader und FileReader
StringReader und CharArrayReader
18.3.3 Schachteln von Eingabe-Streams
BufferedReader
LineNumberReader
FilterReader und PushbackReader
java.io.Reader
Analog zu den write-Methoden von Writer stellt die Klasse Reader eine Reihe von read-Methoden zum
Lesen von Daten zur Verfgung. Die parameterlose Variante liest das nchste Zeichen aus dem Eingabestrom und
liefert es als int, dessen Wert im Bereich von 0 bis 16383 liegt. Ein Rckgabewert von -1 zeigt das Ende des
Eingabestroms an. Die beiden anderen Varianten von read bertragen eine Reihe von Zeichen in das als Parameter
bergebene Array und liefern die Anzahl der tatschlich gelesenen Zeichen als Rckgabewert. Auch hier wird -1
zurckgegeben, wenn das Ende des Streams erreicht ist.
Die Methode ready liefert true, falls der nchste Aufruf von read erfolgen kann, ohne da die Eingabe blockt,
und close schliet den Eingabestrom. Mit Hilfe der Methoden mark und reset gibt es die Mglichkeit, eine
bestimmte Position innerhalb des Eingabe-Streams zu markieren und zu einem spteren Zeitpunkt wieder
anzuspringen. Zuvor mu allerdings durch einen Aufruf von markSupported berprft werden, ob das
Markieren berhaupt untersttzt wird. Ist dies nicht der Fall, wrde ein Aufruf von mark oder reset eine
Ausnahme erzeugen. Die Methode mark merkt sich die aktuelle Leseposition und spezifiziert, wie viele Bytes
anschlieend maximal gelesen werden knnen, bevor die Markierung ungltig wird. Ein Aufruf von reset setzt
den Lesezeiger an die markierte Stelle zurck.
Mit der Methode skip ist es mglich, n Zeichen im Eingabestrom zu berspringen. Dabei kann es aus
verschiedenen Grnden vorkommen, da nicht exakt die angegebene Anzahl an Zeichen bersprungen wird
(beispielsweise, wenn nicht mehr gengend Zeichen vorhanden sind). Der Rckgabewert von skip gibt die
tatschliche Anzahl an.
Bedeutung
InputStreamReader
FileReader
FilterReader
PushbackReader
BufferedReader
LineNumberReader
StringReader
CharArrayReader
PipedReader
Hinweis
java.io.FileReader
Bei der bergabe der Zeichenkette fileName wird die Datei mit dem angegebenen Namen zum Lesen geffnet.
Falls sie nicht vorhanden ist, lst der Konstruktor eine Ausnahme des Typs FileNotFoundException aus. Die
beiden anderen Konstruktoren erwarten ein File-Objekt, das eine zu ffnende Datei spezifiziert, oder ein
FileDescriptor-Objekt, das eine bereits geffnete Datei angibt.
Das folgende Beispiel demonstriert die Anwendung der Klasse FileReader. Das Programm liest die Datei
config.sys und gibt ihren Inhalt auf dem Bildschirm aus:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
/* Listing1806.java */
Listing1806.java
import java.io.*;
public class Listing1806
{
public static void main(String[] args)
{
FileReader f;
int c;
try {
f = new FileReader("c:\\config.sys");
while ((c = f.read()) != -1) {
System.out.print((char)c);
}
f.close();
} catch (IOException e) {
System.out.println("Fehler beim Lesen der Datei");
}
}
}
unterscheidet sich von der Basisklasse Reader nur durch die genderten Konstruktoren:
public StringReader(String s)
java.io.StringReader
java.io.CharArrayReader
Das folgende Programm zeigt die Verwendung der Klasse StringReader am Beispiel eines Programms, das
einen Reader konstruiert, der den Satz liest, der hier an dieser Stelle steht:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
/* Listing1807.java */
Listing1807.java
import java.io.*;
public class Listing1807
{
public static void main(String[] args)
{
Reader f;
int c;
String s;
s = "Das folgende Programm zeigt die Verwendung\r\n";
s += "der Klasse StringReader am Beispiel eines\r\n";
s += "Programms, das einen Reader konstruiert, der\r\n";
s += "den Satz liest, der hier an dieser Stelle steht:\r\n";
try {
f = new StringReader(s);
while ((c = f.read()) != -1) {
System.out.print((char)c);
}
f.close();
} catch (IOException e) {
System.out.println("Fehler beim Lesen des Strings");
}
}
}
java.io.BufferedReader
Eine Textzeile wird dabei durch die Zeichen '\n' oder '\r' oder durch die Folge "\r\n" begrenzt. Der
Rckgabewert von readLine ist ein String, der den Zeileninhalt ohne Begrenzungszeichen enthlt bzw. null,
falls das Ende des Streams erreicht ist. BufferedReader besitzt zwei Konstruktoren:
public BufferedReader(Reader in)
java.io.BufferedReader
Der erste Parameter in ist das Reader-Objekt, auf dem der BufferedReader aufgesetzt werden soll. Der
optionale zweite Parameter sz gibt die Gre des internen Puffers an. Fehlt er, so wird eine fr die meisten
Situationen angemessene Standardeinstellung verwendet.
Das folgende Beispiel demonstriert den Einsatz der Eingabepufferung durch die Erweiterung von Listing 18.6.
Auch hier wird die Datei config.sys eingelesen und auf dem Bildschirm ausgegeben. Durch den Einsatz der Klasse
BufferedReader versucht das Programm, die Perfomance beim Lesen der Datei zu erhhen:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
/* Listing1808.java */
import java.io.*;
public class Listing1808
{
public static void main(String[] args)
{
BufferedReader f;
String line;
try {
f = new BufferedReader(
new FileReader("c:\\config.sys"));
while ((line = f.readLine()) != null) {
System.out.println(line);
}
f.close();
} catch (IOException e) {
System.out.println("Fehler beim Lesen der Datei");
}
}
}
Listing1808.java
java.io.LineNumberReader
Mit getLineNumber wird der aktuelle Stand des Zeilenzhlers abgefragt, mit setLineNumber kann er sogar
verndert werden.
Das folgende Beispiel erweitert unsere bisherigen Programme zur Ausgabe der Datei config.sys in der Weise, da
nun jede einzelne Zeile mit vorangestellter Zeilennummer angezeigt wird. Dazu wird der BufferedReader
durch einen LineNumberReader ersetzt und vor der Ausgabe jeder einzelnen Zeile zunchst die
korrespondierende Zeilennummer ausgegeben:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
/* Listing1809.java */
import java.io.*;
public class Listing1809
{
public static void main(String[] args)
{
LineNumberReader f;
String line;
try {
f = new LineNumberReader(
new FileReader("c:\\config.sys"));
while ((line = f.readLine()) != null) {
System.out.print(f.getLineNumber() + ": ");
System.out.println(line);
}
f.close();
} catch (IOException e) {
System.out.println("Fehler beim Lesen der Datei");
}
}
}
Listing1809.java
3:
4:
5:
6:
7:
DEVICE=c:\windows\emm386.exe noems
SHELL=c:\command.com/e:1536/p
FILES=101
BUFFERS=5
DEVICEHIGH=c:\windows\command\ansi.sys
Ein PushbackReader kann beispielsweise angewendet werden, wenn eine Methode das nchste Eingabezeichen
kennen mu, um zu entscheiden, welche Aktion als nchstes auszufhren ist. Falls die Methode selbst nicht fr die
Behandlung des Eingabezeichens zustndig ist, kann sie das Zeichen an den Eingabestrom zurckgeben, und eine
andere Methode kann mit der Bearbeitung beauftragt werden. Derartige Techniken finden beispielsweise in
Werkzeugen zur lexikalischen Analyse Anwendung, wie sie im Compilerbau verwendet werden.
Die Rckgabe eines Zeichens wird mit Hilfe der Methode unread durchgefhrt. Diese steht in verschiedenen
Varianten zur Verfgung und kann zur Rckgabe eines einzelnen Zeichens oder mehrerer Zeichen verwendet
werden:
java.io.PushbackReader
Hierbei mu das oder die zurckzugebenden Zeichen als Parameter unread bergeben werden.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
19.2 Ausgabe-Streams
19.2 Ausgabe-Streams
19.2.1 Die Basisklasse OutputStream
19.2.2 Aus OutputStream direkt abgeleitete Klassen
FileOutputStream
ByteArrayOutputStream
ObjectOutputStream
PipedOutputStream
19.2.3 Aus FilterOutputStream abgeleitete Klassen
BufferedOutputStream
PrintStream
DataOutput und DataOutputStream
Komprimieren von Dateien
Berechnung von Prfsummen
java.io.OutputStream
Der parameterlose Konstruktor initialisiert einen OutputStream. Er ist protected und wird in abgeleiteten
Klassen berlagert. Mit close wird der OutputStream geschlossen, und flush schreibt die gepufferten Daten
physikalisch auf das Ausgabegert und leert alle Puffer.
Die write-Methoden erwarten Bytes oder Byte-Arrays als Daten. Wird ein byte-Array angegeben, so gibt die
Klasse es vollstndig aus, wenn nicht zustzlich ein Arrayoffset und die Anzahl der zu schreibenden Bytes
angegeben wird. Die Methode zum Schreiben eines einzelnen Bytes erwartet ein int als Argument, gibt aber
lediglich seine unteren 8 Bit aus und ignoriert alle brigen.
FileOutputStream
Der FileOutputStream stellt einen Byte-Stream zur Ausgabe in eine Datei zur Verfgung. Er besitzt folgende
Konstruktoren:
public FileOutputStream(String name)
throws FileNotFoundException
java.io.FileOutputStream
Wird lediglich ein Dateiname angegeben, legt ein FileOutputStream die gewnschte Ausgabedatei neu an und
setzt den Dateizeiger auf den Anfang der Datei. Wird der zweite Konstruktor verwendet und true als zweites
Argument bergeben, wird der Dateizeiger auf das Ende der Datei positioniert, falls diese bereits existiert.
Andernfalls entspricht das Verhalten dem des ersten Konstruktors. Der dritte Konstruktor entspricht dem ersten,
erwartet aber ein File-Objekt anstelle eines Strings.
Das folgende Programm zeigt beispielhaft die Anwendung eines FileOutputStream. Es legt die in der
Kommandozeile angegebene Datei an (bzw. springt zu ihrem Ende, falls sie bereits vorhanden ist) und hngt 256
Bytes mit den Werten 0 bis 255 an.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
/* Listing1901.java */
Listing1901.java
import java.io.*;
public class Listing1901
{
public static void main(String[] args)
{
try {
FileOutputStream out = new FileOutputStream(
args[0],
true
);
for (int i = 0; i < 256; ++i) {
out.write(i);
}
out.close();
} catch (Exception e) {
System.err.println(e.toString());
System.exit(1);
}
}
}
public ByteArrayOutputStream()
public ByteArrayOutputStream(int size)
java.io.ByteArrayOutputStream
Der parameterlose Konstruktor legt ein Ausgabearray mit einer anfnglichen Gre von 32 Byte an, der andere
erlaubt die freie Vorgabe der initialen Puffergre.
ObjectOutputStream
Ein ObjectOutputStream erlaubt es, primitive Datentypen und komplette Objekte (inklusive aller
referenzierten Objekte) auszugeben. Zwar ist er nicht von FilterOutputStream abgeleitet, wird aber ebenso
verwendet und erwartet im Konstruktor einen OutputStream zur Weiterleitung der Ausgabedaten. Die Klasse
ObjectOutputStream ist eine der Sulen des Serialisierungs-APIs in Java. Sie wird in Abschnitt 41.1.2
ausfhrlich beschrieben.
PipedOutputStream
Ein PipedOutputStream dient zusammen mit einem PipedInputStream zur Kommunikation zweier
Threads. Beide zusammen implementieren eine Message Queue, in die einer der beiden Threads seine Daten
hineinschreibt, whrend der andere sie daraus liest. Ein ausfhrliches Beispiel zur Anwendung der beiden Klassen
findet sich in Abschnitt 22.4.4.
java.io.FilterOutputStream
Die Aufgabe der aus FilterOutputStream abgeleiteten Klassen besteht darin, die Schreibzugriffe abzufangen,
in einer fr sie charakteristischen Weise zu verarbeiten und dann an das eigentliche Ausgabegert (den im
Konstruktor bergebenen OutputStream) weiterzuleiten.
BufferedOutputStream
BufferedOutputStream puffert die Ausgabe in einen OutputStream. Er kann insbesondere dann die
Ausgabe beschleunigen, wenn viele einzelne write-Aufrufe erfolgen, die jeweils nur wenig Daten bergeben. Ein
BufferedOutputStream besitzt zwei zustzliche Konstruktoren und eine Methode flush, die dafr sorgt,
da die gepufferten Daten tatschlich geschrieben werden:
public BufferedOutputStream(OutputStream out)
public BufferedOutputStream(OutputStream out, int size)
java.io.BufferedOutputStream
PrintStream
Ein PrintStream bietet die Mglichkeit, Strings und primitive Typen im Textformat auszugeben. Er stellt eine
Vielzahl von print- und println-Methoden fr unterschiedliche Datentypen zur Verfgung. Seine Schnittstelle
und Anwendung entspricht der Klasse PrintWriter, die in Abschnitt 18.2.3 beschrieben wurde.
Im Zuge der Internationalisierung des JDK wurden mit der Version 1.1 die ffentlichen
JDK1.1-1.4
Konstruktoren der Klasse PrintStream als deprecated markiert (wegen der mglicherweise
unzulnglichen Konvertierung zwischen Bytes und Zeichen). Damit war die Klasse praktisch nicht
mehr verwendbar. Insbesondere war es nicht mehr mglich, die Methoden setOut und setErr
der Klasse System sinnvoll zu verwenden (siehe Abschnitt 16.3.2). Spter wurde die Entscheidung
als falsch angesehen und mit dem JDK 1.2 revidiert. Seither sind die Konstruktoren wieder zulssig.
Einen einfach anzuwendenden Workaround, der die deprecated-Warnungen im JDK 1.1
vermeidet, gibt es leider nicht.
DataOutput und DataOutputStream
Ein DataOutputStream ermglicht es, primitive Datentypen in definierter (und portabler) Weise auszugeben.
So geschriebene Daten knnen mit Hilfe eines DataInputStream wieder eingelesen werden. Ein
DataOutputStream implementiert das Interface DataOutput, das folgende Methoden enthlt:
void write(int b)
throws IOException
void write(byte[] b)
throws IOException
void write(byte[] b, int off, int len)
throws IOException
void writeBoolean(boolean v)
throws IOException
void writeByte(int v)
throws IOException
void writeShort(int v)
throws IOException
void writeChar(int v)
throws IOException
void writeInt(int v)
throws IOException
void writeLong(long v)
throws IOException
void writeFloat(float v)
throws IOException
void writeDouble(double v)
throws IOException
void writeBytes(String s)
throws IOException
void writeChars(String s)
throws IOException
void writeUTF(String str)
throws IOException
java.io.DataOutput
Zu jeder einzelnen Methode ist in der JDK-Dokumentation genau angegeben, auf welche Weise der jeweilige
Datentyp ausgegeben wird. Dadurch ist garantiert, da eine mit DataOutputStream geschriebene Datei auf
jedem anderen Java-System mit einem DataInputStream lesbar ist. Die Beschreibungen sind sogar so genau,
da Interoperabilitt mit Nicht-Java-Systemen erreicht werden kann, wenn diese in der Lage sind, die primitiven
Typen in der beschriebenen Weise zu verarbeiten.
Eine Sonderstellung nimmt die Methode writeUTF ein. Sie dient dazu, die 2 Byte langen UNICODE-Zeichen, mit
denen Java intern arbeitet, in definierter Weise in 1, 2 oder 3 Byte lange Einzelzeichen zu verwandeln. Hat das
Zeichen einen Wert zwischen \u0000 und \u007F, wird es als Einzelbyte ausgeben. Hat es einen Wert zwischen
\u0080 und \u07FF, belegt es zwei Byte, und in allen anderen Fllen werden drei Byte verwendet. Diese
Darstellung wird als UTF-8-Codierung bezeichnet und ist entsprechend Tabelle 19.1 implementiert. Zustzlich
werden an den Anfang jedes UTF-8-Strings zwei Lngenbytes geschrieben.
Von
Bis
Byte
Darstellung
\u0000
\u007F
0nnnnnnn
\u0080
\u07FF
110nnnnn 10nnnnnn
\u0800
\uFFFF
Listing1902.java
/* Listing1902.java */
import java.io.*;
public class Listing1902
{
public static void main(String[] args)
{
try {
DataOutputStream out = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream("test.txt")));
out.writeInt(1);
out.writeInt(-1);
out.writeDouble(Math.PI);
out.writeUTF("hliches");
out.writeUTF("Entlein");
out.close();
} catch (IOException e) {
System.err.println(e.toString());
}
}
}
........@.!.TD-.
..h....liches..E
ntlein
Die nchsten 4 Byte reprsentieren die -1 (es wird die bliche Zweierkomplementdarstellung verwendet).
Anschlieend folgen 8 Byte mit der double-Darstellung der Zahl pi.
Nun folgt die Lngeninformation 000B des ersten UTF-8-Strings und danach dessen 11 Zeichen. Man kann
sehen, da die beiden Umlaute und jeweils zwei Byte belegen, alle brigen Zeichen jedoch nur eines.
Schlielich folgt der zweite UTF-8-String. Er hat die Lnge 7, und alle Zeichen werden mit einem Byte
dargestellt.
DataOutput wird nicht nur von DataOutputStream implementiert, sondern auch von der Klasse
RandomAccessFile, die darber hinaus das Interface DataInput implementiert. Sollen primitive
Daten wahlweise seriell oder wahlfrei verarbeitet werden, ist es daher sinnvoll, die serielle Verarbeitung mit
Hilfe der Klassen DataOutputStream und DataInputStream vorzunehmen. Die Verarbeitung von
Random-Access-Dateien wird in Kapitel 20 behandelt.
Tip
Das ZIP-Format stammt aus der PC-Welt, ist aber mittlerweile unter praktisch allen Betriebssystemen
verfgbar. Es kann mehrere Dateien komprimieren und in einem Archiv zusammenfassen.
Das GZIP-Format stammt aus der GNU-Welt und kann lediglich eine einzige Datei komprimieren. Zur
Erstellung von Archiven, die mehrere Dateien enthalten, wird es meist zusammen mit tar verwendet.
Das JAR-Format entspricht dem ZIP-Format, beinhaltet aber zustzlich noch Manifest-Dateien, die
Metainformationen ber die gespeicherten Dateien enthalten.
Das folgende Listing zeigt, wie mehrere Dateien mit Hilfe eines ZipOutputStream komprimiert und in eine
gemeinsame Archivdatei geschrieben werden. Es wird als Kommandozeilenprogramm aufgerufen und erwartet die
Namen der zu erstellenden Archivdatei und der Eingabedateien als Argumente. Das erzeugte Archiv kann mit jar,
winzip oder pkunzip ausgepackt werden.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
/* Zip.java */
import java.io.*;
import java.util.zip.*;
public class Zip
{
public static void main(String[] args)
{
if (args.length < 2) {
System.out.println("Usage: java Zip zipfile files...");
System.exit(1);
}
try {
byte[] buf = new byte[4096];
ZipOutputStream out = new ZipOutputStream(
new FileOutputStream(args[0]));
for (int i = 1; i < args.length; ++i) {
String fname = args[i];
System.out.println("adding " + fname);
FileInputStream in = new FileInputStream(fname);
out.putNextEntry(new ZipEntry(fname));
int len;
Zip.java
024
while ((len = in.read(buf)) > 0) {
025
out.write(buf, 0, len);
026
}
027
in.close();
028
}
029
out.close();
030
} catch (IOException e) {
031
System.err.println(e.toString());
032
}
033
}
034 }
Listing 19.3: Erstellen eines ZIP-Archivs
Bitte beachten Sie, da das Programm nur die Grundzge des Erstellens von ZIP-Dateien demonstriert.
Insbesondere verzichtet es darauf, die verschiedenen Eigenschaften des jeweiligen ZIP-Eintrags korrekt
zu setzen (Gre, Datum/Uhrzeit, Prfsumme etc.). Um dies zu tun, mten die entsprechenden Daten
ermittelt und dann an die jeweiligen set-Methoden des ZipEntry-Objekts bergeben werden.
Hinweis
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Das Java-Farbmodell basiert auf dem RGB-Farbmodell. Dieses stellt Farben mit 24
Bit Tiefe dar und setzt jede Farbe aus einer Mischung der drei Grundfarben Rot,
Grn und Blau zusammen. Da jede dieser Grundfarben einen Anteil von 0 bis 255
haben kann und so mit jeweils 8 Datenbits darstellbar ist, ergibt sich eine gesamte
Farbtiefe von 24 Bit. Ein Wert von 0 fr eine der Grundfarben bedeutet dabei, da
diese Grundfarbe nicht in das Ergebnis eingeht, ein Wert von 255 zeigt die maximale
Intensitt dieser Farbe an. RGB-Farben werden blicherweise durch ganzzahlige
Tripel (r,g,b) dargestellt, die den Anteil an der jeweiligen Grundfarbe in der
Reihenfolge Rot, Grn und Blau darstellen. Tabelle 25.1 listet einige gebruchliche
Farben und ihre RGB-Werte auf:
Farbe
Rot-Anteil
Grn-Anteil
Blau-Anteil
Wei
255
255
255
Schwarz
Grau
127
127
127
Rot
255
Grn
255
Blau
255
Yellow
255
255
Magenta
255
255
Cyan
255
255
<
>
>>
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
29.1 Window-Events
29.1 Window-Events
Ein Window-Event wird immer dann generiert, wenn sich am Status eines Fensters eine nderung ergeben hat,
die fr das Anwendungsprogramm von Interesse sein knnte. So erlangt das Programm beispielsweise Kenntnis
davon, wenn ein Fenster erstellt oder zerstrt, aktiviert oder deaktiviert oder wenn es symbolisiert oder
wiederhergestellt wird.
Ein Empfnger fr Window-Events mu das Interface WindowListener implementieren und bekommt
Events des Typs WindowEvent bergeben. WindowEvent erweitert die Klasse ComponentEvent und
stellt neben getID und getSource die Methode getWindow zur Verfgung, mit der das Fenster ermittelt
werden kann, das die Nachricht ausgelst hat.
public Window getWindow()
java.awt.event.WindowEvent
Die Registrierung der Empfngerklasse erfolgt mit der Methode addWindowListener, die in den Klassen
Dialog und Frame zur Verfgung steht:
public void addWindowListener(WindowListener l)
java.awt.Frame
Tabelle 29.1 gibt eine bersicht der Methoden von WindowListener und erklrt ihre Bedeutung:
Ereignismethode
Bedeutung
windowActivated
Das Fenster wurde aktiviert. Diese Methode wird nach dem Erstellen des Fensters
aufgerufen und wenn ein Fenster, das im Hintergrund stand, erneut in den
Vordergrund gelangt.
windowClosed
windowClosing
Das Fenster soll geschlossen werden. Diese Methode wird aufgerufen, wenn der
Anwender das Fenster ber die Titelleiste, das Systemmen oder die
Tastenkombination [ALT]+[F4] schlieen will. Es liegt in der Verantwortung
der Anwendung, in diesem Fall Code zur Verfgung zu stellen, der das Fenster
tatschlich schliet. Standardmig reagiert das Programm nicht auf diese
Benutzeraktionen.
windowDeactivated
windowDeiconified
windowIconified
windowOpened
/* Listing2901.java */
import java.awt.*;
import java.awt.event.*;
class CloseableFrame
extends Frame
{
public CloseableFrame()
{
this("");
}
public CloseableFrame(String title)
{
super(title);
addWindowListener(
new WindowAdapter() {
public void windowClosing(WindowEvent event)
{
setVisible(false);
dispose();
}
}
);
}
}
public class Listing2901
{
public static void main(String[] args)
{
CloseableFrame wnd = new CloseableFrame("CloseableFrame");
wnd.setBackground(Color.lightGray);
wnd.setSize(300,200);
wnd.setLocation(200,100);
wnd.setVisible(true);
wnd.addWindowListener(
new WindowAdapter() {
public void windowClosed(WindowEvent event)
Listing2901.java
041
{
042
System.out.println("terminating program...");
043
System.exit(0);
044
}
045
}
046
);
047
}
048 }
Listing 29.1: Die Klasse CloseableFrame
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Jeder JDBC-Treiber hat einen statischen Initialisierer, der beim Laden der Klasse
aufgerufen wird. Seine Aufgabe besteht darin, sich beim Treibermanager zu registrieren,
um bei spteren Verbindungsanfragen von diesem angesprochen werden zu knnen. Das
Laden der Treiberklasse wird blicherweise durch Aufruf der Methode forName der
Klasse Class erledigt (siehe Abschnitt 43.2.2). Um einen Treiber zu laden, mu man also
seinen vollstndigen Klassennamen kennen:
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
sun.jdbc.odbc.JdbcOdbcDriver ist der Name der JDBC-ODBC-Bridge, mit der
die oben erwhnten Typ-1-Treiber realisiert werden. Die Namen alternativer Treiber sind
der Dokumentation des jeweiligen Herstellers zu entnehmen.
Nachdem der Treiber geladen wurde, kann er dazu verwendet werden, eine Verbindung zu
einer Datenbank aufzubauen. Dazu wird an die statische Methode getConnection der
Klasse DriverManager ein String und eventuell weitere Parameter bergeben, um den
Treibertyp, die Datenbank und ntigenfalls weitere Informationen festzulegen.
getConnection gibt es in drei Ausprgungen:
static Connection getConnection(
String url
)
java.sql.DriverManager
Die erste Variante erwartet lediglich einen Connection-String als Argument, der in Form
eines URL (Uniform Ressource Locator, siehe Abschnitt 40.1.1) bergeben wird. Der
Connection-String besteht aus mehreren Teilen, die durch Doppelpunkte voneinander
getrennt sind. Der erste Teil ist immer "jdbc" und zeigt an, da es sich um einen JDBCURL handelt. Der zweite Teil wird als Sub-Protokoll bezeichnet und gibt an, welcher
Treiber verwendet werden soll. Die brigen Teile sind treiberspezifisch. ConnectionStrings fr die JDBC-ODBC-Bridge beginnen immer mit "jdbc:odbc", gefolgt von einem
weiteren Doppelpunkt, nach dem der Name der ODBC-Datenquelle angegeben wird:
con = DriverManager.getConnection("jdbc:odbc:DirDB");
Die zweite Variante von getConnection erlaubt es, zustzlich den Benutzernamen und
das Passwort an die Datenbank zu bergeben. Das ist bei vielen Datenbanken erforderlich,
um eine Verbindung aufbauen zu knnen. Bei der dritten Variante knnen zustzlich mit
Hilfe eines Properties-Objekts weitere, treiberspezifische Informationen bergeben
werden. Welche Variante zu verwenden ist, mu der jeweiligen Treiberdokumentation
entnommen werden.
Falls die Datenbank nicht geffnet werden konnte, lst getConnection eine Hinweis
Ausnahme des Typs SQLException aus. Diese Ausnahme wird auch von fast
allen anderen Methoden und Klassen verwendet, um einen Fehler beim Zugriff
auf die Datenbank anzuzeigen.
Wenn die Verbindung erfolgreich aufgebaut werden konnte, liefert getConnection ein
Objekt, das das Interface Connection implementiert. Dieses Verbindungsobjekt
reprsentiert die aktuelle Datenbanksitzung und dient dazu, Anweisungsobjekte zu
erzeugen und globale Einstellungen an der Datenbank zu verndern. Das ConnectionObjekt kann durch Aufruf von close explizit geschlossen werden. Die Verbindung wird
automatisch geschlossen, wenn die Connection-Variable vom Garbage Collector
zerstrt wird.
java.sql.Connection
Die einfachste Form ist dabei das von createStatement erzeugte StatementObjekt. Es kann dazu verwendet werden, unparametrisierte Abfragen und nderungen der
Datenbank zu erzeugen. Seine beiden wichtigsten Methoden sind executeQuery und
executeUpdate. Sie erwarten einen SQL-String als Argument und reichen diesen an
die Datenbank weiter. Zurckgegeben wird entweder ein einfacher numerischer
Ergebniswert, der den Erfolg der Anweisung anzeigt, oder eine Menge von
Datenbankstzen, die das Ergebnis der Abfrage reprsentieren. Auf die beiden brigen
Anweisungstypen werden wir spter zurckkommen.
42.2.3 Datenbankabfragen
Hat man ein Statement-Objekt beschafft, kann dessen Methode executeQuery
verwendet werden, um Daten aus der Datenbank zu lesen:
public ResultSet executeQuery(String sql)
throws SQLException
java.sql.Statement
Die Methode erwartet einen SQL-String in Form einer gltigen SELECT-Anweisung und
gibt ein Objekt vom Typ ResultSet zurck, das die Ergebnismenge reprsentiert. Als
Argument drfen beliebige SELECT-Anweisungen bergeben werden, sofern sie fr die
zugrunde liegende Datenbank gltig sind. Die folgende SQL-Anweisung selektiert
beispielsweise alle Stze aus der Tabelle dir, deren Feld did den Wert 7 hat:
SELECT * FROM dir WHERE did = 7
Das zurckgegebene Objekt vom Typ ResultSet besitzt eine Methode next, mit der
die Ergebnismenge schrittweise durchlaufen werden kann:
boolean next()
java.sql.ResultSet
Nach dem Aufruf von executeQuery steht der Satzzeiger zunchst vor dem ersten
Element, jeder Aufruf von next positioniert ihn auf das nchste Element. Der
Rckgabewert gibt an, ob die Operation erfolgreich war. Ist er false, gibt es keine
weiteren Elemente in der Ergebnismenge. Ist er dagegen true, konnte das nchste
Element erfolgreich ausgewhlt werden, und mit Hilfe verschiedener get...-Methoden
kann nun auf die einzelnen Spalten zugegriffen werden. Jede dieser Methoden steht in
zwei unterschiedlichen Varianten zur Verfgung:
Wird ein numerischer Wert n als Argument bergeben, so wird dieser als
Spaltenindex interpretiert und der Wert der n-ten Spalte zurckgegeben. Wichtig:
Anders als bei Arrays hat die erste Spalte den Index 1.
Wird ein String als Argument bergeben, so wird er als Name interpretiert und
der Wert der Spalte mit diesem Namen zurckgegeben. Diese Variante soll zwar
marginal langsamer als die erste sein, ist aber weniger fehlertrchtig. Da der Aufruf
nicht mehr von der Spaltenreihenfolge der Abfrage abhngt, ist ihr normalerweise
der Vorzug zu geben (das klappt leider nicht immer, wie die Warnung am Ende von
Abschnitt 42.4.5 deutlich macht).
Um dem Entwickler lstige Typkonvertierungen zu ersparen, gibt es alle getXXXMethoden in unterschiedlichen Typisierungen. So liefert beispielsweise getString das
gewnschte Feld als String, whrend getInt es als int zurckgibt. Wo es mglich
und sinnvoll ist, werden automatische Typkonvertierungen durchgefhrt; getString
kann beispielsweise fr nahezu alle Typen verwendet werden. Tabelle 42.1 gibt eine
bersicht ber die wichtigsten get-Methoden der Klasse ResultSet. In Tabelle 42.4
findet sich eine bersicht der wichtigsten SQL-Datentypen.
Rckgabewert
Methodenname
boolean
getBoolean
byte
getByte
byte[]
getBytes
Date
getDate
double
getDouble
float
getFloat
int
getInt
long
getLong
short
getShort
String
getString
Time
getTime
Timestamp
getTimestamp
Soll festgestellt werden, ob eine Spalte den Wert NULL hatte, so kann das nach Hinweis
dem Aufruf der get-Methode durch Aufruf von wasNull abgefragt werden.
wasNull gibt genau dann true zurck, wenn die letzte abgefragte Spalte
einen NULL-Wert als Inhalt hatte. Bei allen Spalten, die NULL-Werte enthalten
knnen, mu diese Abfrage also erfolgen. Bei den get-Methoden, die ein
Objekt als Ergebniswert haben, geht es etwas einfacher. Hier wird null
zurckgegeben, wenn der Spaltenwert NULL war.
42.2.4 Datenbanknderungen
Datenbanknderungen werden mit den SQL-Anweisungen INSERT INTO, UPDATE oder
DELETE FROM oder den SQL-DDL-Anweisungen (Data Definition Language) zum
ndern der Datenbankstruktur durchgefhrt. Im Gegensatz zu Datenbankabfragen geben
diese Anweisungen keine Ergebnismenge zurck, sondern lediglich einen einzelnen Wert.
Im Falle von INSERT INTO, UPDATE und DELETE FROM gibt dieser Wert an, wie
viele Datenstze von der nderung betroffen waren, bei DDL-Anweisungen ist er immer
0.
Um solche Anweisungen durchzufhren, stellt das Interface Statement die Methode
executeUpdate zur Verfgung:
public int executeUpdate(String sql)
throws SQLException
java.sql.Statement
Auch sie erwartet als Argument einen String mit einer gltigen SQL-Anweisung,
beispielsweise:
INSERT INTO dir VALUES (1, 'x.txt', 0)
Knnte diese Anweisung erfolgreich ausgefhrt werden, wrde sie 1 zurckgeben.
Andernfalls wrde eine SQLException ausgelst.
java.sql.SQLException
int getErrorCode()
String getSQLState()
SQLException getNextException()
Titel
<<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
In diesem Abschnitt wollen wir eine Reihe von Themen ansprechen, die bei der bisherigen
Darstellung zu kurz gekommen sind. Aufgrund des beschrnkten Platzes werden wir jedes Thema
allerdings nur kurz ansprechen und verweisen fr genauere Informationen auf die JDBC-Beschreibung
(sie ist Bestandteil der seit dem JDK 1.2 ausgelieferten Online-Dokumentation) und auf
weiterfhrende Literatur zum Thema JDBC.
42.4.1 Metadaten
In Abschnitt 42.3.3 sind wir bereits kurz auf die Verwendung von Metadaten eingegangen. Neben den
Datenbankmetadaten gibt es die Methode getMetaData der Klasse ResultSet:
ResultSetMetaData getMetaData()
java.sql.ResultSet
Sie liefert ein Objekt vom Typ ResultSetMetaData, das Meta-Informationen ber die
Ergebnismenge zur Verfgung stellt. Wichtige Methoden sind:
int getColumnCount()
String getColumnName(int column)
String getTableName(int column)
int getColumnType(int column)
java.sql.ResultSetMetaData
Mit getColumnCount kann die Anzahl der Spalten in der Ergebnismenge abgefragt werden.
getColumnName und getTableName liefern den Namen der Spalte bzw. den Namen der Tabelle,
zu der diese Spalte in der Ergebnismenge gehrt, wenn man ihren numerischen Index angibt. Mit
getColumnType kann der Datentyp einer Spalte abgefragt werden. Als Ergebnis wird eine der
statischen Konstanten aus der Klasse java.sql.Types zurckgegeben.
42.4.2 Escape-Kommandos
Mit den Escape-Kommandos wurde ein Feature eingefhrt, das die Portierbarkeit von
Datenbankanwendungen verbessern soll. In Anlehnung an ODBC fordert die JDBC-Spezifikation
dazu, da die JDBC-Treiber in der Lage sein mssen, besondere Zeichenfolgen in SQL-Anweisungen
zu erkennen und in die spezifische Darstellung der jeweiligen Datenbank zu bersetzen. Auf diese
Weise knnen beispielsweise Datums- und Zeitliterale portabel bergeben oder eingebaute Funktionen
aufgerufen werden. Die Escape-Kommandos haben folgende Syntax:
"{" <Kommandoname> [<Argumente>] "}"
Am Anfang steht eine geschweifte Klammer, dann folgen der Name des Escape-Kommandos und
mgliche Argumente, und am Ende wird das Kommando durch eine weitere geschweifte Klammer
abgeschlossen.
Um beispielsweise unabhngig von seiner konkreten Darstellung einen Datumswert einzufgen, kann
das Escape-Kommando "d" verwendet werden. Es erwartet als Argument eine SQL-Zeichenkette im
Format "yyyy-mm-dd" und erzeugt daraus das zur jeweiligen Datenbank passende Datumsliteral. In
Listing 42.5 haben wir dieses Kommando verwendet, um das nderungsdatum der Datei in die
Tabelle file zu schreiben.
42.4.3 Transaktionen
Die drei Methoden commit, rollback und setAutoCommit des Connection-Objekts steuern
das Transaktionsverhalten der Datenbank:
void commit()
java.sql.Connection
void rollback()
void setAutoCommit(boolean autoCommit)
Nach dem Aufbauen einer JDBC-Verbindung ist die Datenbank (gem JDBC-Spezifikation)
zunchst im Auto-Commit-Modus. Dabei gilt jede einzelne Anweisung als separate Transaktion, die
nach Ende des Kommandos automatisch besttigt wird. Durch Aufruf von setAutoCommit und
bergabe von false kann das gendert werden. Danach mssen alle Transaktionen explizit durch
Aufruf von commit besttigt bzw. durch rollback zurckgesetzt werden. Nach dem Abschlu
einer Transaktion beginnt automatisch die nchste.
Wichtig ist auch der Transaction Isolation Level, mit dem der Grad der Parallelitt von
Datenbanktransaktionen gesteuert wird. Je hher der Level, desto weniger Konsistenzprobleme
knnen durch gleichzeitigen Zugriff mehrerer Transaktionen auf dieselben Daten entstehen. Um so
geringer ist aber auch der Durchsatz bei einer groen Anzahl von gleichzeitigen Zugriffen.
Transaction Isolation Levels werden von der Datenbank blicherweise mit Hilfe von gemeinsamen
und exklusiven Sperren realisiert. JDBC untersttzt die folgenden Levels:
Connection.TRANSACTION_NONE
Connection.TRANSACTION_READ_UNCOMMITTED
Connection.TRANSACTION_READ_COMMITTED
Connection.TRANSACTION_REPEATABLE_READ
Connection.TRANSACTION_SERIALIZABLE
java.sql.Connection
Mit der Methode supportsTransactionIsolationLevel des DatabaseMetaDataObjekts kann abgefragt werden, ob eine Datenbank einen bestimmten Transaction Isolation Level
untersttzt oder nicht.
42.4.4 JDBC-Datentypen
In den meisten Fllen braucht man keine exakte Kenntnis des Datentyps einer Tabellenspalte, wenn
man diese abfragt. Die oben beschriebenen get-Methoden des ResultSet-Objekts fhren
geeignete Konvertierungen durch. Soll dagegen mit CREATE TABLE eine neue Datenbank definiert
werden, mu zu jeder Spalte der genaue Datentyp angegeben werden. Leider unterscheiden sich die
Datenbanken bezglich der untersttzten Typen erheblich, und die CREATE TABLE-Anweisung ist
wenig portabel. Die Klasse java.sql.Types listet alle JDBC-Typen auf und gibt fr jeden eine
symbolische Konstante an. Mit der Methode getTypeInfo der Klasse DatabaseMetaData kann
ein ResultSet mit allen Typen der zugrunde liegenden Datenbank und ihren spezifischen
Eigenschaften beschafft werden. In Tabelle 42.4 findet sich eine bersicht der wichtigsten SQLDatentypen.
Frage, woher sie das dafr erforderliche Statement-Objekt nehmen soll. Wird es jedesmal lokal
angelegt, kann schnell der Speicher knapp werden. Wird es dagegen als statische oder als
Klassenvariable angelegt, kann es zu Konflikten mit konkurrierenden Methoden kommen (die
blichen Probleme globaler Variablen).
Eine gut funktionierende Lsung fr dieses Problem besteht darin, Statement-Objekte auf der
Connection zu cachen, also zwischenzuspeichern. Das kann etwa mit einer Queue erfolgen, in die
nicht mehr bentigte Statement-Objekte eingestellt werden. Anstelle eines Aufrufs von
createStatement wird dann zunchst in der Queue nachgesehen, ob ein recyclebares Objekt
vorhanden ist, und dieses gegebenenfalls wiederverwendet. Es sind dann zu keinem Zeitpunkt mehr
Statement-Objekte angelegt, als parallel bentigt werden. Natrlich drfen nur Objekte in die
Queue gestellt werden, die nicht mehr bentigt werden; ihr ResultSet sollte also vorher mglichst
geschlossen werden. Das Statement-Objekt selbst darf nicht geschlossen werden, wenn es noch
verwendet werden soll.
Ein einfache Implementierung wird in dem folgendem Listing vorgestellt. Das Objekt vom Typ
CachedConnection wird mit einem Connection-Objekt instanziert. Die Methoden
getStatement und releaseStatement dienen dazu, Statement-Objekte zu beschaffen
bzw. wieder freizugeben. Die Klasse LinkedQueue wurde in Abschnitt 15.4 vorgestellt.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
/* CachedConnection.java */
import java.sql.*;
import java.util.*;
public class CachedConnection
{
private Connection con;
private LinkedQueue cache;
private int
stmtcnt;
public CachedConnection(Connection con)
{
this.con
= con;
this.cache
= new LinkedQueue();
this.stmtcnt = 0;
}
public Statement getStatement()
throws SQLException
{
if (cache.size() <= 0) {
return con.createStatement();
} else {
return (Statement)cache.retrieve();
}
}
public void releaseStatement(Statement statement)
CachedConnection.java
030
{
031
cache.add(statement);
032
}
033 }
Listing 42.11: Die Klasse CachedConnection
Es ist wichtig, die JDBC-Objekte auch dann zu schlieen, wenn eine Ausnahme whrend der
Bearbeitung aufgetreten ist. Andernfalls wrden mglicherweise Ressourcen nicht freigegeben und
das Programm wrde so nach und nach mehr Speicher oder Rechenzeit verbrauchen. Am einfachsten
kann dazu die finally-Klausel der try-catch-Anweisung verwendet werden.
Eine weitere Eigenschaft der Klasse ResultSet verdient besondere Beachtung. Bei
Warnung
manchen JDBC-Treibern erlauben die zurckgegebenen ResultSet-Objekte das Lesen
einer bestimmten Tabellenspalte nur einmal. Der zweite Versuch wird mit einer
Fehlermeldung "No data" (oder hnlich) quittiert. Manche Treiber erfordern sogar, da
die Spalten des ResultSet in der Reihenfolge ihrer Definition gelesen werden. In
beiden Fllen ist es gefhrlich, einen ResultSet als Parameter an eine Methode zu
bergeben, denn die Methode wei nicht, welche Spalten bereits vom Aufrufer gelesen
wurden und umgekehrt. Eine Lsung knnte darin bestehen, einen ResultSet mit
integriertem Cache zu entwickeln, der sich bereits gelesene Spaltenwerte merkt.
Alternativ knnte man auch einen objekt-relationalen Ansatz versuchen, bei dem jeder
gelesene Satz der Ergebnismenge direkt ein passendes Laufzeitobjekt erzeugt, das dann
beliebig oft gelesen werden kann. Wir wollen auf beide Varianten an dieser Stelle nicht
weiter eingehen.
java.sql.Connection
Als Argument wird ein String bergeben, der die gewnschte SQL-Anweisung enthlt. Die formalen
Parameter werden durch Fragezeichen dargestellt. Bei den meisten Datenbanken drfen sowohl
nderungs- als auch Abfrageanweisungen vorkompiliert werden. Sie werden dann spter mit
java.sql.PreparedStatement
Bevor eine dieser Methoden aufgerufen werden darf, ist es erforderlich, die vorkompilierte Anweisung
zu parametrisieren. Dazu mu fr jedes Fragezeichen eine passende set-Methode aufgerufen und das
gewnschte Argument bergeben werden. Die set-Methoden gibt es fr alle JDBC-Typen (siehe
beispielsweise die analoge Liste der get-Methoden in Tabelle 42.1):
public void setBoolean(int parameterIndex, boolean x) java.sql.PreparedStatement
throws SQLException
public void setByte(int parameterIndex, byte x)
throws SQLException
...
Der erste Parameter gibt die Position des Arguments in der Argumentliste an. Das erste Fragezeichen
hat den Index 1, das zweite den Index 2 usw. Der zweite Parameter liefert den jeweiligen Wert, der
anstelle des Fragezeichens eingesetzt werden soll.
Als Beispiel wollen wir uns eine abgewandelte Form der in Abschnitt 42.3.5 vorgestellten Methode
countRecords ansehen, bei der anstelle eines Statement-Objekts ein PreparedStatement
verwendet wird:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
Warnung
42.4.7 SQL-Kurzreferenz
Dieser Abschnitt gibt eine kurze bersicht der gebruchlichsten SQL-Anweisungen in ihren
grundlegenden Ausprgungen. Er ersetzt weder ein Tutorial noch eine Referenz und ist zu keinem der
bekannten SQL-Standards vollstndig kompatibel. Trotzdem mag er fr einfache Experimente
ntzlich sein und helfen, die ersten JDBC-Anbindungen zum Laufen zu bringen. Fr "ernsthafte"
Datenbankanwendungen sollte zustzliche Literatur konsultiert und dabei insbesondere auf die
Spezialitten der verwendeten Datenbank geachtet werden.
Die nachfolgenden Syntaxbeschreibungen sind an die bei SQL-Anweisungen bliche Backus-NaurForm angelehnt:
TabName, ColName und IndexName sind SQL-Bezeichner. ConstExpr ist ein konstanter Ausdruck,
der einen Standardwert fr eine Spalte vorgibt. DataType gibt den Datentyp der Spalte an, die
gebruchlichsten von ihnen knnen Tabelle 42.4 entnommen werden.
Bezeichnung
Bedeutung
CHAR(n)
VARCHAR(n)
SMALLINT
INTEGER
REAL
FLOAT
DECIMAL(n,m)
DATE
TabName und ColName sind die Bezeichner der gewnschten Tabelle bzw. Spalte. Expr kann eine
literale Konstante oder ein passender Ausdruck sein. SearchCond ist eine Suchbedingung, mit der
angegeben wird, auf welche Stze die UPDATE- oder DELETE FROM-Anweisung angewendet
werden soll. Wird sie ausgelassen, wirken die nderungen auf alle Stze. Wir kommen im nchsten
Abschnitt auf die Syntax der Suchbedingung zurck. Wird bei der INSERT INTO-Anweisung die
optionale Feldliste ausgelassen, mssen Ausdrcke fr alle Felder angegeben werden.
Lesen von Daten
Das Lesen von Daten erfolgt mit der SELECT-Anweisung. Ihre festen Bestandteile sind die Liste der
Spalten ColList und die Liste der Tabellen, die in der Abfrage verwendet werden sollen. Daneben gibt
es eine Reihe von optionalen Bestandteilen:
SELECT [ALL|DISTINCT] ColList
FROM TabName [,TabName]...
[WHERE SearchCond]
[GROUP BY ColName [,ColName]...]
[HAVING SearchCond]
[UNION SubQuery]
[ORDER BY ColName [ASC|DESC]
[,ColName [ASC|DESC]]...]
Die Spaltenliste kann entweder einzelne Felder aufzhlen, oder es knnen durch Angabe eines
Sternchens "*" alle Spalten angegeben werden. Wurde mehr als eine Tabelle angegeben und sind die
Spaltennamen nicht eindeutig, kann ein Spaltenname durch Voranstellen des Tabellennamens und
eines Punkts qualifiziert werden. Zustzlich knnen die Spaltennamen mit dem Schlsselwort "AS"
ein (mglicherweise handlicheres) Synonym erhalten. Die Syntax von ColList ist:
ColExpr [AS ResultName]
[,ColExpr AS ResultName]]...
Zustzlich gibt es einige numerische Aggregatfunktionen, mit denen der Wert der als Argument
angegebenen Spalte ber alle Stze der Ergebnismenge kumuliert werden kann:
Bezeichnung
Bedeutung
COUNT
AVG
Durchschnitt
SUM
Summe
MIN
Kleinster Wert
MAX
Grter Wert
Mit Hilfe der Wildcards "%" und "_" knnen auch unscharf definierte Begriffe gesucht werden. Jedes
Vorkommen von "%" pat auf eine beliebige Anzahl beliebiger Zeichen, jedes "_" steht fr genau ein
beliebiges Zeichen. Manche Datenbanken unterscheiden zwischen Gro- und Kleinschreibung, andere
nicht.
Mit Hilfe der Klauseln IS NULL und IS NOT NULL kann getestet werden, ob der Inhalt einer
Spalte den Wert NULL enthlt oder nicht:
ColName IS [NOT] NULL
Mit dem BETWEEN-Operator kann bequem festgestellt werden, ob ein Ausdruck innerhalb eines
vorgegebenen Wertebereichs liegt oder nicht:
Expr BETWEEN Expr AND Expr
Neben den einfachen Abfragen gibt es eine Reihe von Abfragen, die mit Subqueries (Unterabfragen)
arbeiten:
EXISTS (SubQuery)
Expr [NOT] IN (SubQuery)
Expr RelOp {ALL|ANY} (SubQuery)
Die Syntax von SubQuery entspricht der einer normalen SELECT-Anweisung. Sie definiert eine
separat definierte Menge von Daten, die als Teilausdruck in einer Suchbedingung angegeben wird.
Der EXISTS-Operator testet, ob die Unterabfrage mindestens ein Element enthlt. Mit dem INOperator wird getestet, ob der angegebene Ausdruck in der Ergebnismenge enthalten ist. Die
Ergebnismenge kann auch literal als komma-separierte Liste von Werten angegeben werden.
Schlielich kann durch Angabe eines relationalen Operators getestet werden, ob der Ausdruck zu
mindestens einem (ANY) oder allen (ALL) Stzen der Unterabfrage in der angegebenen Beziehung
steht. Bei den beiden letzten Unterabfragen sollte jeweils nur eine einzige Spalte angegeben werden.
Die GROUP BY-Klausel dient dazu, die Stze der Ergebnismenge zu Gruppen zusammenzufassen, bei
denen die Werte der angegebenen Spalten gleich sind. Sie wird typischerweise zusammen mit den
oben erwhnten Aggregatfunktionen verwendet. Mit HAVING kann zustzlich eine Bedingung
angegeben werden, mit der die gruppierten Ergebnisstze "nachgefiltert" werden.
Mit dem UNION-Operator knnen die Ergebnismengen zweier SELECT-Anweisungen
zusammengefat werden. Das wird typischerweise gemacht, wenn die gesuchten Ergebnisstze aus
mehr als einer Tabelle stammen (andernfalls knnte der OR-Operator verwendet werden).
Die ORDER BY-Klausel kann angegeben werden, um die Reihenfolge der Stze in der Ergebnismenge
festzulegen. Die Stze werden zunchst nach der ersten angegebenen Spalte sortiert, bei
Wertegleichheit nach der zweiten, der dritten usw. Mit Hilfe der Schlsselwrter ASC und DESC kann
angegeben werden, ob die Werte auf- oder absteigend sortiert werden sollen.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Das Testing wird durch ein Rahmenprogramm Test ausgelst, das als Argument den Namen der zu
testenden Klasse erwartet.
Die zu testende Klasse soll automatisch gefunden, geladen und mit dem Default-Konstruktor
instanziert werden.
In der Klasse mit dem Testcode sollen automatisch alle ffentlichen, nicht-statischen Methoden
ausgefhrt werden, deren Name mit "test" beginnt.
Der Schlssel zur Implementierung der Klasse Test liegt in der Anwendung des Reflection-APIs. Das
Laden der Testklasse entspricht dem vorigen Beispiel, zum Aufzhlen aller Methoden bedienen wir uns der
Methode getMethods der Klasse Class:
public Method[] getMethods()
throws SecurityException
java.lang.Class
getMethods liefert ein Array von Objekten des Typs Method, das fr jede ffentliche Methode der Klasse
ein Element enthlt. Um auch die nicht-ffentlichen Methoden aufzulisten, kann die Methode
getDeclaredMethods verwendet werden. Die Klasse Method stellt einige Methoden zum Zugriff auf
das Methodenobjekt zur Verfgung. Die wichtigsten sind:
String getName()
java.lang.reflect.Method
int getModifiers()
Class[] getParameterTypes()
Object invoke(Object obj, Object[] args)
Mit getName kann der Name der Methode ermittelt werden. getModifiers liefert eine bitverknpfte
Darstellung der Methodenattribute (static, private usw.). Der Rckgabewert kann an die statischen
Methoden der Klasse Modifier bergeben werden, um festzustellen, welche Attribute die Methode besitzt:
static
static
static
static
static
static
static
static
static
static
static
static
static
boolean
boolean
boolean
boolean
boolean
boolean
boolean
boolean
boolean
boolean
boolean
boolean
boolean
isAbstract(int mod)
isExplicit(int mod)
isFinal(int mod)
isInterface(int mod)
isNative(int mod)
isPrivate(int mod)
isProtected(int mod)
isPublic(int mod)
isStatic(int mod)
isStrict(int mod)
isSynchronized(int mod)
isTransient(int mod)
isVolatile(int mod)
java.lang.reflect.Modifier
getParameterTypes liefert ein Array mit Objekten des Typs Class, das dazu verwendet werden kann,
die Anzahl und Typen der formalen Argumente der Methode festzustellen. Jedes Array-Element reprsentiert
dabei die Klasse des korrespondierenden formalen Arguments. Hat das Array die Lnge 0, so ist die Methode
parameterlos. Gibt es beispielsweise zwei Elemente mit den Typen String und Double, so besitzt die
Methode zwei Parameter, die vom Typ String und Double sind.
Um auch primitive Typen auf diese Weise darstellen zu knnen, gibt es in den Wrapper-Klassen der
primitiven Typen (siehe Abschnitt 10.2) jeweils ein statisches Class-Objekt mit der Bezeichnung TYPE,
das den zugehrigen primitiven Datentyp bezeichnet. Ein int-Argument wird also beispielsweise dadurch
angezeigt, da der Rckgabewert von getParameterTypes an der entsprechenden Stelle ein Objekt des
Typs Integer.TYPE enthlt. Insgesamt gibt es neun derartige Klassenobjekte, und zwar fr die acht
primitiven Typen und fr den "leeren" Rckgabewert void:
Klassenobjekt
Typ
Boolean.TYPE
boolean
Character.TYPE
char
Byte.TYPE
byte
Short.TYPE
short
Integer.TYPE
int
Long.TYPE
long
Float.TYPE
float
Double.TYPE
double
Void.TYPE
void
Tip
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
/* Test.java */
import java.lang.reflect.*;
public class Test
{
public static Object createTestObject(String name)
{
//Klassennamen zusammenbauen
int pos = name.lastIndexOf('.');
if (pos == -1) {
name = "Test" + name;
} else {
name = name.substring(0, pos + 1) + "Test" +
name.substring(pos + 1);
}
//Klasse laden
Object ret = null;
try {
Class testclass = Class.forName(name);
//Testobjekt instanzieren
System.out.println("==============================");
System.out.println("Instanzieren von: " + name);
System.out.println("--");
ret = testclass.newInstance();
} catch (ClassNotFoundException e) {
System.err.println("Kann Klasse nicht laden: " + name);
} catch (InstantiationException e) {
System.err.println("Fehler beim Instanzieren: " + name);
} catch (IllegalAccessException e) {
System.err.println("Unerlaubter Zugriff auf: " + name);
}
return ret;
}
public static void runTests(Object tester)
{
Class clazz = tester.getClass();
Method[] methods = clazz.getMethods();
int cnt = 0;
for (int i = 0; i < methods.length; ++i) {
//Methodenname mu mit "test" anfangen
String name = methods[i].getName();
if (!name.startsWith("test")) {
continue;
}
//Methode mu parameterlos sein
Class[] paras = methods[i].getParameterTypes();
if (paras.length > 0) {
continue;
}
//Methode darf nicht static sein
int modifiers = methods[i].getModifiers();
if (Modifier.isStatic(modifiers)) {
Test.java
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082 }
continue;
}
//Nun kann die Methode aufgerufen werden
++cnt;
System.out.println("==============================");
System.out.println("Aufgerufen wird: " + name);
System.out.println("--");
try {
methods[i].invoke(tester, new Object[0]);
} catch (Exception e) {
System.err.println(e.toString());
}
}
if (cnt <= 0) {
System.out.println("Keine Testmethoden gefunden");
}
}
public static void main(String[] args)
{
if (args.length <= 0) {
System.err.println("Aufruf: java Test <KlassenName>");
System.exit(1);
}
Object tester = createTestObject(args[0]);
runTests(tester);
}
Hinweis
/* FloatTables.java */
import java.lang.reflect.*;
public class FloatTables
{
public static double times2(double value)
{
return 2 * value;
}
public static double sqr(double value)
{
return value * value;
}
FloatTables.java
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054 }
Ist der Klassenname zur Compilezeit bekannt, kann anstelle des Aufrufs von forName die abkrzende Tip
Schreibweise .class verwendet werden. Das hat den Vorteil, da bereits der Compiler berprfen
kann, ob die genannte Klasse vorhanden ist.
Anders als im vorigen Abschnitt generiert das Programm nun nicht eine Liste aller Methoden, sondern sucht
mit getMethod ganz konkret nach einer bestimmten:
public Method getMethod(String name, Class[] parameterTypes)
java.lang.Class
Dazu mssen der Name der Methode und eine Beschreibung ihrer formalen Argumente an getMethod
bergeben werden. Auch hier werden die Argumente durch ein Array mit korrespondierenden
Klassenobjekten reprsentiert. Da die Methoden, die in diesem Fall aufgerufen werden sollen, nur ein
einziges Argument vom Typ double haben sollen, hat unsere Parameterspezifikation formParas lediglich
ein einziges Element Double.TYPE. Wurde keine solche Methode gefunden oder besitzt sie nicht das
static-Attribut, lst getMethods eine Ausnahme des Typs NoSuchMethodException aus.
Wrde die Methode anstelle des primitiven Typs double ein Argument des Referenztyps Double
erwarten, htten wir ein Klassenobjekt der Klasse Double bergeben mssen. Dafr gibt es
verschiedene Mglichkeiten, beispielsweise die beiden folgenden:
Tip
Auch eine parametrisierte Methode kann mit invoke aufgerufen werden. Im Unterschied zur
parameterlosen mu nun allerdings ein nicht-leeres Object-Array mit den aktuellen Argumenten bergeben
werden. Hier zeigt sich ein vermeintliches Problem, denn in einem Array vom Object[] knnen keine
primitiven Typen abgelegt werden. Um Methoden mit primitiven Parametern aufrufen zu knnen, werden
diese einfach in die passende Wrapper-Klasse verpackt (siehe Abschnitt 10.2). Beim Aufruf von invoke
werden sie dann automatisch ausgepackt und dem primitiven Argument zugewiesen.
Wir verpacken also den zu bergebenden Wert in ein Double-Objekt und stellen dieses in Zeile 037 in das
Array mit den aktuellen Argumenten. Beim Methodenaufruf in der nchsten Zeile wird es dann automatisch
ausgepackt und steht innerhalb der Methode als double-Wert zur Verfgung. Das Programm ruft die
Methode fr jeden der Werte 0.0, 1.0, 2.0, 3.0, 4.0 und 5.0 auf und erzeugt so eine einfache Wertetabelle.
Anders als in Listing 43.3 wird in diesem Beispiel als erstes Argument von invoke nicht das
Hinweis
Objekt bergeben, an dem die Methode aufgerufen werden soll, sondern der Wert null. Das liegt
daran, da wir eine statische Methode aufrufen, die keiner Objektinstanz, sondern dem
Klassenobjekt zugeordnet ist. Bei nicht-statischen Methoden ist die bergabe von null natrlich
nicht erlaubt und wrde zu einer NullPointerException fhren.
Die Ausgabe des Programms ist:
Wertetabelle fuer times2
0.0 -> 0.0
1.0 -> 2.0
java.lang.reflect.Constructor
newInstance erwartet ebenfalls ein Array von Argumenten des Typs Object. Diese werden
gegebenenfalls in der zuvor beschriebenen Weise auf primitive Typen abgebildet und rufen schlielich den
passenden Konstruktor auf. Als Rckgabewert von newInstance wird das neu instanzierte Objekt
geliefert.
Das folgende Listing zeigt die Verwendung parametrisierter Konstruktoren mit dem Reflection-API:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
/* Listing4306.java */
import java.lang.reflect.*;
public class Listing4306
{
public static void main(String[] args)
{
Class clazz = TestConstructors.class;
//Formale Parameter definieren
Class[] formparas = new Class[2];
formparas[0] = String.class;
formparas[1] = String.class;
try {
Constructor cons = clazz.getConstructor(formparas);
//Aktuelle Argumente definieren
Object[] actargs = new Object[] {"eins", "zwei"};
Object obj = cons.newInstance(actargs);
((TestConstructors)obj).print();
} catch (Exception e) {
System.err.println(e.toString());
System.exit(1);
}
}
}
class TestConstructors
{
private String arg1;
private String arg2;
public TestConstructors()
{
arg1 = "leer";
arg2 = "leer";
}
public TestConstructors(String arg1)
{
this();
this.arg1 = arg1;
}
public TestConstructors(String arg1, String arg2)
{
this();
this.arg1 = arg1;
this.arg2 = arg2;
}
public void print()
{
Listing4306.java
053
System.out.println("arg1 = " + arg1);
054
System.out.println("arg2 = " + arg2);
055
}
056 }
Listing 43.6: Parametrisierte Konstruktoren mit Reflection aufrufen
Das Programm erzeugt zunchst ein Klassenobjekt zu der Klasse TestConstructors. Anschlieend wird
ein Array mit zwei Klassenobjekten der Klasse String erzeugt und als Spezifikation der formalen
Parameter an getConstructor bergeben. Das zurckgegebene Constructor-Objekt wird dann mit
zwei aktuellen Argumenten "eins" und "zwei" vom Typ String ausgestattet, die an seine Methode
newInstance bergeben werden. Diese instanziert das Objekt, castet es auf die Klasse
TestConstructors und ruft deren Methode print auf. Die Ausgabe des Programms ist:
arg1 = eins
arg2 = zwei
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
In diesem Abschnitt wird beschrieben, wie einem Applet der Zugriff auf geschtzte Ressourcen gewhrt werden kann.
Wir gehen dazu in folgenden Schritten vor:
1. Zunchst wird ein Applet entwickelt, das auf einige beschrnkte Ressourcen zugreift. Ohne weitere
Vorkehrungen ist es nicht lauffhig, sondern wird mit einer SecurityException abgebrochen.
2. Anschlieend zeigen wir, wie das Applet mit Hilfe des in Abschnitt 47.1.6 erzeugten Schlsselpaars signiert
wird.
3. Danach demonstrieren wir die Weitergabe von Zertifikaten.
4. Schlielich zeigen wir, wie die Sicherheitseinstellungen so angepat werden, da das Applet Zugriff auf die
gewnschten Ressourcen erhlt.
/* TrustedApplet.java */
import
import
import
import
java.awt.*;
java.applet.*;
java.util.*;
java.io.*;
TrustedApplet.java
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064 }
Hinweis
<html>
<head>
<title>TrustedApplet Demo</title>
</head>
<body>
<h1>TrustedApplet Demo</h1>
<applet
code="TrustedApplet.class"
width=600
height=200
>
TrustedApplet Demo
</applet>
</body>
</html>
Nachdem wir in Abschnitt 47.1.6 bereits ein Schlsselpaar mit dem Alias "hjp3" erzeugt und in der Schlsseldatenbank
abgelegt haben, mu der Aufruf von jarsigner so erfolgen:
C:\--->jarsigner -signedjar strapp.jar trapp.jar hjp3
Hinweis
Bisher wurde das Applet direkt aus der .class-Datei geladen. Um es aus einem jar-Archiv zu laden, mu das APPLETTag der HTML-Datei um das archive-Argument erweitert werden:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
TrustedApplet.html
<html>
<head>
<title>TrustedApplet Demo</title>
</head>
<body>
<h1>TrustedApplet Demo</h1>
<applet
archive="strapp.jar"
code="TrustedApplet.class"
width=600
height=200
>
TrustedApplet Demo
</applet>
</body>
</html>
Die System-Policies befinden sich in der Datei java.policy im Unterverzeichnis jre\lib\security des JDKInstallationsverzeichnisses. Diese Datei braucht normalerweise nicht verndert zu werden, sie enthlt die
globalen Sicherheitseinstellungen.
Die benutzerbezogenen Sicherheitseinstellungen knnen in der Datei .java.policy abgelegt werden. Sie liegt im
Home-Verzeichnis des aktuellen Benutzers. Auf Windows-95/98-Einzelplatzsystemen liegt sie (wie die
Schlsseldatenbank) im Verzeichnis c:\windows. Diese Datei ist standardmig nicht vorhanden, kann aber
leicht selbst angelegt werden.
Policy-Dateien knnen auch an beliebigen anderen Stellen im Dateisystem liegen. In diesem Fall mu beim
Tip
Aufruf des Java-Interpreters das System-Property "java.security.policy" mit dem Namen der zu verwendenen
Policy-Datei angegeben werden. Wre beispielsweise hjp3policy die zu verwendende Policy-Datei, so mte der
Appletviewer mit der Option "-J-Djava.security.policy=hjp3policy" aufgerufen werden.
Das Format einer Policy-Datei
Policy-Dateien sind zeilenorientierte Textdateien, die mit einem gewhnlichen Texteditor bearbeitet werden knnen.
Alternativ stellt das JDK ein einfaches GUI-basiertes Hilfsprogramm mit dem Namen policytool zur Verfgung,
mit dem Policy-Dateien erstellt und bearbeitet werden knnen. Auf seine Verwendung wollen wir aber nicht weiter
eingehen.
Eine Policy-Datei enthlt zwei Arten von Eintrgen. Beide sind optional:
Alle Eintrge werden mit einem Semikolon beendet. Kommentare knnen an beliebiger Stelle durch // oder /* ... */
angelegt werden.
Der "keystore"-Eintrag erwartet als Argument einen URL, der auf die Schlsseldatenbank verweist. Auf einem
Windows-98-Einzelplatzsystem sieht er beispielsweise so aus:
keystore "file:/c:/windows/.keystore";
Die "grant"-Eintrge haben folgende Syntax:
grant [SignedBy "signer"] [, CodeBase "URL"] {
permission permission_class [ "target" ] [, "action"]
[, SignedBy "signers"];
...
};
Eine Berechtigung kann wahlweise an einen Unterzeichner oder eine Ladeadresse (oder beide) vergeben werden. Die
Option "SignedBy" fhrt eine Liste von Aliasnamen auf, deren Zertifikate vorhanden sein mssen, damit die
Berechtigung gewhrt wird. Die Option "CodeBase" spezifiziert die Adresse, von der ein Applet geladen werden darf,
um die Berechtigung zu halten. Fehlt die CodeBase-Klausel, wird nur die Unterzeichnerliste verwendet; fehlt die
SignedBy-Klausel, ist es nur die Ladeadresse.
Nach einer ffnenden geschweiften Klammer folgen beliebig viele Berechtigungen, die jeweils durch das
Schlsselwort "permission" eingeleitet werden. Anschlieend wird der Eintrag mit einer schlieenden geschweiften
Klammer abgeschlossen. Der zuvor spezifizierte Berechtigte erhlt alle Berechtigungen, die zwischen den beiden
Klammern angegeben sind.
Jede Berechtigung mu als erstes Argument den Namen einer Berechtigungsklasse angeben. Daneben kann sie zwei
weitere Argumente target und action haben, mit denen Details spezifiziert werden. Tabelle 47.1 listet die
gebruchlichsten Berechtigungen und ihre Argumente auf. Details knnen (und mssen) in dem Dokument "Java
Security Architecture" nachgelesen werden, das Bestandteil der JDK-Dokumentation ist.
Klasse
Zugriff auf
Target
java.io.FilePermission
Dateien und
Verzeichnisse
java.net.SocketPermission
TCP/IPVerbindungen
accept, connect,
listen, resolve
read, write
-
java.util.PropertyPermission SystemProperties
Action
java.lang.RuntimePermission
Die Klasse
Runtime
java.awt.AWTPermission
WindowRessourcen
accessClipboard,
showWindowWithoutWarningBanner,
...
java.security.AllPermission
Alle Ressourcen -
Nach diesen Vorbemerkungen knnen wir die Policy-Datei \windows\.java.policy erstellen. Sie hat folgenden Inhalt:
keystore "file:/c:/windows/.keystore";
grant SignedBy "hjp3" {
permission java.io.FilePermission "c:\\tmp\\applets\\*", "read,write";
permission java.util.PropertyPermission "user.name", "read";
permission java.util.PropertyPermission "user.home", "read";
permission java.util.PropertyPermission "user.dir", "read";
};
Im ersten Eintrag geben wir die Position der Schlsseldatenbank an, sie liegt im Verzeichnis c:\windows. Anschlieend
definieren wir die Berechtigungen fr alle Applets, die mit dem Zertifikat "hjp3" signiert wurden. Zunchst erhalten sie
Schreib- und Leseberechtigung im Verzeichnis c:\tmp\applets. Dort knnen sie ohne weitere Einschrnkungen Dateien
anlegen, berschreiben und lesen. Zustzlich erlauben wir den so signierten Applets, die drei System-Properties
"user.name", "user.home" und "user.dir" zu lesen.
Nun lt sich unser signiertes Applet ohne SecurityException aufrufen und gibt die erlsende Meldung "Alle
Sicherheitshuerden ueberwunden" aus. Im Verzeichnis c:\tmp\applets sollte sich anschlieend eine Datei
TrustedApplet.log befinden und etwa folgenden Inhalt haben:
Erzeugt von Applet: 30.12.1999 20:50:40
user.name=Guido Krger
user.home=C:\WINDOWS
user.dir=C:\arc\doku\hjp3\misc
...
System.setSecurityManager(new SecurityManager());
...
Das folgende Beispielprogramm gibt den Inhalt des System-Properties "user.name" aus. Normalerweise kann es ohne
Fehler ausgefhrt werden:
001
002
003
004
005
006
007
008
009
010
011
SecuMgrTest.java
/* SecuMgrTest.java */
public class SecuMgrTest
{
public static void main(String[] args)
{
System.out.println(
"user.name is " + System.getProperty("user.name")
);
}
}
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt Suchen
<
>
Index
>>
DOC
API
49.2 Tuning-Tips
49.2 Tuning-Tips
49.2.1 String und StringBuffer
String-Verkettung
Einfgen und Lschen in Strings
Die Methode toString der Klasse StringBuffer
Die Unvernderlichkeit von String-Objekten
Durchlaufen von Zeichenketten
49.2.2 Methodenaufrufe
49.2.3 Vektoren und Listen
49.2.4 Dateizugriffe
Schreiben von Streams
Lesen von Streams
RandomAccess-Dateien
49.2.5 Speicher-Optimierung
String s;
s = "";
for (int i = 0; i < 20000; ++i) {
s += "x";
}
Das Programmfragment hat die Aufgabe, einen String zu erstellen, der aus 20000
aneinandergereihten "x" besteht. Das ist zwar nicht sehr praxisnah, illustriert aber die hufig
vorkommende Verwendung des +=-Operators auf Strings. Der obige Code ist sehr ineffizient, denn
er luft langsam und belastet das Laufzeitsystem durch 60000 temporre Objekte, die alloziert und
vom Garbage Collector wieder freigegeben werden mssen. Der Compiler bersetzt das
Programmfragment etwa so:
001
002
003
004
005
String s;
s = "";
for (int i = 0; i < 20000; ++i) {
s = new StringBuffer(s).append("x").toString();
}
String s;
StringBuffer sb = new StringBuffer(1000);
for (int i = 0; i < 20000; ++i) {
sb.append("x");
}
s = sb.toString();
umkopiert werden mu. Anders als beispielsweise bei der Klasse Vector, die einen vernderbaren
Ladefaktor besitzt, verdoppelt sich die Gre eines StringBuffer-Objekts bei jeder
Kapazittserweiterung. Dadurch wird zwar mglicherweise mehr Speicher als ntig alloziert, aber
die Anzahl der Kopiervorgnge wchst hchstens logarithmisch mit der Gesamtmenge der
eingefgten Daten. In unserem Beispiel kann der interne Puffer zunchst 1000 Zeichen aufnehmen,
wird beim nchsten berlauf auf etwa 2000 Zeichen vergrert, dann auf 4000, 8000, 16000 und
schlielich auf 32000 Zeichen. Htten wir die initiale Gre auf 20000 Zeichen gesetzt, wre sogar
berhaupt kein Kopiervorgang erforderlich geworden und das Programm htte 12000 Zeichen
weniger alloziert.
Bei der Verwendung der Operatoren + und += auf String-Objekten sollte man
Warnung
zustzlich bedenken, da deren Laufzeit nicht konstant ist (bzw. ausschlielich von der
Lnge des anzuhngenden Strings abhngt). Tatschlich hngt sie auch stark von der
Lnge des Strings ab, an den angehngt werden soll, denn die Laufzeit eines
Kopiervorgangs wchst nun einmal proportional zur Lnge des zu kopierenden Objekts.
Damit wchst das Laufzeitverhalten der Schleife in Listing 49.1 nicht linear, sondern
annhernd quadratisch. Es verschlechtert sich also mit zunehmender Lnge der Schleife
berproportional.
Einfgen und Lschen in Strings
Ein immer noch deutlicher, wenn auch nicht ganz so drastischer Vorteil bei der Verwendung von
StringBuffer ergibt sich beim Einfgen von Zeichen am vorderen Ende des Strings:
001
002
003
004
005
String s;
s = "";
for (int i = 0; i < 10000; ++i) {
s = "x" + s;
}
String s;
StringBuffer sb = new StringBuffer(1000);
for (int i = 0; i < 10000; ++i) {
sb.insert(0, "x");
}
s = sb.toString();
JDK1.11.4
erreicht werden:
String s2 = new String(s1);
Da der interne Puffer hierbei kopiert wird, ist der Aufruf natrlich ineffizienter als die einfache
Zuweisung.
Auch die Methode substring der Klasse String konnte sehr effizient implementiert werden.
Sie erzeugt zwar ein neues String-Objekt, aber den internen Zeichenpuffer teilt es sich mit dem
bisherigen Objekt. Lediglich die Membervariablen, in denen die Startposition und relevante Lnge
des Puffers festgehalten werden, mssen im neuen Objekt angepat werden. Dadurch ist auch das
Extrahieren von langen Teilzeichenketten recht performant. Dasselbe gilt fr die Methode trim,
die ebenfalls substring verwendet und daher keine Zeichen kopieren mu.
Durchlaufen von Zeichenketten
Soll ein String durchlaufen werden, so kann mit der Methode length seine Lnge ermittelt
werden, und durch wiederholten Aufruf von charAt knnen alle Zeichen nacheinander abgeholt
werden. Alternativ knnte man auch zunchst ein Zeichenarray allozieren und durch Aufruf von
getChars alle Zeichen hineinkopieren. Beim spteren Durchlaufen wre dann kein
Methodenaufruf mehr erforderlich, sondern die einzelnen Array-Elemente knnten direkt verwendet
werden. Die Laufzeitunterschiede zwischen beiden Varianten sind allerdings minimal und werden
in der Praxis kaum ins Gewicht fallen (da die Klasse String als final deklariert wurde und die
Methode charAt nicht synchronized ist, kann sie sehr performant aufgerufen werden).
49.2.2 Methodenaufrufe
Eine der hufigsten Operationen in objektorientierten Programmiersprachen ist der Aufruf einer
Methode an einer Klasse oder einem Objekt. Generell werden Methodenaufrufe in Java recht
performant ausgefhrt. Ihr Laufzeitverhalten ist jedoch stark von ihrer Signatur und ihren Attributen
abhngig. Tabelle 49.1 gibt einen berblick ber die Laufzeit (in msec.) von 10 Millionen Aufrufen
einer trivialen Methode unter unterschiedlichen Bedingungen. Alle Messungen wurden mit dem
JDK 1.2 Beta 4 auf einem PentiumII-266 unter Windows 95 vorgenommen.
Signatur/Attribute
Ohne
JIT
Mit JIT
public
5650
280
7800
390
public static
5060
110
protected
5770
280
private
5820
50
public synchronized
9500
4660
public final
6260
50
In jedem Fall bringt der JIT einen erheblichen Geschwindigkeitsvorteil. Er liegt (mit
Ausnahme der synchronized-Methode) in diesem Beispiel durchweg bei ber einer
Zehnerpotenz.
Methoden des Typs final und private werden am schnellsten ausgefhrt, insbesondere
bei aktiviertem JIT.
Klassenmethoden werden schneller ausgefhrt als Instanzmethoden.
Die bergabe von Parametern erfordert zustzliche Zeit. In unserem Beispiel wurden vier
Argumente (int, String, double und boolean) bergeben.
Mit Abstand am langsamsten ist der Aufruf von Methoden, die das synchronizedAttribut verwenden, denn der Zugriff auf die Sperre zur Synchronisation in Multi-ThreadingUmgebungen kostet erhebliche Zeit. Auch der Just-In-Time-Compiler bringt hier keine
nennenswerten Vorteile.
Weiterhin ist zu beachten, da der polymorphe Aufruf von Methoden Zeit kostet (was nicht aus
dieser Tabelle abgelesen werden kann). Ist beispielsweise aus einer Klasse A eine weitere Klasse B
abgeleitet, so ist der Aufruf von Methoden auf einem Objekt des Typs A kostspieliger als der auf
einem Objekt des Typs B.
Aus diesen Ergebnissen allgemeingltige Empfehlungen abzuleiten, ist schwierig. Zwar empfiehlt
es sich offensichtlich, Methoden als private bzw. final zu deklarieren, wenn sicher ist, da sie
in abgeleiteten Klassen nicht aufgerufen bzw. berlagert werden sollen. Auch knnte man
versuchen, verstrkt Klassenmethoden zu verwenden oder zur Vermeidung von polymorphen
Aufrufen die Vererbungshierachie zu beschrnken oder mit Hilfe des Attributs final ganz
abzuschneiden. All diese Entscheidungen htten aber einen starken Einflu auf das Klassendesign
der Anwendung und knnten sich leicht an anderer Stelle als Sackgasse herausstellen.
Der einzig wirklich allgemeingltige Rat besteht darin, Methoden nur dann als synchronized zu
deklarieren, wenn es wirklich erforderlich ist. Eine Methode, die keine Membervariablen
verwendet, die gleichzeitig von anderen Threads manipuliert werden, braucht auch nicht
synchronisiert zu werden. Eine Anwendung, die nur einen einzigen Thread besitzt und deren
Methoden nicht von Hintergrundthreads aufgerufen werden, braucht berhaupt keine
synchronisierten Methoden in eigenen Klassen.
Elemente. Ein Vector besitzt dazu die beiden Attribute Kapazitt und Ladefaktor. Die Kapazitt
gibt an, wie viele Elemente insgesamt aufgenommen werden knnen, also wie gro der interne
Puffer ist. Der Ladefaktor bestimmt, um wie viele Elemente der interne Puffer erweitert wird, wenn
beim Einfgen eines neuen Elements nicht mehr ausreichend Platz vorhanden ist. Je kleiner die
anfngliche Kapazitt und der Ladefaktor sind, desto hufiger ist beim fortgesetzten Einfgen von
Elementen ein zeitaufwendiges Umkopieren erforderlich.
Wird ein Vector ohne Argumente instanziert, so hat sein Puffer eine anfngliche Kapazitt von 10
Objekten und der Ladefaktor ist 0. Letzteres bedeutet, da die Kapazitt bei jeder Erweiterung
verdoppelt wird (analog zur Klasse StringBuffer, s. Abschnitt 49.2.1). Alternativ kann die
Kapazitt oder auch beide Werte beim Instanzieren an den Konstruktor bergeben werden. Durch
die folgende Deklaration wird ein Vector mit einer anfnglichen Kapazitt von 100 Elementen
und einem Ladefaktor von 50 angelegt:
Vector v = new Vector(100, 50);
Ein weiteres Problem der Klasse Vector ist, da die meisten ihrer Methoden als synchronized
deklariert wurden. Dadurch kann ein Vector zwar sehr einfach als gemeinsame Datenstruktur
mehrerer Threads verwendet werden. Die Zugriffsmethoden sind aber leider auch ohne MultiThreading-Betrieb entsprechend langsam.
Seit der Version 1.2 des JDK stehen mit den Klassen LinkedList und
JDK1.1-1.4
ArrayList auch alternative Listenimplementierungen zur Verfgung, die anstelle
von Vector verwendet werden knnen. Hier ist jedoch Vorsicht geboten, soll das
Programm nicht langsamer laufen als vorher. Die Klasse LinkedList
implementiert die Datenstruktur in klassischer Form als doppelt verkettete Liste ihrer
Elemente. Zwar entfallen dadurch die Kopiervorgnge, die beim Erweitern des
Arrays erforderlich waren. Durch die Vielzahl der allozierten Objekte, in denen die
Listenelemente und die Zeiger gespeichert werden mssen, und die teilweise
ineffiziente Implementierung einiger Grundoperationen (insbesondere add) hat sich
LinkedList jedoch im Test als relativ ineffizient herausgestellt. Wesentlich
bessere Ergebnisse gab es mit der Klasse ArrayList. Sie ist hnlich wie Vector
implementiert, verzichtet aber (wie die meisten 1.2er Collections) auf die
synchronized-Attribute und ist daher - insbesondere bei aktiviertem JIT und
Zugriff mit add und get sehr - performant.
Listing 49.6 zeigt drei Methoden, die jeweils ein String-Array bergeben bekommen und daraus
eine bestimmte Anzahl von Elementen zurckgeben. Die erste Version verwendet einen Vector,
die zweite eine LinkedList und die dritte eine ArrayList zur Datenspeicherung. Im Test war
die dritte Version eindeutig die schnellste. Bei aktiviertem JIT und bergabe von 100000
Elementen, von denen jeweils die Hlfte zurckgegeben wurden, war das Verhltnis der Laufzeiten
der drei Methoden etwa 3:18:1.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
49.2.4 Dateizugriffe
Schreiben von Streams
Seit dem JDK 1.1 gibt es die Writer-Klassen, mit denen Character-Streams verarbeitet werden
knnen. Passend zur internen Darstellung des char-Typs in Java verwenden sie 16-Bit breite
UNICODE-Zeichen zur Ein- und Ausgabe. Um eine Datei zu erzeugen, kann ein FileWriterObjekt angelegt werden, und die Zeichen werden mit den write-Methoden geschrieben. Um die
Performance zu erhhen, kann der FileWriter in einen BufferedWriter gekapselt werden,
der mit Hilfe eines internen Zeichenpuffers die Anzahl der Schreibzugriffe reduziert. Im Test ergab
sich dadurch ein Geschwindigkeitszuwachs um den Faktor drei bis vier gegenber dem
ungepufferten Zugriff. Die von BufferedWriter verwendete Standard-Puffergre von 8 kByte
ist in aller Regel ausreichend, weitere Vergrerungen bringen keine nennenswerten
Beschleunigungen.
Das Dilemma der Writer-Klassen besteht darin, da die meisten externen Dateien mit 8-BitZeichen arbeiten, statt mit 16-Bit-UNICODE-Zeichen. Ein FileWriter fhrt also vor der
Ausgabe eine Konvertierung der UNICODE-Zeichen durch, um sie im korrekten Format
abzuspeichern. Der Aufruf der dazu verwendeten Methoden der Klasse CharToByteConverter
aus dem Paket sun.io kostet natrlich Zeit und vermindert die Performance der Writer-Klasse.
Wesentlich schneller sind die (lteren) OutputStream-Klassen, die nicht mit Zeichen, sondern
mit Bytes arbeiten. Sie fhren keine aufwendige Konvertierung durch, sondern geben je Zeichen
einfach dessen niederwertige 8 Bit aus. Das spart viel Zeit und fhrte im Test zu einer nochmals um
den Faktor drei bis vier beschleunigten Ausgabe (wenn auch der FileOutputStream in einen
BufferedOutputStream eingeschlossen wurde).
Die OutputStream-Klassen sind also immer dann den Writer-Klassen vorzuziehen, wenn
entweder sowieso Binrdaten ausgegeben werden sollen oder wenn sichergestellt ist, da keine
UNICODE-Zeichen verwendet werden, die durch das simple Abschneiden der oberen 8 Bit falsch
ausgegeben wrden. Da der UNICODE-Zeichensatz in den ersten 256 Zeichen zum ISO-8859-1Zeichensatz kompatibel ist, sollten sich fr die meisten europischen und angelschsischen
Sprachen keine Probleme ergeben, wenn zur Ausgabe von Zeichen die OutputStream-Klassen
verwendet werden.
Listing 49.7 zeigt das Erzeugen einer etwa 300 kByte groen Datei, bei der zunchst die Writerund dann die OutputStream-Klassen verwendet wurden. Im Test lag die
Ausfhrungsgeschwindigkeit der zweiten Variante um etwa eine Zehnerpotenz ber der ersten.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
sehr langsam, er liegt etwa in der Grenordnung des ungepufferten Zugriffs auf CharacterStreams. Wesentlich schneller kann mit Hilfe der read- und write-Methoden gearbeitet werden,
wenn nicht nur ein einzelnes, sondern ein ganzes Array von Bytes verarbeitet wird. Je nach
Puffergre und Verarbeitungsaufwand werden dann Geschwindigkeiten wie bei gepufferten
Bytestreams oder hher erzielt. Das folgende Beispiel zeigt, wie man mit einem 100 Byte groen
Puffer eine Random-Access-Datei bereits sehr schnell lesen kann.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
49.2.5 Speicher-Optimierung
Neben den direkten Prozessoraktivitten hat auch die Art und Weise, in der das Programm mit dem
Hauptspeicher umgeht, einen erheblichen Einflu auf dessen Performance. Einige der Aspekte, die
dabei eine Rolle spielen, sind:
Jede Allozierung von Speicher kostet Rechenzeit. Der Speicher mu entweder vom
Betriebssystem oder vom Laufzeitsystem der VM beschafft werden. Auch das
(automatische) Initialisieren des Speichers kostet Zeit. Das Anlegen eines Arrays mit 1000
Elementen dauert wesentlich lnger als das eines mit 10 Elementen.
Objekte mit aufwendigen Konstruktoren bentigen mglicherweise viel Zeit zur
Initialisierung. Bei ihnen kann es sinnvoll sein, sie zu recyceln. Dazu werden sie nach
Titel
<<
Inhalt Suchen
<
>
Index
DOC
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
49.3.1 Grundlagen
Die bisher vorgestellten Tips und Tricks sind sicherlich eine Hilfe, um bereits whrend der Entwicklung eines
Programms grundstzliche Performance-Probleme zu vermeiden. Luft das fertige Programm dann trotzdem nicht mit
der gewnschten Geschwindigkeit (was in der Praxis hufig vorkommt), helfen pauschale Hinweise leider nicht weiter.
Statt dessen gilt es herauszufinden, welche Teile des Programms fr dessen schlechte Performance verantwortlich sind.
Bei greren Programmen, die aus vielen tausend Zeilen Quellcode bestehen, ist das eine komplizierte Aufgabe, die
nur mit Hilfe eines guten Profilers bewltigt werden kann. Der Profiler ist ein Werkzeug, mit dessen Hilfe im
laufenden Programm Performanceparameter, wie beispielsweise die verbrauchte CPU-Zeit, die Anzahl der allozierten
Objekte oder die Anzahl der Aufrufe bestimmter Methoden, berwacht und gemessen werden knnen. Durch manuelle
Inspektion der erzeugten Logdateien oder mit Hilfe eines Auswertungsprogramms kann dann festgestellt werden,
welche Teile des Programms die grte Last erzeugen und daher verbesserungsbedrftig sind.
Das Standard-JDK enthlt bereits seit der Version 1.0 einen eingebauten Profiler, der Informationen ber JDK1.1-1.4
Laufzeit und Aufrufhufigkeit von Methoden geben kann. Im JDK 1.2 wurde er erweitert und kann
seither den Speicherverbrauch messen und Profiling-Informationen threadweise ausgeben. Mit dem JDK
1.3 wurde er erneut verbessert und mit einem offenen Profiler-API versehen (JVMPI, Java Virtual
Machine Profiler Interface). Mit dessen Hilfe ist es mglich, eigene Profiling-Werkzeuge zu schreiben.
Fester Bestandteil des JDK ist eine Beispielimplementierung hprof, die fr einfache ProfilingAufgaben verwendet werden kann.
Im Vergleich zu spezialisierten Produkten sind die Fhigkeiten des eingebauten Profilers etwas rudimentr.
Insbesondere die vom Profiler erzeugte Ausgabedatei erfordert einigen Nachbearbeitungsaufwand. Zudem gibt es keine
grafischen Auswertungen wie bei kommerziellen Profilern. Dennoch ist der JDK-Profiler ein brauchbares und
hilfreiches Instrument, mit dem Performanceprobleme und Speicherengpsse analysiert werden knnen. Wir wollen
uns in diesem Abschnitt mit den Grundlagen seiner Bedienung vertraut machen.
Als Beispiel fr die Anwendung des Profiler wollen wir ein Programm verwenden, dessen simple Aufgabe darin
besteht, einen String mit 10000 Punkten zu erzeugen und auf dem Bildschirm auszugeben. Statt die Ratschlge aus
dem vorigen Abschnitt zu beherzigen, verwendet das Programm den Operator +=, um den String zeichenweise in
einer Schleife zusammenzusetzen. Auf langsameren Rechnern kann es durchaus einige Sekunden dauern, bis der String
erzeugt und vollstndig auf dem Bildschirm ausgegeben wurde:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
/* ProfTest1A.java */
ProfTest1A.java
import java.util.*;
public class ProfTest1A
{
public static String dots(int len)
{
String ret = "";
for (int i = 0; i < len; ++i) {
ret += ".";
}
return ret;
}
public static void main(String[] args)
{
String s = dots(10000);
System.out.println(s);
}
}
Mgliche Werte
cpu
heap
file
depth
Die unterschiedlichen Stacktraces werden mit einem Aufrufzhler versehen, der immer dann um 1 erhht wird, wenn
bei einer Unterbrechung ein entsprechender Stacktrace gefunden wird. Aus dem Endwert der Zhler kann dann
abgeleitet werden, wo das Programm die meiste Rechenzeit verbraucht hat. Denn je hher der Zhler war, desto fter
wurde das Programm an der zugehrigen Programmstelle angetroffen und desto wahrscheinlicher ist es, da dort
nennenswert Rechenzeit verbraucht wird.
Natrlich ist diese Vorgehensweise nicht sehr przise, und es sind Flle denkbar, bei den sie ganz versagt. Warnung
Aber sie ist einfach zu implementieren und beeintrchtigt die Laufzeit des Programms nur unwesentlich. In
der Praxis sollten die Ergebnisse mit der ntigen Vorsicht betrachtet werden. Sie drfen nicht als absolute
Mewerte angesehen werden, sondern sind vorwiegend dazu geeignet, Tendenzen zu erkennen und
Programmteile mit besonders hoher Rechenzeit ausfindig zu machen.
Der zweite Modus "times" arbeitet etwas anders. Statt lediglich die Anzahl der Stacktraces zu zhlen, misst er
tatschlich die innerhalb der einzelnen Methoden verbrauchte Rechenzeit. Allerdings wird dadurch auch die Laufzeit
des Programms strker erhht als im Modus "samples". In der Praxis kann eine gemischte Vorgehensweise sinnvoll
sein, bei der zunchst per "samples" die grten Performancefresser eliminiert werden und dann per "times" das
Feintuning vorgenommen wird. Die Option "old" erstellt die Ausgabedatei in einem Format, wie sie von den Profilern
der Pr-1.2-Versionen verwendet wurde. Wir wollen hier nicht weiter darauf eingehen.
Bei der Verwendung des CPU-Profilers sind weiterhin die Optionen file und depth von Bedeutung. Mit file
kann der Name der Ausgabedatei angegeben werden, er ist standardmig java.hprof.txt. Mit depth wird festgelegt,
mit welcher maximalen Tiefe die Stacktraces aufgezeichnet werden (standardmig 4). Ist die Aufrufkette einer
Methode lnger als der angegebene Wert, wird der Stacktrace abgeschnitten, und bei der Analyse ist nicht mehr bis ins
letzte Detail erkennbar, von welcher Stelle aus der Aufruf erfolgte. Wird depth auf 1 gesetzt, sind nur noch die
Aufrufstellen sichtbar, die Aufrufer selbst bleiben unsichtbar.
Wir wollen einen ersten Lauf mit dem CPU-Profiler im Modus "samples" und mit einer maximalen Stacktiefe von 10
machen und rufen das Programm daher wie folgt auf:
java -Xint -Xrunhprof:cpu=samples,depth=10 ProfTest1A
Die Option -Xint geben wir an, um das Programm im Interpreter-Modus laufen zu lassen und mgliche
Verflschungen durch den Hotspot-Compiler zu vermeiden.
Hinweis
Das CPU-Profile
Das Programm erzeugt nun die Ausgabedatei java.hprof.txt mit den Profiling-Informationen. Sie besteht aus drei
Teilen:
Im oberen Teil werden allgemeine Informationen zur Struktur der Datei und den darin verwendeten Eintrgen
gegeben
Im mittleren Teil befinden sich die Stacktraces
Im unteren Teil werden die Sampling-Ergebnisse ausgegeben
Die Analyse beginnt im unteren Teil. Er sieht bei unserer Beispielsitzung so aus (die Samples ab Position 11 wurden
aus Grnden der bersichtlichkeit entfernt):
CPU SAMPLES
rank
self
1 53.66%
2 17.48%
3 17.07%
4 1.63%
5 1.22%
6 0.81%
7 0.81%
8 0.81%
9 0.41% 93.90%
10 0.41% 94.31%
...
CPU SAMPLES END
1
1
20 java.lang.StringBuffer.append
14 java.lang.String.getChars
Die Ausgabe ist nach Aufrufhufigkeit geordnet. Von den insgesamt 246 Samples, die whrend des Programmlaufs
gezogen wurden, waren 132 in der Methode expandCapacity der Klasse StringBuffer und 43 und noch
einmal 42 in der Methode arraycopy der Klasse System. Damit fielen insgesamt 88.21 Prozent aller Samples in
diese Methoden.
Da beide Methoden nicht selbstgeschrieben sind und sich damit unseren Optimierungsversuchen entziehen, kann eine
Performance-Verbesserung lediglich dadurch erreicht werden, da die Anzahl ihrer Aufrufe vermindert wird. Um die
Aufrufer herauszufinden, mssen wir uns die in der fnften Spalten angegebenen Stacktraces ansehen. Der Stacktrace
zu expandCapacity hat die Nummer 9 und sieht so aus:
TRACE 9:
java.lang.StringBuffer.expandCapacity(StringBuffer.java:202)
java.lang.StringBuffer.append(StringBuffer.java:401)
ProfTest1A.dots(ProfTest1A.java:11)
ProfTest1A.main(ProfTest1A.java:18)
Er besagt, da der Sample in Zeile 202 der Methode expandCapacity der Klasse StringBuffer erzeugt wurde.
Diese wurde von append aufgerufen, das von unserer eigenen Methode dots in Zeile 11 aufgerufen wurde. In Zeile
11 steht zwar kein Aufruf von append, dort befindet sich aber der +=-Operator zur Verkettung der Strings. Dieser
wird vom Compiler in entsprechende Methoden- und Konstruktorenaufrufe der Klassen String und
StringBuffer bersetzt.
Als erste Erkenntnis stellen wir also fest, da offensichtlich der +=-Operator zur String-Verkettung interne
StringBuffer-Objekte erzeugt, die einen erheblichen Teil der CPU-Zeit bentigen, um whrend des Anfgens von
Zeichen vergrert zu werden.
Die Stacktraces 11 und 13 der nchsten beiden Kandidaten verstrken den Eindruck, da der +=-Operator in unserem
Programm CPU-intensiven Code erzeugt:
TRACE 11:
java.lang.System.arraycopy(System.java:Native method)
java.lang.String.getChars(String.java:553)
java.lang.StringBuffer.append(StringBuffer.java:402)
ProfTest1A.dots(ProfTest1A.java:11)
ProfTest1A.main(ProfTest1A.java:18)
TRACE 13:
java.lang.System.arraycopy(System.java:Native method)
java.lang.StringBuffer.expandCapacity(StringBuffer.java:203)
java.lang.StringBuffer.append(StringBuffer.java:401)
ProfTest1A.dots(ProfTest1A.java:11)
ProfTest1A.main(ProfTest1A.java:18)
Beide Arten von Samples gehen letztlich auf Zeile 11 unseres Programmes zurck und zeigen Rechenzeitverbruche,
die durch die vom +=-Operator ausgelste Verarbeitung temporrer Strings und StringBuffer verursacht werden.
Obwohl die Samples in beiden Fllen in arraycopy gezogen wurden, gibt es zwei unterschiedliche Stacktraces, weil
die Aufruferkette in beiden Fllen unterschiedlich ist. Einerseits wird arraycopy aus getChars aufgerufen (da
Strings immutable sind, mu getChars bei jedem Aufruf eine Kopie des Zeichenarrays erstellen), andererseits wird
arraycopy in expandCapacity bentigt, um das bei einer Verlngerung des StringBuffers erforderliche
Umkopieren der Zeichen zu erledigen.
Unsere erste Vermutung hat sich also besttigt: Der harmlos aussehende Aufruf des +=-Operators in Zeile 11 unseres
Programms erzeugt temporre String- und StringBuffer-Objekte, in der ein Groteil der Rechenzeit durch das
Anhngen und Kopieren von Zeichen und das Erhhen der Kapazitt verbraucht wird.
Das Heap-Profile
Einen noch deutlicheren Eindruck vermittelt ein Heap-Profile. Wir erstellen es, indem wir das Programm mit der
Option heap=samples erneut unter Profiler-Kontrolle laufen lassen:
java -Xint -Xrunhprof:heap=sites,depth=10 ProfTest1A
Die Ausgabe besteht wie beim CPU-Profiling aus drei Teilen. Die ersten beiden entsprechen dem CPU-Profiler, der
dritte enthlt Informationen zur dynamischen Heap-Belegung:
SITES BEGIN (ordered by live bytes) Sun Jun 18 18:37:28 2000
percent
live
alloc'ed stack class
rank
self accum
bytes objs
bytes objs trace name
1 48.27% 48.27%
189974
25 75074238 19950
471 [C
2 38.00% 86.27%
149524 157 149524 157
1 [I
3 4.91% 91.17%
19304 741
19364 747
1 [C
4 2.44% 93.61%
9588 153
9588 153
1 [B
5 2.29% 95.90%
9010 1550
9022 1552
1 <Unknown>
6 1.13% 97.03%
4460 199
4460 199
1 [S
7 1.06% 98.09%
4164 253
4220 260
1 [L<Unknown>;
8 0.06% 98.15%
238
3
238
3
403 [B
9 0.04% 98.20%
172
1
172
1
396 [B
10 0.04% 98.24%
170
10
170
10
390 [C
...
SITES END
Auch hier geben die erste Spalte die Rangordnung und die nchsten beiden die einzelnen und kumulierten
Prozentanteile am Gesamtverbrauch an. die Spalten 4 und 5 geben einen berblick ber die aktiven Objekte, die
nchsten beiden ber die insgesamt allozierten Objekte (jeweils mit der Gesamtzahl allozierter Bytes und der Anzahl
der Objekte). Die letzte Spalte stellt den Datentyp des Objekts dar. [C steht beispielsweise fr ein Zeichen-Array, [I
fr ein Integer-Array.
Am aufflligsten ist die oberste Zeile und die darin ersichtliche Diskrepanz zwischen aktiven und allozierten Objekten.
Dort steht, da unser Programm 19950 Zeichen-Arrays mit insgesamt 75 MByte Speicher alloziert hat, davon aber nur
noch 25 Objekte mit kaum 200 kByte Speicher aktiv sind. Hier wurden also in erheblichen Umfang kurzlebige Objekte
erzeugt und anschlieend wieder fallengelassen. Stacktrace 471 sieht so aus:
TRACE 471:
java.lang.StringBuffer.expandCapacity(StringBuffer.java:202)
java.lang.StringBuffer.append(StringBuffer.java:401)
ProfTest1A.dots(ProfTest1A.java:11)
ProfTest1A.main(ProfTest1A.java:18)
Wieder liegt der Verursacher in Zeile 11 unseres Programmes, und wir sehen, da der +=-Operator nicht nur viel
Rechenzeit verbraucht, sondern zudem eine groe Anzahl temporrer Objekte erzeugt und damit das Laufzeitsystem
und den Garbage Collector belastet.
Die optimierte Version des Programms
Da wir auf die Interna der Klassen String und StringBuffer keinen Einflu haben, kann die Optimierung nur
darin bestehen, die Verwendung des +=-Operators einzuschrnken oder eine besser geeignete Alternative zu whlen.
Diese ist natrlich schon aus Abschnitt 49.2.1 bekannt und besteht darin, direkt mit StringBuffer-Objekten zu
arbeiten. Die verbesserte Version unseres Programms sieht dann so aus:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
/* ProfTest1B.java */
ProfTest1B.java
import java.util.*;
public class ProfTest1B
{
public static String dots(int len)
{
StringBuffer sb = new StringBuffer(len + 10);
for (int i = 0; i < len; ++i) {
sb.append('.');
}
return sb.toString();
}
public static void main(String[] args)
{
String s = dots(10000);
System.out.println(s);
}
}
49.3.3 Ausblick
Egal, ob mit dem eingebauten Profiler das Laufzeitverhalten oder der Speicherverbrauch der Anwendung untersucht
werden soll, die prinzipielle Vorgehensweise ist stets gleich:
Zunchst wird das Programm mit der Option -Xrunhprof gestartet und eine Datei mit ProfilingInformationen erstellt.
Die grten Rechenzeit- und Speicherverbraucher werden ermittelt, und ber ihre Stacktraces wird bestimmt,
woher sie aufgerufen werden und in welcher Weise sie zum Programmergebnis beitragen.
Stehen gengend Erkenntnisse zur Verfgung, kann das Programm verbessert werden und ein erneuter
Profilerlauf durchgefhrt werden.
Sind die Ergebnisse zufriedenstellend, kann der Profiler deaktiviert werden, andernfalls beginnt das Spiel von
vorne.
Ist der Anteil von lokalem Code am Rechenzeitverbrauch hoch, kann versucht werden, diesen zu verringern. Typische
Ansatzpunkte dafr sind das Vermindern der Anzahl von Schleifendurchlufen (durch bessere Algorithmen), die
Verwendung von Ganz- statt Fliekommazahlen, das Herausziehen von schleifeninvariantem Code, das Vermeiden der
Doppelauswertung von gemeinsamen Teilausdrcken, das Wiederverwenden bekannter Teilergebnisse, die
Verwendung alternativer Datenstrukturen, das Eliminieren von unbenutztem Code oder das Reduzieren der Strke von
Ausdrcken, indem sie durch algebraisch gleichwertige, aber schnellere Ausdrcke ersetzt werden. Wird ein groer
Anteil der Rechenzeit dagegen in Aufrufen von Untermethoden verbraucht, kann versucht werden, deren
Aufrufhufigkeit zu vermindern, sie durch performantere Aufrufe zu ersetzen, oder - im Falle eigener Methoden - ihre
Ablaufgeschwindigkeit zu erhhen.
Der zielgerichtete Einsatz dieser Techniken erfordert gute Werkzeuge, namentlich einen guten Profiler. Bei kleineren
Problemen mag es ausreichend sein, die Ablaufgeschwindigkeit mit Ausgabeanweisungen und
System.currentTimeMillis zu ermitteln, und auch der JDK-Profiler kann mit Erfolg eingesetzt werden.
Daneben gibt es einige kommerzielle und experimentelle Produkte mit wesentlich erweiterten Fhigkeiten. Beispiele
fr solche Profiler sind JProbe (http://www.klg.com), der auch fr Teile der Software zu diesem Buch verwendet
wurde, OptimizeIt (http://www.optimizeit.com) oder JInsight (http://www.alphaWorks.ibm.com). Zu ihren Fhigkeiten
zhlen beispielsweise:
Die graphische Darstellung der Aufrufhierarchie mit Laufzeitinformationen zu Methoden und Aufrufen.
Das Ermitteln von Profiling-Informationen bis auf die Ebene einzelner Quelltextzeilen hinab.
Das Erstellen dynamischer Profile, um die Vernderung wichtiger Parameter bereits whrend des laufenden
Programms beobachten zu knnen.
Die Rckverfolgung von alloziertem Speicher sowohl zu den Programmstellen, die den Speicher belegt haben,
als auch zu den Variablen, in denen die zugehrigen Objekte gehalten werden.
Das Vergleichen von Profiling-Lufen, um die Auswirkungen von Optimierungsversuchen studieren zu knnen.
Die Ausgabe von Reports und HTML-Dateien zu Vergleichs- und Dokumentationszwecken.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
In diesem Kapitel wollen wir die wichtigsten Hilfsprogramme des Java Development
Kit bzw. Java Runtime Environment vorstellen. Die meisten Programme arbeiten
kommandozeilenorientiert und werden aus einer DOS-Box oder einer Shell heraus
aufgerufen. Einige von ihnen sind interaktiv und haben eine einfache grafische
Oberflche. Wir wollen die Aufrufsyntax und wichtigsten Optionen der folgenden
Programme vorstellen:
50.1.1 Aufruf
50.1.2 Beschreibung
Der Compiler javac bersetzt Sourcecode in Bytecode. Zu jeder Klasse, die
innerhalb einer .java-Datei definiert wurde, wird eine eigene .class-Datei erzeugt. Der
Compiler wacht automatisch ber die Abhngigkeiten zwischen den Quelldateien.
Wird beispielsweise in einer Klasse X in der Datei X.java eine Klasse Y, die in Y.java
liegt, verwendet, so wird Y.java automatisch mit bersetzt, wenn es erforderlich ist.
Anstelle der Liste von Dateinamen kann nach einem @ auch eine Textdatei
angegeben werden, in der die zu bersetzenden Dateien durch Whitespaces getrennt
angegeben werden.
50.1.3 Optionen
Option
Bedeutung
-classpath path
-g
-nowarn
-O
-verbose
-depend
-deprecation
-target version
-source version
<
>
>>
Titel
<<
>
>>
API
50.2.1 Aufruf
java [ options ] classname [{ args }]
javaw [ options ] classname [{ args }]
50.2.2 Beschreibung
Der Interpreter java dient dazu, kompilierte Java-Programme auszufhren, die als
Bytecode in .class-Dateien vorliegen. javaw erfllt denselben Zweck, erzeugt aber
kein Terminalfenster beim Start des Programms und erlaubt nicht die Verwendung der
Standard-Streams System.in, System.out und System.err. Beim Aufruf
beider Programme wird der Name einer Klassendatei erwartet (ohne die Erweiterung
.class). Damit sie ausgefhrt werden kann, mu sie eine Klassenmethode main mit
folgender Signatur enthalten:
public static void main(String[] args)
Alle Argumente, die nach dem Namen der Klassendatei an java bergeben werden,
stehen nach dem Aufruf von main in args zur Verfgung. Der Java-Interpreter wird
nach dem Rcksprung aus main beendet, wenn keine eigenen Threads erzeugt
wurden. Falls weitere Threads erzeugt wurden, wird er verlassen, nachdem der letzte
Vordergrund-Thread beendet wurde.
Da whrend der Ausfhrung eines Java-Programms meist weitere Klassendateien
bentigt werden, mu der Interpreter wissen, wo diese zu finden sind. Standardmig
sucht er dabei im systemspezifischen Installationsverzeichnis und im aktuellen
Verzeichnis. Die Suchstrategie kann durch Setzen der Umgebungsvariable
CLASSPATH oder mit Hilfe der Option -classpath verndert werden. Sollen nur
die Standardbibliotheken des JDK verwendet werden, sind weder CLASSPATH noch classpath erforderlich. Weitere Informationen zu CLASSPATH-Einstellungen
finden Sie in Abschnitt 13.2.2.
In der Windows-Version des JDK 1.2 ist ein Just-In-Time-Compiler
(kurz JIT) enthalten, der standardmig aktiviert ist. Der JIT bersetzt
zur Laufzeit des Programmes hufig bentigte Bytecodes in
Maschinencode und beschleunigt so die weitere Ausfhrung des
Programms. Soll der JIT deaktiviert werden, kann die
Umgebungsvariable JAVA_COMPILER oder die Systemeigenschaft
java.compiler auf den Wert NONE gesetzt werden.
JDK1.1-1.4
50.2.3 Optionen
Option
Bedeutung
-classpath path
-prof
-version
-help
-verbose
-verbosegc
-DpropName=value
-Xms n
-Xmx n
-enableassertions
In den 1.1er Versionen des JDK gab es ein Programm jre, das dazu
JDK1.1-1.4
diente, den Interpreter des Laufzeitsystems zu starten (Java Runtime
Environment). Dieses Programm ist in der aktuellen JDK-Version nicht
mehr vorhanden, sondern wird durch das Programm java ersetzt.
Auch der in frheren Versionen vorhandene debugging-fhige
Interpreter java_g existiert seit dem JDK 1.2 nicht mehr.
Titel
<<
>
>>
API
50.3.1 Aufruf
appletviewer [ options ] { url }
50.3.2 Beschreibung
appletviewer ist ein Hilfsprogramm zum Ausfhren von Applets. Er interpretiert
dazu die als Argument bergebenen (und auf HTML-Dokumente zeigenden) URLs
und ffnet fr jedes darin gefundene APPLET-Tag ein Fenster, in dem das
zugehrige Applet gestartet wird. Alle brigen HTML-Befehle werden ignoriert.
Details zur Struktur der HTML-Dateien und zur Ausfhrung von Applets finden sich
in Kapitel 39.
50.3.3 Optionen
Option
Bedeutung
-debug
<
>
>>
50.4.1 Aufruf
jdb [ classfile ]
50.4.2 Beschreibung
jdb ist der Debugger des JDK. Er bietet die Mglichkeit, Programme kontrolliert
ablaufen zu lassen und dabei Breakpoints zu setzen, im Einzelschrittmodus den
nchsten Befehl ausfhren zu lassen oder den Inhalt von Variablen oder Objekten zu
inspizieren. Man sollte allerdings nicht zu groe Erwartungen an jdb stellen, denn
das Programm ist ein Kommandozeilendebugger in schnster UNIX-Tradition. Mit
den grafischen Debuggern der integrierten Java-Entwicklungsumgebungen hat er nur
wenig gemeinsam. Leider ist er nicht nur umstndlicher zu bedienen, sondern lt
(insbesondere in Pr-1.2-JDKs) auch einige wichtige Features moderner Debugger
vermissen.
Seit der Version 1.3 besitzt das JDK eine neue Debugging-Architektur. JDK1.1-1.4
Sie wird als JPDA (Java Platform Debugger Architecture) bezeichnet
und erlaubt es, Debugger komplett in Java zu schreiben. Zwar wird
nach wie vor kein ausgefeilter GUI-Debugger mit dem JDK
ausgeliefert. Doch befindet sich im Unterverzeichnis jpda des DemoVerzeichnisses des JDK ein Beispielprogramm javadt, da als
Prototyp eines GUI-Debuggers angesehen werden kann. Hinweise zu
seiner Verwendung finden sich in der JDK-Dokumentation. Auch
integrierte Entwicklungsumgebungen verwenden diese Schnittstelle,
um ihre eigenen Debugger einzubinden.
50.4.3 Vorbereitungen
Damit ein Programm debugt werden kann, sollte es mit der Option -g bersetzt
werden. Dadurch werden symbolische Informationen in die Klassendatei
geschrieben, die der Debugger zur Interpretation von lokalen Variablen bentigt.
Beim Aufruf des Debuggers kann die zu untersuchende Klassendatei als Argument
angegeben werden:
jdb classfile
Damit jdb berhaupt startet, mu ein laufender TCP/IP-Stack vorhanden Hinweis
sein. Dies ist fr Anwender unangenehm, die nur eine Whlverbindung zu
ihrem Provider haben, denn bei jedem Starten des Debuggers die
Verbindung aufzubauen ist nicht nur umstndlich, sondern auch
kostspielig. Die bliche Empfehlung im Usenet lautet in diesem Fall, eine
Datei hosts anzulegen und den folgenden Eintrag einzufgen:
127.0.0.1
localhost
Warnung
Nachdem der Debugger gestartet wurde, meldet er sich mit seiner Kommandozeile
und ist bereit, Befehle entgegenzunehmen. Die wichtigsten von ihnen werden in
Tabelle 50.4 vorgestellt und kurz erlutert.
Kommando
Bedeutung
!!
load
classname
run
quit
stop in
stop at
clear
list
step
next
step up
cont
Fhrt das Programm nach einem Breakpoint fort. Es luft dann bis
zum nchsten Breakpoint oder bis es mit dem suspendKommando angehalten wird.
Zeigt den Inhalt der Variablen an, die als Argument bergeben
wurde. print verwendet dazu die Methode toString, die in
allen Objektvariablen implementiert ist. Damit eine Variable
angezeigt werden kann, mu sie an der Aufrufstelle sichtbar sein.
Es gibt leider keine Mglichkeit, den Inhalt einer Variablen aus
dem Debugger heraus zu verndern.
dump
locals
Gibt eine Liste aller lokalen Variablen und ihrer aktuellen Inhalte
aus.
where
<
>
>>
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
50.5.1 Aufruf
javadoc [ options ] { package | sourcefile }
50.5.2 Beschreibung
javadoc ist ein Programm, das aus Java-Quelltexten Dokumentationen im HTML-Format erstellt.
Dazu verwendet es die ffentlichen Klassen-, Interface- und Methodendeklarationen und fgt zustzliche
Informationen aus eventuell vorhandenen Dokumentationskommentaren hinzu. Zu jeder Klassendatei
xyz.java wird eine HTML-Seite xyz.html generiert, die ber verschiedene Querverweise mit den anderen
Seiten desselben Projekts in Verbindung steht. Zustzlich generiert javadoc diverse Index- und
Hilfsdateien, die das Navigieren in den Dokumentationsdateien erleichtern.
50.5.3 Dokumentationskommentare
Bereits ohne zustzliche Informationen erstellt javadoc aus dem Quelltext eine brauchbare
Beschreibung aller Klassen und Interfaces. Durch das Einfgen von Dokumentationskommentaren kann
die Ausgabe zustzlich bereichert werden. Ein Dokumentationskommentar beginnt mit /** und endet
mit */ und hnelt damit einem gewhnlichen Kommentar. Er mu im Quelltext immer unmittelbar vor
dem zu dokumentierenden Item plaziert werden (einer Klassendefinition, einer Methode oder einer
Instanzvariable). Er kann aus mehreren Zeilen bestehen. Die erste Zeile des Kommentars wird spter als
Kurzbeschreibung verwendet.
Zur Erhhung der bersichtlichkeit darf am Anfang jeder Zeile ein Sternchen stehen, es wird Hinweis
spter ignoriert. Innerhalb der Dokumentationskommentare drfen neben normalem Text
auch HTML-Tags vorkommen. Sie werden unverndert in die Dokumentation bernommen
und erlauben es damit, bereits im Quelltext die Formatierung der spteren Dokumentation
vorzugeben. Die Tags <h1> und <h2> sollten mglichst nicht verwendet werden, da sie von
javadoc selbst zur Strukturierung der Ausgabe verwendet werden.
javadoc erkennt des weiteren markierte Abstze innerhalb von Dokumentationskommentaren. Die
Markierung mu mit dem Zeichen @ beginnen und - abgesehen von Leerzeichen - am Anfang der Zeile
stehen. Jede Markierung leitet einen eigenen Abschnitt innerhalb der Beschreibung ein, alle
Markierungen eines Typs mssen hintereinanderstehen. Tabelle 50.5 gibt eine bersicht der wichtigsten
Markierungen und beschreibt, wie sie verwendet werden.
Markierung und
Parameter
Dokumentation
Verwendung in
@author name
Klasse, Interface
Klasse, Interface
@version version Erzeugt einen Versionseintrag. Darf hchstens
einmal je Klasse oder Interface verwendet werden.
@since jdkversion
@see reference
Klasse, Interface
@see java.util.Vector
@see Vector
@see Vector#addElement
@see <a
href="Spez.html">Spez</a>
@param name
description
Methode
@return
description
Methode
@exception
classname
description
Methode
@deprecated
description
Methode
JDK1.1-1.4
Die zweite Art von Verweisen ist die auf Klassen oder Methoden der Java-Klassenbibliothek (z.B.
java.lang.String). Im JDK 1.1 geht javadoc davon aus, da sich die Dokumentationsdateien zu
allen externen Klassen und Methoden im selben Verzeichnis wie die zu erstellenden
Dokumentationsdateien befinden. Damit also externe Verweise funktionieren, mte man die HTMLFiles der Originaldokumentation des JDK in sein eigenes Dokumentationsverzeichnis kopieren, was
sicherlich nicht immer praktikabel ist.
Mit dem JDK 1.2 wurde die Option -link eingefhrt, mit der ein Pfad auf die
Dokumentation der Standardklassen angegeben werden kann. Der Pfad mu als URL
angegeben werden und das Verzeichnis beschreiben, in dem die Datei package-list der
Dokumentationsdateien liegt. In der Dokumentation zu javadoc gibt SUN folgendes
Beispiel an:
JDK1.11.4
Dadurch wird bei Standard-Klassennamen auf die auf dem SUN-Server liegende Originaldokumentation
verwiesen. Soll dagegen auf eine lokal installierte Dokumentation verwiesen werden, kann auch ein
file-URL angegeben werden. Liegt die Dokumentation des JDK beispielsweise im Verzeichnis
c:\jdk1.4\docs (und somit die API-Dokumentation im Verzeichnis c:\jdk1.4\docs\api), kann javadoc
wie folgt aufgerufen werden:
javadoc -link file:///c:/jdk1.4/docs/api ...
50.5.5 Optionen
Option
Bedeutung
-classpath path
Gibt die Liste der Pfade zur Suche von Klassendateien an.
-public
-protected
Elemente des Typs public und protected werden dokumentiert (das ist
die Voreinstellung).
-package
-private
-version
Versionseintrag generieren
-author
Autoreneintrag generieren
-verbose
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
50.6.1 Aufruf
jar [ commands ] archive { input-file }
50.6.2 Beschreibung
jar ist ein Archivierungsprogramm, das Dateien und komplette Unterverzeichnisse
komprimieren und in eine gemeinsame Archivdatei packen kann. Es verwendet ein
Kompressionsformat, das den diversen zip-/unzip-Programmen hnelt, und wird
analog dem UNIX-Tool tar bedient. Ein Vorteil von jar ist seine Portabilitt, die
sowohl fr das erzeugte Dateiformat als auch fr das (in Java geschriebene)
Programm selbst gilt.
Wichtigster Einsatzzweck von jar ist es, alle zu einem Java-Programm gehrenden
Dateien (.class-, Image-, Sound-Dateien usw.) in einer einzigen Datei
zusammenzufassen. Neben den organisatorischen Vorteilen, die diese Mglichkeit
zweifellos bietet, wurden jar-Dateien vor allem eingefhrt, um das Laden von
Applets aus dem Internet zu beschleunigen. Dadurch mssen Web-Browser nmlich
nicht mehr fr jede einzelne Datei, die in einem Applet bentigt wird, eine eigene
GET-Transaktion absetzen, sondern knnen alle erforderlichen Files in einem Schritt
laden. Die Ladezeit von Applets wird dadurch drastisch verringert, insbesondere,
wenn viele kleine Dateien bentigt werden.
50.6.3 Kommandos
Im Gegensatz zu den brigen Programmen, die in diesem Kapitel vorgestellt wurden,
kennt jar keine Optionsparameter, sondern erwartet Kommandos an ihrer Stelle. Ein
Kommando besteht aus einem Buchstaben, der ohne den Prfix - angegeben wird.
Sollen mehrere Kommandos kombiniert werden, so werden die zugehrigen
Buchstaben ohne Lcken direkt hintereinander geschrieben. Diese abweichende
Syntax stammt von dem Kommando tar, das auf UNIX-Rechnern zur Archivierung
von Dateien eingesetzt wird. Tabelle 50.7 gibt eine bersicht der verfgbaren
Kommandos.
Kommando
Bedeutung
x file
<html>
<head>
<title>HWApplet</title>
</head>
<body>
<h1>HWApplet</h1>
<applet
code=HWApplet.class
archive=hello.jar
width=300
height=200>
Hier steht das Applet HWApplet.class
</applet>
HWApplet.html
014 </body>
015 </html>
Listing 50.1: HTML mit ARCHIVE-Tag
Nun kann das Applet wie bisher gestartet werden, bentigt aber zum Laden aller
Dateien nur noch eine einzige HTTP-Transaktion.
Leider untersttzen noch nicht alle Browser das jar-Format, so da seine Hinweis
Verwendung zum heutigen Zeitpunkt berlegt sein will. Fr die nahe
Zukunft ist es aber ein wichtiger Schritt zur Verbesserung der Ladezeiten
von Applets.
Handbuch der Java-Programmierung, 3. Auflage,
Addison Wesley, Version 3.0
1998-2002 Guido Krger,
API
http://www.javabuch.de
<
>
>>
50.7.1 Aufruf
javap [ options ] classname
50.7.2 Beschreibung
Der Disassembler javap liest den bersetzten Code einer Klasse und gibt
Informationen darber auf der Standardausgabe aus. Dabei knnen entweder nur
Informationen ber Variablen und Methoden oder der komplette Bytecode der Klasse
ausgegeben werden. javap ist nicht in der Lage, den Java-Quellcode einer
Klassendatei wieder herzustellen. Beim Aufruf ist der Name der Klasse ohne die
Erweiterung .class anzugeben, also beispielsweise:
javap -c java.lang.String
50.7.3 Optionen
Option
Bedeutung
-classpath path
-public
-protected
-package
-private
-c
-s
-l
<
>
>>
50.8.1 Aufruf
serialver -show | classname
50.8.2 Beschreibung
Mit serialver kann auf die serialVersionUID einer Klasse zugegriffen
werden (siehe Abschnitt 41.2.1). Wenn das Programm mit einem Klassennamen
aufgerufen wird, gibt es die serialVersionUID auf der Console aus. Wird es mit
der Option -show (und ohne Klassennamen) aufgerufen, erfolgt der Aufruf mit einer
einfachen grafischen Oberflche.
50.8.3 Optionen
Option
Bedeutung
-show
<
>
>>
50.11.1 Aufruf
jarsigner [ options ] jarfile alias
50.11.2 Beschreibung
jarsigner dient zum Zugriff auf die Signatur von jar-Archiven. Dazu wird der
Schlssel verwendet, der unter dem als Argument angegebenen Alias in der
Schlsseldatenbank gespeichert ist. Seine wichtigste Anwendung, das Signieren von
Archiven wurde bereits in Abschnitt 47.3.2 erlutert.
50.11.3 Optionen
Option
Bedeutung
-signedjar file
-keystore url
-storetype type
-storepass pass
-keypass pass
-verify
<
>
>>
Titel
<<
>
>>
API
50.12.1 Aufruf
rmic [ options ] classname
50.12.2 Beschreibung
rmic dient dazu, aus der Implementierung eines Remote-Objekts die zugehrigen
RMI-Stubs und -Skeletons zu erzeugen. Seine Anwendung wurde in Abschnitt 46.2.3
erlutert.
50.12.3 Optionen
Option
Bedeutung
-classpath path
-d directory
-g
-keepgenerated
-vcompat
Erzeugt Stubs und Skeletons, die zu den StubProtokollen 1.1 und 1.2 kompatibel sind
(Voreinstellung).
-v1.1
-v1.2
Titel
<<
>
>>
API
Titel
<<
Bevor wir uns in diesem Kapitel mit den Datentypen von Java befassen, sollen zunchst
einmal die wichtigsten lexikalischen Eigenschaften der Sprache vorgestellt werden.
Hierzu zhlen der Eingabezeichensatz, die Kommentare und die Struktur von
Bezeichnern.
4.1.1 Eingabezeichen
Ein Java-Programm besteht aus einer Folge von Unicode-Zeichen. Der UnicodeZeichensatz fat eine groe Zahl internationaler Zeichenstze zusammen und integriert
sie in einem einheitlichen Darstellungsmodell. Da die 256 verfgbaren Zeichen eines 8Bit-Wortes bei weitem nicht ausreichen, um die ber 30.000 unterschiedlichen Zeichen
des Unicode-Zeichensatzes darzustellen, ist ein Unicode-Zeichen 2 Byte, also 16 Bit,
lang. Der Unicode ist mit den ersten 128 Zeichen des ASCII- und mit den ersten 256
Zeichen des ISO-8859-1-Zeichensatzes kompatibel.
Die Integration des Unicode-Zeichensatzes geht in Java so weit, da neben
String- und char-Typen auch die literalen Symbole und Bezeichner der
Programmiersprache im Unicode realisiert sind. Es ist daher ohne weiteres
mglich, Variablen- oder Klassennamen mit nationalen Sonderzeichen oder
anderen Symbolen zu versehen.
4.1.2 Kommentare
Es gibt in Java drei Arten von Kommentaren:
Hinweis
Einzeilige Kommentare beginnen mit // und enden am Ende der aktuellen Zeile.
Mehrzeilige Kommentare beginnen mit /* und enden mit */. Sie knnen sich
ber mehrere Zeilen erstrecken.
Dokumentationskommentare beginnen mit /** und enden mit */ und knnen
sich ebenfalls ber mehrere Zeilen erstrecken.
Kommentare derselben Art sind nicht schachtelbar. Ein Java-Compiler akzeptiert aber
einen einzeiligen innerhalb eines mehrzeiligen Kommentars und umgekehrt.
Dokumentationskommentare dienen dazu, Programme im Quelltext zu dokumentieren.
Mit Hilfe des Tools javadoc werden sie aus der Quelle extrahiert und in ein HTMLDokument umgewandelt (siehe Kapitel 50). Kapitel 18 der Sprachspezifikation erklrt
die Verwendung von Dokumentationskommentaren ausfhrlich. Wir wollen uns hier
lediglich auf ein kleines Beispiel beschrnken, das besagter Beschreibung entnommen
wurde:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
/**
* Compares two Objects for equality.
* Returns a boolean that indicates whether this Object
* is equivalent to the specified Object. This method is
* used when an Object is stored in a hashtable.
* @param obj
the Object to compare with
* @return
true if these Objects are equal;
*
false otherwise.
* @see
java.util.Hashtable
*/
public boolean equals(Object obj)
{
return (this == obj);
}
4.1.3 Bezeichner
Ein Bezeichner ist eine Sequenz von Zeichen, die dazu dient, die Namen von Variablen,
Klassen oder Methoden zu spezifizieren. Ein Bezeichner in Java kann beliebig lang sein,
und alle Stellen sind signifikant. Bezeichner mssen mit einem Unicode-Buchstaben
beginnen (das sind die Zeichen 'A' bis 'Z', 'a' bis 'z', '_' und '$') und drfen dann weitere
Buchstaben oder Ziffern enthalten. Unterstrich und Dollarzeichen sollen nur aus
historischen Grnden bzw. bei maschinell generiertem Java-Code verwendet werden.
Ein Buchstabe im Sinne des Unicode-Zeichensatzes mu nicht zwangslufig Warnung
aus dem lateinischen Alphabet stammen. Es ist auch zulssig, Buchstaben
aus anderen Landessprachen zu verwenden. Java-Programme knnen daher
ohne weiteres Bezeichner enthalten, die nationalen Konventionen folgen.
Java-Bezeichner drfen jedoch nicht mit Schlsselwrtern, den booleschen
Literalen true und false oder dem Literal null kollidieren.
Titel
<<
Es gibt keinen Prprozessor in Java und damit auch keine #define-, #includeund #ifdef-Anweisungen.
Der Backslash \ darf nicht zur Verkettung von zwei aufeinanderfolgenden Zeilen
verwendet werden.
Konstante Strings, die mit + verkettet werden, fat der Compiler zu einem
einzigen String zusammen.
>
>>
API
4.3 Variablen
4.3 Variablen
4.3.1 Grundeigenschaften
4.3.2 Deklaration von Variablen
4.3.3 Lebensdauer/Sichtbarkeit
4.3.1 Grundeigenschaften
Variablen dienen dazu, Daten im Hauptspeicher eines Programms abzulegen und
gegebenenfalls zu lesen oder zu verndern. In Java gibt es drei Typen von Variablen:
Hinweis
Dabei wird eine Variable des Typs Typname mit dem Namen Variablenname
angelegt. Variablendeklarationen drfen in Java an beliebiger Stelle im
Programmcode erfolgen. Das folgende Beispielprogramm legt die Variablen a, b, c
und d an und gibt ihren Inhalt auf dem Bildschirm aus:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
/* Listing0402.java */
public class Listing0402
{
public static void main(String[] args)
{
int a;
a = 1;
char b = 'x';
System.out.println(a);
double c = 3.1415;
System.out.println(b);
System.out.println(c);
boolean d = false;
System.out.println(d);
}
}
Listing0402.java
1
x
3.1415
false
Wie in diesem Beispiel zu sehen ist, drfen Variablen gleich bei der
Deklaration initialisiert werden. Dazu ist einfach der gewnschte Wert hinter
einem Zuweisungsoperator an die Deklaration anzuhngen:
Tip
char b = 'x';
double c = 3.1415;
boolean d = false;
Listing 4.3: Initialisieren von Variablen
4.3.3 Lebensdauer/Sichtbarkeit
Die Sichtbarkeit lokaler Variablen erstreckt sich von der Stelle ihrer Deklaration bis
zum Ende der Methode, in der sie deklariert wurden. Falls innerhalb eines Blocks
lokale Variablen angelegt wurden, sind sie bis zum Ende des Blocks sichtbar. Die
Lebensdauer einer lokalen Variable beginnt, wenn die zugehrige
Deklarationsanweisung ausgefhrt wird. Sie endet mit dem Ende des
Methodenaufrufs. Falls innerhalb eines Blocks lokale Variablen angelegt wurden,
endet ihre Lebensdauer mit dem Verlassen des Blocks. Es ist in Java nicht erlaubt,
lokale Variablen zu deklarieren, die gleichnamige lokale Variablen eines weiter
auen liegenden Blocks verdecken. Das Verdecken von Klassen- oder
Instanzvariablen ist dagegen zulssig.
Instanzvariablen werden zum Zeitpunkt des Erzeugens einer neuen Instanz einer
Klasse angelegt. Sie sind innerhalb der ganzen Klasse sichtbar, solange sie nicht von
gleichnamigen lokalen Variablen verdeckt werden. In diesem Fall ist aber der Zugriff
mit Hilfe des this-Zeigers mglich: this.name greift immer auf die Instanzoder Klassenvariable name zu, selbst wenn eine gleichnamige lokale Variable
existiert. Mit dem Zerstren des zugehrigen Objektes werden auch alle
Instanzvariablen zerstrt.
Klassenvariablen leben whrend der kompletten Laufzeit des Programms. Die Regeln
fr ihre Sichtbarkeit entsprechen denen von Instanzvariablen.
<
>
>>
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
4.4 Arrays
4.4 Arrays
4.4.1 Deklaration und Initialisierung
4.4.2 Zugriff auf Array-Elemente
4.4.3 Mehrdimensionale Arrays
Arrays in Java unterscheiden sich dadurch von Arrays in anderen Programmiersprachen, da sie Objekte sind.
Obwohl dieser Umstand in vielen Fllen vernachlssigt werden kann, bedeutet er dennoch:
Dennoch bleibt ein Array immer eine (mglicherweise mehrdimensionale) Reihung von Elementen Hinweis
eines festen Grundtyps. Arrays in Java sind semidynamisch, d.h. ihre Gre kann zur Laufzeit
festgelegt, spter aber nicht mehr verndert werden.
Die Deklaration eines Arrays entspricht syntaktisch der einer einfachen Variablen, mit dem Unterschied, da
an den Typnamen eckige Klammern angehngt werden:
001 int[] a;
002 double[] b;
003 boolean[] c;
Listing 4.4: Deklaration von Arrays
Wahlweise knnen die eckigen Klammern auch hinter den Variablennamen geschrieben werden,
aber das ist ein Tribut an die Kompatibilitt zu C/C++ und sollte in neuen Java-Programmen
vermieden werden.
Hinweis
Zum Zeitpunkt der Deklaration wird noch nicht festgelegt, wie viele Elemente das Array haben soll. Dies
geschieht erst spter bei seiner Initialisierung, die mit Hilfe des new-Operators oder durch Zuweisung eines
Array-Literals ausgefhrt wird. Sollen also beispielsweise die oben deklarierten Arrays 5, 10 und 15 Elemente
haben, wrden wir das Beispiel wie folgt erweitern:
018
019
020
021
022
023
024
}
025 }
System.out.println(prim[1]);
System.out.println(prim[2]);
System.out.println(prim[3]);
System.out.println(prim[4]);
System.out.println(b[0]);
System.out.println(b[1]);
Listing0409.java
014
a[1][2] = 6;
015
System.out.println(""+a[0][0]+a[0][1]+a[0][2]);
016
System.out.println(""+a[1][0]+a[1][1]+a[1][2]);
017
}
018 }
Listing 4.9: Zugriff auf mehrdimensionale Arrays
Die Ausgabe des Programms ist:
123
456
Da mehrdimensionale Arrays als geschachtelte Arrays gespeichert werden, ist es auch mglich, nichtrechteckige Arrays zu erzeugen. Das folgende Beispiel deklariert und initialisiert ein
zweidimensionales dreieckiges Array und gibt es auf dem Bildschirm aus. Gleichzeitig zeigt es die
Verwendung der length-Variable, um die Gre des Arrays oder Sub-Arrays herauszufinden.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
Tip
Listing0410.java
/* Listing0410.java */
public class Listing0410
{
public static void main(String[] args)
{
int[][] a = { {0},
{1,2},
{3,4,5},
{6,7,8,9}
};
for (int i=0; i<a.length; ++i) {
for (int j=0; j<a[i].length; ++j) {
System.out.print(a[i][j]);
}
System.out.println();
}
}
}
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
4.5 Referenztypen
4.5 Referenztypen
4.5.1 Beschreibung
4.5.2 Speichermanagement
4.5.1 Beschreibung
Referenztypen sind neben den primitiven Datentypen die zweite wichtige Klasse von
Datentypen in Java. Zu den Referenztypen gehren Objekte, Strings und Arrays.
Weiterhin gibt es die vordefinierte Konstante null, die eine leere Referenz
bezeichnet.
Eigentlich sind auch Strings und Arrays Objekte, aber es gibt bei ihnen einige
Besonderheiten, die eine Unterscheidung von normalen Objekten rechtfertigen:
Sowohl bei Strings als auch bei Arrays kennt der Compiler Literale, die einen
expliziten Aufruf des new-Operators berflssig machen.
Arrays sind klassenlose Objekte. Sie knnen ausschlielich vom Compiler
erzeugt werden, besitzen aber keine explizite Klassendefinition. Dennoch
haben sie eine ffentliche Instanzvariable length und werden vom
Laufzeitsystem wie normale Objekte behandelt.
Die Klasse String ist zwar wie eine gewhnliche Klasse in der
Laufzeitbibliothek von Java vorhanden. Der Compiler hat aber Kenntnisse
ber den inneren Aufbau von Strings und generiert bei Stringoperationen
Code, der auf Methoden der Klassen String und StringBuffer zugreift.
Eine hnlich enge Zusammenarbeit zwischen Compiler und
Laufzeitbibliothek gibt es auch bei Threads und Exceptions. Wir werden auf
diese Besonderheiten in den nachfolgenden Kapiteln noch einmal
zurckkommen.
Hinweis
Die Zuweisung einer Referenz kopiert lediglich den Verweis auf das
betreffende Objekt, das Objekt selbst dagegen bleibt unkopiert. Nach einer
Zuweisung zweier Referenztypen zeigen diese also auf ein und dasselbe
Objekt. Sollen Referenztypen kopiert werden, so ist ein Aufruf der Methode
clone erforderlich (siehe Abschnitt 8.1.2).
Der Gleichheitstest zweier Referenzen testet, ob beide Verweise gleich sind,
d.h. auf ein und dasselbe Objekt zeigen. Das ist aber eine strengere Forderung
als inhaltliche Gleichheit. Soll lediglich auf inhaltliche Gleichheit getestet
werden, kann dazu die equals-Methode verwendet werden, die von den
meisten Klassen implementiert wird (ebenfalls in Abschnitt 8.1.2 erlutert).
Analoges gilt fr den Test auf Ungleichheit.
Anders als in C und C++, wo der *-Operator zur Dereferenzierung eines Zeigers
ntig ist, erfolgt in Java der Zugriff auf Referenztypen in der gleichen Weise wie der
auf primitive Typen. Einen expliziten Dereferenzierungsoperator gibt es dagegen
nicht.
4.5.2 Speichermanagement
Whrend primitive Typen lediglich deklariert werden, reicht dies bei Referenztypen
nicht aus. Sie mssen mit Hilfe des new-Operators oder - im Falle von Arrays und
Strings - durch Zuweisung von Literalen zustzlich noch explizit erzeugt werden.
Vector v = new Vector();
Listing 4.11: Erzeugen eines Objekts mit dem new-Operator
Java verfgt ber ein automatisches Speichermanagement. Dadurch braucht man sich
als Java-Programmierer nicht um die Rckgabe von Speicher zu kmmern, der von
Referenzvariablen belegt wird. Ein mit niedriger Prioritt im Hintergrund arbeitender
Garbage Collector sucht periodisch nach Objekten, die nicht mehr referenziert
werden, um den durch sie belegten Speicher freizugeben.
<
>
>>
beispielsweise der Ausdruck a-b+c wie (a-b)+c ausgewertet und nicht wie a(b+c).
Neben ihrer eigentlichen Funktion, einen Rckgabewert zu produzieren, haben einige
Operatoren auch Nebeneffekte. Als Nebeneffekt bezeichnet man das Verhalten eines
Ausdrucks, auch ohne explizite Zuweisung die Inhalte von Variablen zu verndern.
Meist sind diese Nebeneffekte erwnscht, wie etwa bei der Verwendung der
Inkrement- und Dekrementoperatoren. Komplexere Ausdrcke knnen wegen der
oftmals verdeckten Auswertungsreihenfolge der Teilausdrcke jedoch unter
Umstnden schwer verstndliche Nebeneffekte enthalten und sollten deshalb mit
Vorsicht angewendet werden.
Im Gegensatz zu vielen anderen Programmiersprachen ist in Java die Reihenfolge der
Auswertung der Operanden innerhalb eines Teilausdrucks wohldefiniert. Die
Sprachdefinition schreibt explizit vor, den linken Operanden eines Ausdrucks
vollstndig vor dem rechten Operanden auszuwerten. Falls also beispielsweise i den
Wert 2 hat, dann ergibt der Ausdruck (i=3) * i in jedem Fall den Wert 9 und
nicht 6.
Neben der natrlichen Auswertungsreihenfolge, die innerhalb eines Ausdrucks durch
die Bindungs- und Assoziativittsregeln vorgegeben wird, lt sich durch eine
explizite Klammerung jederzeit eine andere Reihenfolge erzwingen. Whrend das
Ergebnis des Ausdrucks 4+2*5 wegen der Bindungsregeln 14 ist, liefert (4+2)*5
das Resultat 30. Die Regeln fr die Klammerung von Teilausdrcken in Java
gleichen denen aller anderen Programmiersprachen und brauchen daher nicht weiter
erlutert zu werden.
In Java gibt es ein Konzept, das sich Definite Assignment nennt. Gemeint ist damit
die Tatsache, da jede lokale Variable vor ihrer ersten Verwendung definitiv
initialisiert sein mu. Das wnscht sich eigentlich zwar auch jeder Programmierer,
aber in Java wird dies durch den Compiler sichergestellt! Dazu mu im Quelltext
eine Datenfluanalyse durchgefhrt werden, die jeden mglichen Ausfhrungspfad
von der Deklaration einer Variablen bis zu ihrer Verwendung ermittelt und
sicherstellt, da kein Weg existiert, der eine Initialisierung auslassen wrde.
Die folgende Methode Test lt sich beispielsweise deshalb nicht fehlerfrei
kompilieren, weil k vor der Ausgabeanweisung nicht initialisiert wird, wenn i
kleiner 2 ist:
<
>
>>
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Neben den bisher vorgestellten Operatoren stellt Java noch eine Reihe weiterer Operatoren zur Verfgung, die
in diesem Abschnitt erlutert werden sollen.
Hinweis
String-Verkettung
Der +-Operator kann nicht nur mit numerischen Operanden verwendet werden, sondern auch zur Verkettung
von Strings. Ist wenigstens einer der beiden Operatoren in a + b ein String, so wird der gesamte Ausdruck als
String-Verkettung ausgefhrt. Hierzu wird gegebenenfalls zunchst der Nicht-String-Operand in einen String
umgewandelt und anschlieend mit dem anderen Operanden verkettet. Das Ergebnis der Operation ist wieder
ein String, in dem beide Operanden hintereinanderstehen.
In Java ist die Konvertierung in einen String fr nahezu jeden Typen definiert. Bei primitiven Typen
Tip
wird die Umwandlung vom Compiler und bei Referenztypen durch die Methode toString ausgefhrt.
Die String-Verkettung ist daher sehr universell zu verwenden und ermglicht (beispielsweise zu
Ausgabezwecken) eine sehr bequeme Zusammenfassung von Ausdrcken unterschiedlichen Typs. Ein
typisches Beispiel fr die Verwendung der String-Verkettung ist die Ausgabe von numerischen
Ergebnissen auf dem Bildschirm:
001
002
003
004
005
006
007
008
009
010
011
012
013
/* Listing0503.java */
Listing0503.java
Warnung
001
002
003
004
005
006
007
008
009
/* Listing0504.java */
Listing0504.java
/* Listing0505.java */
Listing0505.java
Hinweis
Ein einfaches Beispiel ist der Vergleich zweier Strings a und b, die beide den Inhalt "hallo" haben:
001
002
003
004
005
006
007
008
009
010
011
012
/* Listing0506.java */
Listing0506.java
a == b liefert false
a != b liefert true
Dies ist das erwartete Verhalten fr fast alle Objektreferenzen. Werden die Strings als Literale
dagegen zur Compile-Zeit angelegt und damit vom Compiler als konstant erkannt, sind sie genau
dann Instanzen derselben Klasse, wenn sie tatschlich inhaltlich gleich sind. Dies liegt daran, da
String-Literale mit Hilfe der Methode String.intern angelegt werden, die einen Puffer von
String-Objekten verwaltet, in dem jede Zeichenkette nur einmal vorkommt. Wird ein bereits
existierender String noch einmal angelegt, so findet die Methode den Doppelgnger und liefert
einen Zeiger darauf zurck. Dieses Verhalten ist so nur bei Strings zu finden, andere Objekte
besitzen keine konstanten Werte und keine literalen Darstellungen. Die korrekte Methode, Strings
auf inhaltliche bereinstimmung zu testen, besteht darin, die Methode equals der Klasse
String aufzurufen (siehe Listing 5.7).
001
002
003
004
005
006
007
008
009
010
011
/* Listing0507.java */
Warnung
Listing0507.java
aufgerufen werden knnen. Die Syntax des Methodenaufrufs gleicht der anderer Programmiersprachen und
erfolgt in der (etwas vereinfachten) Form f() bzw. f(parameterliste). Der Typ des Ausdrucks
entspricht dem vereinbarten Rckgabetyp der Methode. Der Wert des Ausdrucks ist der von der Methode mit
Hilfe der return-Anweisung zurckgegebene Wert.
Methoden knnen selbstverstndlich Nebeneffekte haben und werden in vielen Fllen ausschlielich zu diesem
Zweck geschrieben. Ist dies der Fall, so sollte eine Methode als void deklariert werden und damit anzeigen,
da sie keinen Rckgabewert produziert. Die einzig sinnvolle Verwendung einer solchen Methode besteht
darin, sie innerhalb einer Ausdrucksanweisung (siehe Kapitel 6) aufzurufen.
Da die Realisierung der Methodenaufrufe in Java recht kompliziert ist (die Sprachspezifikation
widmet diesem Thema mehr als 10 Seiten), werden wir in Kapitel 7 noch einmal ausfhrlich darauf
eingehen.
Hinweis
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Hinweis
Bedeutung
Die einfachste Anweisung in Java ist die leere Anweisung. Sie besteht nur aus dem
Semikolon und hat keinerlei Effekt auf das laufende Programm. Eine leere
Anweisung kann da verwendet werden, wo syntaktisch eine Anweisung erforderlich
ist, aber von der Programmlogik her nichts zu tun ist.
Syntax
{
Anweisung1;
Anweisung2;
...
}
Bedeutung
Ein Block ist eine Zusammenfassung von Anweisungen, die nacheinander ausgefhrt
werden. Alle im Block zusammengefaten Anweisungen gelten in ihrer Gesamtheit
als eine einzelne Anweisung und drfen berall da verwendet werden, wo syntaktisch
eine einzelne elementare Anweisung erlaubt wre. Ein Block darf auch Anweisungen
zur Deklaration von lokalen Variablen haben. Diese sind dann nur innerhalb des
Blocks gltig und sichtbar.
6.1.3 Variablendeklarationen
Syntax
Typname VariablenName;
oder
Typname VariablenName = InitialerWert;
Bedeutung
Die Deklaration einer lokalen Variable gilt in Java als ausfhrbare Anweisung. Sie
darf daher berall dort erfolgen, wo eine Anweisung verwendet werden darf. Die
Sichtbarkeit einer lokalen Variable erstreckt sich von der Deklaration bis zum Ende
des umschlieenden Blocks.
Lokale Variablen drfen sich nicht gegenseitig verdecken. Es ist also nicht Hinweis
erlaubt, eine bereits deklarierte Variable x in einem tiefer geschachtelten
Block erneut zu deklarieren. Das Verdecken von Klassen- oder
Instanzvariablen dagegen ist zulssig und wird besonders hufig in
Konstruktoren bei der Initialisierung von Instanzvariablen verwendet:
001 class Punkt
002 {
003
int x;
004
int y;
005
006
Punkt(int x, int y)
007
{
008
this.x = x;
009
this.y = y;
010
}
011 }
Listing 6.1: Verdecken von Klassen- oder Instanzvariablen
Hier wird eine Klasse Punkt definiert, die zwei Instanzvariablen x und y enthlt.
Der Konstruktor initialisiert die Instanzvariablen mit den gleichnamigen Parametern.
Um nun nicht fr die formalen Parameter neue Namen erfinden zu mssen, lassen
sich durch lokale Variablen verdeckte Instanzvariablen dadurch wieder aufdecken,
da sie mit dem this-Pointer qualifiziert werden.
Das vorliegende Beispiel erscheint zum jetzigen Zeitpunkt sicherlich schwer
verstndlich, denn bisher wurden weder Klassen noch Methoden noch Konstruktoren
eingefhrt. Nach Lektre von Kapitel 7, das sich mit den objektorientierten
Eigenschaften von Java beschftigt, wird es verstndlicher sein. Einprgen sollte man
sich diesen Stil aber jetzt schon, denn er wird in Java hufig verwendet.
6.1.4 Ausdrucksanweisungen
Syntax
Ausdruck;
Bedeutung
Zuweisung
Inkrement und Dekrement
Methodenaufruf
Instanzerzeugung
<
>
>>
6.2 Verzweigungen
6.2 Verzweigungen
6.2.1 Die if-Anweisung
Syntax
Bedeutung
Dangling else
Bedingtes Kompilieren
6.2.2 Die switch-Anweisung
Syntax
Bedeutung
oder
if (ausdruck)
anweisung1;
else
anweisung2;
Bedeutung
Die if-Anweisung wertet zunchst den Ausdruck ausdruck aus. Danach fhrt sie
die Anweisung anweisung genau dann aus, wenn das Ergebnis des Ausdrucks
true ist. Ist ausdruck hingegen false, so wird die Anweisung nicht ausgefhrt,
sondern mit der ersten Anweisung nach der if-Anweisung fortgefahren.
Mit der if-else-Anweisung gibt es eine weitere Verzweigung in Java. Falls
ausdruck wahr ist, wird anweisung1 ausgefhrt, andernfalls anweisung2.
Eine der beiden Anweisungen wird also in jedem Fall ausgefhrt.
Anstelle einer einzelnen Anweisung kann jeweils auch eine Folge von Anweisungen
angegeben werden, wenn sie innerhalb eines Blocks steht. Dieser wird als Einheit
betrachtet und komplett ausgefhrt, wenn die entsprechende Bedingung zutrifft.
Zu beachten ist, da der Testausdruck in der Schleife vom Typ boolean Hinweis
sein mu. Anders als in C ist es in Java nicht erlaubt, einen numerischen
Ausdruck an seiner Stelle zu verwenden.
Dangling else
Eine der Mehrdeutigkeiten, die in fast allen blockstrukturierten
Programmiersprachen auftauchen knnen, wurde auch von den JavaEntwicklern nicht beseitigt. Als Beispiel wollen wir uns das folgende
Codefragment ansehen, das leider nicht so ausgefhrt wird, wie es die
Einrckung erwarten lt:
001 if (a)
002
if (b)
003
s1;
004 else
005
s2;
Listing 6.2: Dangling else
Warnung
Bedeutung
Die switch-Anweisung ist eine Mehrfachverzweigung. Zunchst wird der
Ausdruck ausdruck, der vom Typ byte, short, char oder int sein mu,
ausgewertet. In Abhngigkeit vom Ergebnis wird dann die Sprungmarke
angesprungen, deren Konstante mit dem Ergebnis des Ausdrucks bereinstimmt. Die
Konstante und der Ausdruck mssen dabei zuweisungskompatibel sein.
Das optionale default-Label wird dann angesprungen, wenn keine passende
Sprungmarke gefunden wird. Ist kein default-Label vorhanden und wird auch
keine passende Sprungmarke gefunden, so wird keine der Anweisungen innerhalb der
switch-Anweisung ausgefhrt. Jede Konstante eines case-Labels darf nur einmal
auftauchen. Das default-Label darf maximal einmal verwendet werden.
int q = (n+7)/8;
switch (n%8) {
case 0: do { foo();
case 1:
foo();
case 2:
foo();
case 3:
foo();
case 4:
foo();
case 5:
foo();
case 6:
foo();
case 7:
foo();
} while (--q >= 0);
}
<
>
>>
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
6.3 Schleifen
6.3 Schleifen
6.3.1 Die while-Schleife
Syntax
Bedeutung
6.3.2 Die do-Schleife
Syntax
Bedeutung
6.3.3 Die for-Schleife
Syntax
Bedeutung
break und continue
Bedeutung
Zuerst wird der Testausdruck, der vom Typ boolean sein mu, geprft. Ist er true, wird die
Anweisung ausgefhrt, andernfalls wird mit der ersten Anweisung hinter der Schleife weitergemacht.
Nachdem die Anweisung ausgefhrt wurde, wird der Testausdruck erneut geprft usw. Die Schleife
wird beendet, sobald der Test false ergibt.
do
anweisung;
while (ausdruck);
Bedeutung
Die do-Schleife arbeitet nichtabweisend, d.h. sie wird mindestens einmal ausgefhrt. Da zunchst die
Schleifenanweisung ausgefhrt und erst dann der Testausdruck berprft wird, kann die do-Schleife
frhestens nach einem Durchlauf regulr beendet werden. Die Bearbeitung der Schleife wird immer
dann beendet, wenn der Test des Schleifenausdrucks false ergibt.
Bedeutung
Der Kopf der for-Schleife besteht aus drei Ausdrcken, die jeder fr sich optional sind:
Der init-Ausdruck wird einmal vor dem Start der Schleife aufgerufen. Er dient dazu,
Initialisierungen durchzufhren, die durch die Auswertung von Ausdrcken mit Nebeneffekten
verursacht werden. Der Rckgabewert der Ausdrcke wird vollstndig ignoriert.
Der init-Teil darf auch aus mehreren Ausdrcken bestehen, wenn die einzelnen Teilausdrcke
durch Kommata getrennt sind. Diese syntaktische Erweiterung ist allerdings nur innerhalb des
Initialisierungsteils einer for-Schleife erlaubt. Einen allgemeinen Komma-Operator (wie in C
und C++) gibt es in Java nicht.
Fehlt der Initialisierungsteil, wird keine Initialisierung im Kopf der Schleife durchgefhrt.
Der init-Teil darf auch Variablendeklarationen enthalten, beispielsweise, um einen
Schleifenzhler zu erzeugen. Die Variablen mssen bei der Deklaration initialisiert werden.
Sichtbarkeit und Lebensdauer erstrecken sich auf den Block, der die Schleifenanweisungen
enthlt. Damit ist es mglich, den Namen einer Schleifenvariablen innerhalb einer Methode
mehrfach zu deklarieren.
Der test-Teil bildet den Testausdruck der Schleife. Analog zur while-Schleife wird er am Anfang
der Schleife ausgefhrt, und die Schleifenanweisung wird nur ausgefhrt, wenn die Auswertung
des Testausdrucks true ergibt. Fehlt der Testausdruck, so setzt der Compiler an seiner Stelle die
Konstante true ein.
Der update-Ausdruck dient dazu, den Schleifenzhler zu verndern. Er wird nach jedem
Durchlauf der Schleife ausgewertet, bevor der Testausdruck das nchste Mal ausgewertet wird.
Wie der init-Teil darf auch der update-Teil aus mehreren Ausdrcken bestehen. Der
Rckgabewert des Ausdrucks wird ignoriert. Fehlt der update-Ausdruck, so wird keine
automatische Modifikation des Schleifenzhlers durchgefhrt.
In Java gibt es zwei weitere Mglichkeiten, die normale Auswertungsreihenfolge in einer Schleife zu
verndern. Taucht innerhalb einer Schleife eine break-Anweisung auf, wird die Schleife verlassen und
das Programm mit der ersten Anweisung nach der Schleife fortgesetzt. Taucht dagegen eine
continue-Anweisung auf, springt das Programm an das Ende des Schleifenrumpfs und beginnt mit
der nchsten Iteration.
In der einfachsten Form arbeiten break und continue genauso wie in C und C++. Beide
Anweisungen knnen innerhalb von do-, while- und for-Schleifen verwendet werden. Befindet sich
ein break innerhalb einer mehrfach geschachtelten Schleife, so verlt es die innerste Schleife. Dies
gilt analog fr continue. Neben dieser einfachen Form von break und continue gibt es in Java
noch die mit einem Label versehene Form:
break Label;
continue Label;
Zu jedem mit einem Label versehenen break oder continue mu es eine mit einem Label versehene
Kontrollstruktur geben, die diese Anweisung umschliet. Oft wird ein mit einem Label versehenes
break verwendet, um zwei oder mehr ineinander geschachtelte Schleifen zu beenden:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
/* Listing0606.java */
import java.io.*;
public class Listing0606
{
public static void main(String[] args)
{
int[][] data = new int[10][10];
//Multiplikationstafel erstellen
for (int i = 1; i <= 10; ++i) {
for (int j = 1; j <= 10; ++j) {
data[i - 1][j - 1] = i * j;
}
}
//Produkt grer 43 suchen
loop1:
for (int i = 1; i <= 10; ++i) {
for (int j = 1; j <= 10; ++j) {
if (data[i - 1][j - 1] > 43) {
System.out.println(i + "*" + j + "=" + (i*j));
break loop1;
}
}
}
}
}
Listing0606.java
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
DOC
API
7.2.1 Klassen
Eine Klassendefinition in Java wird durch das Schlsselwort class eingeleitet. Anschlieend
folgt innerhalb von geschweiften Klammern eine beliebige Anzahl an Variablen- und
Methodendefinitionen. Das folgende Listing ist ein Beispiel fr eine einfache Klassendefinition:
001
002
003
004
005
006
007
008
/* Auto.java */
Auto.java
7.2.2 Objekte
Um von einer Klasse ein Objekt anzulegen, mu eine Variable vom Typ der Klasse deklariert
und ihr mit Hilfe des new-Operators ein neu erzeugtes Objekt zugewiesen werden:
Tip
Titel
<<
>
>>
DOC
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
7.3 Methoden
7.3 Methoden
7.3.1 Definition
7.3.2 Aufruf
7.3.3 Parameter
7.3.4 Rckgabewert
7.3.5 berladen von Methoden
Die Signatur einer Methode
7.3.6 Konstruktoren
Default-Konstruktoren
Verkettung von Konstruktoren
Initialisierungsreihenfolge
7.3.7 Destruktoren
7.3.1 Definition
Methoden definieren das Verhalten von Objekten. Sie werden innerhalb einer Klassendefinition angelegt
und haben Zugriff auf alle Variablen des Objekts. Methoden sind das Pendant zu den Funktionen anderer
Programmiersprachen, arbeiten aber immer mit den Variablen des aktuellen Objekts. Globale Funktionen,
die vollkommen unabhngig von einem Objekt oder einer Klasse existieren, gibt es in Java ebensowenig wie
globale Variablen. Wir werden spter allerdings Klassenvariablen und -methoden kennenlernen, die nicht an
eine konkrete Instanz gebunden sind.
Die Syntax der Methodendefinition in Java hnelt der von C/C++:
{Modifier}
Typ Name([Parameter])
{
{Anweisung;}
}
Nach einer Reihe von Modifiern (wir kommen weiter in Abschnitt 8.2 darauf zurck) folgen der Typ des
Rckgabewerts der Funktion, ihr Name und eine optionale Parameterliste. In geschweiften Klammern folgt
dann der Methodenrumpf, also die Liste der Anweisungen, die das Verhalten der Methode festlegen. Die
Erweiterung unserer Beispielklasse um eine Methode zur Berechnung des Alters des Auto-Objekts wrde
beispielsweise so aussehen:
7.3.2 Aufruf
Der Aufruf einer Methode erfolgt hnlich der Verwendung einer Instanzvariablen in Punktnotation. Zur
Unterscheidung von einem Variablenzugriff mssen zustzlich die Parameter der Methode in Klammern
angegeben werden, selbst wenn die Liste leer ist. Das folgende Programm wrde demnach die Zahl 10 auf
dem Bildschirm ausgeben.
001 Auto golf1 = new Auto();
002 golf1.erstzulassung = 1990;
003 System.out.println(golf1.alter());
Listing 7.7: Aufruf einer Methode
Wie an der Definition von alter zu erkennen ist, darf eine Methode auf die Instanzvariablen ihrer
Klasse zugreifen, ohne die Punktnotation zu verwenden. Das funktioniert deshalb, weil der Compiler
alle nicht in Punktnotation verwendeten Variablen x, die nicht lokale Variablen sind, auf das Objekt
this bezieht und damit als this.x interpretiert.
Tip
Bei this handelt es sich um einen Zeiger, der beim Anlegen eines Objekts automatisch generiert wird.
this ist eine Referenzvariable, die auf das aktuelle Objekt zeigt und dazu verwendet wird, die eigenen
Methoden und Instanzvariablen anzusprechen. Der this-Zeiger ist auch explizit verfgbar und kann wie
eine ganz normale Objektvariable verwendet werden. Er wird als versteckter Parameter an jede nichtstatische Methode bergeben. Die Methode alter htte also auch so geschrieben werden knnen:
001 public int alter()
002 {
003
return 2000 - this.erstzulassung;
004 }
Listing 7.8: Verwendung von this
Manchmal ist es sinnvoll, this explizit zu verwenden, auch wenn es nicht unbedingt erforderlich ist. Tip
Dadurch wird hervorgehoben, da es sich um den Zugriff auf eine Instanzvariable, und nicht eine
lokale Variable, handelt.
7.3.3 Parameter
Eine Methode kann mit Parametern definiert werden. Dazu wird bei der Methodendefinition eine
Parameterliste innerhalb der Klammern angegeben. Jeder formale Parameter besteht aus einem Typnamen
und dem Namen des Parameters. Soll mehr als ein Parameter definiert werden, so sind die einzelnen
Definitionen durch Kommata zu trennen.
Alle Parameter werden in Java per call by value bergeben. Beim Aufruf einer Methode wird also der
aktuelle Wert in die Parametervariable kopiert und an die Methode bergeben. Vernderungen der
Parametervariablen innerhalb der Methode bleiben lokal und wirken sich nicht auf den Aufrufer aus. Das
folgende Beispiel definiert eine Methode printAlter, die das Alter des Autos insgesamt wieoft mal
auf dem Bildschirm ausgibt:
001 public void printAlter(int wieoft)
002 {
003
while (wieoft-- > 0) {
004
System.out.println("Alter = " + alter());
005
}
006 }
Listing 7.9: Eine Methode zur Ausgabe des Alters
Obwohl der Parameter wieoft innerhalb der Methode verndert wird, merkt ein Aufrufer nichts von
diesen nderungen, da innerhalb der Methode mit einer Kopie gearbeitet wird. Das folgende Programm
wrde das Alter des Objekts auto daher insgesamt neunmal auf dem Bildschirm ausgeben:
001
002
003
004
005
006
007
...
int a = 3;
auto.printAlter(a);
auto.printAlter(a);
auto.printAlter(a);
...
Listing 7.10: Wiederholter Aufruf der Methode zur Ausgabe des Alters
Wie bereits erwhnt, sind Objektvariablen Referenzen, also Zeiger. Zwar werden auch sie bei der bergabe
an eine Methode per Wert bergeben. Da innerhalb der Methode aber der Zeiger auf das Originalobjekt zur
Verfgung steht (wenn auch in kopierter Form), wirken sich Vernderungen an dem Objekt natrlich direkt
auf das Originalobjekt aus und sind somit fr den Aufrufer der Methode sichtbar. Wie in allen anderen
Programmiersprachen entspricht die call by value-bergabe eines Zeigers damit natrlich genau der
Semantik von call by reference.
Die bergabe von Objekten an Methoden hat damit zwei wichtige Konsequenzen:
Hinweis
Die Methode erhlt keine Kopie, sondern arbeitet mit dem Originalobjekt.
Die bergabe von Objekten ist performant, gleichgltig wie gro sie sind.
Sollen Objekte kopiert werden, so mu dies explizit durch Aufruf der Methode clone der Klasse Object
erfolgen.
Die bergabe von Objekten und Arrays per Referenz kann leicht zu verdeckten Fehlern fhren. Warnung
Da die aufgerufene Methode mit dem Originalobjekt arbeitet, kann sie deren Membervariablen
bzw. Elemente verndern, ohne da der Aufrufer es merkt. Auch der final-Modifier (siehe
Abschnitt 8.2) bietet dagegen keinen Schutz. Das unbeabsichtigte ndern einer modifizierbaren
Referenzvariable bei der bergabe an eine Methode kann nur durch vorheriges Kopieren
verhindert werden.
7.3.4 Rckgabewert
Jede Methode in Java ist typisiert. Der Typ einer Methode wird zum Zeitpunkt der Definition festgelegt und
bestimmt den Typ des Rckgabewerts. Dieser kann von einem beliebigen primitiven Typ, einem Objekttyp
(also einer Klasse) oder vom Typ void sein. Die Methoden vom Typ void haben gar keinen
Rckgabewert und drfen nicht in Ausdrcken verwendet werden. Sie sind lediglich wegen ihrer
Nebeneffekte von Interesse und drfen daher nur als Ausdrucksanweisung verwendet werden.
Hat eine Methode einen Rckgabewert (ist also nicht vom Typ void), so kann sie mit Hilfe der returnAnweisung einen Wert an den Aufrufer zurckgeben. Die return-Anweisung hat folgende Syntax:
return Ausdruck;
Wenn diese Anweisung ausgefhrt wird, fhrt dies zum Beenden der Methode, und der Wert des
angegebenen Ausdrucks wird an den Aufrufer zurckgegeben. Der Ausdruck mu dabei
zuweisungskompatibel zum Typ der Funktion sein. Die in Kapitel 5 erluterte Datenfluanalyse sorgt dafr,
da hinter der return-Anweisung keine unerreichbaren Anweisungen stehen und da jeder mgliche
Ausgang einer Funktion mit einem return versehen ist. Der in C beliebte Fehler, einen Funktionsausgang
ohne return-Anweisung zu erzeugen (und damit einen undefinierten Rckgabewert zu erzeugen), kann in
Java also nicht passieren.
Tip
Das folgende Beispiel erweitert die Klasse Auto um eine weitere Methode alter, die das Alter des Autos
nicht nur zurckgibt, sondern es auch mit einem als Parameter bergebenen Titel versieht und auf dem
Bildschirm ausgibt:
001 public int alter(String titel)
002 {
003
int alter = alter();
004
System.out.println(titel+alter);
005
return alter;
006 }
Listing 7.11: berladen einer Methode
Die Signatur einer Methode
Innerhalb dieser Methode wird der Name alter in drei verschiedenen Bedeutungen verwendet. Erstens ist
alter der Name der Methode selbst. Zweitens wird die lokale Variable alter definiert, um drittens den
Rckgabewert der parameterlosen alter-Methode aufzunehmen. Der Compiler kann die Namen in allen
drei Fllen unterscheiden, denn er arbeitet mit der Signatur der Methode. Unter der Signatur einer Methode
versteht man ihren internen Namen. Dieser setzt sich aus dem nach auen sichtbaren Namen plus codierter
Information ber die Reihenfolge und Typen der formalen Parameter zusammen. Die Signaturen zweier
gleichnamiger Methoden sind also immer dann unterscheidbar, wenn sie sich wenigstens in einem
Parameter voneinander unterscheiden.
7.3.6 Konstruktoren
In jeder objektorientierten Programmiersprache lassen sich spezielle Methoden definieren, die bei der
Initialisierung eines Objekts aufgerufen werden: die Konstruktoren. In Java werden Konstruktoren als
Methoden ohne Rckgabewert definiert, die den Namen der Klasse erhalten, zu der sie gehren.
Konstruktoren drfen eine beliebige Anzahl an Parametern haben und knnen berladen werden. Die
Erweiterung unserer Auto-Klasse um einen Konstruktor, der den Namen des Auto-Objekts vorgibt, sieht
beispielsweise so aus:
001 public class Auto
002 {
003
public String name;
004
public int
erstzulassung;
005
public int
leistung;
006
007
public Auto(String name)
008
{
009
this.name = name;
010
}
011 }
Listing 7.12: Definition eines parametrisierten Konstruktors
Soll ein Objekt unter Verwendung eines parametrisierten Konstruktors instanziert werden, so sind die
Argumente wie bei einem Methodenaufruf in Klammern nach dem Namen des Konstruktors anzugeben:
001 Auto dasAuto = new Auto("Porsche 911");
002 System.out.println(dasAuto.name);
Tip
Wir wollen das vorige Beispiel um einen Konstruktor erweitern, der alle Instanzvariablen initialisiert:
001 public class Auto
002 {
003
public String name;
004
public int
erstzulassung;
005
public int
leistung;
006
007
public Auto(String name)
008
{
009
this.name = name;
010
}
011
012
public Auto(String name,
013
int
erstzulassung,
014
int
leistung)
015
{
016
this.name = name;
017
this.erstzulassung = erstzulassung;
018
this.leistung = leistung;
019
}
020 }
Listing 7.14: Eine Klasse mit mehreren Konstruktoren
Default-Konstruktoren
Falls eine Klasse berhaupt keinen expliziten Konstruktor besitzt, wird vom Compiler automatisch ein
parameterloser default-Konstruktor generiert. Seine einzige Aufgabe besteht darin, den parameterlosen
Konstruktor der Superklasse aufzurufen. Enthlt eine Klassendeklaration dagegen nur parametrisierte
Konstruktoren, wird kein default-Konstruktor erzeugt, und die Klassendatei besitzt berhaupt keinen
parameterlosen Konstruktor.
Verkettung von Konstruktoren
Unterschiedliche Konstruktoren einer Klasse knnen in Java verkettet werden, d.h. sie knnen sich
gegenseitig aufrufen. Der aufzurufende Konstruktor wird dabei als eine normale Methode angesehen, die
ber den Namen this aufgerufen werden kann. Die Unterscheidung zum bereits vorgestellten thisPointer nimmt der Compiler anhand der runden Klammern vor, die dem Aufruf folgen. Der im vorigen
Beispiel vorgestellte Konstruktor htte damit auch so geschrieben werden knnen:
Hinweis
Wird ein Konstruktor in einem anderen Konstruktor derselben Klasse explizit aufgerufen, mu
dies als erste Anweisung innerhalb der Methode geschehen. Steht der Aufruf nicht an erster
Stelle, gibt es einen Compiler-Fehler.
Warnung
Es gibt noch eine zweite Form der Konstruktorenverkettung. Sie findet automatisch statt und dient dazu,
abgeleitete Klassen whrend der Instanzierung korrekt zu initialisieren. In Abschnitt 8.1.4 werden wir auf
die Details dieses Mechanismus eingehen.
Initialisierungsreihenfolge
Beim Instanzieren eines neuen Objekts werden die Initialisierungschritte in einer genau festgelegten
Reihenfolge ausgefhrt:
/* Listing0716.java */
public class Listing0716
{
public static String getAndPrint(String s)
{
System.out.println(s);
return s;
}
public static void main(String[] args)
{
Son son = new Son();
}
}
Listing0716.java
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
class Father
{
private String s1 = Listing0716.getAndPrint("Father.s1");
public Father()
{
Listing0716.getAndPrint("Father.<init>");
}
}
class Son
extends Father
{
private String s1 = Listing0716.getAndPrint("Son.s1");
public Son()
{
Listing0716.getAndPrint("Son.<init>");
}
}
7.3.7 Destruktoren
Neben Konstruktoren, die whrend der Initialisierung eines Objekts aufgerufen werden, gibt es in Java auch
Destruktoren. Sie werden unmittelbar vor dem Zerstren eines Objekts aufgerufen.
Ein Destruktor wird als geschtzte (protected) parameterlose Methode mit dem Namen finalize
definiert:
001 protected void finalize()
002 {
003
...
004 }
Listing 7.17: Die finalize-Methode
Da Java ber ein automatisches Speichermanagement verfgt, kommt den Destruktoren hier eine Hinweis
viel geringere Bedeutung zu als in anderen objektorientierten Sprachen. Anders als etwa in C++
mu sich der Entwickler ja nicht um die Rckgabe von belegtem Speicher kmmern; und das ist
sicher eine der Hauptaufgaben von Destruktoren in C++.
Tatschlich garantiert die Sprachspezifikation nicht, da ein Destruktor berhaupt aufgerufen wird. Wenn er
aber aufgerufen wird, so erfolgt dies nicht, wenn die Lebensdauer des Objektes endet, sondern dann, wenn
der Garbage Collector den fr das Objekt reservierten Speicherplatz zurckgibt. Dies kann unter Umstnden
nicht nur viel spter der Fall sein (der Garbage Collector luft ja als asynchroner Hintergrundproze),
sondern auch gar nicht. Wird nmlich das Programm beendet, bevor der Garbage Collector das nchste Mal
aufgerufen wird, werden auch keine Destruktoren aufgerufen. Selbst wenn Destruktoren aufgerufen werden,
ist die Reihenfolge oder der Zeitpunkt ihres Aufrufs undefiniert. Der Einsatz von Destruktoren in Java sollte
also mit der ntigen Vorsicht erfolgen.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
8.1 Vererbung
8.1 Vererbung
8.1.1 Ableiten einer Klasse
8.1.2 Die Klasse Object
8.1.3 berlagern von Methoden
Dynamische Methodensuche
Aufrufen einer verdeckten Methode
8.1.4 Vererbung von Konstruktoren
Konstruktorenverkettung
Der Default-Konstruktor
berlagerte Konstruktoren
Destruktorenverkettung
Um eine neue Klasse aus einer bestehenden abzuleiten, ist im Kopf der Klasse mit
Hilfe des Schlsselworts extends ein Verweis auf die Basisklasse anzugeben.
Hierdurch erbt die abgeleitete Klasse alle Eigenschaften der Basisklasse, d.h. alle
Variablen und alle Methoden. Durch Hinzufgen neuer Elemente oder berladen der
vorhandenen kann die Funktionalitt der abgeleiteten Klasse erweitert werden.
Als Beispiel wollen wir eine neue Klasse Cabrio definieren, die sich von Auto nur
dadurch unterscheidet, da sie zustzlich die Zeit, die zum ffnen des Verdecks
bentigt wird, speichern soll:
001
002
003
004
005
class Cabrio
extends Auto
{
int vdauer;
}
001
002
003
004
005
class ZweisitzerCabrio
extends Cabrio
{
boolean notsitze;
}
java.lang.Object
Die Methode equals testet, ob zwei Objekte denselben Inhalt haben, clone
kopiert ein Objekt, toString erzeugt eine String-Reprsentation des Objekts,
und hashCode berechnet einen numerischen Wert, der als Schlssel zur
Speicherung eines Objekts in einer Hashtable verwendet werden kann. Damit
diese Methoden in abgeleiteten Klassen vernnftig funktionieren, mssen sie bei
Bedarf berlagert werden. Fr equals und clone gilt das insbesondere, wenn das
Objekt Referenzen enthlt.
001
002
003
004
005
006
007
008
009
010
class ZweisitzerCabrio
extends Cabrio
{
boolean notsitze;
public int alter()
{
return 12 * (2000 - erstzulassung);
}
}
vernachlssigen und liegt deutlich ber den Kosten eines statischen Methodenaufrufs.
Um das Problem zu umgehen, gibt es mehrere Mglichkeiten, dafr zu sorgen, da
eine Methode nicht dynamisch interpretiert wird. Dabei wird mit Hilfe zustzlicher
Attribute dafr gesorgt, da die betreffende Methode nicht berlagert werden kann:
Methoden vom Typ private sind in abgeleiteten Klassen nicht sichtbar und
knnen daher nicht berlagert werden.
Bei Methoden vom Typ final deklariert der Anwender explizit, da sie
nicht berlagert werden sollen.
Auch bei static-Methoden, die ja unabhngig von einer Instanz existieren,
besteht das Problem nicht.
Falls als erste Anweisung im Konstruktor kein Aufruf von super steht, setzt der
Compiler an dieser Stelle einen impliziten Aufruf super(); ein und ruft damit den
parameterlosen Konstruktor der Vaterklasse auf. Falls ein solcher Konstruktor in der
Vaterklasse nicht definiert wurde, gibt es einen Compiler-Fehler. Das ist genau dann
der Fall, wenn in der Superklassendeklaration lediglich parametrisierte
Konstruktoren angegeben wurden und daher ein parameterloser default-Konstruktor
nicht automatisch erzeugt wurde.
Alternativ zu diesen beiden Varianten, einen Superklassenkonstruktor
aufzurufen, ist es auch erlaubt, mit Hilfe der this-Methode einen
anderen Konstruktor der eigenen Klasse aufzurufen. Um die oben
erwhnten Zusagen einzuhalten, mu dieser allerdings selbst direkt oder
indirekt schlielich einen Superklassenkonstruktor aufrufen.
Hinweis
Der Default-Konstruktor
Das Anlegen von Konstruktoren in einer Klasse ist optional. Falls in einer Klasse
berhaupt kein Konstruktor definiert wurde, erzeugt der Compiler beim bersetzen
der Klasse automatisch einen parameterlosen default-Konstruktor. Dieser enthlt
lediglich einen Aufruf des parameterlosen Superklassenkonstruktors.
berlagerte Konstruktoren
Konstruktoren werden nicht vererbt. Alle Konstruktoren, die in einer
abgeleiteten Klasse bentigt werden, mssen neu definiert werden, selbst
wenn sie nur aus einem Aufruf des Superklassenkonstruktors bestehen.
Hinweis
Durch diese Regel wird bei jedem Neuanlegen eines Objekts eine ganze Kette von
Konstruktoren aufgerufen. Da nach den obigen Regeln jeder Konstruktor zuerst den
Superklassenkonstruktor aufruft, wird die Initialisierung von oben nach unten in der
Vererbungshierarchie durchgefhrt: zuerst wird der Konstruktor der Klasse Object
ausgefhrt, dann der der ersten Unterklasse usw., bis zuletzt der Konstruktor der zu
instanzierenden Klasse ausgefhrt wird.
Destruktorenverkettung
Im Gegensatz zu den Konstruktoren werden die Destruktoren eines
Ableitungszweiges nicht automatisch verkettet. Falls eine
Destruktorenverkettung erforderlich ist, kann sie durch explizite Aufrufe
des Superklassendestruktors mit Hilfe der Anweisung
super.finalize() durchgefhrt werden.
Hinweis
<
>
>>
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
8.2 Modifier
8.2 Modifier
8.2.1 Sichtbarkeit
8.2.2 Die Attribute im berblick
private
protected
public
Standard (package scoped)
static
final
transient
volatile
In diesem Kapitel wurden an verschiedenen Stellen Beispiele gezeigt, in denen Schlsselwrter wie public oder
private zusammen mit bestimmten Programmelementen verwendet wurden. Mit Hilfe dieser Attribute knnen die
Eigenschaften von Klassen, Methoden und Variablen verndert werden. Sie haben insbesondere Einflu auf die
Lebensdauer, Sichtbarkeit und Vernderbarkeit dieser Programmelemente und werden meist als Modifier bezeichnet. Wir
wollen sie nun im Zusammenhang betrachten und ihre Wirkungsweise auf die verschiedenen Elemente eines JavaProgramms erlutern.
8.2.1 Sichtbarkeit
Die eingangs erwhnte Tatsache, da in einer abgeleiteten Klasse alle Eigenschaften der Basisklasse bernommen werden,
ist nicht in allen Fllen ganz korrekt. Zwar besitzt sie immer alle Variablen und Methoden der Basisklasse, kann aber unter
Umstnden nicht darauf zugreifen, wenn ihre Sichtbarkeit eingeschrnkt wurde.
Die Sichtbarkeit von Variablen und Methoden wird mit Hilfe folgender Modifier geregelt:
Elemente des Typs public sind in der Klasse selbst (also in ihren Methoden), in Methoden abgeleiteter Klassen
und fr den Aufrufer von Instanzen der Klasse sichtbar.
Elemente des Typs protected sind in der Klasse selbst und in Methoden abgeleiteter Klassen sichtbar. Zustzlich
knnen Klassen desselben Pakets sie aufrufen.
Elemente des Typs private sind lediglich in der Klasse selbst sichtbar. Fr abgeleitete Klassen und fr Aufrufer
von Instanzen bleiben private-Variablen verdeckt.
Elemente, die ohne einen der drei genannten Modifier deklariert wurden, werden als package scoped oder Elemente
mit Standard-Sichtbarkeit bezeichnet. Sie sind nur innerhalb des Pakets sichtbar, zu dem diese Klasse gehrt. In
anderen Paketen sind sie dagegen unsichtbar.
Mit Hilfe dieser Sichtbarkeitsebenen kann der Zugriff auf Klassenelemente eingeschrnkt werden. privateTip
Elemente sollten immer dann verwendet werden, wenn implementierungsabhngige Details zu verstecken sind, die
auch in abgeleiteten Klassen nicht sichtbar sein sollen. protected-Elemente sind vor Zugriffen von auen
geschtzt, knnen aber von abgeleiteten Klassen verwendet werden. Die public-Elemente schlielich bilden die fr
alle sichtbaren Teile einer Klassendefinition und knnen daher als ihre Schnittstelle angesehen werden. Nachfolgend
werden die verschiedenen Sichtbarkeitsattribute noch einmal genau beschrieben. Elemente mit Standard-Sichtbarkeit
verhalten sich innerhalb des Pakets wie public- und auerhalb wie private-Elemente.
Hinweis
Das folgende Beispielprogramm demonstriert dies mit Hilfe der Klasse ClassWithPrivateA, die eine
private Membervariable a besitzt. An der Implementierung von setOtherA knnen wir erkennen, wie der
Zugriff auf fremde Objekte desselben Typs mglich ist:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
/* Listing0806.java */
Listing0806.java
An der Ausgabe des Programms kann man erkennen, da ber das Objekt a2 auf private Membervariablen des Objekts a1
zugegriffen wurde:
a1 = 999
a2 = 11
protected
Methoden oder Variablen vom Typ protected sind in der aktuellen Klasse und in abgeleiteten Klassen sichtbar. Darber
hinaus sind sie fr Methoden anderer Klassen innerhalb desselben Pakets sichtbar. Sie sind jedoch nicht fr Aufrufer der
Klasse sichtbar, die in anderen Paketen definiert wurden.
public
Membervariablen und Methoden vom Typ public sind im Rahmen ihrer Lebensdauer berall sichtbar. Sie knnen daher
in der eigenen Klasse und von beliebigen Methoden anderer Klassen verwendet werden. Das Attribut public ist
zustzlich auch bei der Klassendefinition selbst von Bedeutung, denn nur Klassen, die als public deklariert wurden, sind
auerhalb des Pakets sichtbar, in dem sie definiert wurden. In jeder Quelldatei darf nur eine Klasse mit dem Attribut
public angelegt werden.
Standard (package scoped)
Klassen, Methoden, Variablen mit Standard-Sichtbarkeit sind nur innerhalb des Pakets sichtbar, in dem sie definiert
wurden. Sie sind beispielsweise ntzlich, um in aufwendigeren Paketen allgemein zugngliche Hilfsklassen zu realisieren,
die auerhalb des Pakets unsichtbar bleiben sollen. Sie knnen mitunter ntzlich sein, um zu verhindern, da Elemente als
public deklariert werden.
static
Variablen und Methoden mit dem Attribut static sind nicht an die Existenz eines konkreten Objekts gebunden, sondern
existieren vom Laden der Klasse bis zum Beenden des Programms. Das static-Attribut beeinflut bei Membervariablen
ihre Lebensdauer und erlaubt bei Methoden den Aufruf, ohne da der Aufrufer ein Objekt der Klasse besitzt, in der die
Methode definiert wurde.
Wird das Attribut static nicht verwendet, so sind Variablen innerhalb einer Klasse immer an eine konkrete Instanz
gebunden. Ihre Lebensdauer beginnt mit dem Anlegen des Objekts und dem Aufruf eines Konstruktors und endet mit der
Freigabe des Objekts durch den Garbage Collector.
final
Membervariablen mit dem Attribut final drfen nicht verndert werden, sind also als Konstanten anzusehen. Methoden
des Typs final drfen nicht berlagert werden; ebensowenig drfen Klassen des Typs final zur Ableitung neuer
Klassen verwendet werden. Wird das Attribut final dagegen nicht verwendet, sind Membervariablen vernderbar,
knnen Methoden berlagert und Klassen abgeleitet werden.
Falls eine Methode oder Klasse das Attribut final besitzt, kann der Compiler auf die dynamische Methodensuche
verzichten. final-Methoden knnen daher performanter aufgerufen werden als normale Methoden. Dies ist einer der
Grnde dafr, da die Java-Designer einige der mitgelieferten Klassen als final deklariert haben. Es fhrt aber
gleichzeitig dazu, da die entsprechenden Klassen nicht mehr erweitert werden knnen. Ein prominentes Beispiel aus der
Laufzeitbibliothek ist die als final deklarierte Klasse String.
Seit dem JDK 1.1 kann das final-Attribut auch auf Parameter von Methoden und lokale Variablen angewendet werden.
Dadurch stellt der Compiler sicher, da die Variable bzw. der Parameter nach der Initialisierung nicht mehr verndert wird.
Die Initialisierung mu dabei nicht unbedingt bei der Deklaration erfolgen, sondern kann auch spter vorgenommen
werden. Wichtig ist, da nur genau einmal ein Wert zugewiesen wird.
Im Gegensatz zu C oder C++ gibt es allerdings bei als final deklarierten Objektparametern keine
Mglichkeit, zwischen dem Objekt insgesamt und seinen einzelnen Elementen zu unterscheiden. Eine als
final deklarierte Objektvariable wird zwar insgesamt vor Zuweisungen geschtzt, der Wert einzelner
Membervariablen kann jedoch verndert werden. Dies gilt analog fr Arrays, die ja ebenfalls Objekte sind:
final bietet keinen Schutz gegen die unerwnschte Zuweisung eines Werts an ein einzelnes Element des
Arrays.
Warnung
transient
Membervariablen knnen mit dem Attribut transient belegt werden, um anzuzeigen, da sie keine persistente Form
besitzen. Sie werden beim Serialisieren und Deserialisieren von Objekten dieses Typs ignoriert. Details werden in Kapitel
41 beschrieben.
volatile
Das Schlsselwort volatile wird verwendet, um anzuzeigen, da Membervariablen asynchron, also auerhalb des
aktuellen Threads, modifiziert werden knnen. Der Wert einer so deklarierten Variable wird daher bei jedem Zugriff erneut
gelesen (anstatt mglicherweise direkt aus einem Register der virtuellen Maschine genommen zu werden). Die Verwendung
von volatile ist eher ungebruchlich. Es kann beispielsweise zur Sicherstellung der Datenintegritt beim Multithreading
verwendet werden oder dient dazu, Zugriffe auf asynchron vernderliche Speicherstellen (etwa eine Echtzeituhr, auf die
ber eine Variable zugegriffen wird) stets aktuell zu halten.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
>
>>
API
8.3.1 Klassenvariablen
Java ist eine konsequent objektorientierte Sprache, in der es weder globale Funktionen noch
globale Variablen gibt. Da es aber mitunter sinnvoll ist, Eigenschaften zu verwenden, die
nicht an Instanzen einer Klasse gebunden sind, haben die Sprachdesigner das Attribut
static fr Methoden und Variablen eingefhrt. Eine Variable, die innerhalb einer Klasse
mit dem Attribut static versehen wurde, nennt man Klassenvariable (oder auch Statische
Variable). Im Gegensatz zu Instanzvariablen, die immer an ein konkretes Objekt gebunden
sind, existieren Klassenvariablen unabhngig von einem Objekt.
Jede Klassenvariable wird nur einmal angelegt und kann von allen Methoden der Klasse
aufgerufen werden. Da sich alle Methoden die Variable teilen, sind Vernderungen, die
eine Instanz vornimmt, auch in allen anderen Instanzen sichtbar. Klassenvariablen sind daher
vergleichbar mit globalen Variablen, denn ihre Lebensdauer erstreckt sich auf das gesamte
Programm. Namenskollisionen knnen allerdings nicht auftreten, denn der Zugriff von auen
erfolgt durch Qualifizierung mit dem Klassennamen in der Form
Klassenname.Variablenname.
Ein einfaches Beispiel fr die Verwendung von Klassenvariablen besteht darin, einen
Instanzenzhler in eine Klasse einzubauen. Hierzu wird eine beliebige Klassenvariable
eingefhrt, die beim Erzeugen eines Objekts hoch- und beim Zerstren heruntergezhlt wird.
Das folgende Beispiel demonstriert das fr die Klasse Auto:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
/* Testauto.java */
Testauto.java
8.3.2 Konstanten
Eine andere Anwendung von Klassenvariablen besteht in der Deklaration von Konstanten.
Dazu wird das static-Attribut mit dem final-Attribut kombiniert, um eine
unvernderliche Variable mit unbegrenzter Lebensdauer zu erzeugen:
8.3.3 Klassenmethoden
Neben Klassenvariablen gibt es in Java auch Klassenmethoden, d.h. Methoden, die
unabhngig von einer bestimmten Instanz existieren. Sie werden auch als statische Methoden
bezeichnet. Klassenmethoden werden ebenfalls mit Hilfe des static-Attributs deklariert
und - analog zu Klassenvariablen - durch Voranstellen des Klassennamens aufgerufen.
Da Klassenmethoden unabhngig von konkreten Instanzen ihrer Klasse existieren, ist ein
Zugriff auf Instanzvariablen nicht mglich. Diese Trennung uert sich darin, da
Klassenmethoden keinen this-Zeiger besitzen. Der Zugriff auf Instanzvariablen und der
Aufruf von Instanzmethoden wird daher schon zur Compile-Zeit als Fehler erkannt.
Klassenmethoden werden hufig da eingesetzt, wo Funktionalitten zur Verfgung
Tip
gestellt werden, die nicht datenzentriert arbeiten oder auf primitiven Datentypen
operieren. Beispiele fr beide Arten sind in der Klassenbibliothek zu finden. Zur ersten
Gruppe gehren beispielsweise die Methoden der Klasse System. Die Klasse
System ist eine Art Toolbox, die Funktionen wie Aufruf des Garbage Collectors oder
Beenden des Programms zur Verfgung stellt. Zur zweiten Gruppe gehren
beispielsweise die Methoden der Klasse Math, die eine groe Anzahl an Funktionen
zur Fliekomma-Arithmetik zur Verfgung stellen. Da die Fliekommatypen primitiv
sind, htte ein instanzbasiertes Methodendesign an dieser Stelle wenig Sinn.
Eine weitere Anwendung fr Klassenmethoden liegt in der Instanzierung von Objekten
der eigenen Klasse. Beispiele dafr finden sich in den Design-Patterns Singleton (siehe
Abschnitt 10.3.1) und Factory-Methode (siehe Abschnitt 10.3.4).
Das folgende Listing zeigt die Verwendung der Klassenmethode sqrt der Klasse Math zur
Ausgabe einer Tabelle von Quadratwurzeln:
001
002
003
004
005
006
007
008
009
010
011
012
013
/* Listing0809.java */
Listing0809.java
>
>>
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
die Gehaltsberechnung durchgefhrt wird. Dazu wird ein Array ma mit konkreten Untertypen der Klasse
Mitarbeiter gefllt (hier nur angedeutet; die Daten knnten zum Beispiel aus einer Datenbank
gelesen werden) und dann fr alle Elemente das Monatsgehalt durch Aufruf von monatsBrutto
ermittelt.
Das Listing ist ebenfalls ein Beispiel fr Polymorphismus. Eine Variable vom Typ einer
Hinweis
Basisklasse nimmt zur Laufzeit unterschiedliche Objekte aus abgeleiteten Klassen auf. Da
bereits in der Basisklasse die Definition von monatsBrutto vorgenommen wurde,
akzeptiert der Compiler den Aufruf dieser Methode bereits bei Objekten dieser vermeintlich
abstrakten Klasse. Erst zur Laufzeit ist dann bekannt, welcher abgeleitete Typ hinter jedem
einzelnen Array-Element steht, und das Laufzeitsystem ruft die darin implementierte Variante
der Methode monatsBrutto auf.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
/* Gehaltsberechnung.java */
import java.util.Date;
abstract class Mitarbeiter
{
int persnr;
String name;
Date eintritt;
public Mitarbeiter()
{
}
public abstract double monatsBrutto();
}
class Arbeiter
extends Mitarbeiter
{
double stundenlohn;
double anzahlstunden;
double ueberstundenzuschlag;
double anzahlueberstunden;
double schichtzulage;
public double monatsBrutto()
{
return stundenlohn*anzahlstunden+
ueberstundenzuschlag*anzahlueberstunden+
schichtzulage;
}
}
class Angestellter
extends Mitarbeiter
{
double grundgehalt;
double ortszuschlag;
Gehaltsberechnung.java
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
double zulage;
public double monatsBrutto()
{
return grundgehalt+
ortszuschlag+
zulage;
}
}
class Manager
extends Mitarbeiter
{
double fixgehalt;
double provision1;
double provision2;
double umsatz1;
double umsatz2;
public double monatsBrutto()
{
return fixgehalt+
umsatz1*provision1/100+
umsatz2*provision2/100;
}
}
public class Gehaltsberechnung
{
private static final int ANZ_MA = 100;
private static Mitarbeiter[] ma;
private static double bruttosumme;
public static void main(String[] args)
{
ma = new Mitarbeiter[ANZ_MA];
//Mitarbeiter-Array fllen, z.B.
//ma[0] = new Manager();
//ma[1] = new Arbeiter();
//ma[2] = new Angestellter();
//...
//Bruttosumme berechnen
bruttosumme = 0.0;
for (int i=0; i<ma.length; ++i) {
bruttosumme += ma[i].monatsBrutto();
}
System.out.println("Bruttosumme = "+bruttosumme);
}
}
Zuerst werden die Konstruktoren der Vaterklassen aufgerufen und so deren Membervariablen
initialisiert.
Dann werden die Initialisierer und initialisierenden Zuweisungen der eigenen Klasse aufgerufen.
Schlielich wird der Rumpf des eigenen Konstruktors ausgefhrt.
Wird nun im eigenen Konstruktor eine Methode aufgerufen, die in einer abgeleiteten Klasse berlagert
wurde, sind die Membervariablen der abgeleiteten Klasse noch nicht initialisiert. Ihr Konstruktor wird ja
erst spter aufgerufen. Das folgende Beispiel illustriert dieses Problem:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
/* Listing0813.java */
Listing0813.java
class SingleValue
{
protected int value1;
public SingleValue(int value1)
{
this.value1 = value1;
print();
}
public void print()
{
System.out.println("value = " + value1);
}
}
class ValuePair
extends SingleValue
{
protected int value2;
public ValuePair(int value1, int value2)
{
super(value1);
this.value2 = value2;
}
public void print()
{
System.out.println(
"value = (" + value1 + "," + value2 + ")"
);
}
}
public class Listing0813
{
public static void main(String[] args)
{
new ValuePair(10,20);
}
}
SingleValue komplett abgearbeitet wre), wird an seiner Stelle 0 ausgegeben (beim Anlegen eines
neuen Objekts wird der belegte Speicher mit Nullen gefllt). Das kann zu schwer auffindbaren Fehlern
fhren. Aufrufe von Methoden, die mglicherweise berlagert werden, sollten daher im Konstruktor
vermieden werden.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
9.1 Grundlagen
9.1 Grundlagen
9.1.1 Definition eines Interfaces
9.1.2 Implementierung eines Interfaces
9.1.3 Verwenden eines Interfaces
Es wurde bereits erwhnt, da es in Java keine Mehrfachvererbung von Klassen gibt. Die mglichen
Schwierigkeiten beim Umgang mit mehrfacher Vererbung und die Einsicht, da das Erben nichttrivialer
Methoden aus mehr als einer Klasse in der Praxis selten zu realisieren ist, haben die Designer dazu
veranlat, dieses Feature nicht zu implementieren. Andererseits sah man es als wnschenswert an, da
Klassen eine oder mehrere Schnittstellendefinitionen erben knnen, und hat mit den Interfaces ein
Ersatzkonstrukt geschaffen, das dieses Feature bietet.
/* Groesse.java */
Groesse.java
/* Auto2.java */
Auto2.java
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
FussballPlatz.java
PapierBlatt.java
/* PapierBlatt.java */
public class PapierBlatt
implements Groesse
{
public int format; //0=DIN A0, 1=DIN A1 usw.
public int laenge()
{
int ret = 0;
if (format == 0) {
ret = 1189;
} else if (format ==
ret = 841;
} else if (format ==
ret = 594;
} else if (format ==
ret = 420;
} else if (format ==
ret = 297;
}
//usw...
return ret;
}
public int hoehe()
1) {
2) {
3) {
4) {
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048 }
{
return 0;
}
public int breite()
{
int ret = 0;
if (format == 0) {
ret = 841;
} else if (format ==
ret = 594;
} else if (format ==
ret = 420;
} else if (format ==
ret = 297;
} else if (format ==
ret = 210;
}
//usw...
return ret;
}
1) {
2) {
3) {
4) {
Hinweis
Eine Klasse kann ein Interface auch dann implementieren, wenn sie nicht alle seine Methoden
implementiert. In diesem Fall ist die Klasse allerdings als abstract zu deklarieren und kann
nicht dazu verwendet werden, Objekte zu instanzieren.
Tip
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
/* Listing0905.java */
Listing0905.java
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Interfaces werden verwendet, um Eigenschaften auszudrcken, die auf Klassen aus unterschiedlichen
Klassenhierarchien zutreffen knnen. Das erkennt man auch daran, da ihre Namen oft (substantivierte)
Eigenschaftswrter sind. Ein bekanntes Beispiel, das mit der Version 1.2 in der Java-Klassenbibliothek
eingefhrt wurde, ist das Interface Comparable des Pakets java.lang:
public interface Comparable
{
public int compareTo(Object o);
}
Dieses Interface kann von Klassen implementiert werden, deren Objekte paarweise vergleichbar sind.
Die Methode compareTo liefert genau dann einen Wert kleiner 0, wenn das Objekt kleiner; grer
0, wenn es grer, und gleich 0, wenn es gleich dem als Argument bergebenen Objekt o ist. In
der Klassenbibliothek gibt es eine ganze Reihe von Klassen, die Comparable implementieren,
beispielsweise String und Character oder die numerischen Wrapper-Klassen (letztere werden in
Abschnitt 10.2 erlutert).
Mit Hilfe von Comparable kann die Reihenfolge der Objekte einer Klasse ermittelt werden. Aus dem
paarweisen Vergleich lt sich eine (nicht notwendigerweise eindeutige) implizite Ordnung der
Elemente ableiten, denn fr alle aufeinanderfolgenden Objekte a und b mu a.compareTo(b) <=
0 gelten. Damit ist es mglich, Methoden zu schreiben, die das kleinste oder grte Element einer
Menge von Objekten ermitteln oder diese sortieren:
Listing0907.java
001 /* Listing0907.java */
002
003 public class Listing0907
004 {
005
public static Object getSmallest(Comparable[] objects)
006
{
007
Object smallest = objects[0];
008
for (int i = 1; i < objects.length; ++i) {
009
if (objects[i].compareTo(smallest) < 0) {
010
smallest = objects[i];
011
}
012
}
013
return smallest;
014
}
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049 }
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Tip
Titel
<<
>
>>
API
9.3.1 Mehrfachimplementierung
Es ist durchaus mglich (und gebruchlich), da eine Klasse mehrere Interfaces
implementiert. Sie mu dann zu jedem Interface alle darin definierten Methoden
implementieren. Mit jedem implementierten Interface wird sie zu dem dadurch
definierten Datentyp kompatibel. Eine Klasse, die n Interfaces implementiert, ist
demnach zu n + 1 Datentypen (plus ihren jeweiligen Oberklassen) kompatibel:
Der Vaterklasse, aus der sie abgeleitet wurde (bzw. der Klasse Object, falls
keine extends-Klausel vorhanden war).
Den n Interfaces, die sie implementiert.
Wir knnten beispielsweise die in Listing 9.2 definierte Klasse Auto2 erweitern und
sie die Interfaces Groesse und Comparable implementieren lassen:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
/* Auto3.java */
Auto3.java
/* Auto4.java */
Auto4.java
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
Listing0910.java
/* Listing0910.java */
interface EinDimensional
{
public int laenge();
}
interface ZweiDimensional
extends EinDimensional
{
public int breite();
}
interface DreiDimensional
extends ZweiDimensional
{
public int hoehe();
}
interface VierDimensional
extends DreiDimensional
{
public int lebensdauer();
}
public class Listing0910
implements VierDimensional
{
public int laenge() { return 0; }
public int breite() { return 0; }
public int hoehe() { return 0; }
public int lebensdauer() { return 0; }
}
Titel
<<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Listing0911.java
/* Listing0911.java */
interface Debug
{
public static
public static
public static
public static
}
final
final
final
final
boolean
boolean
boolean
boolean
FUNCTIONALITY1
FUNCTIONALITY2
FUNCTIONALITY3
FUNCTIONALITY4
=
=
=
=
false;
true;
false;
false;
026 }
Listing 9.11: Konstanten in Interfaces
/* BinTreeNode.java */
class BinTreeNode
implements Cloneable
{
String
name;
BinTreeNode leftChild;
BinTreeNode rightChild;
public BinTreeNode(String name)
{
this.name
= name;
this.leftChild = null;
this.rightChild = null;
}
public Object clone()
{
try {
BinTreeNode newNode = (BinTreeNode)super.clone();
if (this.leftChild != null) {
newNode.leftChild = (BinTreeNode)this.leftChild.clone();
}
BinTreeNode.java
024
if (this.rightChild != null) {
025
newNode.rightChild = (BinTreeNode)this.rightChild.clone();
026
}
027
return newNode;
028
} catch (CloneNotSupportedException e) {
029
//Kann eigentlich nicht auftreten...
030
throw new InternalError();
031
}
032
}
033 }
Listing 9.12: Implementierung einer tiefen Kopie
/* DoubleMethod.java */
DoubleMethod.java
/* Listing0914.java */
class MathExp
implements DoubleMethod
{
public double compute(double value)
{
return Math.exp(value);
}
}
class MathSqrt
implements DoubleMethod
Listing0914.java
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
{
public double compute(double value)
{
return Math.sqrt(value);
}
}
class Times2
implements DoubleMethod
{
public double compute(double value)
{
return 2 * value;
}
}
class Sqr
implements DoubleMethod
{
public double compute(double value)
{
return value * value;
}
}
public class Listing0914
{
public static void printTable(DoubleMethod meth)
{
System.out.println("Wertetabelle " + meth.toString());
for (double x = 0.0; x <= 5.0; x += 1) {
System.out.println(" " + x + "->" + meth.compute(x));
}
}
public static void main(String[] args)
{
printTable(new Times2());
printTable(new MathExp());
printTable(new Sqr());
printTable(new MathSqrt());
}
}
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Hinweis
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
In den vorigen Abschnitten wurde gezeigt, da es viele Gemeinsamkeiten zwischen (abstrakten) Klassen und Interfaces
gibt. Whrend der Designphase eines komplexen Softwaresystems ist es daher hufig schwierig, sich fr eine von beiden
Varianten zu entscheiden. Fr die Verwendung des Interfaces spricht die grere Flexibilitt durch die Mglichkeit, in
unterschiedlichen Klassenhierarchien verwendet zu werden. Fr die Verwendung einer Klasse spricht die Mglichkeit,
bereits ausformulierbare Teile der Implementation zu realisieren und die Fhigkeit, statische Bestandteile und
Konstruktoren unterzubringen.
Wir wollen in diesem Abschnitt zeigen, wie sich beide Anstze vereinen lassen. Dabei wird zunchst jeweils ein
Interface zur Verfgung gestellt und seine Anwendung dann unter Verwendung einer Hilfsklasse vereinfacht.
/* SimpleTreeNode.java */
public interface SimpleTreeNode
{
public void addChild(SimpleTreeNode child);
public int getChildCnt();
public SimpleTreeNode getChild(int pos);
}
SimpleTreeNode.java
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
/* DefaultTreeNode.java */
DefaultTreeNode.java
Auto5.java
/* Auto5.java */
public class Auto5
implements SimpleTreeNode
{
public String name;
public int
erstzulassung;
public int
leistung;
private SimpleTreeNode treenode = new DefaultTreeNode("");
public void addChild(SimpleTreeNode child)
{
treenode.addChild(child);
}
public int getChildCnt()
{
return treenode.getChildCnt();
}
public SimpleTreeNode getChild(int pos)
{
return treenode.getChild(pos);
}
public String toString()
{
return name;
}
}
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
10.1.1 Grundlagen
Bis zum JDK 1.0 wurden Klassen immer auf Paketebene definiert, eine Schachtelung war nicht mglich.
Das hat die Compiler-Implementierung vereinfacht und die Struktur der Klassen innerhalb eines Pakets
flach und bersichtlich gemacht. Unhandlich wurde dieses Konzept immer dann, wenn eine Klasse nur
lokale Bedeutung hatte oder wenn "auf die Schnelle eine kleine Klasse" gebraucht wurde. Da es in Java
keine Funktionszeiger gibt, besteht die einzige Mglichkeit, kleine Codebestandteile zwischen
verschiedenen Programmteilen auszutauschen, darin, ein Interface zu deklarieren und die bentigte
Funktionalitt in einer implementierenden Klasse zur Verfgung zu stellen. Diese Technik wurde in
Abschnitt 9.4.3 vorgestellt.
Insbesondere bei den Erweiterungen fr die Programmierung grafischer Oberflchen, die mit dem JDK 1.1
eingefhrt wurden, entstand der Wunsch nach einem flexibleren Mechanismus. Durch das neue
Ereignismodell (siehe Kapitel 28) mssen seit dem JDK 1.1 sehr viel hufiger kleine Programmteile
geschrieben werden, die nur in einem eng begrenzten Kontext eine Bedeutung haben. Die Lsung fr dieses
Problem wurde mit der Einfhrung von lokalen und anonymen Klassen geschaffen (im JDK werden sie als
Inner Classes bezeichnet). Dabei wird innerhalb einer bestehenden Klasse X eine neue Klasse Y definiert,
die nur innerhalb von X sichtbar ist. Objektinstanzen von Y knnen damit auch nur innerhalb von X erzeugt
werden. Anders herum kann Y auf die Membervariablen von X zugreifen.
Lokale und anonyme Klassen sind ein mchtiges - und manchmal leicht verwirrendes - Feature von Java.
Wir wollen nachfolgend seine wichtigsten Anwendungen vorstellen. Darber hinaus gibt es seltener
genutzte Varianten, die hauptschlich durch trickreiche Anwendung von Modifiern auf die lokale Klasse
oder ihrer Member entstehen. Auf diese wollen wir hier nicht weiter eingehen.
oder whrend ihrer Initialisierung. Die innere Klasse kann auf die Membervariablen der ueren Klasse
zugreifen und umgekehrt. Das folgende Listing illustriert dies an einem einfachen Beispiel:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
/* Listing1001.java */
Listing1001.java
class Outer
{
String name;
int
number;
public void createAndPrintInner(String iname)
{
Inner inner = new Inner();
inner.name = iname;
System.out.println(inner.getQualifiedName());
}
class Inner
{
private String name;
private String getQualifiedName()
{
return number + ":" + Outer.this.name + "." + name;
}
}
}
public class Listing1001
{
public static void main(String[] args)
{
Outer outer = new Outer();
outer.name = "Outer";
outer.number = 77;
outer.createAndPrintInner("Inner");
}
}
Hinweis
Klassen in Methoden
Innere Klassen knnen nicht nur auf der uersten Ebene einer anderen Klasse definiert werden, sondern
auch innerhalb ihrer Methoden und sogar innerhalb eines beliebigen Blocks. In diesem Fall knnen sie auch
auf die lokalen Variablen der umgebenden Methode oder des umgebenden Blocks zugreifen. Bedingung ist
allerdings, da diese mit Hilfe des Schlsselworts final als konstant deklariert wurden.
Diese Art, lokale Klassen zu definieren, ist nicht sehr gebruchlich, taucht aber mitunter in fremdem
Programmcode auf. In der Praxis werden an ihrer Stelle meist anonyme Klassen verwendet, wie sie im
folgenden Abschnitt besprochen werden. Wir wollen uns dennoch ein einfaches Beispiel ansehen:
001 /* Listing1002.java */
002
003 class Outer2
004 {
005
public void print()
006
{
007
final int value = 10;
008
009
class Inner2
010
{
011
public void print()
012
{
013
System.out.println("value = " + value);
014
}
015
}
016
Listing1002.java
017
018
019
020
021
022
023
024
025
026
027
028
029
025 }
Listing 10.3: Anwendung anonymer Klassen
Statt eine vordefinierte Klasse zu instanzieren, wird hier in Zeile 016 eine lokale anonyme Klasse definiert
und instanziert. Syntaktisch unterscheidet sie sich von der Instanzierung einer vordefinierten Klasse
dadurch, da nach dem new KlassenName(...) nicht ein Semikolon, sondern eine geschweifte
Klammer steht. Anschlieend folgt die Definition der Klasse. Wird als Klassenname ein Interface
angegeben, implementiert die anonyme Klasse dieses Interface. Handelt es sich dagegen um den Namen
einer Klasse, wird die anonyme Klasse daraus abgeleitet. In unserem Beispiel wird das Interface
DoubleMethod implementiert.
Das Programm hat durch die Verwendung der anonymen Klasse nicht unbedingt an
Hinweis
bersichtlichkeit gewonnen. Tatschlich sind sowohl Nutzen als auch Syntax anonymer Klassen
Gegenstand vieler Diskussionen gewesen. Der groe Vorteil anonymer Klassen besteht in ihrer
Flexibilitt. Eine anonyme Klasse kann da deklariert werden, wo sie gebraucht wird (hier
beispielsweise beim Aufruf von printTable). Zudem kann sie Code weitergeben, der auf
lokale Variablen und Membervariablen ihrer unmittelbaren Umgebung zugreift.
Ihre Anwendung sollte sich auf die Flle beschrnken, in denen eine Klasse mit wenigen Zeilen
Code erzeugt werden mu, die nur an einer bestimmten Programmstelle bedeutsam ist. Ist die
Klasse umfangreicher oder wird sie an verschiedenen Stellen bentigt, sollte eine benannte
Klasse definiert und an den Aufrufstellen instanziert werden.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
Listing1004.java
/* Listing1004.java */
class Outer3
{
static class Inner3
{
public void print()
{
System.out.println("Inner3");
}
}
}
public class Listing1004
{
public static void main(String[] args)
{
Outer3.Inner3 inner = new Outer3.Inner3();
inner.print();
}
}
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Hinweis
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
bersetzen, knnte darin bestehen, ein temporres StringBuffer-Objekt zu konstruieren und die Operanden
mit Hilfe der append-Methode anzuhngen. Das resultierende Objekt knnte dann mit der toString-Methode
effizient in einen String konvertiert werden. Die Klasse StringBuffer dient dazu, vernderliche Strings zu
implementieren. Sie wird im nchsten Abschnitt vorgestellt.
Diese Vorgehensweise soll an dem folgenden Beispiel erlutert werden. Das Programm gibt zweimal
hintereinander Hallo, Welt aus. Beim ersten Mal wird der +-Operator verwendet, beim zweiten Mal die
Variante mit dem temporren StringBuffer-Objekt:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
/* Listing1103.java */
Listing1103.java
ntzlich, Zeichenketten zur Verfgung zu haben, die sich dynamisch verndern knnen (beispielsweise bentigt
sie der Compiler selbst zur Implementierung der String-Verkettung). In Java gibt es zu diesem Zweck die Klasse
StringBuffer. Sie arbeitet hnlich wie String, implementiert aber Zeichenketten, die ihre Lnge zur
Laufzeit ndern knnen. StringBuffer besitzt nicht so viele Methoden zur Auswertung der Zeichenkette,
sondern legt den Schwerpunkt auf Operationen zur Vernderung ihres Inhalts. Die wichtigsten Methoden sollen
im folgenden vorgestellt werden.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
001
002
003
004
005
006
007
008
009
010
011
012
013
014
/* RTErrorProg1.java */
RTErrorProg1.java
Listing1203.java
016
"40 ist keine Zahl zur Basis "+base
017
);
018
}
019
}
020 }
Listing 12.3: Abfangen des Laufzeitfehlers mit einer try-catch-Anweisung
Zwar ist 40 immer noch keine Zahl zur Basis 4, aber das Programm fngt diesen Fehler nun ab und gibt
eine geeignete Fehlermeldung aus:
40
40
40
40
40
40
40
base 10 = 40
base 9 = 36
base 8 = 32
base 7 = 28
base 6 = 24
base 5 = 20
ist keine Zahl zur Basis 4
java.lang.Throwable
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
/* RTErrorProg2.java */
RTErrorProg2.java
Die Klasse Error ist Superklasse aller schwerwiegenden Fehler. Diese werden hauptschlich
durch Probleme in der virtuellen Java-Maschine ausgelst. Fehler der Klasse Error sollten in
der Regel nicht abgefangen werden, sondern (durch den Standard-Fehlerhandler) nach einer
entsprechenden Meldung zum Abbruch des Programms fhren.
Alle Fehler, die mglicherweise fr die Anwendung selbst von Interesse sind, befinden sich in
der Klasse Exception oder einer ihrer Unterklassen. Ein Fehler dieser Art signalisiert einen
abnormen Zustand, der vom Programm abgefangen und behandelt werden kann.
Viele Pakete der Java-Klassenbibliothek definieren ihre eigenen Fehlerklassen. So gibt es spezielle
Fehlerklassen fr die Dateiein- und -ausgabe, die Netzwerkkommunikation oder den Zugriff auf Arrays.
Wir werden diese speziellen Fehlerklassen immer dann erlutern, wenn eine Methode besprochen wird,
die diese Art von Fehler erzeugt.
/* Listing1205.java */
public class Listing1205
{
public static void main(String[] args)
{
int i, base = 0;
for (base = 10; base >= 2; --base) {
try {
i = Integer.parseInt("40",base);
System.out.println("40 base "+base+" = "+i);
} catch (NumberFormatException e) {
System.out.println(
"40 ist keine Zahl zur Basis "+base
);
}
}
}
}
base
base
base
base
base
10 = 40
9 = 36
8 = 32
7 = 28
6 = 24
Listing1205.java
40
40
40
40
base 5 = 20
ist keine Zahl zur Basis 4
ist keine Zahl zur Basis 3
ist keine Zahl zur Basis 2
Ob es sinnvoller ist, nach einem Fehler mit der Programmausfhrung fortzufahren oder das
Programm abzubrechen, ist von der Art des Fehlers und der Stelle im Programm, an der er
aufgetreten ist, abhngig. Hier mu von Fall zu Fall entschieden werden, wie vorgegangen
werden soll.
Hinweis
Listing1206.java
023
System.out.println(
024
"***IndexOutOfBoundsException: " + e1.toString()
025
);
026
} catch (NumberFormatException e2) {
027
System.out.println(
028
"***NumberFormatException: " + e2.toString()
029
);
030
}
031
}
032 }
Listing 12.6: Mehr als eine catch-Klausel
Die Ausgabe des Programms ist nun:
10 base 10 = 10
20 base 10 = 20
30 base 10 = 30
***IndexOutOfBoundsException: java.lang.ArrayIndexOutOfBoundsException
Die finally-Klausel ist also der ideale Ort, um Aufrumarbeiten durchzufhren. Hier
knnen beispielsweise Dateien geschlossen oder Ressourcen freigegeben werden.
Hinweis
Die folgende Variation unseres Beispielprogramms benutzt finally dazu, am Ende des Programms
eine Meldung auszugeben:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
Listing1207.java
/* Listing1207.java */
public class Listing1207
{
public static void main(String[] args)
{
int i, base = 0;
try {
for (base = 10; base >= 2; --base) {
i = Integer.parseInt("40",base);
System.out.println("40 base "+base+" = "+i);
}
} catch (NumberFormatException e) {
System.out.println(
"40 ist keine Zahl zur Basis "+base
);
} finally {
System.out.println(
"Sie haben ein einfaches Beispiel " +
"sehr glcklich gemacht."
);
}
}
}
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
DOC
API
Hinweis
Die Behandlung dieser Fehler folgt den blichen Regeln. Sie entspricht damit genau dem Fall,
wenn anstelle der throw-Anweisung eine aufgerufene Methode denselben Fehler ausgelst
htte: Zunchst wird in den umgebenden Blcken nach einem Fehler-Handler gesucht. Falls
Titel
DOC
<<
<
>
>>
API
Titel
<<
DOC
API
13.1 Programmelemente
13.1 Programmelemente
13.1.1 Anweisungen
13.1.2 Blcke
13.1.3 Methoden
13.1.4 Klassen
13.1.5 Pakete
13.1.6 Applikationen
13.1.7 Applets
Wie in jeder Programmiersprache kann man auch in Java Strukturen erkennen, die auf den
unterschiedlichen Abstraktionsebenen die Bestandteile eines Programms bilden. Die Kenntnis
und Bezeichnung dieser Strukturen ist hilfreich fr das Verstndnis der entwickelten Programme
und fr die Kommunikation mit anderen Programmierern.
Wir wollen uns dazu in diesem Kapitel die folgenden Programmelemente ansehen:
Anweisungen
Blcke
Methoden
Klassen
Pakete
Applikationen
Applets
13.1.1 Anweisungen
Anweisungen gehren zu den elementarsten ausfhrbaren Programmelementen in Java. Eine
Anweisung kann eine Deklaration enthalten, einen Ausdruck auswerten oder den
Programmablauf steuern. Wir wollen Anweisungen an dieser Stelle als die kleinsten Bausteine
von Java betrachten, und alle anderen Elemente sollen darauf aufbauen.
13.1.2 Blcke
Ein Block ist eine Kollektion von Anweisungen, die nacheinander ausgefhrt werden. Anders als
eine einfache Anweisung kann ein Block eigene Variablen definieren, die nur innerhalb des
Blocks sichtbar sind. Sie werden angelegt, wenn der Block aufgerufen wird, und zerstrt, wenn
er wieder verlassen wird.
Interessant ist ein Block vor allem deshalb, weil er selbst eine Anweisung ist. Whrend er in
seiner Zusammensetzung aus vielen verschiedenen Anweisungen bestehen kann, stellt sich ein
Block nach auen hin als eine einzige Anweisung dar. Die ganze Semantik einer
blockstrukturierten Sprache ist darauf ausgelegt, da ein Block fr die Anweisungen, die ihn
verwenden, wie eine homogene Einzelanweisung aussieht.
Rekursive Konstruktionsschemata, wie das hier beschriebene, treten an vielen verschiedenen
Stellen und in vielen verschiedenen Erscheinungsformen bei der Programmentwicklung auf. Es
gibt Methoden, die sich selbst aufrufen, Strukturvariablen, die andere Variablen enthalten, oder
eben Blcke, die Anweisungen enthalten und Anweisungen sind. Grundlage von rekursiven Partof-Beziehungen ist immer der Umstand, da die Kollektion selbst vom Typ der darin enthaltenen
Elemente ist.
Die Sichtbarkeit und Lebensdauer von Blcken in Java entspricht den blichen Regeln, die auch
in anderen Programmiersprachen gltig sind. Lokale Variablen werden angelegt, wenn die
Ausfhrung des Blocks beginnt, und wieder zerstrt, wenn der Block verlassen wird. Innerhalb
eines Blocks sind die lokalen Variablen des Blocks und die lokalen Variablen der umgebenden
Blcke bzw. der umgebenden Methode sichtbar. Zustzlich sind die Variablen der Klasse
sichtbar, in der die den Block umschlieende Methode enthalten ist.
Lokale Variablen verdecken gleichnamige Instanz- oder Klassenvariablen. Durch
Tip
Voranstellen des this-Zeigers kann trotzdem auf sie zugegriffen werden. Dies wird in
Java oft ausgenutzt, um im Konstruktor einer Methode Membervariablen zu initialisieren,
die denselben Namen wie formale Parameter haben:
001 class Point
002 {
003
private int x, y;
004
public Point(int x, int y)
005
{
006
this.x = x;
007
this.y = y;
008
}
009 }
Listing 13.1: Zugriff auf verdeckte Membervariablen
Diese Vorgehensweise hat den Vorteil, da man sich fr die formalen Parameter nicht extra
Namen ausdenken mu, nur damit sie sich von den zugehrigen Membervariablen
unterscheiden.
Sie hat aber auch den Nachteil, da Membervariablen versehentlich durch lokale
Variablen verdeckt werden knnen. Das folgende Beispiel zeigt einen typischen
Fehler dieser Art:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
/* Listing1302.java */
Warnung
Listing1302.java
13.1.3 Methoden
Methoden sind wie Blcke Kollektionen, die Deklarationen und Anweisungen enthalten knnen.
Genaugenommen enthalten sie neben dem Funktionskopf genau einen Block, der alle anderen
Sie haben einen Namen und knnen von verschiedenen Stellen des Programms aus
aufgerufen werden
Sie sind parametrisierbar, und ihr Verhalten ist so zur Laufzeit strukturiert vernderbar
Sie knnen einen Rckgabewert haben, mit dem ein Wert an den Aufrufer zurckgegeben
werden kann
13.1.4 Klassen
Klassen sind das wichtigste Strukturierungsmittel objektorientierter Sprachen. Eine Klasse
enthlt eine Menge von Variablen, die den Zustand von Objekten dieser Klasse beschreiben, und
eine Menge von Methoden, die das Verhalten der Objekte festlegen.
Klassen sind insofern schachtelbar, als ihre Instanzvariablen vom Typ einer Klasse sein knnen.
Dabei ist es insbesondere erlaubt, da die Membervariablen von demselben Typ wie die zu
definierende Klasse sind. Da in Java alle Objekte als Referenzen abgelegt werden, knnen auf
diese Weise rekursive Datentypen erzeugt und zur Konstruktion von dynamischen
Datenstrukturen verwendet werden.
13.1.5 Pakete
In groen Programmsystemen reichen Klassen als Strukturelemente alleine nicht aus. Deshalb
bietet Java mit den Packages (oder Paketen) oberhalb der Ebene der Klassen eine weitere
Kollektion fr Programmelemente an.
Ein Paket ist eine Sammlung von Klassen, die einen gemeinsamen Zweck verfolgen oder aus
anderen Grnden zusammengefat werden sollen. Jede Klasse in Java ist Bestandteil genau eines
Pakets. Paketnamen knnen aus mehreren Teilen bestehen und beliebig tiefe Hierarchien
ausdrcken.
Der Name einer Methode oder einer Variablen besteht damit grundstzlich aus drei Elementen:
Paketname
Klassen- oder Objektname
Methoden- bzw. Variablenname
13.1.6 Applikationen
Applikationen bilden eigenstndige Programme in Java. Sie bentigen keinen Browser zur
Ausfhrung, sondern nur den Java-Interpreter und die .class-Files der verwendeten Klassen.
Technisch betrachtet ist eine Applikation nicht mehr als eine einzelne Klasse, in der eine
Methode vom Typ public static void main definiert wurde. Jede Klasse, die eine
solche Methode enthlt, kann als Applikation verwendet werden.
Durch einfaches Hinzufgen einer Methode public static void main kann also Tip
jede beliebige Klasse sehr leicht in eine Applikation verwandelt und vom Java-Interpreter
aufgerufen werden. Dies kann beispielsweise ntzlich sein, um in Low-Level-Klassen, die
eigentlich nur als Dienstleister auftreten, eigenstndig ausfhrbaren Testcode
unterzubringen, oder um eine solche Klasse mit Benutzungshinweisen auszustatten, die
der Entwickler durch einfaches Starten der Klasse als Applikation abrufen kann.
13.1.7 Applets
Applets sind ebenfalls lauffhige Java-Programme. Anders als Applikationen, werden sie aus
einer HTML-Seite heraus aufgerufen und bentigen zur Ausfhrung einen Web-Browser (oder
ein Hilfsprogramm wie den Appletviewer, das sich in gewissem Sinne wie ein Web-Browser
verhlt).
Applets werden nicht durch die Methode public static void main gestartet, Hinweis
sondern mssen aus der Klasse Applet abgeleitet und nach deren
Architekturmerkmalen konstruiert werden. Zum Starten des Programms erzeugt der
Browser eine Instanz der abgeleiteten Klasse und ruft nacheinander eine Reihe
vordefinierter Callback-Methoden auf. Callback-Methoden sind Methoden, die von
der abgeleiteten Klasse zur Verfgung gestellt und vom Browser oder AppletViewer
aufgerufen werden. Weitere Details zur Applet-Programmierung finden sich in
Kapitel 39 und Kapitel 40.
Titel
<<
>
>>
DOC
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Das Programm kann dann aus dem Installationsverzeichnis c:\gkapp durch Angabe des qualifizierten
Klassennamens gestartet werden:
java com.gkrueger.app.App
Voraussetzung zum Starten der Applikation ist, da der Klassenpfad korrekt gesetzt ist. Soll das Programm stets
aus dem Installationsverzeichnis heraus gestartet werden, mu das aktuelle Verzeichnis "." im CLASSPATH
enthalten sein (alternativ kann seit dem JDK 1.2 auch gar kein Klassenpfad gesetzt sein). Soll das Programm von
beliebiger Stelle aus gestartet werden knnen, mu der Pfad des Installationsverzeichnisses c:\gkapp in den
Klassenpfad aufgenommen werden. Dazu kann entweder die Umgebungsvariable CLASSPATH modifiziert oder
der gewnschte Klassenpfad mit einer der Optionen -cp oder -classpath an den Java-Interpreter bergeben
werden. Dafr wre beispielsweise folgender Aufruf geeignet:
java -cp c:\gkapp com.gkrueger.app.App
Die Variante, bei der die Umgebungsvariable unverndert bleibt, ist natrlich die bessere, denn
Hinweis
Modifikationen an Umgebungsvariablen beeinflussen mglicherweise auch andere Programme. Zudem
hat die explizite und przise bergabe des Klassenpfades an den Interpreter den Vorteil, da nicht
versehentlich weitere Verzeichnisse im Klassenpfad enthalten sind und unerwnschte Nebeneffekte
verursachen knnen.
Verwendung eines jar-Archivs
Noch einfacher wird die Installation, wenn anstelle der verschiedenen Klassendateien eine einzelne jar-Datei
ausgeliefert wird. Auf dem Zielsystem spielt es dann keine Rolle mehr, aus welchem Verzeichnis heraus die
Applikation aufgerufen wird und wie die CLASSPATH-Variable gesetzt ist. Weitere Hinweise zur Anwendung des
jar-Programms finden sich in Abschnitt 50.6. Dort wird auch erlutert, wie Applets in jar-Dateien ausgeliefert
werden knnen.
Angenommen, das Basisverzeichnis fr die Programmentwicklung auf der Quellmaschine ist c:\prog\java und die
Klassendateien liegen in den Verzeichnissen c:\prog\java\com\gkrueger\app und c:\prog\java\com\gkrueger\util.
Um eine jar-Datei zu unserem Beispielprojekt zu erstellen, ist aus dem Entwicklungsverzeichnis c:\prog\java
heraus folgendes jar-Kommando abzusetzen:
jar cvf myapp.jar com
Dadurch werden alle Dateien aus dem Unterverzeichnis com und allen darin enthaltenen Unterverzeichnissen unter
Beibehaltung der Verzeichnishierarchie in die jar-Datei myapp.jar kopiert. Um das Programm auf der
Zielmaschine aufzurufen, reicht es aus, myapp.jar an eine beliebige Stelle zu kopieren und den Java-Interpreter wie
folgt zu starten:
java -cp myapp.jar com.gkrueger.app.App
Anstelle des Unterverzeichnisses mit den Klassendateien haben wir nun die jar-Datei im Klassenpfad angegeben.
Fr den Interpreter sind beide Varianten gleichwertig und er wird das Programm in der gleichen Weise wie zuvor
starten. Vorteilhaft an dieser Vorgehensweise ist, da auf der Zielmaschine nur noch eine einzige Datei bentigt
wird und nicht mehr ein ganzes System von Verzeichnissen und Unterverzeichnissen wie zuvor. Das vereinfacht
nicht nur die Installation, sondern erhht auch ihre Haltbarkeit, denn einzelne Dateien knnen nicht mehr
berschrieben werden oder verlorengehen.
Das oben beschriebene jar-Kommando kopiert alle Dateien aus dem com-Unterverzeichnis und den
Warnung
darin enthaltenen Unterverzeichnissen in die jar-Datei. Dazu zhlen natrlich auch die Quelldateien,
sofern sie in denselben Verzeichnissen liegen. Da diese aber meist nicht mit ausgeliefert werden
sollen, empfiehlt es sich, entweder die Klassendateien (unter Beibehaltung der
Unterverzeichnisstruktur) zuvor in ein leeres Verzeichnis zu kopieren und das jar-Kommando von dort
aus zu starten. Oder die Klassendateien werden schon bei der Entwicklung von den Quelltexten
getrennt, indem beim Kompilieren die Option -d verwendet wird.
Ausfhrbare jar-Archive
Wir knnen sogar noch einen Schritt weitergehen und die jar-Datei selbst ausfhrbar machen. Dazu mssen wir
eine Manifest-Datei erstellen, in der der Name der Klasse angegeben wird, die die main-Methode enthlt.
Anschlieend lt sich die jar-Datei mir der Option -jar des Interpreters ohne explizite Angabe der Hauptklasse
starten.
Die Manifest-Datei ist eine Hilfsdatei mit Informationen ber den Inhalt der jar-Datei. Sie hat den
Namen manifest.mf und liegt im Unterverzeichnis meta-inf des jar-Archivs. Die Manifest-Datei kann
mit einem normalen Texteditor erstellt und mit Hilfe der Option "m" in das jar-Archiv eingebunden
werden. In unserem Fall mu sie lediglich einen Eintrag Main-Class enthalten. Weitere
Informationen zu Manifest-Dateien finden sich in Abschnitt 44.3.3 bei der Beschreibung der JavaBeans-Architektur.
Hinweis
Wir erstellen also zunchst eine Textdatei manifest.txt mit folgendem Inhalt (bitte mit einer Zeilenschaltung
abschlieen, sonst wird die Manifest-Datei nicht korrekt erstellt):
Main-Class: com.gkrueger.app.App
Nun kann das jar-Archiv erzeugt und die Manifest-Datei einbezogen werden:
jar cvfm myapp.jar manifest.txt com
Dieses Archiv kann nun mit Hilfe der Option -jar ohne Angabe des Klassennamens gestartet werden:
java -jar myapp.jar
Auch jetzt verhlt sich das Programm genauso wie in den beiden zuvor beschriebenen Beispielen. Diese Variante
ist vor allem ntzlich, wenn die komplette Applikation in einem einzigen jar-Archiv ausgeliefert wird und nur eine
einzige main-Methode enthlt. Sie bietet sich beispielsweise fr kleinere Programme, Beispiel- oder
Testapplikationen an (die Demos des JDK sind beispielsweise in derartigen jar-Dateien untergebracht). Ist die
Konfiguration dagegen komplizierter oder gibt es mehr als eine lauffhige Applikation in der jar-Datei, sollte der
zuvor beschriebenen Variante der Vorzug gegeben werden.
Programmen standardmig zur Verfgung. Whrend der Klassenlader normalerweise von der virtuellen Maschine
dazu verwendet wird, Klassendateien einzulesen, kann er von der Anwendung zum Einlesen beliebiger Dateien
zweckentfremdet werden. Einzige Bedingung ist, da die Ressourcen-Dateien in einem Verzeichnis stehen, das
vom Klassenlader erreicht werden kann, das also innerhalb des Klassenpfades liegt.
Dazu kann beispielsweise im Basisverzeichnis der Anwendung ein Unterverzeichnis resources angelegt und die
Ressourcen-Dateien dort hineinkopiert werden. In unserem Fall wrden wir also ein Unterverzeichnis
com.gkrueger.resources anlegen. Mit Hilfe der Methode getResourceAsStream der Klasse Class (siehe
Abschnitt 43.2.2) kann ein InputStream beschafft werden, mit dem die Datei Byte fr Byte eingelesen werden
kann:
public InputStream getResourceAsStream(String name)
java.lang.Class
Als Parameter wird der vollstndige Name der Ressourcen-Datei angegeben, allerdings unter Beachtung einiger
Besonderheiten. Zunchst werden die einzelnen Paketnamen nicht wie gewohnt durch Punkte, sondern durch
Schrgstriche "/" voneinander getrennt (auch unter Windows wird dazu nicht der Backslash verwendet). Nur die
Dateierweiterung wird wie blich hinter einem Punkt angegeben. Zudem mu als erstes Zeichen ebenfalls ein
Schrgstrich angegeben werden. Soll also beispielsweise die Datei hello.txt aus dem Ressourcenverzeichnis
geladen werden, so lautet der an getResourceAsStream zu bergebende Dateiname
/com/gkrueger/resources/hello.txt.
Das folgende Listing zeigt eine einfache Methode, in der die angesprochenen Regeln implementiert werden. Sie
beschafft zu einer beliebigen Datei, die sich in einem vorgegebenen Ressource-Verzeichnis befindet, das innerhalb
des Klassenpfades liegt, einen InputStream, mit dem die darin enthaltenen Daten eingelesen werden knnen:
001 private InputStream getResourceStream(String pkgname, String fname)
002 {
003
String resname = "/" + pkgname.replace('.', '/') + "/" + fname;
004
Class clazz = getClass();
005
InputStream is = clazz.getResourceAsStream(resname);
006
return is;
007 }
Listing 13.7: Einen InputStream zu einer Ressourcen-Datei beschaffen
Als Argument wird der Paketname (in Punktnotation) und der Name der Ressourcen-Datei angegeben. Den obigen
Regeln entsprechend werden beide in einen Ressourcen-Namen umgewandelt und an getResourceAsStream
bergeben. Diese liefert einen InputStream, der zum Einlesen der Daten verwendet werden kann. Soll
beispielsweise eine Text-Ressource in einen String geladen werden, kann dazu folgende Methode verwendet
werden:
001
002
003
004
005
006
007
008
009
010
011
012
013
/* TestResource.inc */
import java.io.*;
import java.awt.*;
//...
public String loadTextResource(String pkgname, String fname)
throws IOException
{
String ret = null;
InputStream is = getResourceStream(pkgname, fname);
if (is != null) {
TestResource.inc
014
StringBuffer sb = new StringBuffer();
015
while (true) {
016
int c = is.read();
017
if (c == -1) {
018
break;
019
}
020
sb.append((char)c);
021
}
022
is.close();
023
ret = sb.toString();
024
}
025
return ret;
026 }
027
028 //...
Listing 13.8: Laden einer Text-Ressource
Auch das Laden von Image-Ressourcen (Programm-Icons, Abbildungen etc.) kann auf hnliche Weise realisiert
werden:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
/* ImageResource.inc */
import java.io.*;
import java.awt.*;
//...
public Image loadImageResource(String pkgname, String fname)
throws IOException
{
Image ret = null;
InputStream is = getResourceStream(pkgname, fname);
if (is != null) {
byte[] buffer = new byte[0];
byte[] tmpbuf = new byte[1024];
while (true) {
int len = is.read(tmpbuf);
if (len <= 0) {
break;
}
byte[] newbuf = new byte[buffer.length + len];
System.arraycopy(buffer, 0, newbuf, 0, buffer.length);
System.arraycopy(tmpbuf, 0, newbuf, buffer.length, len);
buffer = newbuf;
}
//create image
ret = Toolkit.getDefaultToolkit().createImage(buffer);
is.close();
}
return ret;
}
//...
ImageResource.inc
Voraussetzung ist natrlich, da das Format der in den Puffer buffer eingelesenen Datei von der Methode
createImage verstanden wird. Beispiele fr gltige Formate sind GIF oder JPEG.
Der groe Vorteil bei dieser Vorgehensweise ist, da sie sowohl mit separaten Klassendateien funktioniert, als
auch, wenn die komplette Applikation inklusive der Ressourcen-Dateien innerhalb einer einzigen jar-Datei
ausgeliefert wird. Fr die Anwendung selbst ist es vollkommen gleichgltig, aus welcher Quelle die Dateien
stammen.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Die Klasse Vector aus dem Paket java.util ist die Java-Reprsentation einer linearen Liste. Die Liste kann
Elemente beliebigen Typs enthalten, und ihre Lnge ist zur Laufzeit vernderbar. Vector erlaubt das Einfgen
von Elementen an beliebiger Stelle und bietet sowohl sequentiellen als auch wahlfreien Zugriff auf die Elemente.
Das JDK realisiert einen Vector als Array von Elementen des Typs Object. Daher sind Zugriffe auf
vorhandene Elemente und das Durchlaufen der Liste schnelle Operationen. Lschungen und Einfgungen, die die
interne Kapazitt des Arrays berschreiten, sind dagegen relativ langsam, weil Teile des Arrays umkopiert werden
mssen. In der Praxis knnen diese implementierungsspezifischen Details allerdings meist vernachlssigt werden,
und ein Vector kann als konkrete Implementierung einer linearen Liste angesehen werden.
java.util.Vector
Nach dem Anlegen ist ein Vector zunchst leer, d.h. er enthlt keine Elemente. Durch Aufruf von isEmpty
kann geprft werden, ob ein Vector leer ist; size liefert die Anzahl der Elemente:
public final boolean isEmpty()
java.util.Vector
Elemente knnen an beliebiger Stelle in die Liste eingefgt werden. Ein Vector erlaubt die Speicherung
beliebiger Objekttypen, denn die Einfge- und Zugriffsmethoden arbeiten mit Instanzen der Klasse Object. Da
jede Klasse letztlich aus Object abgeleitet ist, knnen auf diese Weise beliebige Objekte in die Liste eingefgt
werden.
Leider ist der Zugriff auf die gespeicherten Elemente damit natrlich nicht typsicher. Der Compiler
Warnung
kann nicht wissen, welche Objekte an welcher Stelle im Vector gespeichert wurden, und geht daher
davon aus, da beim Zugriff auf Elemente eine Instanz der Klasse Object geliefert wird. Mit Hilfe
des Typkonvertierungsoperators mu diese dann in das ursprngliche Objekt zurckverwandelt
werden. Die Verantwortung fr korrekte Typisierung liegt damit beim Entwickler. Mit Hilfe des
Operators instanceof kann bei Bedarf zumindest eine Laufzeit-Typberprfung vorgeschaltet
werden.
Neue Elemente knnen wahlweise an das Ende des Vektors oder an einer beliebigen anderen Stelle eingefgt
werden. Das Einfgen am Ende erfolgt mit der Methode addElement:
public void addElement(Object obj)
java.util.Vector
In diesem Fall wird das Objekt obj an das Ende der bisherigen Liste von Elementen angehngt.
Soll ein Element dagegen an einer beliebigen Stelle innerhalb der Liste eingefgt werden, ist die Methode
insertElementAt zu verwenden:
public void insertElementAt(Obj obj, int index)
throws ArrayIndexOutOfBoundsException
java.util.Vector
Diese Methode fgt das Objekt obj an der Position index in den Vektor ein. Alle bisher an dieser oder einer
dahinterliegenden Position befindlichen Elemente werden um eine Position weitergeschoben.
java.util.Vector
firstElement liefert das erste Element, lastElement das letzte. Mit Hilfe von elementAt wird auf das
Element an Position index zugegriffen. Alle drei Methoden verursachen eine Ausnahme, wenn das gesuchte
Element nicht vorhanden ist.
java.util.Enumeration
In der Klasse Vector liefert die Methode elements einen Enumerator fr alle Elemente, die sich im Vektor
befinden:
java.util.Enumeration
Listing1401.java
/* Listing1401.java */
import java.util.*;
public class Listing1401
{
public static void main(String[] args)
{
Vector v = new Vector();
v.addElement("eins");
v.addElement("drei");
v.insertElementAt("zwei",1);
for (Enumeration el=v.elements(); el.hasMoreElements(); ) {
System.out.println((String)el.nextElement());
}
}
}
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Tip
Ein Stack ist eine Datenstruktur, die nach dem LIFO-Prinzip (last-in-first-out)
arbeitet. Die Elemente werden am vorderen Ende der Liste eingefgt und von dort
auch wieder entnommen. Das heit, die zuletzt eingefgten Elemente werden zuerst
entnommen und die zuerst eingefgten zuletzt.
In Java ist ein Stack eine Ableitung eines Vektors, der um neue Zugriffsfunktionen
erweitert wurde, um das typische Verhalten eines Stacks zu implementieren. Obwohl
dies eine konomische Vorgehensweise ist, bedeutet es, da ein Stack alle
Methoden eines Vektors erbt und damit auch wie ein Vektor verwendet werden kann.
Wir wollen diese Tatsache hier ignorieren und uns mit den spezifischen
Eigenschaften eines Stacks beschftigen.
Der Konstruktor der Klasse Stack ist parameterlos:
public Stack()
java.util.Stack
Das Anfgen neuer Elemente wird durch einen Aufruf der Methode push erledigt
und erfolgt wie blich am oberen Ende des Stacks. Die Methode liefert als
Rckgabewert das eingefgte Objekt:
public Object push(Object item)
java.util.Stack
Der Zugriff auf das oberste Element kann mit einer der Methoden pop oder peek
erfolgen. Beide liefern das oberste Element des Stacks, pop entfernt es anschlieend
vom Stack:
java.util.Stack
Des weiteren gibt es eine Methode empty, um festzustellen, ob der Stack leer ist,
und eine Methode search, die nach einem beliebigen Element sucht und als
Rckgabewert die Distanz zwischen gefundenem Element und oberstem StackElement angibt:
public boolean empty()
java.util.Stack
/* Listing1402.java */
import java.util.*;
public class Listing1402
{
public static void main(String[] args)
{
Stack s = new Stack();
s.push("Erstes Element");
s.push("Zweites Element");
s.push("Drittes Element");
while (true) {
try {
System.out.println(s.pop());
} catch (EmptyStackException e) {
break;
}
}
}
}
Listing1402.java
Das Programm erzeugt einen Stack und fgt die Werte "Erstes Element", "Zweites
Element" und "Drittes Element" ein. Anschlieend entfernt es so lange Elemente aus
dem Stack, bis die Ausgabeschleife durch eine Ausnahme des Typs
EmptyStackException beendet wird. Durch die Arbeitsweise des Stack
werden die Elemente in der umgekehrten Eingabereihenfolge auf dem Bildschirm
ausgegeben:
Drittes Element
Zweites Element
Erstes Element
<
>
>>
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Die Klasse Hashtable ist eine Konkretisierung der abstrakten Klasse Dictionary. Diese stellt
einen assoziativen Speicher dar, der Schlssel auf Werte abbildet und ber den Schlsselbegriff einen
effizienten Zugriff auf den Wert ermglicht. Ein Dictionary speichert also immer zusammengehrige
Paare von Daten, bei denen der Schlssel als Name des zugehrigen Wertes angesehen werden kann.
ber den Schlssel kann spter der Wert leicht wiedergefunden werden.
Da ein Dictionary auf unterschiedliche Weise implementiert werden kann, haben die Java-Designer
entschieden, dessen abstrakte Eigenschaften in einer Basisklasse zusammenzufassen. Die
Implementierung Hashtable benutzt das Verfahren der Schlsseltransformation, also die
Verwendung einer Transformationsfunktion (auch Hash-Funktion genannt), zur Abbildung von
Schlsseln auf Indexpositionen eines Arrays. Weitere Konkretisierungen der Klasse Dictionary,
etwa auf der Basis binrer Bume, gibt es in Java derzeit nicht.
Neben den erwhnten abstrakten Eigenschaften besitzt Hashtable noch die konkreten Merkmale
Kapazitt und Ladefaktor. Die Kapazitt gibt die Anzahl der Elemente an, die insgesamt untergebracht
werden knnen. Der Ladefaktor zeigt dagegen an, bei welchem Fllungsgrad die Hash-Tabelle
vergrert werden mu. Das Vergrern erfolgt automatisch, wenn die Anzahl der Elemente innerhalb
der Tabelle grer ist als das Produkt aus Kapazitt und Ladefaktor. Seit dem JDK 1.2 darf der
Ladefaktor auch grer als 1 sein. In diesem Fall wird die Hashtable also erst dann vergrert,
wenn der Fllungsgrad grer als 100 % ist und bereits ein Teil der Elemente in den
berlaufbereichen untergebracht wurde.
Wichtig bei der Verwendung der Dictionary-Klassen ist, da das Einfgen und der
Zugriff auf Schlssel nicht auf der Basis des Operators ==, sondern mit Hilfe der Methode
equals erfolgt. Schlssel mssen daher lediglich inhaltlich gleich sein, um als identisch
angesehen zu werden. Eine Referenzgleichheit ist dagegen nicht erforderlich.
Hinweis
public Hashtable()
java.util.Hashtable
Das Einfgen von Elementen erfolgt durch Aufruf der Methode put:
public Object put(Object key, Object value)
java.util.Hashtable
Dieser Aufruf fgt das Schlssel-Werte-Paar (key, value) in die Hashtable ein. Weder key noch
value drfen dabei null sein. Falls bereits ein Wertepaar mit dem Schlssel key enthalten ist, wird
der bisherige Wert gegen den neuen ausgetauscht, und put liefert in diesem Fall den Wert zurck, der
bisher dem Schlssel zugeordnet war. Falls der Schlssel bisher noch nicht vorhanden ist, ist der
Rckgabewert null.
java.util.Hashtable
Zustzlich zu den bisher erwhnten Methoden gibt es noch zwei weitere mit den Namen contains
und containsKey. Sie berprfen, ob ein bestimmter Wert bzw. ein bestimmter Schlssel in der
Hashtable enthalten ist:
public boolean contains(Object value)
public boolean containsKey(Object key)
java.util.Hashtable
Der Rckgabewert ist true, falls das gesuchte Element enthalten ist, andernfalls ist er false. Bei
der Verwendung dieser Funktionen ist zu beachten, da die Suche nach einem Wert wahrscheinlich
viel ineffizienter ist als die Suche nach einem Schlssel. Whrend der Schlssel ber die
Transformationsfunktion sehr schnell gefunden wird, erfordert die Suche nach einem Wert einen
sequentiellen Durchlauf durch die Tabelle.
java.util.Hashtable
Die Methode elements liefert einen Iterator fr die Auflistung aller Werte in der Hashtable. In
welcher Reihenfolge die Elemente dabei durchlaufen werden, ist nicht definiert. Da eine HashFunktion die Eigenschaft hat, Schlssel gleichmig ber den verfgbaren Speicher zu verteilen, ist
davon auszugehen, da die Iteratoren ihre Rckgabewerte in einer zuflligen Reihenfolge liefern.
Analog zu elements liefert keys eine Auflistung aller Schlssel, die sich in der Hash-Tabelle
befinden. Wie blich liefern beide Methoden ein Objekt, welches das Interface Enumeration
implementiert. Wie zuvor erklrt, erfolgt der Zugriff daher mit Hilfe der Methoden
hasMoreElements und nextElement.
Das folgende Beispiel verdeutlicht die Anwendung einer Hashtable:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
/* Listing1403.java */
Listing1403.java
import java.util.*;
public class Listing1403
{
public static void main(String[] args)
{
Hashtable h = new Hashtable();
//Pflege der Aliase
h.put("Fritz","f.mueller@test.de");
h.put("Franz","fk@b-blabla.com");
h.put("Paula","user0125@mail.uofm.edu");
h.put("Lissa","lb3@gateway.fhdto.northsurf.dk");
//Ausgabe
Enumeration e = h.keys();
while (e.hasMoreElements()) {
String alias = (String)e.nextElement();
System.out.println(
alias + " --> " + h.get(alias)
);
}
}
}
java.util.Properties
Der erste legt eine leere Property-Liste an, der zweite fllt sie mit den bergebenen Default-Werten.
Der Zugriff auf die einzelnen Elemente erfolgt mit den Methoden getProperty und
propertyNames:
java.util.Properties
public String getProperty(String key)
public String getProperty(String key, String defaultValue)
Die erste Variante von getProperty liefert die Eigenschaft mit der Bezeichnung key. Ist sie nicht
vorhanden, wird null zurckgegeben. Die zweite Variante hat dieselbe Aufgabe, gibt aber den
Standardwert defaultValue zurck, wenn die gesuchte Eigenschaft nicht gefunden wurde. Mit
propertyNames kann ein Enumeration-Objekt beschafft werden, mit dem alle Eigenschaften
der Property-Liste aufgezhlt werden knnen.
Zum Speichern einer Property-Liste steht die Methode store zur Verfgung:
public void store(OutputStream out, String header)
throws IOException
java.util.Properties
Sie erwartet einen OutputStream als Ausgabegert (siehe Kapitel 19) und einen Header-String,
der als Kommentar in die Ausgabedatei geschrieben wird.
Das Gegenstck zu store ist load. Mit ihr kann eine Property-Datei eingelesen werden:
public void load(InputStream in)
throws IOException
java.util.Properties
Hier mu ein InputStream bergeben werden (siehe ebenfalls Kapitel 19), der die Daten der
Property-Liste zur Verfgung stellt.
Im JDK 1.1 wurde statt der Methode store die Methode save zum Speichern einer
JDK1.1-1.4
Property-Liste verwendet. Diese hatte dieselbe Signatur, lste aber bei I/O-Problemen
keine IOException aus. Mit dem JDK 1.2 wurde save als deprecated deklariert
und durch store ersetzt.
Property-Dateien sind Textdateien mit einem recht einfachen Aufbau. Je Zeile enthalten sie Hinweis
einen Schlssel und den zugehrigen Wert. Beide sind durch ein Gleichheitszeichen
voneinander getrennt. Falls das erste nicht-leere Zeichen einer Zeile ein "#" oder "!" ist,
wird die gesamte Zeile als Kommentar angesehen und beim Einlesen ignoriert. Zustzlich
sind einige Escape-Zeichen wie \t, \n, \r, \\, \" oder \' erlaubt, und es gibt die Mglichkeit,
sehr lange Schlssel-Wert-Paare auf mehrere Zeilen zu verteilen. Weitere Details knnen in
der API-Dokumentation der Methoden load und store nachgelesen werden.
Wir wollen an dieser Stelle die Betrachtung der Klasse Properties abschlieen. Ein weiteres
Beispiel zu ihrer Verwendung findet sich in Abschnitt 16.3.1.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Die Klasse BitSet dient dazu, Mengen ganzer Zahlen zu reprsentieren. Sie erlaubt es, Zahlen einzufgen
oder zu lschen und zu testen, ob bestimmte Werte in der Menge enthalten sind. Die Klasse bietet zudem die
Mglichkeit, Teil- und Vereinigungsmengen zu bilden.
Ein BitSet erlaubt aber auch noch eine andere Interpretation. Sie kann nmlich auch als eine Menge
von Bits, die entweder gesetzt oder nicht gesetzt sein knnen, angesehen werden. In diesem Fall
entspricht das Einfgen eines Elements dem Setzen eines Bits, das Entfernen dem Lschen und die
Vereinigungs- und Schnittmengenoperationen den logischen ODER- bzw. UND-Operationen.
Tip
Diese Analogie wird insbesondere dann deutlich, wenn man eine Menge von ganzen Zahlen in der Form
reprsentiert, da in einem booleschen Array das Element an Position i genau dann auf true gesetzt wird,
wenn die Zahl i Element der reprsentierten Menge ist. Mit BitSet bietet Java nun eine Klasse, die sowohl
als Liste von Bits als auch als Menge von Ganzzahlen angesehen werden kann.
java.util.BitSet
Das Einfgen einer Zahl (bzw. das Setzen eines Bits) erfolgt mit Hilfe der Methode set. Das Entfernen einer
Zahl (bzw. das Lschen eines Bits) erfolgt mit der Methode clear. Die Abfrage, ob eine Zahl in der Menge
enthalten ist (bzw. die Abfrage des Zustands eines bestimmten Bits), erfolgt mit Hilfe der Methode get:
public void set(int bit)
java.util.BitSet
eine weitere Menge, die als Parameter bergeben wird. Das Ergebnis der Operation wird dem aktuellen
Objekt zugewiesen. Das Bilden der Vereinigungsmenge (bzw. die bitweise ODER-Operation) erfolgt durch
Aufruf der Methode or, das Bilden der Durchschnittsmenge (bzw. die bitweise UND-Operation) mit Hilfe
von and. Zustzlich gibt es die Methode xor, die ein bitweises Exklusiv-ODER durchfhrt. Deren
mengentheoretisches quivalent ist die Vereinigung von Schnittmenge und Schnittmenge der
Umkehrmengen.
Seit dem JDK 1.2 gibt es zustzlich die Methode andNot, mit der die Bits der
Ursprungsmenge gelscht werden, deren korrespondierendes Bit in der Argumentmenge
gesetzt ist.
public void or(BitSet set)
JDK1.1-1.4
java.util.BitSet
Das folgende Programm verdeutlicht die Anwendung der Klasse BitSet am Beispiel der Konstruktion der
Menge der Primzahlen kleiner gleich 20. Dabei werden besagte Primzahlen einfach als Menge X der
natrlichen Zahlen bis 20 angesehen, bei der jedes Element keinen echten Teiler in X enthlt:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
/* Listing1404.java */
import java.util.*;
public class Listing1404
{
private final static int MAXNUM = 20;
public static void main(String[] args)
{
BitSet b;
boolean ok;
System.out.println("Die Primzahlen <= " + MAXNUM + ":");
b = new BitSet();
for (int i = 2; i <= MAXNUM; ++i) {
ok = true;
for (int j = 2; j < i; ++j) {
if (b.get(j) && i % j == 0) {
ok = false;
break;
}
}
if (ok) {
b.set(i);
}
}
for (int i = 1; i <= MAXNUM; ++i) {
if (b.get(i)) {
System.out.println(" " + i);
Listing1404.java
031
}
032
}
033
}
034 }
Listing 14.4: Konstruktion von Primzahlen mit der Klasse BitSet
Die Ausgabe des Programms ist:
Die Primzahlen <= 20:
2
3
5
7
11
13
17
19
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
java.util.List
boolean addAll(Collection c)
boolean addAll(int index, Collection c)
Mit add wird ein einfaches Element in die Liste eingefgt. Wenn die Methode mit
einem einzelnen Object als Parameter aufgerufen wird, hngt sie das Element an das
Ende der Liste an. Wird zustzlich der Index angegeben, so wird das Element an der
spezifizierten Position eingefgt und alle brigen Elemente um eine Position nach rechts
geschoben. Mit addAll kann eine komplette Collection in die Liste eingefgt werden.
Auch hier knnen die Elemente wahlweise an das Ende angehngt oder an einer
beliebigen Stelle in der Liste eingefgt werden.
Der Rckgabewert von add ist true, wenn die Liste durch den Aufruf von Hinweis
add verndert, also das Element hinzugefgt wurde. Er ist false, wenn die
Liste nicht verndert wurde. Das kann beispielsweise dann der Fall sein, wenn
die Liste keine Doubletten erlaubt und ein bereits vorhandenes Element noch
einmal eingefgt werden soll. Konnte das Element dagegen aus einem
anderen Grund nicht eingefgt werden, wird eine Ausnahme des Typs
UnsupportedOperationException, ClassCastException oder
IllegalArgumentException ausgelst.
Das Lschen von Elementen kann mit den Methoden remove, removeAll und
retainAll erfolgen:
Object remove(int index)
boolean remove(Object o)
java.util.List
boolean removeAll(Collection c)
boolean retainAll(Collection c)
An remove kann dabei wahlweise der Index des zu lschenden Objekts oder das Objekt
selbst bergeben werden. Mit removeAll werden alle Elemente gelscht, die auch in
der als Argument bergebenen Collection enthalten sind, und retainAll lscht alle
Elemente auer den in der Argument-Collection enthaltenen.
15.2.2 Implementierungen
Das Interface List wird seit dem JDK 1.2 von verschiedenen Klassen implementiert:
Die Klasse AbstractList ist eine abstrakte Basisklasse, bei der alle
optionalen Methoden die Ausnahme UnsupportedOperationException
auslsen und diverse obligatorische Methoden als abstract deklariert wurden.
Sie dient als Basisklasse fr eigene List-Implementierungen.
Die Klasse LinkedList realisiert eine Liste, deren Elemente als doppelt
verkettete lineare Liste gehalten werden. Ihre Einfge- und Lschoperationen
sind im Prinzip (viele Elemente vorausgesetzt) performanter als die der
ArrayList. Der wahlfreie Zugriff ist dagegen normalerweise langsamer.
Die Klasse ArrayList implementiert die Liste als Array von Elementen, das
bei Bedarf vergrert wird. Hier ist der wahlfreie Zugriff schneller, aber bei
groen Elementzahlen kann das Einfgen und Lschen lnger dauern als bei einer
LinkedList.
Aus Grnden der Vereinheitlichung implementiert seit dem JDK 1.2 auch die
Klasse Vector das List-Interface. Neben den bereits in Abschnitt 14.2
erwhnten Methoden besitzt ein 1.2-Vector also auch die entsprechenden
Methoden des List-Interfaces.
Soll im eigenen Programm eine Liste verwendet werden, stellt sich die Frage, welche der
genannten Implementierungen dafr am besten geeignet ist. Whrend die Klasse
AbstractList nur als Basisklasse eigener Listenklassen sinnvoll verwendet werden
kann, ist die Entscheidung fr eine der drei brigen Klassen von den Spezifika der
jeweiligen Anwendung abhngig. Bleibt die Liste klein, wird hauptschlich wahlfrei
darauf zugegriffen; berwiegen die lesenden gegenber den schreibenden Zugriffen
deutlich, so liefert die ArrayList die besten Ergebnisse. Ist die Liste dagegen sehr
gro und werden hufig Einfgungen und Lschungen vorgenommen, ist wahrscheinlich
die LinkedList die bessere Wahl. Wird von mehreren Threads gleichzeitig auf die
Liste zugegriffen, kann die Klasse Vector verwendet werden, denn ihre Methoden sind
bereits weitgehend als synchronized deklariert. Weitere Untersuchungen zur
Performance der Listentypen sind in Abschnitt 49.2.3 zu finden.
Das folgende Beispiel zeigt das Anlegen und Bearbeiten zweier unterschiedlicher Listen:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
/* Listing1501.java */
Listing1501.java
import java.util.*;
public class Listing1501
{
static void fillList(List list)
{
for (int i = 0; i < 10; ++i) {
list.add("" + i);
}
list.remove(3);
list.remove("5");
}
static void printList(List list)
{
for (int i = 0; i < list.size(); ++i) {
System.out.println((String)list.get(i));
}
System.out.println("---");
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039 }
}
public static void main(String[] args)
{
//Erzeugen der LinkedList
LinkedList list1 = new LinkedList();
fillList(list1);
printList(list1);
//Erzeugen der ArrayList
ArrayList list2 = new ArrayList();
fillList(list2);
printList(list2);
//Test von removeAll
list2.remove("0");
list1.removeAll(list2);
printList(list1);
}
9
--0
--Anschlieend wird aus list2 das Element "0" entfernt und dann aus list1 alle
Elemente gelscht, die noch in list2 enthalten sind. Nach dem Aufruf von
removeAll verbleibt also nur noch das (zu diesem Zeitpunkt nicht mehr in list2
enthaltene) Element "0" in list1 und wird durch den folgenden Aufruf von
printList ausgegeben.
Titel
<<
>
>>
Titel
<<
>
>>
API
15.3 Iteratoren
15.3 Iteratoren
15.3.1 Das Interface Iterator
15.3.2 Das Interface ListIterator
java.util.Iterator
Object next()
void remove()
hasNext gibt genau dann true zurck, wenn der Iterator mindestens ein weiteres
Element enthlt. next liefert das nchste Element bzw. lst eine Ausnahme des Typs
NoSuchElementException aus, wenn es keine weiteren Elemente gibt. Wie beim
Enumeration-Interface ist der Rckgabewert als Object deklariert und mu daher
auf das passende Object gecastet werden. Als neues Feature (gegenber einer
Enumeration) bietet ein Iterator die Mglichkeit, die Collection whrend der
Abfrage zu ndern, indem das zuletzt geholte Element mit der Methode remove
gelscht wird. Bei allen Collections, die das Interface Collection implementieren,
kann ein Iterator zum Durchlaufen aller Elemente mit der Methode iterator
beschafft werden.
Dies ist auch gleichzeitig die einzige erlaubte Mglichkeit, die Collection
whrend der Verwendung eines Iterators zu ndern. Alle direkt ndernden
Zugriffe auf die Collection machen das weitere Verhalten des Iterators
undefiniert.
Warnung
Wir wollen uns die Benutzung eines Iterators an einem Beispiel ansehen:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
/* Listing1502.java */
Listing1502.java
import java.util.*;
public class Listing1502
{
public static void main(String[] args)
{
//Fllen der Liste
ArrayList list = new ArrayList();
for (int i = 1; i <= 20; ++i) {
list.add("" + i);
}
//Lschen von Elementen ber Iterator
Iterator it = list.iterator();
while (it.hasNext()) {
String s = (String) it.next();
if (s.startsWith("1")) {
it.remove();
}
}
//Ausgeben der verbleibenden Elemente
it = list.iterator();
while (it.hasNext()) {
System.out.println((String) it.next());
}
}
}
Anschlieend wird durch Aufruf von iterator ein Iterator ber alle Elemente der
Liste beschafft. Mit seiner Hilfe werden alle Elemente der Liste durchlaufen und
diejenigen, die mit "1" anfangen, gelscht. Der zweite Durchlauf durch die Liste zeigt
dann nur noch die briggebliebenen Elemente an:
2
3
4
5
6
7
8
9
20
java.util.ListIterator
int nextIndex()
int previousIndex()
void add(Object o)
void set(Object o)
Mit hasPrevious kann bestimmt werden, ob es vor der aktuellen Position ein
weiteres Element gibt; der Zugriff darauf wrde mit previous erfolgen. Die
Methoden nextIndex und previousIndex liefern den Index des nchsten bzw.
vorigen Elements des Iterators. Wird previousIndex am Anfang des Iterators
aufgerufen, ist sein Rckgabewert -1. Wird nextIndex am Ende aufgerufen, liefert
es size() als Rckgabewert. Mit add kann ein neues Element an der Stelle in die
Liste eingefgt werden, die unmittelbar vor dem nchsten Element des Iterators liegt.
set erlaubt es, das durch den letzten Aufruf von next bzw. previous beschaffte
Element zu ersetzen.
Ebenso wie beim Interface Collection sind die ndernden Methoden der Hinweis
Iteratoren optional. Falls ein Iterator eine dieser Methoden nicht zur
Verfgung stellen will, lst er bei ihrem Aufruf eine Ausnahme des Typs
UnsupportedOperationException aus. Das gilt fr die Methoden
add, set und remove.
Titel
<<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
15.4.1 Anforderungen
Nachdem wir gezeigt haben, wie vordefinierte Collections verwendet werden, wollen wir uns nun ansehen,
wie man eigene Collections erstellen kann. Wir wollen dazu die Datenstruktur einer Queue implementieren,
die folgende Eigenschaften hat:
Die Elemente sollen als einfach verkettete Liste von Elementen gehalten werden. Jedes Element wird in einer
Instanz der lokalen Klasse ElementWrapper gehalten, die neben dem Element selbst auch den Zeiger auf
sein Nachfolgeelement verwaltet. Um (ganz nebenbei) den Umgang mit Listenstrukturen mit Zeigern zu
zeigen, wollen wir die Daten nicht einer Collection des Typs LinkedList halten, sondern die
Zeigeroperationen selbst implementieren.
15.4.2 Implementierung
Zunchst definieren wir ein Interface Queue, in dem wir die abstrakten Eigenschaften unserer Queue-Klasse
spezifizieren:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
/* Queue.java */
Queue.java
import java.util.*;
/**
* Das Interface der Queue-Collection.
*/
public interface Queue
{
/**
* Fgt das Element o am Ende der Queue an. Falls
* keine Ausnahme ausgelst wurde, gibt die Methode
* true zurck.
*/
public boolean add(Object o);
/**
* Liefert das erste Element der Queue und entfernt es
* daraus. Falls die Queue leer ist, wird eine Ausnahme
* des Typs NoSuchElementException ausgelst.
*/
public Object retrieve()
throws NoSuchElementException;
/**
* Liefert die Anzahl der Elemente der Queue.
*/
public int size();
/**
* Entfernt alle Elemente aus der Queue.
*/
public void clear();
/**
* Liefert einen Iterator ber alle Elemente der Queue.
*/
public Iterator iterator();
}
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
/* LinkedQueue.java */
import java.util.*;
/**
* Die folgende Klasse realisiert eine Queue-Collection
* auf der Basis einer einfach verketteten linearen Liste.
* Die LinkedQueue kann im Prinzip beliebig viele Elemente
* aufnehmen. Die Laufzeiten der Einfge- und Lschmethoden
* sind O(1). Der von iterator() gelieferte Iterator
* besitzt KEINE Implementierung der Methode remove().
*/
public class LinkedQueue
implements Queue
{
protected ElementWrapper first;
protected ElementWrapper last;
protected int count;
public LinkedQueue()
{
first = last = null;
count = 0;
}
public boolean add(Object o)
{
if (count == 0) {
//insert first element
first = new ElementWrapper();
last = first;
count = 1;
} else {
//insert element into non-empty queue
last.next = new ElementWrapper();
last = last.next;
++count;
}
last.element = o;
last.next = null;
return true;
}
public Object retrieve()
throws NoSuchElementException
{
if (count <= 0) {
throw new NoSuchElementException();
}
ElementWrapper ret = first;
--count;
first = first.next;
if (first == null) {
last = null;
LinkedQueue.java
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
count = 0;
}
return ret.element;
}
public int size()
{
return count;
}
public void clear()
{
while (first != null) {
ElementWrapper tmp = first;
first = first.next;
tmp.next = null;
}
first = last = null;
count = 0;
}
public Iterator iterator()
{
return new Iterator()
{
ElementWrapper tmp = first;
public boolean hasNext()
{
return tmp != null;
}
public Object next()
{
if (tmp == null) {
throw new NoSuchElementException();
}
Object ret = tmp.element;
tmp = tmp.next;
return ret;
}
public void remove()
{
throw new UnsupportedOperationException();
}
};
}
/**
* Lokale Wrapperklasse fr die Queue-Elemente.
*/
class ElementWrapper
{
109
public Object element;
110
public ElementWrapper next;
111
}
112 }
Listing 15.4: Die Klasse LinkedQueue
Die einzelnen Elemente der Queue werden in Objekten der Wrapperklasse ElementWrapper gehalten. Die
beiden ElementWrapper-Zeiger first und last zeigen auf das erste bzw. letzte Element der Liste und
werden zum Einfgen und Lschen von Elementen gebraucht. Der Zhler count hlt die Anzahl der
Elemente der Queue und wurde eingefhrt, um die Methode size performant implementieren zu knnen.
Die Methoden add und retrieve fhren die erforderlichen Zeigeroperationen aus, um Elemente
einzufgen bzw. zu entfernen, sie sollen hier nicht nher erlutert werden. In clear werden alle Elemente
durchlaufen und ihr jeweiliger Nachfolgezeiger explizit auf null gesetzt, um dem Garbage Collector die
Arbeit zu erleichtern.
Interessanter ist die Implementierung von iterator. Hier wird eine anonyme lokale Klasse definiert und
zurckgegeben, die das Interface Iterator implementiert. Sie besitzt eine Membervariable tmp, mit dem
die Elemente der Queue nacheinander durchlaufen werden. Wichtig ist dabei, da tmp auf den Originaldaten
der Liste arbeitet; es ist nicht ntig, Elemente oder Zeiger zu kopieren (dieses Prinzip ist auch der Schlssel
zur Implementierung der Methode remove, die wir hier nicht realisiert haben). hasNext mu also lediglich
prfen, ob tmp der Leerzeiger ist, was sowohl bei leerer Queue als auch nach Ende des Durchlaufs der Fall
ist. next fhrt diese Prfung ebenfalls aus, liefert gegebenenfalls das im Wrapperobjekt enthaltene Element
zurck und stellt tmp auf das nchste Element.
Die hier gezeigte Anwendung von anonymen lokalen Klassen ist durchaus blich und wird meist Hinweis
auch von den JDK-Klassen zur Implementierung der Methode iterator verwendet. Prgen Sie
sich diese Vorgehensweise ein, denn sie zeigt ein wichtiges und hufig angewendetes
Designprinzip in Java. Weitere Hinweise zu anonymen und lokalen Klassen sind in Abschnitt 10.1
zu finden.
Abschlieend wollen wir uns noch ein Beispiel fr die Anwendung unserer Queue ansehen:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
/* Listing1505.java */
import java.util.*;
public class Listing1505
{
public static void main(String[] args)
{
//Erzeugen der Queue
LinkedQueue q = new LinkedQueue();
for (int i = 1; i <= 20; ++i) {
if (i % 4 == 0) {
System.out.println(
"Lsche: " + (String)q.retrieve()
);
} else {
q.add("" + i);
}
}
//Ausgeben aller Elemente
Listing1505.java
021
Iterator it = q.iterator();
022
while (it.hasNext()) {
023
System.out.println((String)it.next());
024
}
025
}
026 }
Listing 15.5: Anwendung der Queue-Klasse
Das Programm fllt die Queue, indem eine Schleife von 1 bis 20 durchlaufen wird. Ist der Schleifenzhler
durch vier teilbar, wird das vorderste Element der Queue entfernt, andernfalls wird der Zhler in einen
String konvertiert und an das Ende der Queue angehngt. Nach Ende der Schleife enthlt die Queue also
die Strings "1", "2", ..., "20", sofern sie nicht durch vier teilbar sind, und abzglich der ersten fnf Elemente,
die durch den Aufruf von retrieve entfernt wurden. Die Ausgabe des Programms ist demnach:
Lsche:
Lsche:
Lsche:
Lsche:
Lsche:
7
9
10
11
13
14
15
17
18
19
1
2
3
5
6
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
15.5.2 Implementierungen
Das Set-Interface wird im JDK nur von den Klassen HashSet und AbstractSet implementiert. Die
abstrakte Implementierung dient lediglich als Basisklasse fr eigene Ableitungen. In der voll funktionsfhigen
HashSet-Implementierung werden die Elemente intern in einer HashMap gespeichert. Vor jedem Einfgen wird
geprft, ob das einzufgende Element bereits in der HashMap enthalten ist. Die Performance der Einfge-, Lsch-
und Zugriffsfunktionen ist im Mittel konstant. Neben den oben erwhnten Standardkonstruktoren besitzt die
Klasse HashSet zwei weitere Konstruktoren, deren Argumente direkt an den Konstruktor der HashMap
weitergereicht werden:
HashSet(int initialCapacity)
HashSet(int initialCapacity, float loadFactor)
java.util.HashSet
Wir wollen uns die Anwendung eines HashSet am Beispiel der Generierung eines Lottotips ansehen. Ein
hnliches Beispiel gibt es in Listing 16.1. Dort wird ein BitSet verwendet, um doppelte Zahlen zu verhindern.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
/* Listing1506.java */
Listing1506.java
import java.util.*;
public class Listing1506
{
public static void main(String[] args)
{
HashSet set = new HashSet(10);
int doubletten = 0;
//Lottozahlen erzeugen
while (set.size() < 6) {
int num = (int)(Math.random() * 49) + 1;
if (!set.add(new Integer(num))) {
++doubletten;
}
}
//Lottozahlen ausgeben
Iterator it = set.iterator();
while (it.hasNext()) {
System.out.println(((Integer)it.next()).toString());
}
System.out.println("Ignorierte Doubletten: " + doubletten);
}
}
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
DOC
API
java.util.Map
Die Methoden size, isEmpty, remove, clear, equals und hashCode sind mit den
gleichnamigen Methoden des Collection-Interfaces identisch und brauchen daher nicht noch
einmal erklrt zu werden.
Mit Hilfe von put wird ein neues Schlssel-Wert-Paar eingefgt bzw. dem bereits vorhandenen
Schlssel ein neuer Wert zugeordnet. Die Methode putAll macht das fr alle Paare der als
Argument bergebenen Map. Auch hier gilt die Regel, da ein Schlssel, der bereits vorhanden
ist, nicht neu eingefgt wird. Lediglich sein zugeordneter Wert wird ausgetauscht. Mit der
Methode get kann der Wert zu einem Schlssel beschafft werden. Da der Rckgabewert vom
Typ Object ist, mu er auf den erwarteten Wert gecastet werden.
Falls der angegebene Schlssel nicht in der Map enthalten ist, wird null
Hinweis
zurckgegeben. Hier ist Vorsicht geboten, denn ein null-Wert wird auch dann
zurckgegeben, wenn die Map das Einfgen von null-Werten erlaubt und ein solcher
diesem Schlssel explizit zugeordnet wurde. Um diese beiden Flle zu unterscheiden,
kann die Methode containsKey verwendet werden. Sie gibt genau dann true
zurck, wenn der gewnschte Schlssel in der Map enthalten ist. Andernfalls liefert sie
false. Analog dazu kann mit containsValue festgestellt werden, ob ein
bestimmter Wert mindestens einmal in der Map enthalten ist.
Im Vergleich zum Collection-Interface fllt auf, da eine Map keine Methode iterator
besitzt. Statt dessen kann sie drei unterschiedliche Collections erzeugen, die dann natrlich
ihrerseits dazu verwendet werden knnen, einen Iterator zu liefern. Diese Collections werden als
Views, also als Sichten auf die Collection bezeichnet:
Die Methode keySet liefert die Menge der Schlssel. Da per Definition keine doppelten
Schlssel in einer Map auftauchen, ist diese Collection vom Typ Set.
Die Methode values liefert die Menge der Werte der Map. Da Werte sehr wohl doppelt
enthalten sein knnen, ist der Rckgabewert lediglich vom Typ Collection, wird also
typischerweise eine Liste oder eine anonyme Klasse mit entsprechenden Eigenschaften
sein.
Die Methode entrySet liefert eine Menge von Schlssel-Wert-Paaren. Jedes Element
dieser Menge ist vom Typ Map.Entry, d.h. es implementiert das lokale Interface Entry
des Interfaces Map. Dieses stellt u.a. die Methoden getKey und getValue zur
Verfgung, um auf die beiden Komponenten des Paares zuzugreifen.
Neben den im Interface definierten Methoden sollte eine konkrete Map-Implementierung zwei
Konstruktoren zur Verfgung stellen. Ein leerer Konstruktor dient dazu, eine leere Map zu
erzeugen. Ein zweiter Konstruktor, der ein einzelnes Map-Argument erwartet, erzeugt eine neue
Map mit denselben Schlssel-Wert-Paaren wie die als Argument bergebene Map.
15.6.2 Implementierungen
Das JDK stellt mehrere Implementierungen des Map-Interfaces zur Verfgung:
wird ein Speicher fester Gre angelegt, und die Schlssel werden mit Hilfe der HashFunktion, die den Speicherort direkt aus dem Schlssel berechnet, mglichst gleichmig
auf die verfgbaren Speicherpltze abgebildet. Da die Anzahl der potentiellen Schlssel
meist wesentlich grer als die Anzahl der verfgbaren Speicherpltze ist, knnen beim
Einfgen Kollisionen auftreten, die mit geeigneten Mitteln behandelt werden mssen (bei
der HashMap werden alle kollidierenden Elemente in einer verketteten Liste gehalten).
Die altbekannte Klasse Hashtable implementiert seit dem JDK 1.2 ebenfalls das MapInterface. Ihre Arbeitsweise entspricht im Prinzip der von HashMap, allerdings mit dem
Unterschied, da ihre Methoden synchronisiert sind und da es nicht erlaubt ist, nullWerte einzufgen (die HashMap lt dies zu).
Weitere Details zur Klasse Hashtable finden sich in Abschnitt 14.4. Dort gibt es auch ein
kleines Anwendungsbeispiel, das wir hier in einer fr die Klasse HashMap angepaten Form
zeigen wollen:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
/* Listing1507.java */
Listing1507.java
import java.util.*;
public class Listing1507
{
public static void main(String[] args)
{
HashMap h = new HashMap();
//Pflege der Aliase
h.put("Fritz","f.mueller@test.de");
h.put("Franz","fk@b-blabla.com");
h.put("Paula","user0125@mail.uofm.edu");
h.put("Lissa","lb3@gateway.fhdto.northsurf.dk");
//Ausgabe
Iterator it = h.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry)it.next();
System.out.println(
(String)entry.getKey() + " --> " +
(String)entry.getValue()
);
}
}
}
eine Menge der Schlssel-Wert-Paare beschaffen. Diese liefert dann einen Iterator, der fr jedes
Element ein Objekt des Typs Map.Entry zurckgibt. Dessen Methoden getKey und
getValue liefern den Schlssel bzw. Wert des jeweiligen Eintrags. Alternativ htten wir auch
mit keySet die Menge der Schlssel durchlaufen und mit get auf den jeweils aktuellen Wert
zugreifen knnen. Dazu mten die Zeilen 018 bis 025 im vorigen Listing durch folgenden Code
ersetzt werden:
Iterator it = h.keySet().iterator();
while (it.hasNext()) {
String key = (String)it.next();
System.out.println(
key + " --> " + (String)h.get(key)
);
}
Die Ausgabe des Programms unterscheidet sich nur in der Sortierung von der von Listing 14.3:
Franz
Fritz
Paula
Lissa
Titel
<<
-->
-->
-->
-->
fk@b-blabla.com
f.mueller@test.de
user0125@mail.uofm.edu
lb3@gateway.fhdto.northsurf.dk
>
>>
DOC
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
java.lang.Comparable
Comparable besitzt lediglich eine einzige Methode compareTo, die aufgerufen wird, um das aktuelle
Element mit einem anderen zu vergleichen.
compareTo mu einen Wert kleiner 0 zurckgeben, wenn das aktuelle Element vor dem zu
vergleichenden liegt.
compareTo mu einen Wert grer 0 zurckgeben, wenn das aktuelle Element hinter dem zu
vergleichenden liegt.
compareTo mu 0 zurckgeben, wenn das aktuelle Element und das zu vergleichende gleich sind.
JDK1.1-1.4
Die zweite Mglichkeit, eine Menge von Elementen zu sortieren, besteht darin, an den Konstruktor der
Collection-Klasse ein Objekt des Typs Comparator zu bergeben. Comparator ist ein Interface, das
java.util.Comparator
Das bergebene Comparator-Objekt bernimmt die Aufgabe einer Vergleichsfunktion, deren Methode
compare immer dann aufgerufen wird, wenn bestimmt werden mu, in welcher Reihenfolge zwei
beliebige Elemente zueinander stehen.
java.util.SortedSet
Mit first und last kann das (gem der Sortierordnung) erste bzw. letzte Element der Menge beschafft
werden. Die brigen Methoden dienen dazu, aus der Originalmenge Teilmengen auf der Basis der
Sortierung der Elemente zu konstruieren:
headSet liefert alle Elemente, die echt kleiner als das als Argument bergebene Element sind.
tailSet liefert alle Elemente, die grer oder gleich dem als Argument bergebenen Element sind.
subSet liefert alle Elemente, die grer oder gleich fromElement und kleiner als toElement
sind.
Neben dem hier beschriebenen Interface fordert die Dokumentation zu SortedSet eine Reihe von
Konstruktoren:
Ein parameterloser Konstruktor erzeugt eine leere Menge, deren (zuknftige) Elemente bezglich
ihrer natrlichen Ordnung sortiert werden.
Ein Konstruktor mit einem Argument des Typs Comparator erzeugt eine leere Menge, deren
(zuknftige) Elemente bezglich der durch den Comparator vorgegebenen Ordnung sortiert
werden.
Ein Konstruktor mit einem Argument vom Typ Collection erzeugt eine Menge, die alle
eindeutigen Elemente der als Argument bergebenen Collection in ihrer natrlichen Ordnung enthlt.
Ein Konstruktor mit einem Argument des Typs SortedSet erzeugt eine Menge mit denselben
Elementen und derselben Sortierung wie die als Argument bergebene Menge.
Die einzige Klasse im JDK, die das Interface SortedSet implementiert, ist TreeSet. Sie implementiert
die sortierte Menge mit Hilfe der Klasse TreeMap. Diese verwendet einen Red-Black-Tree als
Datenstruktur, also einen Baum, der durch eine imaginre Rot-Schwarz-Frbung seiner Knoten und
spezielle Einfge- und Lschfunktionen davor geschtzt wird, im Extremfall zu einer linearen Liste zu
entarten. Alle Basisoperationen knnen in logarithmischer Zeit bezglich der Anzahl der Elemente des
Baums ausgefhrt werden und sind damit auch bei groen Elementzahlen recht schnell.
Fr uns interessanter ist die Fhigkeit, einen sortierten Iterator zu erzeugen. Wir wollen uns dazu ein
Beispiel ansehen. Das folgende Programm erzeugt eine sortierte Menge und fgt einige Strings unsortiert
ein. Anschlieend werden die Strings mit Hilfe eines Iterators ausgegeben:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
/* Listing1508.java */
Listing1508.java
import java.util.*;
public class Listing1508
{
public static void main(String[] args)
{
//Konstruieren des Sets
TreeSet s = new TreeSet();
s.add("Kiwi");
s.add("Kirsche");
s.add("Ananas");
s.add("Zitrone");
s.add("Grapefruit");
s.add("Banane");
//Sortierte Ausgabe
Iterator it = s.iterator();
while (it.hasNext()) {
System.out.println((String)it.next());
}
}
}
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
/* Listing1509.java */
Listing1509.java
import java.util.*;
public class Listing1509
{
public static void main(String[] args)
{
//Konstruieren des Sets
TreeSet s = new TreeSet(new ReverseStringComparator());
s.add("Kiwi");
s.add("Kirsche");
s.add("Ananas");
s.add("Zitrone");
s.add("Grapefruit");
s.add("Banane");
//Rckwrts sortierte Ausgabe
Iterator it = s.iterator();
while (it.hasNext()) {
System.out.println((String)it.next());
}
}
}
class ReverseStringComparator
implements Comparator
{
public int compare(Object o1, Object o2)
{
return ((String)o2).compareTo((String)o1);
}
}
Neben einem sortierten Set gibt es auch eine sortierte Map. Das Interface SortedMap ist analog zu
SortedSet aufgebaut und enthlt folgende Methoden:
java.util.SortedMap
Object first()
Object last()
SortedMap headMap(Object toElement)
SortedMap subMap(Object fromElement, Object toElement)
SortedMap tailMap(Object fromElement)
Als konkrete Implementierung von SortedMap gibt es die Klasse TreeMap, die analog zu TreeSet
arbeitet. Die Methoden keySet und entrySet liefern Collections, deren Iteratoren ihre Elemente in
aufsteigender Reihenfolge abliefern. Auch bei einer SortedMap kann wahlweise mit der natrlichen
Ordnung der Schlssel gearbeitet werden oder durch bergabe eines Comparator-Objekts an den
Konstruktor eine andere Sortierfolge erzwungen werden.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Im Paket java.util gibt es eine Klasse Collections (man achte auf das s am Ende), die eine
groe Anzahl statischer Methoden zur Manipulation und Verarbeitung von Collections enthlt. Darunter
finden sich Methoden zum Durchsuchen, Sortieren, Kopieren und Synchronisieren von Collections sowie
solche zur Extraktion von Elementen mit bestimmten Eigenschaften. Wir wollen uns hier nur einige der
interessanten Methoden dieser Klasse ansehen und verweisen fr weitere Informationen auf die JDKDokumentation.
java.util.Collections
Mit Hilfe von sort knnen beliebige Listen sortiert werden. Als Argument werden die Liste und
wahlweise ein Comparator bergeben. Fehlt der Comparator, wird die Liste in ihrer natrlichen
Ordnung sortiert. Dazu mssen alle Elemente das Comparable-Interface implementieren und ohne
Typfehler paarweise miteinander vergleichbar sein. Gem JDK-Dokumentation verwendet diese Methode
ein modifiziertes Mergesort, das auch im Worst-Case eine Laufzeit von n*log(n) hat (auch bei der Klasse
LinkedList) und damit auch fr groe Listen geeignet sein sollte.
Wir wollen als Beispiel noch einmal Listing 15.8 aufgreifen und zeigen, wie man die unsortierten
Elemente einer Liste mit Hilfe der Methode sort sortieren kann:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
/* Listing1510.java */
Listing1510.java
import java.util.*;
public class Listing1510
{
public static void main(String[] args)
{
//Konstruieren des Sets
List l = new ArrayList();
l.add("Kiwi");
l.add("Kirsche");
l.add("Ananas");
l.add("Zitrone");
l.add("Grapefruit");
l.add("Banane");
//Unsortierte Ausgabe
Iterator it = l.iterator();
while (it.hasNext()) {
System.out.println((String)it.next());
}
System.out.println("---");
//Sortierte Ausgabe
Collections.sort(l);
it = l.iterator();
while (it.hasNext()) {
System.out.println((String)it.next());
}
}
}
Auch hier gibt es wieder eine Variante, die gem der natrlichen Ordnung vorgeht, und eine zweite, die
einen expliziten Comparator erfordert.
Collection synchronizedCollection(Collection c)
List synchronizedList(List list)
Map synchronizedMap(Map m)
Set synchronizedSet(Set s)
SortedMap synchronizedSortedMap(SortedMap m)
SortedSet synchronizedSortedSet(SortedSet s)
java.util.Collections
Die Methoden erzeugen jeweils aus der als Argument bergebenen Collection eine synchronisierte
Variante und geben diese an den Aufrufer zurck. Erreicht wird dies, indem eine neue Collection
desselben Typs erzeugt wird, deren smtliche Methoden synchronisiert sind. Wird eine ihrer Methoden
aufgerufen, leitet sie den Aufruf innerhalb eines synchronized-Blocks einfach an die als
Membervariable gehaltene Original-Collection weiter (dieses Designpattern entspricht etwa dem in
Abschnitt 10.3.6 vorgestellten Delegate-Pattern).
Collection unmodifiableCollection(Collection c)
List unmodifiableList(List list)
Map unmodifiableMap(Map m)
Set unmodifiableSet(Set s)
SortedMap unmodifiableSortedMap(SortedMap m)
SortedSet unmodifiableSortedSet(SortedSet s)
java.util.Collections
Auch hier wird jeweils eine Wrapper-Klasse erzeugt, deren Methodenaufrufe an die Original-Collection
delegiert werden. Alle Methoden, mit denen die Collection verndert werden knnte, werden so
implementiert, da sie beim Aufruf eine Ausnahme des Typs UnsupportedOperationException
auslsen.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
java.util.Random
java.util.Random
Hinweis
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
/* Listing1601.java */
Listing1601.java
import java.util.*;
public class Listing1601
{
public static void main(String[] args)
{
BitSet b = new BitSet();
Random r = new Random();
System.out.print("Mein Lottotip: ");
int cnt = 0;
while (cnt < 6) {
int num = 1 + Math.abs(r.nextInt()) % 49;
if (!b.get(num)) {
b.set(num);
++cnt;
}
}
for (int i = 1; i <= 49; ++i) {
if (b.get(i)) {
System.out.print(i + " ");
}
}
System.out.println("");
}
}
Tip
java.util.Random
Auch hier erzeugt ein Aufruf die jeweils nchste Zufallszahl auf der Basis des vorherigen
Werts.
Titel
<<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
16.4.1 Grundlagen
Die Klasse Runtime des Pakets java.lang ermglicht die Interaktion eines Java-Programmes mit dem
Bestandteil seiner Laufzeitumgebung, der dafr verantwortlich ist, Programme zu starten und zu beenden.
Dabei ist es insbesondere mglich, mit seiner Hilfe externe Programme und Kommandos zu starten, mit ihnen
zu kommunizieren und ihren Zustand zu berwachen.
Objekte der Klasse Runtime drfen vom Programm nicht selbst instanziert werden, sondern werden ber die
statische Methode getRuntime der Klasse Runtime beschafft:
public static Runtime getRuntime()
java.lang.Runtime
Um ein externes Programm zu starten, gibt es vier Methoden mit dem Namen exec:
public Process exec(String command)
throws IOException
java.lang.Runtime
Das auszufhrende Kommando kann wahlweise als Einzelstring angegeben werden (Kommandoname plus
Argumente, getrennt durch Leerzeichen), oder die einzelnen Bestandteile knnen in einem Array bergeben
werden. Das optionale zweite Argument envp ermglicht es, dem zu startenden Programm eine Liste von
Umgebungsvariablen zu bergeben.
Das folgende Listing zeigt die einfachste Form der Anwendung von exec. Das Programm startet den notepadEditor unter Windows:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
/* Listing1608.java */
Listing1608.java
import java.io.*;
public class Listing1608
{
public static void main(String[] args)
{
try {
Runtime.getRuntime().exec("notepad");
} catch (Exception e) {
System.err.println(e.toString());
}
}
}
java.lang.Process
Ein Aufruf von waitFor terminiert erst, wenn der zugehrige Prozess beendet wurde. Diese Methode kann
also dazu verwendet werden, auf das Ende des gestarteten Programms zu warten. Das ist beispielsweise
sinnvoll, um den Rckgabewert des Programms auszuwerten oder um mit von ihm erzeugten oder vernderten
Daten zu arbeiten. Wird waitFor nicht aufgerufen, kann auf aktuellen Betriebssystemen wohl davon
ausgegangen werden, da das externe Programm parallel zum Java-Programm gestartet wird und asynchron
weiterluft. Diese Aussage ist allerdings mit Vorsicht zu genieen, denn spezifiziert ist dieses Verhalten nicht.
Im Zweifel hilft ausprobieren.
Nach Ende des externen Programms kann mit exitValue sein Rckgabewert abgefragt werden. Dieser gibt
bei vielen Programmen an, ob es fehlerfrei ausgefhrt werden konnte oder nicht. Das ist aber nicht
zwangslufig so. Unter Windows wird insbesondere beim Aufruf von Batch-Dateien und bei der expliziten
Ausfhrung eines Kommandos in einen eigenen Kommandointerpreter der Rckgabewert nicht
weitergegeben. In diesem Fall liefert exitValue immer den Wert 0. Wird exitValue aufgerufen, wenn
der Prozess noch luft, gibt es eine IllegalThreadStateException.
Die Methode destroy dient dazu, das externe Programm abzubrechen. Es handelt sich hierbei nicht um das
normale Beenden eines Programmes, sondern um einen harten Abbruch. Ungesicherte nderungen gehen also
verloren und es knnen Inkonsistenzen in manipulierten Daten entstehen.
Das Process-Objekt bietet zustzlich die Mglichkeit, die Standardein- und -ausgabe des externen
Kommandos umzuleiten und aus dem eigenen Programm heraus anzusprechen:
public OutputStream getOutputStream()
java.lang.Process
getInputStream und getErrorStream liefern einen InputStream, mit dem die Ausgaben des
Prozesses auf Standardausgabe und Standardfehler gelesen werden knnen. Von getOutputStream wird
ein OutputStream zur Verfgung gestellt, mit dem Daten in die Standardeingabe des Prozesses
geschrieben werden knnen. Auf diese Weise lassen sich Programme, die ber Standardein- und -ausgabe
kommunizieren, fernsteuern bzw. fernabfragen.
Das folgende Programm fasst die wichtigsten Mglichkeiten zusammen:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
/* RunCommand.java */
import java.io.*;
public class RunCommand
{
static final int MODE_UNCONNECTED = 0;
static final int MODE_WAITFOR
= 1;
static final int MODE_CATCHOUTPUT = 2;
private static void runCommand(String cmd, int mode)
throws IOException
{
Runtime rt = Runtime.getRuntime();
System.out.println("Running " + cmd);
Process pr = rt.exec(cmd);
if (mode == MODE_WAITFOR) {
System.out.println("waiting for termination");
try {
pr.waitFor();
} catch (InterruptedException e) {
}
} else if (mode == MODE_CATCHOUTPUT) {
System.out.println("catching output");
BufferedReader procout = new BufferedReader(
new InputStreamReader(pr.getInputStream())
);
String line;
while ((line = procout.readLine()) != null) {
System.out.println(" OUT> " + line);
}
}
RunCommand.java
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
try {
System.out.println(
"done, return value is " + pr.exitValue()
);
} catch (IllegalThreadStateException e) {
System.out.println(
"ok, process is running asynchronously"
);
}
}
private static void runShellCommand(String cmd, int mode)
throws IOException
{
String prefix = "";
String osName = System.getProperty("os.name");
osName = osName.toLowerCase();
if (osName.indexOf("windows") != -1) {
if (osName.indexOf("95") != -1) {
prefix = "command.com /c ";
} else if (osName.indexOf("98") != -1) {
prefix = "command.com /c ";
}
}
if (prefix.length() <= 0) {
System.out.println(
"unknown OS: don\'t know how to invoke shell"
);
} else {
runCommand(prefix + cmd, mode);
}
}
public static void main(String[] args)
{
try {
if (args.length <= 0) {
System.out.println(
"Usage: java RunCommand [-shell] " +
"[-waitfor|-catchoutput] <command>"
);
System.exit(1);
}
boolean shell = false;
int mode = MODE_UNCONNECTED;
String cmd = "";
for (int i = 0; i < args.length; ++i) {
if (args[i].startsWith("-")) {
if (args[i].equals("-shell")) {
shell = true;
} else if (args[i].equals("-waitfor")) {
mode = MODE_WAITFOR;
} else if (args[i].equals("-catchoutput")) {
mode = MODE_CATCHOUTPUT;
}
088
} else {
089
cmd = args[i];
090
}
091
}
092
if (shell) {
093
runShellCommand(cmd, mode);
094
} else {
095
runCommand(cmd, mode);
096
}
097
} catch (Exception e) {
098
System.err.println(e.toString());
099
}
100
}
101 }
Listing 16.9: Starten externer Programme
Das Hauptprogramm erwartet das zu startende Programm und seine Parameter als Argumente. Wird die
Option "-catchoutput" angegeben, liest das Programm die Ausgaben des gestarteten Programms und gibt sie
auf seiner eigenen Standardausgabe aus. Wird "-waitfor" angegeben, wartet das Programm auf das Ende des
gestarteten Programms, ohne dessen Ausgaben anzuzeigen. In beiden Fllen wird schlielich der
Rckgabewert des Programms ausgegeben. Durch Angabe der Option "-shell" kann das externe Programm mit
einem separaten Kommandointerpreter gestartet werden. Das ist beispielsweise ntzlich, um ShellKommandos auszufhren, die nicht als eigenstndige Programmdateien existieren.
Der folgende Aufruf verwendet das Beispielprogramm, um das interne MS-DOS-Kommando "set"
auszufhren (es gibt die Inhalte aller Umgebungsvariablen aus):
java RunCommand -shell -catchoutput set
Seine Ausgabe knnte etwa so aussehen:
Running command.com /c set
catching output
OUT> winbootdir=C:\WINDOWS
OUT> COMSPEC=C:\COMMAND.COM
OUT> TEMP=C:\tmp
OUT> TMP=c:\tmp
OUT> USER=guido
OUT> windir=C:\WINDOWS
OUT> PATH=C:\JDK1.4\BIN;C:\WINDOWS;C:\WINDOWS\COMMAND;
OUT> CLASSPATH=.;c:\arc\prog\java
OUT> PROMPT=$p--$g
done, return value is 0
Die bergabe des Programms an einen separaten Kommandointerpreter erfolgt in der Methode
Hinweis
runShellCommand ab Zeile 044. Wie ein derartiger Aufruf ausgefhrt wird, ist natrlich
betriebssystem- und konfigurationsabhngig. Das Beispielprogramm versucht, einige brauchbare
Varianten fr gngige Betriebssysteme vorzudefinieren. Weitere knnen leicht hinzugefgt werden.
Whrend des Tests von RunCommand gab es mitunter Schwierigkeiten beim Ausfhren interner Warnung
DOS-Programme unter Windows 95 und 98. Whrend sich beispielsweise das Kommando set
problemlos aufrufen lie, gab es beim Aufruf von dir Hnger, nach denen die MS-DOS-Task
hart abgebrochen werden mute. Die JDK Bug Database listet eine ganze Reihe von Problemen in
Zusammenhang mit dem Aufruf von 16-Bit-Programmen unter Windows 95 oder 98 auf. Sie
rhren unter anderem daher, da die Ein- und Ausgabepuffer der DOS-Programme so klein sind,
da die Programme mitunter schon blockieren, bevor die aufrufende Applikation die Chance
hatte, eine Verbindung zu ihnen herzustellen. Echte Workarounds fr diese Probleme scheinen
nicht bekannt zu sein. Beim Aufruf von 32-Bit-Programmen treten die Probleme offenbar nicht
auf.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
DOC
API
Seit dem JDK 1.2 gibt es die Klasse Arrays im Paket java.util, die einige
ntzliche Methoden zum Zugriff auf Arrays zur Verfgung stellt. Sie kann
beispielsweise ein Array mit vorgegebenen Werten fllen, eine binre Suche
durchfhren, das Array sortieren oder zwei Arrays miteinander vergleichen.
JDK1.1-1.4
java.util.Arrays
Alle Methoden stehen auch in vergleichbaren Versionen fr die anderen primitiven Typen zur
Verfgung. Wir wollen uns die Verwendung der Klasse Arrays am Beispiel eines einfachen
Programms ansehen, das ein Array von Ganzzahlen sortiert:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
/* Listing1610.java */
import java.util.*;
public class Listing1610
{
public static void main(String[] args)
{
final int SIZE = 20;
int[] values = new int[SIZE];
Random rand = new Random();
//Erzeugen und Ausgeben des unsortierten Arrays
for (int i = 0; i < SIZE; ++i) {
values[i] = rand.nextInt(10 * SIZE);
Listing1610.java
015
016
017
018
019
020
021
022
023
024
025
026
}
027 }
}
for (int i = 0; i < SIZE; ++i) {
System.out.println(values[i]);
}
//Sortieren des Arrays
Arrays.sort(values);
//Ausgeben der Daten
System.out.println("---");
for (int i = 0; i < SIZE; ++i) {
System.out.println(values[i]);
}
>
>>
DOC
API
Hinweis
Titel
<<
Die Klasse StringTokenizer ist eine ntzliche Hilfsklasse, mit der Strings in einzelne
Tokens zerlegt werden knnen. Ein Token wird dabei als zusammenhngende Sequenz von
Zeichen angesehen, die durch Trennzeichen oder durch das Ende der Zeichenkette begrenzt
ist. Die Klasse StringTokenizer implementiert das Interface Enumeration, so da
sie genauso benutzt werden kann wie die Iteratoren in den Klassen Vector oder
Hashtable.
java.util.StringTokenizer
Die erste Variante bergibt den String str, der tokenisiert werden soll, und bereitet das
Objekt fr die nachfolgenden Zugriffe auf die einzelnen Tokens vor. Die zweite Variante
erwartet zustzlich die bergabe einer Zeichenkette delim, die alle Zeichen enthlt, die als
Trennzeichen zwischen zwei aufeinanderfolgenden Tokens angesehen werden sollen. In der
Variante ohne delim werden die Zeichen '\n', '\r', '\t' und das Leerzeichen als
Begrenzer verwendet.
Der dritte Konstruktor enthlt einen weiteren Parameter, returnTokens. Wird er auf
true gesetzt, geben die Funktionen zur Extraktion der Tokens auch die Trennzeichen
zwischen zwei Tokens zurck. Falls der Parameter false ist, werden die Trennzeichen
lediglich als Begrenzer angesehen, ohne an den Aufrufer zurckgegeben zu werden.
java.util.StringTokenizer
hasMoreElements gibt genau dann true zurck, wenn noch mindestens ein weiteres
Token zur Verfgung steht, andernfalls wird false zurckgegeben. nextElement liefert
das nchste Token, also den Teilstring von der aktuellen Position bis zum nchsten
Trennzeichen bzw. bis zum Ende der Zeichenkette.
Obwohl sehr elegant, ist die Verwendung der Methoden des Interfaces
Tip
Enumeration leider etwas umstndlich, weil nextElement Instanzen der Klasse
Object zurckgibt. Um die Anwendung des StringTokenizer bequemer zu
machen, existiert mit den Methoden hasMoreTokens und nextToken dieselbe
Funktionalitt noch einmal in einer Form, bei der ein Rckgabewert vom Typ
String geliefert wird:
public boolean hasMoreTokens()
java.util.StringTokenizer
Hier liefert nextToken kein Object, sondern einen String, und eine explizite
Typkonvertierung erbrigt sich damit.
Das folgende Programm verdeutlicht die Anwendung eines StringTokenizer, der eine
einfache Zeichenkette in ihre Bestandteile zerlegt:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
/* Listing1701.java */
Listing1701.java
import java.util.*;
public class Listing1701
{
public static void main(String[] args)
{
String s = "Dies ist nur ein Test";
StringTokenizer st = new StringTokenizer(s);
while (st.hasMoreTokens()) {
System.out.println(st.nextToken());
}
}
}
>
>>
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
19.3 Eingabe-Streams
19.3 Eingabe-Streams
19.3.1 Die Basisklasse InputStream
19.3.2 Aus InputStream direkt abgeleitete Klassen
FileInputStream
ByteArrayInputStream
ObjectInputStream
SequenceInputStream
PipedInputStream
19.3.3 Aus FilterInputStream abgeleitete Klassen
BufferedInputStream
PushbackInputStream
DataInputStream und DataInput
CheckedInputStream
Entpacken von Dateien
java.io.InputStream
Die read-Methoden dienen dazu, Bytes zu lesen. Sie knnen entweder einzelne Bytes lesen (die als int
zurckgegeben werden, dessen obere 3 Byte leer sind) oder ihre Daten direkt in einen Bytearray-Puffer schreiben. Mit
skip kann eine beliebige Anzahl Bytes bersprungen werden. available liefert die Anzahl an Bytes, die ohne
Blockieren mindestens gelesen werden knnen (Vorsicht, manche Implementierungen geben hier nie einen Wert grer
als 1 zurck). Mit close wird der Eingabe-Stream geschlossen.
Die Methode markSupported gibt Auskunft darber, ob Markieren/Positionieren untersttzt wird. Ist das der Fall,
kann mit mark die aktuelle Position im Eingabestrom markiert und spter mit reset dorthin zurckgesprungen
werden. Das Argument von mark gibt dabei die maximale Anzahl an Zeichen an, die der Eingabestrom sich merken
soll.
java.io.FileInputStream
Um eine Datei zu ffnen, kann entweder ihr Name oder ein dafr konstruiertes File-Objekt verwendet werden (siehe
Kapitel 21. Existiert die Datei nicht oder kann nicht darauf zugegriffen werden, lst der Konstruktor eine
FileNotFoundException aus. Mit Hilfe des dritten Konstruktors kann ein InputStream zu einer bereits
geffneten Datei erstellt werden.
Das folgende Programm zeigt die Verwendung der Klassen FileInputStream und FileOutputStream zum
Kopieren einer Datei (Vorsicht, eine bereits vorhandene Zieldatei wird ohne Rckfrage berschrieben).
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
/* FileCopy.java */
import java.io.*;
public class FileCopy
{
public static void main(String[] args)
{
if (args.length != 2) {
System.out.println("java FileCopy inputfile outputfile");
System.exit(1);
}
try {
FileInputStream in = new FileInputStream(args[0]);
FileOutputStream out = new FileOutputStream(args[1]);
byte[] buf = new byte[4096];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
FileCopy.java
021
out.close();
022
in.close();
023
} catch (IOException e) {
024
System.err.println(e.toString());
025
}
026
}
027 }
Listing 19.4: Kopieren einer Datei
ByteArrayInputStream
Die Klasse ByteArrayInputStream stellt einen Adapter dar, mit dessen Hilfe die Daten aus einem Byte-Array
gelesen werden knnen. Sie besitzt zwei zustzliche Konstruktoren, in denen die Datenquelle angegeben wird:
java.io.ByteArrayInputStream
public ByteArrayInputStream(byte[] buf)
public ByteArrayInputStream(byte[] buf, int offset, int length)
Im JDK gibt es eine Klasse StringBufferInputStream, die einen String als Datenquelle fr einen
Hinweis
Eingabe-Stream verwendet. Sie ist seit dem JDK 1.1 deprecated, weil die Konvertierung von UNICODEZeichen in Bytes nicht vollstndig korrekt implementiert wurde. An ihrer Stelle ist die Klasse
StringReader zu verwenden; sie wird in Abschnitt 18.3.2 erklrt.
ObjectInputStream
Ein ObjectInputStream erlaubt es, primitive Datentypen und Objekte von einem Input-Stream zu lesen. Zwar ist
er nicht von FilterInputStream abgeleitet, wird aber ebenso verwendet und erwartet im Konstruktor einen
InputStream als Datenquelle. Die Klasse ObjectInputStream ist eine der Sulen des Serialisierungs-APIs in
Java und wird in Abschnitt 41.1.3 ausfhrlich beschrieben.
SequenceInputStream
Ein SequenceInputStream dient dazu, zwei oder mehr Eingabe-Streams so miteinander zu verbinden, da die
Daten nacheinander aus den einzelnen Streams gelesen werden. Die beteiligten Streams knnen entweder in einer
Enumeration oder - wenn es sich um genau zwei von ihnen handelt - direkt an den Konstruktor bergeben werden:
public SequenceInputStream(Enumeration e)
public SequenceInputStream(InputStream s1, InputStream s2)
java.io.SequenceInputStream
PipedInputStream
Ein PipedInputStream ist das Gegenstck zum PipedOutputStream. Beide zusammen bilden eine Pipe zur
Kommunikation zweier Threads. Ein Beispiel zur Anwendung der beiden Klassen findet sich in Abschnitt 22.4.4.
java.io.FilterInputStream
Die Aufgabe der aus FilterInputStream abgeleiteten Klassen besteht darin, die Lesezugriffe abzufangen, in einer
fr sie charakteristischen Weise zu verarbeiten und die Daten erst dann an den Aufrufer weiterzugeben.
BufferedInputStream
Ein BufferedInputStream dient zur Pufferung der Eingabedaten. Er kann insbesondere dann die Performance der
Lesezugriffe erhhen, wenn hufig nur kleine Datenmengen oder einzelne Bytes gelesen werden mssen. Ein
BufferedInputStream besitzt zwei zustzliche Konstruktoren, mit denen die Datenquelle und Puffergre
angegeben werden kann:
public BufferedInputStream(InputStream in)
public BufferedInputStream(InputStream in, int size)
java.io.BufferedInputStream
PushbackInputStream
Die Klasse PushbackInputStream erweitert den Eingabe-Stream um die Fhigkeit, bereits gelesene Zeichen
wieder zurckzunehmen. Dazu besitzt sie drei Methoden mit dem Namen unread, mit denen einzelne Bytes oder ByteArrays wieder zurckgegeben werden knnen:
public void unread(int b)
throws IOException
java.io.PushbackInputStream
java.io.DataInput
int readInt()
throws IOException
long readLong()
throws IOException
float readFloat()
throws IOException
double readDouble()
throws IOException
String readLine()
throws IOException
String readUTF()
throws IOException
Die einzelnen Methoden lesen jeweils ein Element des angegebenen Typs, das in dem durch die korrespondierende
write...-Methode vorgegebenen binren Format vorliegen mu. readFully kann dazu verwendet werden, beliebig
viele Datenbytes ungeachtet ihres Datentyps einzulesen. readLine liest eine Zeile Text aus der Eingabedatei und gibt
sie als String an den Aufrufer zurck. Da die Eingabe byteweise gelesen wird, werden lediglich UNICODE-Zeichen
von \u0000 bis \u00FF korrekt konvertiert. Die readUnsigned...-Methoden betrachten die Eingabe als
vorzeichenlosen Wert im Bereich von 0 bis 255 (bzw. 0 bis 65535) und geben niemals negative Werte zurck.
Das folgende Programm liest die in Listing 19.2 erzeugten Daten ein und gibt sie auf der Console aus:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
/* Listing1905.java */
Listing1905.java
import java.io.*;
public class Listing1905
{
public static void main(String[] args)
{
try {
DataInputStream in = new DataInputStream(
new BufferedInputStream(
new FileInputStream("test.txt")));
System.out.println(in.readInt());
System.out.println(in.readInt());
System.out.println(in.readDouble());
System.out.println(in.readUTF());
System.out.println(in.readUTF());
in.close();
} catch (IOException e) {
System.err.println(e.toString());
}
}
}
java.util.zip.CheckedInputStream
Das im Konstruktor erforderliche Objekt mu das Interface Checksum implementieren. Mit den beiden Klassen
CRC32 und Adler32 stehen im JDK zwei vordefinierte Implementierungen zur Verfgung. Das folgende Programm
berechnet die Adler-32-Prfsumme zu der in der Kommandozeile angegebenen Datei:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
/* Listing1906.java */
Listing1906.java
import java.io.*;
import java.util.zip.*;
public class Listing1906
{
public static void main(String[] args)
{
if (args.length != 1) {
System.out.println("Usage: java Listing1906 file");
System.exit(1);
}
try {
CheckedInputStream in = new CheckedInputStream(
new FileInputStream(args[0]),
new Adler32()
);
byte[] buf = new byte[4096];
int len;
while ((len = in.read(buf)) > 0) {
//nichts
}
System.out.println(in.getChecksum().getValue());
in.close();
} catch (IOException e) {
System.err.println(e.toString());
}
}
}
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
Unzip.java
/* Unzip.java */
import java.io.*;
import java.util.zip.*;
public class Unzip
{
public static void main(String[] args)
{
if (args.length != 1) {
System.out.println("Usage: java Unzip zipfile");
System.exit(1);
}
try {
byte[] buf = new byte[4096];
ZipInputStream in = new ZipInputStream(
new FileInputStream(args[0]));
while (true) {
//Nchsten Eintrag lesen
ZipEntry entry = in.getNextEntry();
if (entry == null) {
break;
}
//Beschreibung ausgeben
System.out.println(
entry.getName() +
" (" + entry.getCompressedSize() + "/" +
entry.getSize() + ")"
);
//Ausgabedatei erzeugen
FileOutputStream out = new FileOutputStream(
entry.getName()
);
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
out.close();
//Eintrag schlieen
in.closeEntry();
}
in.close();
} catch (IOException e) {
System.err.println(e.toString());
}
}
}
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Warnung
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
20.3 Lesezugriffe
20.3 Lesezugriffe
Fr die lesenden Zugriffe auf eine Random-Access-Datei stehen die folgenden Methoden zur Verfgung:
public
public
public
public
public
public
public
public
public
public
public
public
public
public
final
final
final
final
final
final
final
final
final
final
final
final
final
final
java.io.RandomAccessFile
boolean readBoolean()
byte readByte()
char readChar()
double readDouble()
float readFloat()
int readInt()
long readLong()
short readShort()
String readUTF()
void readFully(byte[] b)
void readFully(byte[] b, int off, int len)
String readLine()
int readUnsignedByte()
int readUnsignedShort()
Sie lesen jeweils ein Element des angegebenen Typs und erwarten, da es in der Datei in dem durch die
korrespondierende write...-Methode vorgegebenen binren Format vorliegt. readFully kann dazu
verwendet werden, beliebig viele Datenbytes ungeachtet ihres Datentyps einzulesen. readLine liest
eine ganze Zeile Text aus und gibt sie als String an den Aufrufer zurck.
Darber hinaus steht auch eine Reihe von read-Methoden zur Verfgung, die zum Einlesen eines
einzelnen Bytes oder einer Menge von Bytes verwendet werden knnen:
public int read()
public int read(byte[] b)
public int read(byte[] b, int off, int len)
java.io.RandomAccessFile
Das folgende Listing zeigt die Verwendung der Klasse RandomAccessFile am Beispiel eines
Programms, das die Signatur und Versionsnummer aus einem .class-File herausliest. Die Signatur einer
Klassendatei ergibt das Wort CAFEBABE, wenn man die hexadezimale Darstellung der ersten vier
Bytes ausgibt. In den nchsten beiden Bytes folgt die Minor-Versionsnummer und in den
darauffolgenden zwei Bytes die Major-Versionsnummer. Die Versionsnummer liegt bis zum JDK 1.3 bei
45.3 und wurde mit dem JDK 1.4 auf 46.0 gendert.
Das Programm implementiert eine Klasse ClassFileReader, die den Zugriff auf die Klassendatei
ermglicht. Der Konstruktor ffnet die Datei, und die Ausgabe der Signatur und Versionsinformation
erfolgt mit Hilfe der Methoden printSignature und printVersion.
Das Einlesen der Signatur erfolgt durch Lesen der ersten 4 Byte der Datei, die dann jeweils in High- und
Lowbyte zerlegt und in ihre hexadezimale Darstellung umgewandelt werden. Bei der Verwendung der
Methode read zum Einlesen der Bytes ist zu beachten, da der Rckgabewert vom Typ int ist. Er darf
auch nicht in ein byte konvertiert werden, weil es sonst einen Vorzeichenberlauf geben wrde. Das
Einlesen der Versionsnummern erfolgt mit der Methode readShort, die einen vorzeichenlosen 16-BitWert aus der Datei liest. Auch hier ist der Rckgabewert vom Typ int, um den gesamten Wertebereich
von 0 bis 65535 darstellen zu knnen.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
/* Listing2001.java */
import java.io.*;
class ClassFileReader
{
private RandomAccessFile f;
public ClassFileReader(String name)
throws IOException
{
if (!name.endsWith(".class")) {
name += ".class";
}
f = new RandomAccessFile(name,"r");
}
public void close()
{
if (f != null) {
try {
f.close();
} catch (IOException e) {
//nichts
}
}
}
public void printSignature()
throws IOException
{
String ret = "";
int b;
f.seek(0);
for (int i=0; i<4; ++i) {
b = f.read();
ret += (char)(b/16+'A'-10);
ret += (char)(b%16+'A'-10);
Listing2001.java
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
}
System.out.println(
"Signatur...... "+
ret
);
}
public void printVersion()
throws IOException
{
int minor, major;
f.seek(4);
minor = f.readShort();
major = f.readShort();
System.out.println(
"Version....... "+
major+"."+minor
);
}
}
public class Listing2001
{
public static void main(String[] args)
{
ClassFileReader f;
try {
f = new ClassFileReader("Listing2001");
f.printSignature();
f.printVersion();
} catch (IOException e) {
System.out.println(e.toString());
}
}
}
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
20.4 Schreibzugriffe
20.4 Schreibzugriffe
Die schreibenden Zugriffe erfolgen mit Methoden, die zu denen fr die Lesezugriffe
korrespondieren:
public
public
public
public
public
public
public
public
public
public
public
final
final
final
final
final
final
final
final
final
final
final
void
void
void
void
void
void
void
void
void
void
void
writeBoolean(boolean v)
writeByte(int v)
writeBytes(String s)
writeChar(int v)
writeChars(String s)
writeDouble(double v)
writeFloat(float v)
writeInt(int v)
writeLong(long v)
writeShort(int v)
writeUTF(String str)
java.io.RandomAccessFile
Zustzlich gibt es auch hier einige Methoden zum Schreiben von untypisierten Daten:
public void write(int b)
public void write(byte[] b)
public void write(byte[] b, int off, int len)
java.io.RandomAccessFile
Das folgende Beispiel zeigt ein Programm, das den Inhalt der als Argument bergebenen
Datei liest und spiegelverkehrt an Ende anhngt:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
/* Listing2002.java */
Listing2002.java
import java.io.*;
public class Listing2002
{
public static void main(String[] args)
{
try {
RandomAccessFile f1 = new RandomAccessFile(
args[0], "rw"
);
long len = f1.length();
f1.setLength(2 * len);
for (long i = 0; i < len; ++i) {
f1.seek(i);
int c = f1.read();
f1.seek(2 * len - i - 1);
f1.write(c);
}
f1.close();
} catch (IOException e) {
System.err.println(e.toString());
}
}
}
>
>>
Hinweis
Titel
<<
DOC
API
Die Klasse File besitzt eine ganze Reihe von Methoden, um Informationen ber die durch das
File-Objekt bezeichnete Datei oder das Verzeichnis zu gewinnen:
public boolean exists()
java.io.File
Mit exists kann getestet werden, ob die Datei oder das Verzeichnis berhaupt existiert. Die
Methoden canWrite und canRead ermitteln, ob ein lesender bzw. schreibender Zugriff
mglich ist, mit isHidden kann festgestellt werden, ob die Datei versteckt ist. Mit isFile und
isDirectory kann unterschieden werden, ob es sich um eine Datei oder ein Verzeichnis
handelt. isAbsolute gibt an, ob das Objekt mit Hilfe einer absoluten Pfadbezeichnung
konstruiert wurde.
lastModified liefert den Zeitpunkt der letzten nderung der Datei. Der Rckgabewert liefert
die Anzahl der Millisekunden seit dem 1.1.1970 und kann entweder direkt (etwa in arithmetischen
Vergleichen) oder zur Konstruktion eines Date-Objekts verwendet werden. Das Date-Objekt
kann dann an die Methode setTime der Klasse GregorianCalendar bergeben werden, um
die einzelnen Uhrzeitkomponenten zu extrahieren. Das folgende Beispiel illustriert diese
Vorgehensweise:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
Listing2101.java
/* Listing2101.java */
import java.io.*;
import java.util.*;
public class Listing2101
{
public static void main(String[] args)
{
File file = new File(args[0]);
GregorianCalendar cal = new GregorianCalendar();
cal.setTime(new Date(file.lastModified()));
System.out.print("Letzte nderung: ");
System.out.println(
cal.get(Calendar.DATE) + "." +
(cal.get(Calendar.MONTH)+1) + "." +
cal.get(Calendar.YEAR) + " " +
cal.get(Calendar.HOUR_OF_DAY) + ":" +
cal.get(Calendar.MINUTE) + ":" +
cal.get(Calendar.SECOND)
);
}
}
Titel
<<
>
>>
DOC
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
java.io.File
list liefert ein Array von Strings, das fr jeden gefundenen Verzeichniseintrag ein Element enthlt. Die Liste
enthlt die Namen aller Dateien und Unterverzeichnisse mit Ausnahme von . und ... list gibt es noch in einer
zweiten Variante, bei der die Auswahl der Verzeichniseintrge eingeschrnkt werden kann. Dabei mu ein Objekt
bergeben werden, das das Interface FilenameFilter implementiert. Dieses besitzt eine Methode accept, die
fr jede gefundene Datei aufgerufen wird und entscheidet, ob sie in die Liste aufgenommen werden soll oder nicht.
Zustzlich gibt es die statische Methode listRoots, mit der eine Liste aller Wurzeln der verfgbaren
Dateisysteme beschafft werden kann. Unter UNIX gibt es lediglich die Wurzel "/", unter Windows dagegen eine fr
jeden Laufwerksbuchstaben.
Eine hufig bentigte Funktion besteht darin, ein Verzeichnis samt seiner Unterverzeichnisse rekursiv zu
durchlaufen und bei jedem gefundenen Eintrag eine bestimmte Aufgabe zu erledigen. Wir wollen zeigen, wie mit
Hilfe des Visitor-Patterns (siehe Abschnitt 10.3.8) eine universell verwendbare Lsung geschaffen werden kann.
Zunchst definieren wir dazu ein Interface DirectoryVisitor:
001
002
003
004
005
006
007
008
009
010
/* DirectoryVisitor.java */
DirectoryVisitor.java
import java.io.*;
public interface DirectoryVisitor
{
public void enterDirectory(File dir);
public void leaveDirectory(File dir);
public void visitFile(File file);
}
so einfach wie mglich zu machen, werden sowohl Verzeichnisse als auch Dateien als fertige File-Objekte
bergeben.
Ein konkreter Visitor, der eine Verzeichnisstruktur mit den darin enthaltenen Dateien und Unterverzeichnissen
korrekt eingerckt auf dem Bildschirm ausgibt, entsteht durch Implementieren von DirectoryVisitor wie
folgt:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
/* DirectoryPrintVisitor.java */
DirectoryPrintVisitor.java
import java.io.*;
public class DirectoryPrintVisitor
implements DirectoryVisitor
{
String indent = "";
public void enterDirectory(File dir)
{
System.out.println(indent + "[" + dir.getName() + "]");
indent += " ";
}
public void leaveDirectory(File dir)
{
indent = indent.substring(2);
}
public void visitFile(File file)
{
System.out.println(indent + file.getName());
}
}
/* DirectorySizeVisitor.java */
import java.io.*;
public class DirectorySizeVisitor
implements DirectoryVisitor
{
int files = 0;
int dirs = 0;
long size = 0;
public void enterDirectory(File dir)
{
++dirs;
}
public void leaveDirectory(File dir)
{
}
DirectorySizeVisitor.java
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041 }
/* Listing2105.java */
import java.io.*;
public class Listing2105
{
static void traverse(File dir, DirectoryVisitor visitor)
{
if (!dir.isDirectory()) {
throw new IllegalArgumentException(
"not a directory: " + dir.getName()
);
}
visitor.enterDirectory(dir);
File[] entries = dir.listFiles(
new FileFilter()
{
public boolean accept(File pathname)
{
return true;
}
}
);
for (int i = 0; i < entries.length; ++i) {
if (entries[i].isDirectory()) {
traverse(entries[i], visitor);
} else {
visitor.visitFile(entries[i]);
}
Listing2105.java
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046 }
}
visitor.leaveDirectory(dir);
}
public static void main(String[] args)
{
File file = new File(args[0]);
//Bildschirmausgabe der Struktur
traverse(file, new DirectoryPrintVisitor());
//Gren ermitteln
DirectorySizeVisitor visitor = new DirectorySizeVisitor();
traverse(file, visitor);
System.out.println("directories: " + visitor.getDirs());
System.out.println("files: "
+ visitor.getFiles());
System.out.println("size: "
+ visitor.getSize());
}
JDK1.1-1.4
java.io.File
Die Methode delete lscht die durch das File-Objekt bezeichnete Datei. Mit renameTo wird das FileObjekt in das als Parameter bergebene Objekt umbenannt. Durch Aufruf von mkdir wird das durch das FileObjekt spezifizierte Verzeichnis angelegt. Mit mkdirs werden sogar alle Vaterverzeichnisse automatisch angelegt,
wenn sie noch nicht existieren. Alle Methoden geben true zurck, wenn sie ihre Aufgabe erfolgreich ausfhren
konnten. Andernfalls geben sie false zurck.
Das folgende Listing zeigt die Verwendung der Klasse File und den Aufruf verschiedener Methoden:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
/* TestFile.java */
import java.io.*;
import java.util.*;
public class TestFile
{
public static void main(String[] args)
{
File fil = new File("TestFile.java");
TestFile.printFileInfo(fil);
fil = new File("..");
TestFile.printFileInfo(fil);
}
static void printFileInfo(File fil)
{
System.out.println("Name= "+fil.getName());
System.out.println("Path= "+fil.getPath());
System.out.println("AbsolutePath= "+fil.getAbsolutePath());
System.out.println("Parent= "+fil.getParent());
System.out.println("exists= "+fil.exists());
System.out.println("canWrite= "+fil.canWrite());
System.out.println("canRead= "+fil.canRead());
System.out.println("isFile= "+fil.isFile());
System.out.println("isDirectory= "+fil.isDirectory());
if (fil.isDirectory()) {
String[] fils = fil.list();
for (int i=0; i<fils.length; ++i) {
System.out.println(" "+fils[i]);
}
}
System.out.println("isAbsolute= "+fil.isAbsolute());
System.out.println(
"lastModified= "+(new Date(fil.lastModified()))
);
System.out.println("length= "+fil.length());
System.out.println("");
}
}
TestFile.java
Name= ..
Path= ..
AbsolutePath= C:\ARC\DOKU\java\examples\..
Parent= null
exists= true
canWrite= true
canRead= true
isFile= false
isDirectory= true
makefile
html.cfg
...
jdbc.sgml
tuning.sgml
reflection.sgml
isAbsolute= false
lastModified= Wed Jul 22 16:55:32 GMT+02:00 1998
length= 0
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Das Anlegen von Dateien wurde ja bereits in frheren Abschnitten behandelt. Hier soll es noch einmal
erwhnt werden, weil die Klasse File zustzlich die Mglichkeit bietet, temporre Dateien und
Lockdateien anzulegen. Beide Varianten haben ihre Anwendungen, und wir wollen sie im folgenden kurz
vorstellen.
java.io.File
In beiden Fllen ist es erforderlich, einen Prfix- und einen Suffix-String zu spezifizieren. Als Prfix sollte
normalerweise ein kurzer String von drei oder vier Buchstaben verwendet werden, der den Typ der
temporren Datei identifiziert. Der Suffix wird als Dateierweiterung verwendet, er knnte beispielsweise
.tmp oder .$$$ sein. createTempFile fllt den Platz zwischen Prfix und Erweiterung mit einer
Zeichenkette, so da der resultierende Dateiname eindeutig ist. Wird kein weiterer Parameter angegeben,
legt die Methode eine neue temporre Datei in einem systemspezifischen Verzeichnis an (typischerweise
\tmp oder \temp). Optional kann als drittes Argument ein File-Objekt bergeben werden, das ein
alternatives Verzeichnis fr die temporre Datei angibt.
Das folgende Listing zeigt ein einfaches Beispiel fr die Anwendung der Methode createTempFile:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
/* Listing2107.java */
Listing2107.java
import java.io.*;
public class Listing2107
{
public static void main(String[] args)
{
try {
File tmp = File.createTempFile("xyz", ".tmp", null);
} catch (IOException e) {
System.out.println(e.toString());
}
}
}
21.5.2 Lockdateien
Ein hufiger Kritikpunkt an den JDKs vor der Version 1.2 war, da Java keine portable
Mglichkeit zum Sperren von Dateien vorsah. Damit war es nicht (oder nur mit
zustzlichem Aufwand) mglich, Programme zu schreiben, die in einer
Mehrbenutzerumgebung von unterschiedlichen Arbeitspltzen gleichzeitig kontrolliert auf
dieselben Dateien zugreifen.
JDK1.1-1.4
Mit den Methoden createNewFile und deleteOnExit bietet das JDK seit der Version 1.2 nun eine
rudimentre Mglichkeit, diese Fhigkeiten zu realisieren:
public boolean createNewFile()
throws IOException
java.io.File
createNewFile erzeugt eine neue Datei aus dem Dateinamen des zugehrigen File-Objekts. Die
Datei wird aber nur dann erzeugt, wenn sie bisher noch nicht vorhanden war. War sie dagegen bereits
vorhanden, schlgt der Aufruf fehl und liefert false als Rckgabewert. Bei Erfolg wird die Datei
angelegt und true zurckgegeben. Das JDK garantiert, da die beiden erforderlichen Teiloperationen
Feststellen, ob die Datei bereits existiert und Anlegen einer neuen Datei atomar ablaufen, also nicht
unterbrechbar sind. Durch Aufruf von deleteOnExit kann sichergestellt werden, da die Datei beim
Beenden der VM in jedem Fall gelscht wird, selbst wenn das Ende durch eine Ausnahme ausgelst
wurde.
Beide Methoden knnen nun auf unterschiedliche Weise zur Realisierung eines Locking-Systems
verwendet werden. So kann beispielsweise der Name der Datei als zu sperrende Ressource und die Datei
selbst als eigentliche Sperre angesehen werden. Dann wrde es fr jede zu sperrende Ressource eine
eigene Sperrdatei geben (was bei einer groen Anzahl von potentiellen Ressourcen sehr viele einzelne
Dateien bedeuten wrde). Oder es ist denkbar, da alle Sperrinformationen in einer einzigen Datei
gehalten werden und nur der Zugriff auf diese Datei mit Hilfe einer Sperrdatei gesichert wird. Diese wrde
dann mit createNewFile in der Art eines Semaphors angelegt werden, um die Sperrdatei zu betreten,
und nach Ende der Bearbeitung wieder entfernt werden.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
DOC
API
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
/* Listing2201.java */
Listing2201.java
class MyThread2201
extends Thread
{
public void run()
{
int i = 0;
while (true) {
System.out.println(i++);
}
}
}
public class Listing2201
{
public static void main(String[] args)
{
MyThread2201 t = new MyThread2201();
t.start();
}
}
Hinweis
entfernt.
Wir wollen das vorige Beispiel erweitern und den Thread nach zwei Sekunden durch Aufruf von
stop beenden:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
/* Listing2202.java */
Listing2202.java
class MyThread2202
extends Thread
{
public void run()
{
int i = 0;
while (true) {
System.out.println(i++);
}
}
}
public class Listing2202
{
public static void main(String[] args)
{
MyThread2202 t = new MyThread2202();
t.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
//nichts
}
t.stop();
}
}
Mit dem JDK 1.2 wurde die Methode stop als deprecated markiert, d.h. sie
sollte nicht mehr verwendet werden. Der Grund dafr liegt in der potentiellen
Unsicherheit des Aufrufs, denn es ist nicht voraussagbar und auch nicht definiert,
an welcher Stelle ein Thread unterbrochen wird, wenn ein Aufruf von stop
erfolgt. Es kann nmlich insbesondere vorkommen, da der Abbruch innerhalb
eines kritischen Abschnitts erfolgt (der mit dem synchronized-Schlsselwort
geschtzt wurde) oder in einer anwendungsspezifischen Transaktion auftritt, die
aus Konsistenzgrnden nicht unterbrochen werden darf.
JDK1.1-1.4
Die alternative Methode, einen Thread abzubrechen, besteht darin, im Thread selbst auf
Unterbrechungsanforderungen zu reagieren. So knnte beispielsweise eine Membervariable
cancelled eingefhrt und beim Initialisieren des Threads auf false gesetzt werden. Mit Hilfe
einer Methode cancel kann der Wert der Variable zu einem beliebigen Zeitpunkt auf true
gesetzt werden. Aufgabe der Bearbeitungsroutine in run ist es nun, an geeigneten Stellen diese
Variable abzufragen und fr den Fall, da sie true ist, die Methode run konsistent zu beenden.
Dabei darf cancelled natrlich nicht zu oft abgefragt werden, um das Programm nicht unntig
aufzublhen und das Laufzeitverhalten des Threads nicht zu sehr zu verschlechtern. Andererseits
darf die Abfrage nicht zu selten erfolgen, damit es nicht zu lange dauert, bis auf eine
Abbruchanforderung reagiert wird. Insbesondere darf es keine potentiellen Endlosschleifen
geben, in denen cancelled berhaupt nicht abgefragt wird. Die Kunst besteht darin, diese
gegenstzlichen Anforderungen sinnvoll zu vereinen.
Glcklicherweise gibt es in der Klasse Thread bereits einige Methoden, die einen solchen
Mechanismus standardmig untersttzen:
public void interrupt()
java.lang.Thread
Durch Aufruf von interrupt wird ein Flag gesetzt, das eine Unterbrechungsanforderung
signalisiert. Durch Aufruf von isInterrupted kann der Thread festellen, ob das Abbruchflag
gesetzt wurde und der Thread beendet werden soll. Die statische Methode interrupted stellt
den Status des Abbruchsflags beim aktuellen Thread fest. Ihr Aufruf entspricht dem Aufruf von
currentThread().isInterrupted(), setzt aber zustzlich das Abbruchflag auf seinen
initialen Wert false zurck.
Wir wollen uns den Gebrauch dieser Methoden an einem Beispiel ansehen. Dazu soll ein
Programm geschrieben werden, das in einem separaten Thread ununterbrochen Textzeilen auf
dem Bildschirm ausgibt. Das Hauptprogramm soll den Thread erzeugen und nach 2 Sekunden
durch einen Aufruf von interrupt eine Unterbrechungsanforderung erzeugen. Der Thread soll
dann die aktuelle Zeile fertig ausgeben und anschlieend terminieren.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
/* Listing2203.java */
public class Listing2203
extends Thread
{
int cnt = 0;
public void run()
{
while (true) {
if (isInterrupted()) {
break;
}
printLine(++cnt);
}
}
private void printLine(int cnt)
{
//Zeile ausgeben
System.out.print(cnt + ": ");
for (int i = 0; i < 30; ++i) {
System.out.print(i == cnt % 30 ? "* " : ". ");
}
System.out.println();
//100 ms. warten
try {
Thread.sleep(100);
} catch (InterruptedException e) {
interrupt();
}
}
public static void main(String[] args)
{
Listing2203 th = new Listing2203();
{
//Thread starten
th.start();
//2 Sekunden warten
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
//Thread unterbrechen
th.interrupt();
}
}
}
Listing2203.java
Hinweis
Thread.sleep verwendet, um das Programm pausieren zu lassen. sleep ist eine statische
Methode der Klasse Thread, die mit einem oder zwei Parametern aufgerufen werden kann:
public static void sleep(long millis)
public static void sleep(long millis, int nanos)
java.lang.Thread
Die erste Version sorgt dafr, da der aktuelle Proze fr die (in Millisekunden angegebene) Zeit
unterbrochen wird. Die zweite erlaubt eine noch genauere Eingabe der Wartezeit, indem auch
Bruchteile im Nanosekundenbereich angegeben werden knnen. In beiden Fllen wird die
tatschlich erzielbare Genauigkeit allerdings durch Hardwarerestriktionen der Zielmaschine
begrenzt. Im Falle der aktuellen SUN-JDKs unter Windows liegt sie bei etwa 1 ms und ist damit
wesentlich hher als currentTimeMillis. In Abschnitt 16.3.5 finden sich
Beispielprogramme, mit denen die Auflsungen von currentTimeMillis und sleep
ermittelt werden knnen.
Die Kapselung des Aufrufs von Thread.sleep innerhalb eines try-catchBlocks ist erforderlich, weil sleep whrend der Wartezeit eine Ausnahme vom Typ
InterruptedException auslsen kann. Ohne den try-catch-Block wrde
diese an den Aufrufer weitergegeben werden.
Hinweis
Als Klassenmethode kann sleep aufgerufen werden, ohne da eine Instanz der Klasse Thread
verfgbar ist. Insbesondere kann die Methode auch dazu verwendet werden, das Hauptprogramm
pausieren zu lassen, das ja nicht explizit als Thread erzeugt wurde. Dies funktioniert deshalb, weil
beim Starten eines Java-Programms automatisch ein Thread fr die Ausfhrung des
Hauptprogramms angelegt wurde.
isAlive
Mit dieser Methode kann festgestellt werden, ob der aktuelle Thread noch luft.
public final boolean isAlive()
java.lang.Thread
isAlive gibt immer dann true zurck, wenn der aktuelle Thread gestartet, aber noch nicht
wieder beendet wurde. Beendet wird ein Thread, wenn das Ende der run-Methode erreicht ist
oder wenn (in Pr-1.2-JDKs) die Methode stop aufgerufen wurde.
join
public final void join()
throws InterruptedException
java.lang.Thread
Die Methode join wartet auf das Ende des Threads, fr den sie aufgerufen wurde. Sie
ermglicht es damit, einen Proze zu starten und (hnlich einem Funktionsaufruf) mit der
weiteren Ausfhrung so lange zu warten, bis der Proze beendet ist. join gibt es auch mit einem
long als Parameter. In diesem Fall wartet die Methode maximal die angegebene Zeit in
Millisekunden und fhrt nach Ablauf der Zeit auch dann fort, wenn der Proze noch nicht beendet
ist.
Titel
<<
>
>>
DOC
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Nicht immer ist es mglich, eine Klasse, die als Thread laufen soll, von Thread abzuleiten. Dies ist insbesondere dann nicht
mglich, wenn die Klasse Bestandteil einer Vererbungshierarchie ist, die eigentlich nichts mit Multithreading zu tun hat. Da
Java keine Mehrfachvererbung kennt, kann eine bereits abgeleitete Klasse nicht von einer weiteren Klasse erben. Da sehr
unterschiedliche Klassen als Thread parallel zu vorhandenem Code ausgefhrt werden knnen, ist dies eine sehr unschne
Einschrnkung des Multithreading-Konzepts von Java.
Glcklicherweise gibt es einen Ausweg. Er besteht darin, einen Thread nicht durch Ableiten aus Thread, sondern durch
Implementierung des Interfaces Runnable zu erzeugen. Runnable enthlt nur eine einzige Deklaration, nmlich die der
Methode run:
public abstract void run()
java.lang.Runnable
Nun startet das Thread-Objekt die run-Methode des bergebenen Objekts, das sie ja durch die bergabe im Konstruktor
kennt. Da dieses Objekt das Interface Runnable implementiert, ist garantiert, da eine geeignete Methode run zur Verfgung
steht.
Wir wollen dies an einem Beispiel deutlich machen:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
/* Listing2204.java */
class A2204
{
int irgendwas;
//...
}
class B2204
extends A2204
implements Runnable
{
public void run()
{
int i = 0;
Listing2204.java
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
while (true) {
if (Thread.interrupted()) {
break;
}
System.out.println(i++);
}
}
}
public class Listing2204
{
public static void main(String[] args)
{
B2204 b = new B2204();
Thread t = new Thread(b);
t.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e){
//nichts
}
t.interrupt();
}
}
PrimeNumberTools.java
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048 }
}
}
System.out.println();
}
public int getPrime(int cnt)
{
int i = 1;
int ret = 2;
while (i < cnt) {
++ret;
if (isPrime(ret)) {
++i;
}
}
return ret;
}
private boolean isPrime(int num)
{
for (int i = 2; i < num; ++i) {
if (num % i == 0) {
return false;
}
}
return true;
}
/* Listing2206.java */
import java.io.*;
public class Listing2206
{
public static void main(String[] args)
{
PrimeNumberTools pt = new PrimeNumberTools();
BufferedReader
in = new BufferedReader(
new InputStreamReader(
new DataInputStream(System.in)));
int num;
try {
while (true) {
System.out.print("Bitte eine Zahl eingeben: ");
System.out.flush();
num = (new Integer(in.readLine())).intValue();
if (num == -1) {
break;
}
pt.printPrimeFactors(num);
}
} catch (IOException e) {
//nichts
}
}
Listing2206.java
029 }
Listing 22.6: Verwendung der Klasse zur Primfaktorzerlegung
Das Programm erzeugt eine Instanz der Klasse PrimeNumberTools und fhrt fr jeden eingelesenen Zahlenwert durch
Aufruf der Methode printPrimeFactors die Primfaktorzerlegung durch. Um nun diese Berechnungen asynchron
durchzufhren, entwerfen wir eine Wrapper-Klasse, die von PrimeNumberTools abgeleitet wird und das Interface
Runnable implementiert:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
/* ThreadedPrimeNumberTools.java */
ThreadedPrimeNumberTools.java
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
/* Listing2208.java */
Listing2208.java
import java.io.*;
public class Listing2208
{
public static void main(String[] args)
{
ThreadedPrimeNumberTools pt;
BufferedReader in = new BufferedReader(
new InputStreamReader(
new DataInputStream(System.in)));
int num;
try {
while (true) {
System.out.print("Bitte eine Zahl eingeben: ");
System.out.flush();
num = (new Integer(in.readLine())).intValue();
if (num == -1) {
break;
}
pt = new ThreadedPrimeNumberTools();
pt.printPrimeFactors(num);
}
} catch (IOException e) {
//nichts
}
}
}
991
577
677
-1
Obwohl das gewnschte Verhalten (nmlich die asynchrone Ausfhrung einer zeitaufwendigen Berechnung im
Hintergrund) realisiert wird, ist dieses Beispiel nicht beliebig zu verallgemeinern. Die Ausgabe erfolgt
beispielsweise nur dann ohne Unterbrechung durch Benutzereingaben, wenn alle Eingaben vor der ersten Ausgabe
abgeschlossen sind. Selbst in diesem Fall funktioniert das Programm nicht immer zuverlssig. Es ist generell
problematisch, Hintergrundprozessen zu erlauben, auf die Standardein- oder -ausgabe zuzugreifen, die ja
vorwiegend vom Vordergrund-Thread verwendet wird. Ein- und Ausgaben knnten durcheinander geraten und es
knnte zu Synchronisationsproblemen kommen, die die Ausgabe verflschen. Wir haben nur ausnahmsweise davon
Gebrauch gemacht, um das Prinzip der Hintergrundverarbeitung an einem einfachen Beispiel darzustellen.
Hinweis
Das nchste Problem ist die Realisierung des Dispatchers in run, der mit Hilfe der Instanzvariablen func und arg die
erforderlichen Funktionsaufrufe durchfhrt. Dies funktioniert hier recht problemlos, weil alle Methoden dieselbe
Parametrisierung haben. Im allgemeinen wre hier ein aufwendigerer bergabemechanismus erforderlich.
Des weiteren sind meistens Vorder- und Hintergrundverarbeitung zu synchronisieren, weil der Vordergrundproze die
Ergebnisse des Hintergrundprozesses bentigt. Auch hier haben wir stark vereinfacht, indem die Ergebnisse einfach direkt nach
der Verfgbarkeit vom Hintergrundproze auf den Bildschirm geschrieben wurden. Das Beispiel zeigt jedoch, wie prinzipiell
vorgegangen werden knnte, und ist vorwiegend als Anregung fr eigene Experimente anzusehen.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
22.4 Synchronisation
22.4 Synchronisation
22.4.1 Synchronisationsprobleme
22.4.2 Monitore
Anwendung von synchronized auf einen Block von Anweisungen
Anwendung von synchronized auf eine Methode
22.4.3 wait und notify
22.4.4 PipedInputStream und PipedOutputStream
22.4.1 Synchronisationsprobleme
Wenn man sich mit Nebenlufigkeit beschftigt, mu man sich in aller Regel auch mit Fragen der
Synchronisation nebenlufiger Prozesse beschftigen. In Java erfolgt die Kommunikation zweier Threads auf der
Basis gemeinsamer Variablen, die von beiden Threads erreicht werden knnen. Fhren beide Prozesse
nderungen auf den gemeinsamen Daten durch, so mssen sie synchronisiert werden, denn andernfalls knnen
undefinierte Ergebnisse entstehen.
Wir wollen uns als einleitendes Beispiel ein kleines Programm ansehen, bei dem zwei Threads einen
gemeinsamen Zhler hochzhlen:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
/* Listing2209.java */
public class Listing2209
extends Thread
{
static int cnt = 0;
public static void main(String[] args)
{
Thread t1 = new Listing2209();
Thread t2 = new Listing2209();
t1.start();
t2.start();
}
public void run()
{
while (true) {
System.out.println(cnt++);
}
}
}
Listing2209.java
Lt man das Programm eine Weile laufen, knnte es beispielsweise zu folgender Ausgabe kommen:
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
32
58
59
Beide Prozesse greifen unsynchronisiert auf die gemeinsame Klassenvariable cnt zu. Da die Operation
System.out.println(cnt++); nicht atomar ist, kommt es zu dem Fall, da die Operation mitten in der
Ausfhrung unterbrochen wird und der Scheduler mit dem anderen Thread fortfhrt. Erst spter, wenn der
unterbrochene Proze wieder Rechenzeit erhlt, kann er seinen vor der Unterbrechung errechneten Zhlerwert
von 32 ausgeben. Sein Pendant war in der Zwischenzeit allerdings bereits bis 56 fortgefahren. Um diese Art von
Inkonsistenzen zu beseitigen, bedarf es der Synchronisation der beteiligten Prozesse.
22.4.2 Monitore
Zur Synchronisation nebenlufiger Prozesse hat Java das Konzept des Monitors implementiert. Ein Monitor ist
die Kapselung eines kritischen Bereichs (also eines Programmteils, der nur von jeweils einem Proze zur Zeit
durchlaufen werden darf) mit Hilfe einer automatisch verwalteten Sperre. Diese Sperre wird beim Betreten des
Monitors gesetzt und beim Verlassen wieder zurckgenommen. Ist sie beim Eintritt in den Monitor bereits von
einem anderen Proze gesetzt, so mu der aktuelle Proze warten, bis der Konkurrent die Sperre freigegeben und
den Monitor verlassen hat.
Das Monitor-Konzept wird mit Hilfe des in die Sprache integrierten Schlsselworts synchronized realisiert.
Durch synchronized kann entweder eine komplette Methode oder ein Block innerhalb einer Methode
geschtzt werden. Der Eintritt in den so deklarierten Monitor wird durch das Setzen einer Sperre auf einer
Objektvariablen erreicht. Bezieht sich synchronized auf eine komplette Methode, wird als Sperre der thisPointer verwendet, andernfalls ist eine Objektvariable explizit anzugeben.
Anwendung von synchronized auf einen Block von Anweisungen
Wir wollen uns diese Art der Verwendung an einem Beispiel ansehen, welches das oben demonstrierte
Synchronisationsproblem lst. Die naheliegende Lsung, die Anweisung System.out.println(cnt++);
durch einen synchronized-Block auf der Variablen this zu synchronisieren, funktioniert leider nicht. Da
der Zeiger this fr jeden der beiden Threads, die ja unterschiedliche Instanzen reprsentieren, neu vergeben
wird, wre fr jeden Thread der Eintritt in den Monitor grundstzlich erlaubt.
Statt dessen verwenden wir die (in Abschnitt 43.2.2 erluterte) Methode getClass, die uns ein Klassenobjekt
beschafft (ein und dasselbe fr alle Instanzen), mit dem wir die Klassenvariable cnt schtzen knnen:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
/* Listing2210.java */
public class Listing2210
extends Thread
{
static int cnt = 0;
public static void main(String[] args)
{
Thread t1 = new Listing2210();
Thread t2 = new Listing2210();
t1.start();
t2.start();
}
Listing2210.java
015
016
public void run()
017
{
018
while (true) {
019
synchronized (getClass()) {
020
System.out.println(cnt++);
021
}
022
}
023
}
024 }
Listing 22.10: Synchronisation von Threads mit Klassenobjekten
Nun werden alle Zhlerwerte in aufsteigender Reihenfolge ausgegeben.
Anwendung von synchronized auf eine Methode
Ein anderer Fall ist der, bei dem der Zugriff auf ein Objekt selbst synchronisiert werden mu, weil damit zu
rechnen ist, da mehr als ein Thread zur gleichen Zeit das Objekt verwenden will.
Im folgenden werden die potentiellen Probleme am Beispiel eines Zhlerobjekts erlutert, dessen Aufgabe es ist,
einen internen Zhler zu kapseln, auf Anforderung den aktuellen Zhlerstand zu liefern und den internen Zhler
zu inkrementieren. Hierbei handelt es sich um eine Aufgabe, die beispielsweise in der
Datenbankprogrammierung sehr hufig vorkommt, um Schlsselnummern zu generieren.
Typischerweise wird das Synchronisationsproblem dadurch verschrft, da die Verwendung des Zhlers einige
vergleichsweise langsame Festplattenzugriffe erforderlich macht. In unserem Beispiel wird der Zhler von fnf
Threads verwendet. Die Langsamkeit und damit die Wahrscheinlichkeit, da der Scheduler die Zugriffsoperation
unterbricht, wird in unserem Beispiel durch eine Sequenz eingestreuter Fliekommaoperationen erhht:
001 /* Listing2211.java */
002
003 class Counter2211
004 {
005
int cnt;
006
007
public Counter2211(int cnt)
008
{
009
this.cnt = cnt;
010
}
011
012
public int nextNumber()
013
{
014
int ret = cnt;
015
//Hier erfolgen ein paar zeitaufwendige Berechnungen, um
016
//so zu tun, als sei das Errechnen des Nachfolgezhlers
017
//eine langwierige Operation, die leicht durch den
018
//Scheduler unterbrochen werden kann.
019
double x = 1.0, y, z;
020
for (int i= 0; i < 1000; ++i) {
021
x = Math.sin((x*i%35)*1.13);
022
y = Math.log(x+10.0);
023
z = Math.sqrt(x+y);
024
}
025
//Jetzt ist der Wert gefunden
Listing2211.java
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
cnt++;
return ret;
}
}
public class Listing2211
extends Thread
{
private String name;
private Counter2211 counter;
public Listing2211(String name, Counter2211 counter)
{
this.name = name;
this.counter = counter;
}
public static void main(String[] args)
{
Thread[] t = new Thread[5];
Counter2211 cnt = new Counter2211(10);
for (int i = 0; i < 5; ++i) {
t[i] = new Listing2211("Thread-"+i,cnt);
t[i].start();
}
}
public void run()
{
while (true) {
System.out.println(counter.nextNumber()+" for "+name);
}
}
}
for
for
for
for
for
for
for
for
for
for
for
for
for
for
for
Thread-2
Thread-4
Thread-0
Thread-1
Thread-2
Thread-3
Thread-4
Thread-0
Thread-1
Thread-2
Thread-3
Thread-4
Thread-0
Thread-1
Thread-2
Auch hier gibt es eine einfache Lsung fr das Synchronisationsproblem. Eine einfache Markierung der
Methode nextNumber als synchronized macht diese zu einem Monitor und sorgt dafr, da der komplette
Code innerhalb der Methode als atomares Programmfragment behandelt wird. Eine Unterbrechung des kritischen
Abschnitts durch einen anderen Thread ist dann nicht mehr mglich:
001 public synchronized int nextNumber()
002 {
003
int ret = cnt;
004
//Hier erfolgen ein paar zeitaufwendige Berechnungen, um so
005
//zu tun, als sei das Errechnen des Nachfolgezhlerstandes
006
//eine langwierige Operation, die leicht durch den
007
//Scheduler unterbrochen werden kann.
008
double x = 1.0, y, z;
009
for (int i= 0; i < 1000; ++i) {
010
x = Math.sin((x*i%35)*1.13);
011
y = Math.log(x+10.0);
012
z = Math.sqrt(x+y);
013
}
014
//Jetzt ist der Wert gefunden
015
cnt++;
016
return ret;
017 }
Listing 22.12: Synchronisieren der Zhlermethode
Durch das synchronized-Attribut wird beim Aufruf der Methode die Instanzvariable this gesperrt und
damit der Zugriff fr andere Threads unmglich gemacht. Erst nach Verlassen der Methode und Entsperren von
this kann nextNumber wieder von anderen Threads aufgerufen werden.
Diese Art des Zugriffschutzes wird in Java von vielen Klassen verwendet, um ihre Methoden thread- Hinweis
sicher zu machen. Nach Aussage der Sprachspezifikation kann davon ausgegangen werden, da die
gesamte Java-Klassenbibliothek in diesem Sinne thread-sicher ist. Dies gilt allerdings nicht fr die
Swing-Klassen.
die produzierten Daten verbraucht. Die Kommunikation zwischen beiden erfolgt ber ein gemeinsam
verwendetes Vector-Objekt, das die produzierten Elemente zwischenspeichert und als Medium fr die wait/notify-Aufrufe dient:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
/* Listing2213.java */
import java.util.*;
class Producer2213
extends Thread
{
private Vector v;
public Producer2213(Vector v)
{
this.v = v;
}
public void run()
{
String s;
while (true) {
synchronized (v) {
s = "Wert "+Math.random();
v.addElement(s);
System.out.println("Produzent erzeugte "+s);
v.notify();
}
try {
Thread.sleep((int)(100*Math.random()));
} catch (InterruptedException e) {
//nichts
}
}
}
}
class Consumer2213
extends Thread
{
private Vector v;
public Consumer2213(Vector v)
{
this.v = v;
}
public void run()
{
while (true) {
synchronized (v) {
if (v.size() < 1) {
try {
v.wait();
} catch (InterruptedException e) {
Listing2213.java
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
//nichts
}
}
System.out.print(
" Konsument fand "+(String)v.elementAt(0)
);
v.removeElementAt(0);
System.out.println(" (verbleiben: "+v.size()+")");
}
try {
Thread.sleep((int)(100*Math.random()));
} catch (InterruptedException e) {
//nichts
}
}
}
}
public class Listing2213
{
public static void main(String[] args)
{
Vector v = new Vector();
Producer2213 p = new Producer2213(v);
Consumer2213 c = new Consumer2213(v);
p.start();
c.start();
}
}
Hinweis
/* Listing2214.java */
import java.io.*;
class Producer2214
extends Thread
{
private PipedOutputStream pipe;
public Producer2214(PipedOutputStream pipe)
{
this.pipe = pipe;
Listing2214.java
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
}
public void run()
{
while (true) {
byte b = (byte)(Math.random() * 128);
try {
pipe.write(b);
System.out.println("Produzent erzeugte " + b);
} catch (IOException e) {
System.err.println(e.toString());
}
try {
Thread.sleep((int)(100*Math.random()));
} catch (InterruptedException e) {
//nichts
}
}
}
}
class Consumer2214
extends Thread
{
private PipedInputStream pipe;
public Consumer2214(PipedInputStream pipe)
{
this.pipe = pipe;
}
public void run()
{
while (true) {
try {
byte b = (byte)pipe.read();
System.out.println(" Konsument fand " + b);
} catch (IOException e) {
System.err.println(e.toString());
}
try {
Thread.sleep((int)(100*Math.random()));
} catch (InterruptedException e) {
//nichts
}
}
}
}
public class Listing2214
{
public static void main(String[] args)
throws Exception
{
PipedInputStream inPipe = new PipedInputStream();
PipedOutputStream outPipe = new PipedOutputStream(inPipe);
069
070
071
072
073
}
074 }
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Tip
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Sollten die in den vorangegangenen Abschnitten vorgestellten Drucktechniken nicht funktionieren oder auf Grund praktischer
Schwierigkeiten nicht einsetzbar sein, gibt es eine Reihe anderer Mglichkeiten, Daten unter Java auszudrucken. Die
aufwendigste und flexibelste von ihnen besteht darin, die Druckdaten selbst aufzubereiten und direkt an die serielle oder
parallele Schnittstelle zu senden, an der der Drucker angeschlossen ist. Wir wollen in diesem Abschnitt kurz skizzieren, wie das
mit Hilfe des Java Communications API durchgefhrt werden kann.
Vor Einsatz dieser Technik sollten ihre Nachteile bedacht werden. Einerseits ist der Aufwand unter Umstnden sehr Warnung
gro, denn die Anwendung mu alle Low-Level-Details der Druckeransteuerung selbst implementieren. Das umfat
die Auswahl und Belegung der Schnittstelle ebenso wie das Generieren der druckerabhngigen Steuersequenzen.
Zweitens ist das Verfahren nicht sonderlich portabel. Zwar gibt es das Communication API als JDKStandarderweiterung sowohl fr SOLARIS als auch fr Windows, auf anderen Systemen steht es aber unter
Umstnden nicht zur Verfgung. Auch Drucker, die nicht ber die serielle oder paralle Schnittstelle angesteuert
werden (z.B. USB- oder Netzwerkdrucker), knnen auf diese Weise naturgem nicht angesteuert werden.
Fr das JDK 1.4 ist die Installation des Communications APIs nun abgeschlossen. Bei einem anderen JDK oder auf einem
anderen Betriebssystem ist unter Umstnden anders vorzugehen. Weitere Informationen knnen der Datei Readme.html
entnommen werden.
Prinzipielle Arbeitsweise
Das Communications API liegt im Paket javax.comm. Die Klasse CommPortIdentifier dient zur Beschreibung von
Kommunikationsports. Sie besitzt eine statische Methode getPortIdentifiers, mit der eine Enumeration von
CommPortIdentifier-Objekten erzeugt werden kann, die ein Element je verfgbarem Port enthlt. Zustzlich kann mit der
statischen Methode getPortIdentifier auch direkt auf einen namentlich bekannten Port zugegriffen werden:
javax.comm.CommPortIdentifier
javax.comm.CommPortIdentifier
getName liefert den Namen des Ports (z.B. "COM1" oder "LPT1" unter Windows, "/dev/..." unter UNIX). getPortType
gibt entweder PORT_PARALLEL oder PORT_SERIAL zurck, je nachdem, ob es sich um eine parallele oder serielle
Schnittstelle handelt.
Soll ber einen Port kommuniziert werden, mu sein zugehriger CommPortIdentifier durch Aufruf von open geffnet
werden:
public synchronized CommPort open(String appname, int timeout)
throws PortInUseException
javax.comm.CommPortIdentifier
Die beiden Parameter geben den Namen der zu verwendenden Applikation und die maximale Zeitspanne, die beim ffnen
gewartet wird, an. Der Rckgabewert von open ist vom typ CommPort und kann in eine der (daraus abgeleiteten) Klassen
ParallelPort oder SerialPort umgewandelt werden.
Die wichtigsten Methoden von CommPort sind getInputStream und getOutputStream. Sie beschaffen die zum Lesen
bzw. Schreiben von Daten verwendeten Streams:
public InputStream getInputStream()
throws IOException
javax.comm.CommPort
Darber hinaus stellen CommPort und die daraus abgeleiteten Klassen einige Methoden zur Verfgung, mit denen
Kommunikationsparameter eingestellt, Puffergren gendert oder Portzustnde abgefragt werden knnen. Zudem bieten beide
Klassen die Mglichkeit, Event-Listener zu registrieren, die bei Zustandsnderungen oder eingehenden Daten automatisch
benachrichtigt werden. Wir wollen auf all diese Mglichkeiten nicht weiter eingehen; sie werden in der Dokumentation
ausfhrlich beschrieben.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
Listing2604.java
/* Listing2604.java */
import java.util.*;
import java.io.*;
import javax.comm.*;
public class Listing2604
{
public static void printHello(Writer out)
throws IOException
{
String s = "Hello LPT1 World";
s += " " + s + " " + s;
for (int i = 1; i <= 50; ++i) {
out.write(s.substring(0, i) + "\r\n");
}
out.write("\f");
}
public static void main(String[] args)
{
Enumeration en = CommPortIdentifier.getPortIdentifiers();
while (en.hasMoreElements()) {
CommPortIdentifier cpi = (CommPortIdentifier)en.nextElement();
if (cpi.getPortType() == CommPortIdentifier.PORT_PARALLEL) {
if (cpi.getName().equals("LPT1")) {
try {
ParallelPort lpt1 = (ParallelPort)cpi.open(
"LPT1Test",
1000
);
OutputStreamWriter out = new OutputStreamWriter(
lpt1.getOutputStream()
);
printHello(out);
out.close();
lpt1.close();
System.exit(0);
} catch (PortInUseException e) {
System.err.println(e.toString());
System.exit(1);
} catch (IOException e) {
System.err.println(e.toString());
System.exit(1);
}
}
}
}
}
}
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Um ein Fenster auf dem Bildschirm anzuzeigen, mu zunchst die passende Fensterklasse
instanziert werden. Dafr kommen die Klassen Window, Frame, Dialog, Applet und
FileDialog in Frage. Da die beiden Dialogklassen und die Entwicklung von Applets
spter behandelt werden, verbleiben die Klassen Window und Frame. Beide Klassen
haben unterschiedliche Konstruktoren:
public Frame()
java.awt.Frame
java.awt.Window
Ein Objekt der Klasse Frame kann demnach parameterlos oder mit einem String, der
den Titel des Fensters angibt, erzeugt werden. Bei der Instanzierung eines WindowObjekts mu dagegen das Vaterfenster bergeben werden.
Nach der Instanzierung wird die Methode setVisible aufgerufen, um das Fenster
anzuzeigen:
public void setVisible(boolean visible)
java.awt.Component
Der Parameter visible gibt an, ob das Fenster angezeigt oder geschlossen werden soll.
Wird true bergeben, so wird es angezeigt, andernfalls geschlossen. Die Aufrufsyntax
ist fr Frame und Window identisch, denn setVisible wurde aus der gemeinsamen
Oberklasse Component geerbt. Ein Aufruf von setVisible(true) legt die
Fensterressource an, stattet das Fenster mit einer Reihe von Standardeigenschaften aus und
zeigt es auf dem Bildschirm an.
java.awt.Window
Hinweis
Das folgende Beispiel zeigt ein Programm, das einen Frame auf dem Bildschirm anzeigt,
ihn nach ca. 3 Sekunden wieder entfernt und das Programm dann beendet:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
/* Listing2701.java */
import java.awt.*;
public class Listing2701
{
public static void main(String[] args)
{
Frame frame = new Frame("Frame entfernen");
frame.setSize(300,200);
frame.setVisible(true);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
//nichts
}
frame.setVisible(false);
frame.dispose();
System.exit(0);
}
}
Listing2701.java
Titel
<<
>
>>
API
Titel
<<
27.4 Anzeigezustand
27.4 Anzeigezustand
Seit dem JDK 1.2 kann der Anzeigezustand eines Fensters gendert werden. Dazu gibt es
in der Klasse Frame die Methoden setState und getState:
public synchronized void setState(int state)
java.awt.Frame
Mit setState kann der Anzeigezustand des Fensters zwischen normal und als
Symbol umgeschaltet werden. Wird die Konstante ICONIFIED der Klasse Frame
bergeben, erfolgt die Darstellung als Symbol, wird NORMAL bergeben, erfolgt die
normale Darstellung. Mit getState kann der aktuelle Anzeigezustand abgefragt
werden.
Das folgende Programm ffnet ein Fenster, stellt es nach zwei Sekunden als Symbol und
nach weiteren zwei Sekunden wieder normal dar. Anschlieend wird das Fenster
geschlossen und das Programm beendet:
001
002
003
004
005
006
007
008
009
010
011
012
/* Listing2703.java */
import java.awt.*;
public class Listing2703
{
public static void main(String[] args)
{
Frame frame = new Frame("Anzeigezustand");
frame.setSize(300,200);
frame.setVisible(true);
try {
Listing2703.java
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
}
033 }
Thread.sleep(2000);
} catch (InterruptedException e) {
//nichts
}
frame.setState(Frame.ICONIFIED);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
//nichts
}
frame.setState(Frame.NORMAL);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
//nichts
}
frame.setVisible(false);
frame.dispose();
System.exit(0);
Titel
<<
>
>>
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
30.7.1 berblick
Die Zwischenablage ist in grafischen Oberflchen eines der wichtigsten Hilfsmittel, um Daten zwischen
Dialogelementen oder ber Anwendungsgrenzen hinweg auszutauschen. Seit dem JDK 1.1 gibt es ein
allgemeines API fr den Datenaustausch, das Funktionen fr den Datenaustausch mit der Zwischenablage zur
Verfgung stellt. Wir wollen es hier kurz beschreiben, weil es als typisches Element eines Kontextmens gut in
den Rahmen dieses Kapitels pat.
Die zugehrigen Klassen und Interfaces befinden sich im Paket java.awt.datatransfer. Die fr die
Kommunikation mit der Zwischenablage interessanten Bestandteile dieses APIs sind:
Das Interface Transferable, das die Schnittstelle von Objekten festlegt, die mit der Zwischenablage
ausgetauscht werden knnen.
Die Definition der Datentypen, die ausgetauscht werden knnen. Diese werden in Java als DataFlavors
bezeichnet und basieren auf der MIME-Spezifikation (Multi-purpose Internet Mail Extensions), die in
RFC 1521 und 1522 beschrieben wird (allgemeine Hinweise zu den RFCs finden sich in Abschnitt
45.1.5).
Die Klasse Clipboard, mit der die Zwischenablage implementiert wird. Neben der systemweiten
Zwischenablage knnen auch benutzerdefinierte Zwischenablagen verwendet werden.
java.awt.datatransfer.Transferable
Die Initialisierung eines Transferable-Objekts ist nicht Bestandteil der Schnittstelle, sondern
mu von den implementierenden Klassen in Eigenregie - beispielsweise bei der Instanzierung vorgenommen werden.
Hinweis
Im AWT gibt es eine vordefinierte Klasse StringSelection, die das Interface Transferable
implementiert. Sie ist in der Lage, Strings auszutauschen, und untersttzt die aus DataFlavor abgeleiteten
Datentypen plainTextFlavor und stringFlavor. Beide liefern die Daten als Unicode-kodierte
Zeichenkette. Whrend plainTextFlavor sein Ergebnis als InputStream zur Verfgung stellt und den
MIME-Typ text/plain reprsentiert, liefert stringFlavor einen String und reprsentiert den MIMETyp application/x-java-serialized-object.
Ein StringSelection-Objekt wird initialisiert, indem die zu bertragende Zeichenkette an den
Konstruktor bergeben wird. Anschlieend kann es an die Zwischenablage bergeben werden, die die Daten
durch Aufruf von getTransferData bernimmt. Jeder Aufruf von getTransferData mu in eine trycatch-Anweisung eingebunden werden und folgende Fehler abfangen:
Das Transferable-Objekt ist nicht in der Lage, die Daten in dem gewnschten Format zu liefern.
Da der Rckgabewert vom Typ Object ist, mu er in der Regel in den tatschlich erforderlichen Typ
konvertiert werden. Hierbei kann eine Ausnahme des Typs ClassCastException auftreten.
Bevor die Zwischenablage die Daten aus dem Transferable-Objekt entnehmen kann, mu dieses natrlich
erst einmal an die Zwischenablage bergeben werden. Eine Zwischenablage ist immer eine Instanz der Klasse
Clipboard oder einer ihrer Unterklassen. Zwar ist es mglich, anwendungsspezifische Zwischenablagen
anzulegen, wir wollen uns aber nur mit der systemweit gltigen Zwischenablage des Betriebssystems
beschftigen.
Im Toolkit gibt es eine Methode getSystemClipboard, mit der ein Objekt fr die systemweite
Zwischenablage beschafft werden kann:
public Clipboard getSystemClipboard()
java.awt.Toolkit
Sie stellt im wesentlichen einen Konstruktor und drei Methoden zur Verfgung:
public String getName()
java.awt.datatransfer.Clipboard
Mit getName kann der Name der Zwischenablage ermittelt werden, getContents liefert den Inhalt der
Zwischenablage, und mit setContents kann der Zwischenablage ein neues Objekt zugewiesen werden. Ein
Aufruf von getContents liefert null, wenn die Zwischenablage leer ist. Der Rckgabewert ist ein
Transferable-Objekt, dessen Daten mit getTransferData abgefragt werden knnen. Beim Aufruf
von getContents mu zustzlich ein Objekt requestor bergeben werden, das derzeit keine Funktion
hat.
Ein Objekt, das den Inhalt der Zwischenablage ndern will, tut dies ber den Aufruf der Methode
setContents. Als erstes Argument ist ein Transferable-Objekt zu bergeben, das die Daten enthlt.
Als zweites mu ein Objekt bergeben werden, das das Interface ClipboardOwner implementiert. Da die
Zwischenablage von verschiedenen Objekten verwendet wird, ist es unter Umstnden wichtig zu wissen, wann
die bergebenen Daten verworfen und durch ein neues Objekt ersetzt werden. Dazu definiert
ClipboardOwner die Methode lostOwnership, die aufgerufen wird, wenn der Inhalt der
Zwischenablage verndert wird:
public void lostOwnership(
Clipboard clipboard, Transferable contents
)
java.awt.datatransfer.ClipboardOwner
Nach diesen Vorberlegungen wollen wir uns ein Beispiel ansehen. Dazu soll die Methode
actionPerformed des vorigen Beispiels erweitert werden, um die beiden Meneintrge Kopieren und
Einfuegen mit Funktionalitt zum Datenaustausch auszustatten. Zustzlich implementiert das
Beispielprogramm das Interface ClipboardOwner und definiert dazu die Methode lostOwnership:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
/* clpbrd.inc */
public void actionPerformed(ActionEvent event)
{
Clipboard clip = getToolkit().getSystemClipboard();
String cmd = event.getActionCommand();
if (cmd.equals("Kopieren")) {
String s = "Es ist " + System.currentTimeMillis() + "Uhr";
StringSelection cont = new StringSelection(s);
clip.setContents(cont, this);
} else if (cmd.equals("Einfuegen")) {
Transferable cont = clip.getContents(this);
if (cont == null) {
System.out.println("Zwischenablage ist leer");
} else {
try {
String s = (String) cont.getTransferData(
DataFlavor.stringFlavor
);
System.out.println(s);
} catch (Exception e) {
System.out.println(
"Zwischenablage enthlt keinen Text"
);
}
}
}
}
public void lostOwnership(Clipboard clip, Transferable cont)
{
System.out.println("Inhalt der Zwischenablage ersetzt");
}
clpbrd.inc
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
41.1 Grundlagen
41.1 Grundlagen
41.1.1 Begriffsbestimmung
41.1.2 Schreiben von Objekten
41.1.3 Lesen von Objekten
41.1.1 Begriffsbestimmung
Unter Serialisierung wollen wir die Fhigkeit verstehen, ein Objekt, das im Hauptspeicher der Anwendung
existiert, in ein Format zu konvertieren, das es erlaubt, das Objekt in eine Datei zu schreiben oder ber eine
Netzwerkverbindung zu transportieren. Dabei wollen wir natrlich auch den umgekehrten Weg einschlieen,
also das Rekonstruieren eines in serialisierter Form vorliegenden Objekts in das interne Format der laufenden
Java-Maschine.
Serialisierung wird hufig mit dem Begriff Persistenz gleichgesetzt, vor allem in objektorientierten
Programmiersprachen. Das ist nur bedingt richtig, denn Persistenz bezeichnet genaugenommen das
dauerhafte Speichern von Daten auf einem externen Datentrger, so da sie auch nach dem Beenden
des Programms erhalten bleiben. Obwohl die persistente Speicherung von Objekten sicherlich eine
der Hauptanwendungen der Serialisierung ist, ist sie nicht ihre einzige. Wir werden spter
Anwendungen sehen, bei der die Serialisierung von Objekten nicht zum Zweck ihrer persistenten
Speicherung genutzt werden.
Hinweis
java.io.ObjectOutputStream
Der an den Konstruktor bergebene OutputStream dient als Ziel der Ausgabe. Hier kann ein beliebiges
Objekt der Klasse OutputStream oder einer daraus abgeleiteten Klasse bergeben werden. Typischerweise
wird ein FileOutputStream verwendet, um die serialisierten Daten in eine Datei zu schreiben.
ObjectOutputStream besitzt sowohl Methoden, um primitive Typen zu serialisieren, als auch die wichtige
Methode writeObject, mit der ein komplettes Objekt serialisiert werden kann:
java.io.ObjectOutputStream
Whrend die Methoden zum Schreiben der primitiven Typen hnlich funktionieren wie die gleichnamigen
Methoden der Klasse RandomAccessFile (siehe Abschnitt 20.4), ist die Funktionsweise von
writeObject wesentlich komplexer. writeObject schreibt folgende Daten in den OutputStream:
Insbesondere der letzte Punkt verdient dabei besondere Beachtung. Die Methode writeObject durchsucht
also das bergebene Objekt nach Membervariablen und berprft deren Attribute. Ist eine Membervariable vom
Typ static, wird es nicht serialisiert, denn es gehrt nicht zum Objekt, sondern zur Klasse des Objekts.
Weiterhin werden alle Membervariablen ignoriert, die mit dem Schlsselwort transient deklariert wurden.
Auf diese Weise kann das Objekt Membervariablen definieren, die aufgrund ihrer Natur nicht serialisiert werden
sollen oder drfen. Wichtig ist weiterhin, da ein Objekt nur dann mit writeObject serialisiert werden kann,
wenn es das Interface Serializable implementiert.
Aufwendiger als auf den ersten Blick ersichtlich ist das Serialisieren von Objekten vor allem aus zwei Grnden:
Erstens mu zur Laufzeit ermittelt werden, welche Membervariablen das zu serialisierende Objekt besitzt
(bzw. geerbt hat) und welche von ihnen serialisiert werden sollen. Dazu wird das Reflection-API
verwendet (siehe Kapitel 43).
Zweitens kann eine Membervariable natrlich selbst ein Objekt sein, das seinerseits serialisiert werden
mu. Insbesondere mu writeObject dabei korrekt mit zyklischen Verweisen umgehen und dafr
sorgen, da Objektreferenzen erhalten bleiben (siehe Abschnitt 41.2.3).
Wir wollen uns zunchst ein Beispiel ansehen. Dazu konstruieren wir eine einfache Klasse Time, die eine
Uhrzeit, bestehend aus Stunden und Minuten, kapselt:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
/* Time.java */
Time.java
import java.io.*;
public class Time
implements Serializable
{
private int hour;
private int minute;
public Time(int hour, int minute)
{
this.hour = hour;
this.minute = minute;
}
public String toString()
{
return hour + ":" + minute;
}
}
/* Listing4102.java */
Listing4102.java
import java.io.*;
import java.util.*;
public class Listing4102
{
public static void main(String[] args)
{
try {
FileOutputStream fs = new FileOutputStream("test1.ser");
ObjectOutputStream os = new ObjectOutputStream(fs);
Time time = new Time(10,20);
os.writeObject(time);
os.close();
} catch (IOException e) {
System.err.println(e.toString());
}
}
}
FileOutputStream an dessen Konstruktor. Nun wird ein Time-Objekt fr die Uhrzeit 10:20 konstruiert und
mit writeObject serialisiert. Nach dem Schlieen des Streams steht das serialisierte Objekt in "test1.ser".
Wichtig an der Deklaration von Time ist das Implementieren des Serializable-Interfaces. Zwar Hinweis
definiert Serializable keine Methoden, writeObject testet jedoch, ob das zu serialisierende
Objekt dieses Interface implementiert. Ist das nicht der Fall, wird eine Ausnahme des Typs
NotSerializableException ausgelst.
Ein ObjectOutputStream kann nicht nur ein Objekt serialisieren, sondern beliebig viele, sie werden
nacheinander in den zugrundeliegenden OutputStream geschrieben. Das folgende Programm zeigt, wie
zunchst ein int, dann ein String und schlielich zwei Time-Objekte serialisiert werden:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
/* Listing4103.java */
Listing4103.java
import java.io.*;
import java.util.*;
public class Listing4103
{
public static void main(String[] args)
{
try {
FileOutputStream fs = new FileOutputStream("test2.ser");
ObjectOutputStream os = new ObjectOutputStream(fs);
os.writeInt(123);
os.writeObject("Hallo");
os.writeObject(new Time(10, 30));
os.writeObject(new Time(11, 25));
os.close();
} catch (IOException e) {
System.err.println(e.toString());
}
}
}
java.io.ObjectInputStream
Zudem besitzt die Klasse ObjectInputStream einen Konstruktor, der einen InputStream als Argument
erwartet, der zum Einlesen der serialisierten Objekte verwendet wird:
public ObjectInputStream(InputStream in)
java.io.ObjectInputStream
Das Deserialisieren eines Objektes kann man sich stark vereinfacht aus den folgenden beiden Schritten
bestehend vorstellen:
Zunchst wird ein neues Objekt des zu deserialisierenden Typs angelegt, und die Membervariablen
werden mit Default-Werten vorbelegt. Zudem wird der Default-Konstruktor der ersten nichtserialisierbaren Superklasse aufgerufen.
Anschlieend werden die serialisierten Daten gelesen und den entprechenden Membervariablen des
angelegten Objekts zugewiesen.
Das erzeugte Objekt hat anschlieend dieselbe Struktur und denselben Zustand, den das serialisierte Objekt hatte
(abgesehen von den nicht serialisierten Membervariablen des Typs static oder transient). Da der
Rckgabewert von readObject vom Typ Object ist, mu das erzeugte Objekt in den tatschlichen Typ
(oder eine seiner Oberklassen) umgewandelt werden. Das folgende Programm zeigt das Deserialisieren am
Beispiel des in Listing 41.2 serialisierten und in die Datei test1.ser geschriebenen Time-Objekts:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
/* Listing4104.java */
Listing4104.java
import java.io.*;
import java.util.*;
public class Listing4104
{
public static void main(String[] args)
{
try {
FileInputStream fs = new FileInputStream("test1.ser");
ObjectInputStream is = new ObjectInputStream(fs);
Time time = (Time)is.readObject();
System.out.println(time.toString());
is.close();
} catch (ClassNotFoundException e) {
System.err.println(e.toString());
} catch (IOException e) {
System.err.println(e.toString());
}
}
}
Warnung
Das nchste Element des Eingabestreams ist tatschlich ein Objekt (kein primitiver Typ).
Das Objekt mu sich vollstndig und fehlerfrei aus der Eingabedatei lesen lassen.
Es mu eine Konvertierung auf den gewnschten Typ erlauben, also entweder zu derselben oder einer
daraus abgeleiteten Klasse gehren.
Der Bytecode fr die Klasse des zu deserialisierenden Objekts mu vorhanden sein. Er wird beim
Serialisieren nicht mitgespeichert, sondern mu dem Empfngerprogramm wie blich als kompilierter
Bytecode zur Verfgung stehen.
Die Klasseninformation des serialisierten Objekts und die im deserialisierenden Programm als Bytecode
vorhandene Klasse mssen zueinander kompatibel sein. Wir werden auf diesen Aspekt in Abschnitt
41.2.1 detailliert eingehen.
Soll beispielsweise die in Listing 41.3 erzeugte Datei test2.ser deserialisiert werden, so mssen die Aufrufe der
read-Methoden in Typ und Reihenfolge denen des serialisierenden Programms entsprechen:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
Listing4105.java
/* Listing4105.java */
import java.io.*;
import java.util.*;
public class Listing4105
{
public static void main(String[] args)
{
try {
FileInputStream fs = new FileInputStream("test2.ser");
ObjectInputStream is = new ObjectInputStream(fs);
System.out.println("" + is.readInt());
System.out.println((String)is.readObject());
Time time = (Time)is.readObject();
System.out.println(time.toString());
time = (Time)is.readObject();
System.out.println(time.toString());
is.close();
} catch (ClassNotFoundException e) {
System.err.println(e.toString());
} catch (IOException e) {
System.err.println(e.toString());
}
}
}
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
41.3 Anwendungen
41.3 Anwendungen
41.3.1 Ein einfacher Objektspeicher
41.3.2 Kopieren von Objekten
/* TrivialObjectStore.java */
import java.io.*;
import java.util.*;
/**
* Trivialer Objektspeicher, der Mengen von Name-Objekt* Paaren aufnehmen und persistent speichern kann.
*/
public class TrivialObjectStore
{
//Instance variables
private String fname;
private Hashtable objects;
/**
* Erzeugt einen neuen Objektspeicher mit dem angegebenen
* Namen (die Erweiterung ".tos" ("trivial object store")
* wird ggfs. automatisch angehngt.
*/
public TrivialObjectStore(String fname)
{
this.fname = fname;
if (!fname.endsWith(".tos")) {
this.fname += ".tos";
}
this.objects = new Hashtable(50);
}
/**
TrivialObjectStore.java
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081 }
dies am Beispiel eines fiktiven Tamagotchi-Shops, dessen Eigenschaften Name, Besitzer und Liste der
Produkte im Objektspeicher abgelegt, in die Datei shop.tos geschrieben und anschlieend wieder ausgelesen
werden:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
/* Listing4110.java */
import java.io.*;
import java.util.*;
public class Listing4110
{
public static void main(String[] args)
{
//Erzeugen und Speichern des Objektspeichers
TrivialObjectStore tos = new TrivialObjectStore("shop");
tos.putObject("name", "Tami-Shop Norderelbe");
tos.putObject("besitzer", "Meier, Fridolin");
Vector products = new Vector(10);
products.addElement("Dinky Dino");
products.addElement("96er Classic");
products.addElement("Black Frog");
products.addElement("SmartGotchi");
products.addElement("Pretty Dolly");
tos.putObject("produkte", products);
try {
tos.save();
} catch (IOException e) {
System.err.println(e.toString());
}
//Einlesen des Objektspeichers
TrivialObjectStore tos2 = new TrivialObjectStore("shop");
try {
tos2.load();
Enumeration names = tos2.getAllNames();
while (names.hasMoreElements()) {
String name = (String)names.nextElement();
Object obj = tos2.getObject(name);
System.out.print(name + ": ");
System.out.println(obj.getClass().toString());
if (obj instanceof Collection) {
Iterator it = ((Collection)obj).iterator();
while (it.hasNext()) {
System.out.println(" " + it.next().toString());
}
} else {
System.out.println(" " + obj.toString());
}
}
} catch (IOException e) {
System.err.println(e.toString());
} catch (ClassNotFoundException e) {
System.err.println(e.toString());
}
}
}
Listing4110.java
Anstatt den Objektspeicher immer komplett zu laden und zu speichern, sollte es mglich sein,
einzelne Elemente zu speichern, zu laden und zu lschen.
Der Objektspeicher sollte mehrbenutzerfhig sein und Transaktions- und Recovery-Logik
mitbringen.
Die Suche nach Objekten sollte durch Indexdateien beschleunigt werden knnen.
Leider ist die Implementierung dieser Features nicht trivial. Ein gutes Beispiel fr die Implementierung des ersten
und dritten Punkts findet sich in "Java Algorithms" von Scott Robert Ladd. Der Autor zeigt zunchst, wie man
Objekte auf der Basis von Random-Access-Dateien (anstelle der blichen Streams) serialisiert. Anschlieend
zeigt er die Verwendung von Indexdateien am Beispiel der Implementierung von B-Trees.
/* Listing4111.java */
import java.io.*;
import java.util.*;
public class Listing4111
{
public static Object seriaClone(Object o)
throws IOException, ClassNotFoundException
{
//Serialisieren des Objekts
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(out);
os.writeObject(o);
os.flush();
//Deserialisieren des Objekts
ByteArrayInputStream in = new ByteArrayInputStream(
out.toByteArray()
);
ObjectInputStream is = new ObjectInputStream(in);
Object ret = is.readObject();
is.close();
os.close();
return ret;
}
public static void main(String[] args)
{
try {
//Erzeugen des Buchobjekts
Book book = new Book();
book.author = "Peitgen, Heinz-Otto";
String[] s = {"Jrgens, Hartmut", "Saupe, Dietmar"};
book.coAuthors = s;
book.title = "Bausteine des Chaos";
book.publisher = "rororo science";
book.pubyear = 1998;
book.pages = 514;
book.isbn = "3-499-60250-4";
book.reflist = new Vector();
book.reflist.addElement("The World of MC Escher");
book.reflist.addElement(
"Die fraktale Geometrie der Natur"
);
book.reflist.addElement("Gdel, Escher, Bach");
Listing4111.java
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
System.out.println(book.toString());
//Erzeugen und Verndern der Kopie
Book copy = (Book)seriaClone(book);
copy.title += " - Fraktale";
copy.reflist.addElement("Fractal Creations");
//Ausgeben von Original und Kopie
System.out.print(book.toString());
System.out.println("---");
System.out.print(copy.toString());
} catch (IOException e) {
System.err.println(e.toString());
} catch (ClassNotFoundException e) {
System.err.println(e.toString());
}
}
}
class Book
implements Serializable
{
public String author;
public String[] coAuthors;
public String title;
public String publisher;
public int
pubyear;
public int
pages;
public String isbn;
public Vector reflist;
public String toString()
{
String NL = System.getProperty("line.separator");
StringBuffer ret = new StringBuffer(200);
ret.append(author + NL);
for (int i = 0; i < coAuthors.length; ++i) {
ret.append(coAuthors[i] + NL);
}
ret.append("\"" + title + "\"" + NL);
ret.append(publisher + " " + pubyear + NL);
ret.append(pages + " pages" + NL);
ret.append(isbn + NL);
Enumeration e = reflist.elements();
while (e.hasMoreElements()) {
ret.append(" " + (String)e.nextElement() + NL);
}
return ret.toString();
}
}
Peitgen, Heinz-Otto
Jrgens, Hartmut
Saupe, Dietmar
"Bausteine des Chaos"
rororo science 1998
514 pages
3-499-60250-4
The World of MC Escher
Die fraktale Geometrie der Natur
Gdel, Escher, Bach
--Peitgen, Heinz-Otto
Jrgens, Hartmut
Saupe, Dietmar
"Bausteine des Chaos - Fraktale"
rororo science 1998
514 pages
3-499-60250-4
The World of MC Escher
Die fraktale Geometrie der Natur
Gdel, Escher, Bach
Fractal Creations
An der Programmausgabe kann man erkennen, da das Objekt tatschlich ordnungsgem kopiert wurde. Auch
alle Unterobjekte wurden kopiert und konnten anschlieend unabhngig voneinander gendert werden. Ohne
Serialisierung wre der manuelle Aufwand um ein Vielfaches grer gewesen. Das Verfahren findet dort seine
Grenzen, wo die zu kopierenden Objekte nicht serialisierbar sind oder nicht-serialisierbare Unterobjekte enthalten.
Zudem mu im echten Einsatz das Laufzeitverhalten berprft werden, denn der Vorgang des
Serialisierens/Deserialisierens ist um ein Vielfaches langsamer als das direkte Kopieren der Objektattribute.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
/* Listing4301.java */
import java.io.*;
interface HelloMeth
{
public void hello();
}
class CA
implements HelloMeth
{
public void hello()
{
System.out.println("hello CA");
}
}
class CB
implements HelloMeth
{
public void hello()
{
System.out.println("hello CB");
}
}
class CC
{
public void hello()
{
System.out.println("hello CC");
}
}
class CD
{
public void hallo()
{
System.out.println("hallo CD");
}
}
public class Listing4301
{
public static void main(String[] args)
{
String buf = "";
BufferedReader in = new BufferedReader(
new InputStreamReader(
new DataInputStream(System.in)));
while (true) {
try {
System.out.print("Klassenname oder ende eingeben: ");
buf = in.readLine();
Listing4301.java
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
}
074
}
075 }
}
}
}
}
}
if (buf.equals("ende")) {
break;
}
Class c = Class.forName(buf);
Object o = c.newInstance();
((HelloMeth)o).hello();
catch (IOException e) {
System.out.println(e.toString());
catch (ClassNotFoundException e) {
System.out.println("Klasse nicht gefunden");
catch (ClassCastException e) {
System.out.println(e.toString());
catch (InstantiationException e) {
System.out.println(e.toString());
catch (IllegalAccessException e) {
System.out.println(e.toString());
Mit der statischen Methode forName wurde der Classloader beauftragt, eine Klasse des angegebenen
Namens zu suchen und in die Java-Maschine zu laden. Die VM fhrt anschlieend die statischen
Initialisierungen aus (falls es nicht schon ein Klassenobjekt gibt) und liefert das zugehrige
Klassenobjekt.
Mit dem Klassenobjekt knnen nun verschiedene Aufgaben erledigt werden. Insbesondere ist es
mglich, Informationen ber Konstruktoren, Membervariablen oder Methoden der Klasse abzufragen.
Zudem kann mit newInstance eine neue Instanz der Klasse angelegt werden. Ist der Typ der Klasse
oder ihrer Basisklasse oder eines ihrer Interfaces bekannt, kann das Objekt auf eine Variable dieses
Typs konvertiert und dessen Methoden aufgerufen werden.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
Nachdem wir uns in den vorangegangenen Abschnitten die beiden Reflection-Aspekte Instanzierung und
Methodenaufruf angesehen haben, wollen wir uns nun mit dem Zugriff auf Membervariablen (also auf die
Felder eines Objekts) mit Hilfe von Reflection beschftigen. Prinzipiell gibt es keine groen Unterschiede
gegenber dem Zugriff auf eine Methode oder einen Konstruktor:
1. Zunchst wird ein Klassenobjekt fr das zu bearbeitende Objekt beschafft.
2. Dann wird eine Methode aufgerufen, um ein Bearbeitungsobjekt fr eine ganz bestimmte oder eine
Liste aller Membervariablen zu beschaffen.
3. Das Bearbeitungsobjekt wird verwendet, um die Membervariable zu lesen, zu verndern oder andere
Eigenschaften abzufragen.
Der erste Schritt erfolgt wie gewohnt durch Aufruf von getClass oder die .class-Notation. Die im
zweiten Schritt bentigten Bearbeitungsobjekte sind vom Typ Field, sie knnen auf dem Klassenobjekt
durch Aufruf einer der folgenden Methoden beschafft werden:
Field getField(String name)
Field[] getFields()
java.lang.Class
Mit getField wird die Membervariable mit dem angegebenen Namen beschafft, und getFields liefert
ein Array mit Field-Objekten zu allen ffentlichen Membervariablen. Die beiden Methoden
getDeclaredField und getDeclaredFields liefern darber hinaus auch nicht-ffentliche
Membervariablen.
Die Klasse Field besitzt Methoden zum Zugriff auf die Membervariable:
Class getType()
java.lang.reflect.Field
Mit getType kann der Typ der Membervariable bestimmt werden. Das zurckgegebene Klassenobjekt
beschreibt ihn in derselben Weise wie beispielsweise das Parameterobjekt von getMethod. Mit get kann
auf den Wert zugegriffen werden, den die Membervariable in dem als Argument bergebenen Objekt hat.
Handelt es sich um einen primitiven Typ, wird dieser automatisch in die passende Wrapper-Klasse verpackt
und als Objekt zurckgegeben. Referenztypen werden unverndert zurckgegeben. Mit Hilfe der Methode
set kann der Wert einer Membervariable verndert werden. Das erste Argument reprsentiert das zu
vernderende Objekt und das zweite den neuen Wert der Membervariable. Soll ein primitiver Typ verndert
werden, mu er vor der bergabe in die passende Wrapper-Klasse verpackt werden.
Neben den generischen get- und set-Methoden gibt es diese auch in typisierter Form. So dient
beispielsweise getInt dazu, ein int-Feld abzufragen, mit getDouble kann auf ein double
zugegriffen werden usw. Das Gegenstck dazu sind die Methoden setInt, setDouble usw., mit
denen die Membervariablen typisiert verndert werden knnen.
Tip
Wir wollen als Beispiel eine Klasse PrintableObject erstellen, die direkt aus Object abgeleitet ist
und die Methode toString berlagert. Im Gegensatz zur Implementierung von Object soll unsere
Variante von toString in der Lage sein, die Namen und Inhalte aller Membervariablen des zugehrigen
Objekts auszugeben:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
/* PrintableObject.java */
import java.lang.reflect.*;
public class PrintableObject
{
public String toString()
{
StringBuffer sb = new StringBuffer(200);
Class clazz = getClass();
while (clazz != null) {
Field[] fields = clazz.getDeclaredFields();
for (int i = 0; i < fields.length; ++i) {
sb.append(fields[i].getName() + " = ");
try {
Object obj = fields[i].get(this);
if (obj.getClass().isArray()) {
Object[] ar = (Object[])obj;
for (int j = 0; j < ar.length; ++j) {
sb.append(ar[j].toString() + " ");
}
sb.append("\n");
} else {
sb.append(obj.toString() + "\n");
}
} catch (IllegalAccessException e) {
sb.append(e.toString() + "\n");
}
}
clazz = clazz.getSuperclass();
}
return sb.toString();
}
public static void main(String[] args)
{
JavaProgrammer jim = new JavaProgrammer();
PrintableObject.java
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
jim.name
= "Jim Miller";
jim.department
= "Operating Systems";
jim.age
= 32;
String[] langs
= {"C", "Pascal", "PERL", "Java"};
jim.languages
= langs;
jim.linesofcode
= 55000;
jim.jdk12
= true;
jim.swing
= false;
System.out.println(jim);
}
}
class Employee
extends PrintableObject
{
public String name;
public String department;
public int
age;
}
class Programmer
extends Employee
{
public String[] languages;
public int
linesofcode;
}
class JavaProgrammer
extends Programmer
{
public boolean jdk12;
public boolean swing;
}
Programmer. Das Hauptprogramm erzeugt ein Objekt des Typs JavaProgrammer und weist seinen
eigenen und den geerbten Membervariablen Werte zu, die durch den anschlieenden Aufruf von toString
auf dem Bildschirm ausgegeben werden. Die Ausgabe des Programms ist:
jdk12 = true
swing = false
languages = C Pascal PERL Java
linesofcode = 55000
name = Jim Miller
department = Operating Systems
age = 32
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
45.2 Client-Sockets
45.2 Client-Sockets
45.2.1 Adressierung
45.2.2 Aufbau einer einfachen Socket-Verbindung
45.2.3 Lesen und Schreiben von Daten
45.2.4 Zugriff auf einen Web-Server
45.2.1 Adressierung
Zur Adressierung von Rechnern im Netz wird die Klasse InetAddress des Pakets java.net verwendet. Ein
InetAddress-Objekt enthlt sowohl eine IP-Adresse als auch den symbolischen Namen des jeweiligen
Rechners. Die beiden Bestandteile knnen mit den Methoden getHostName und getHostAddress
abgefragt werden. Mit Hilfe von getAddress kann die IP-Adresse auch direkt als byte-Array mit vier
Elementen beschafft werden:
String getHostName()
java.net.InetAddress
String getHostAddress()
byte[] getAddress()
Um ein InetAddress-Objekt zu generieren, stehen die beiden statischen Methoden getByName und
getLocalHost zur Verfgung:
public static InetAddress getByName(String host)
throws UnknownHostException
java.net.InetAddress
getByName erwartet einen String mit der IP-Adresse oder dem Namen des Hosts als Argument,
getLocalHost liefert ein InetAddress-Objekt fr den eigenen Rechner. Beide Methoden lsen eine
Ausnahme des Typs UnknownHostException aus, wenn die Adresse nicht ermittelt werden kann. Das ist
insbesondere dann der Fall, wenn kein DNS-Server zur Verfgung steht, der die gewnschte Namensauflsung
erledigen knnte (beispielsweise weil die Dial-In-Verbindung zum Provider gerade nicht besteht).
Das folgende Listing zeigt ein einfaches Programm, das zu einer IP-Adresse den symbolischen Namen des
zugehrigen Rechners ermittelt und umgekehrt:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
/* Listing4501.java */
Listing4501.java
import java.net.*;
public class Listing4501
{
public static void main(String[] args)
{
if (args.length != 1) {
System.err.println("Usage: java Listing4501 <host>");
System.exit(1);
}
try {
//Get requested address
InetAddress addr = InetAddress.getByName(args[0]);
System.out.println(addr.getHostName());
System.out.println(addr.getHostAddress());
} catch (UnknownHostException e) {
System.err.println(e.toString());
System.exit(1);
}
}
}
Hinweis
Die nachfolgende Ausgabe zeigt die Ausgabe des Beispielprogramms, wenn es nacheinander mit den
Argumenten java.sun.com, www.gkrueger.com und www.addison-wesley.de aufgerufen wird:
java.sun.com
192.18.97.71
www.gkrueger.com
213.221.123.45
www.addison-wesley.de
194.163.213.76
Whrend die Socket-Programmierung in C eine etwas mhsame Angelegenheit war, ist es in Java recht einfach
geworden. Im wesentlichen sind dazu die beiden Klassen Socket und ServerSocket erforderlich. Sie
reprsentieren Sockets aus der Sicht einer Client- bzw. Server-Anwendung. Nachfolgend wollen wir uns mit den
Client-Sockets beschftigen, die Klasse ServerSocket wird im nchsten Abschnitt behandelt.
Die Klasse Socket besitzt verschiedene Konstruktoren, mit denen ein neuer Socket erzeugt werden kann. Die
wichtigsten von ihnen sind:
public Socket(String host, int port)
throws UnknownHostException, IOException
java.net.Socket
Beide Konstruktoren erwarten als erstes Argument die bergabe des Hostnamens, zu dem eine Verbindung
aufgebaut werden soll. Dieser kann entweder als Domainname in Form eines Strings oder als Objekt des Typs
InetAddress bergeben werden. Soll eine Adresse mehrfach verwendet werden, ist es besser, die zweite
Variante zu verwenden. In diesem Fall kann das bergebene InetAddress-Objekt wiederverwendet werden,
und die Adressauflsung mu nur einmal erfolgen. Wenn der Socket nicht geffnet werden konnte, gibt es eine
Ausnahme des Typs IOException bzw. UnknownHostException (wenn das angegebene Zielsystem nicht
angesprochen werden konnte).
Der zweite Parameter des Konstruktors ist die Portnummer. Wie in Abschnitt 45.1.4 erwhnt, dient sie dazu, den
Typ des Servers zu bestimmen, mit dem eine Verbindung aufgebaut werden soll. Die wichtigsten StandardPortnummern sind in Tabelle 45.2 aufgelistet.
Nachdem die Socket-Verbindung erfolgreich aufgebaut wurde, kann mit den beiden Methoden
getInputStream und getOutputStream je ein Stream zum Empfangen und Versenden von Daten
beschafft werden:
public InputStream getInputStream()
throws IOException
java.net.Socket
Diese Streams knnen entweder direkt verwendet oder mit Hilfe der Filterstreams in einen bequemer zu
verwendenden Streamtyp geschachtelt werden. Nach Ende der Kommunikation sollten sowohl die Eingabe- und
Ausgabestreams als auch der Socket selbst mit close geschlossen werden.
Als erstes Beispiel wollen wir uns ein Programm ansehen, das eine Verbindung zum DayTime-Service auf Port
13 herstellt. Dieser Service luft auf fast allen UNIX-Maschinen und kann gut zu Testzwecken verwendet
werden. Nachdem der Client die Verbindung aufgebaut hat, sendet der DayTime-Server einen String mit dem
aktuellen Datum und der aktuellen Uhrzeit und beendet dann die Verbindung.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
/* Listing4502.java */
Listing4502.java
import java.net.*;
import java.io.*;
public class Listing4502
{
public static void main(String[] args)
{
if (args.length != 1) {
System.err.println("Usage: java Listing4502 <host>");
System.exit(1);
}
try {
Socket sock = new Socket(args[0], 13);
InputStream in = sock.getInputStream();
int len;
byte[] b = new byte[100];
while ((len = in.read(b)) != -1) {
System.out.write(b, 0, len);
}
in.close();
sock.close();
} catch (IOException e) {
System.err.println(e.toString());
System.exit(1);
}
}
}
7 22:58:37 1998
Um in Listing 45.2 den Socket alternativ mit einem InetAddress-Objekt zu ffnen, wre Zeile 015
durch den folgenden Code zu ersetzen:
Tip
Bildschirm ausgibt. Dieser luft unabhngig vom Vordergrund-Thread, in dem die Benutzereingaben abgefragt
und an den Server gesendet werden.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
/* EchoClient.java */
import java.net.*;
import java.io.*;
public class EchoClient
{
public static void main(String[] args)
{
if (args.length != 1) {
System.err.println("Usage: java EchoClient <host>");
System.exit(1);
}
try {
Socket sock = new Socket(args[0], 7);
InputStream in = sock.getInputStream();
OutputStream out = sock.getOutputStream();
//Timeout setzen
sock.setSoTimeout(300);
//Ausgabethread erzeugen
OutputThread th = new OutputThread(in);
th.start();
//Schleife fr Benutzereingaben
BufferedReader conin = new BufferedReader(
new InputStreamReader(System.in));
String line = "";
while (true) {
//Eingabezeile lesen
line = conin.readLine();
if (line.equalsIgnoreCase("QUIT")) {
break;
}
//Eingabezeile an ECHO-Server schicken
out.write(line.getBytes());
out.write('\r');
out.write('\n');
//Ausgabe abwarten
th.yield();
}
//Programm beenden
System.out.println("terminating output thread...");
th.requestStop();
th.yield();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
in.close();
out.close();
sock.close();
} catch (IOException e) {
System.err.println(e.toString());
System.exit(1);
EchoClient.java
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
}
}
}
class OutputThread
extends Thread
{
InputStream in;
boolean
stoprequested;
public OutputThread(InputStream in)
{
super();
this.in = in;
stoprequested = false;
}
public synchronized void requestStop()
{
stoprequested = true;
}
public void run()
{
int len;
byte[] b = new byte[100];
try {
while (!stoprequested) {
try {
if ((len = in.read(b)) == -1) {
break;
}
System.out.write(b, 0, len);
} catch (InterruptedIOException e) {
//nochmal versuchen
}
}
} catch (IOException e) {
System.err.println("OutputThread: " + e.toString());
}
}
}
Wie im vorigen Beispiel wird zunchst ein Socket zu dem als Argument angegebenen Host geffnet. Das
Programm beschafft dann Ein- und Ausgabestreams zum Senden und Empfangen von Daten. Der Aufruf von
setSoTimeout gibt die maximale Wartezeit bei einem lesenden Zugriff auf den Socket an (300 ms.). Wenn bei
einem read auf den InputStream nach Ablauf dieser Zeit noch keine Daten empfangen wurden, terminiert
die Methode mit einer InterruptedIOException; wir kommen darauf gleich zurck. Nun erzeugt das
Programm den Lesethread und bergibt ihm den Eingabestream. In der nun folgenden Schleife (Zeile 027)
werden so lange Eingabezeilen gelesen und an den Server gesendet, bis der Anwender das Programm mit QUIT
beendet.
Das Programm wurde auf einer LINUX-Version entwickelt, die noch kein premptives
Warnung
Multithreading untersttzt. Die verschiedenen Aufrufe von yield dienen dazu, die Kontrolle an den
Lesethread zu bergeben. Ohne diesen Aufruf wrde der Lesethread gar nicht zum Zuge kommen
und das Programm wrde keine Daten vom Socket lesen. Auf Systemen, die premptives
Multithreading untersttzen, sind diese Aufrufe nicht notwendig.
Die Klasse OutputThread implementiert den Thread zum Lesen und Ausgeben der Daten. Da die Methode
stop der Klasse Thread im JDK 1.2 als deprecated markiert wurde, mssen wir mit Hilfe der Variable
stoprequested etwas mehr Aufwand treiben, um den Thread beenden zu knnen. stoprequested steht
normalerweise auf false und wird beim Beenden des Programms durch Aufruf von requestStop auf true
gesetzt. In der Hauptschleife des Threads wird diese Variable periodisch abgefragt, um die Schleife bei Bedarf
abbrechen zu knnen (Zeile 081).
Problematisch bei dieser Technik ist lediglich, da der Aufruf von read normalerweise so lange blockiert, bis
weitere Zeichen verfgbar sind. Steht das Programm also in Zeile 083, so hat ein Aufruf requestStop
zunchst keine Wirkung. Da das Hauptprogramm in Zeile 048 die Streams und den Socket schliet, wrde es zu
einer SocketException kommen. Unser Programm verhindert das durch den Aufruf von setSoTimeout
in Zeile 019. Dadurch wird ein Aufruf von read nach sptestens 300 ms. mit einer
InterruptedIOException beendet. Diese Ausnahme wird in Zeile 087 abgefangen, um anschlieend vor
dem nchsten Schleifendurchlauf die Variable stoprequested erneut abzufragen.
oder Frames, so fordert der Browser die fehlenden Seiten in weiteren GET-Transaktionen von deren Servern ab.
Die Struktur des GET-Kommandos wurde mit der Einfhrung von HTTP 1.0 etwas erweitert. Zustzlich werden
nun am Ende der Zeile eine Versionskennung und wahlweise in den darauffolgenden Zeilen weitere Headerzeilen
mit Zusatzinformationen mitgeschickt. Nachdem die letzte Headerzeile gesendet wurde, folgt eine leere Zeile
(also ein alleinstehendes CRLF), um das Kommandoende anzuzeigen. HTTP 1.0 ist weit verbreitet, und das obige
Kommando wrde von den meisten Browsern in folgender Form gesendet werden (jede der beiden Zeilen mu
mit CRLF abgeschlossen werden):
GET /index.html HTTP/1.0
Wird HTTP/1.0 verwendet, ist auch die Antwort des Servers etwas komplexer. Anstatt lediglich den Inhalt der
Datei zu senden, liefert der Server seinerseits einige Headerzeilen mit Zusatzinformationen, wie beispielsweise
den Server-Typ, das Datum der letzten nderung oder den MIME-Typ der Datei. Auch hier ist jede Headerzeile
mit einem CRLF abgeschlossen, und nach der letzten Headerzeile folgt eine Leerzeile. Erst dann beginnt der
eigentliche Dateiinhalt.
Das folgende Programm kann dazu verwendet werden, eine Datei von einem Web-Server zu laden. Es wird mit
einem Host- und einem Dateinamen als Argument aufgerufen und ldt die Seite vom angegebenen Server. Das
Ergebnis wird (mit allen Headerzeilen) auf dem Bildschirm angezeigt.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
/* Listing4504.java */
import java.net.*;
import java.io.*;
public class Listing4504
{
public static void main(String[] args)
{
if (args.length != 2) {
System.err.println(
"Usage: java Listing4504 <host> <file>"
);
System.exit(1);
}
try {
Socket sock = new Socket(args[0], 80);
OutputStream out = sock.getOutputStream();
InputStream in = sock.getInputStream();
//GET-Kommando senden
String s = "GET " + args[1] + " HTTP/1.0" + "\r\n\r\n";
out.write(s.getBytes());
//Ausgabe lesen und anzeigen
int len;
byte[] b = new byte[100];
while ((len = in.read(b)) != -1) {
System.out.write(b, 0, len);
}
//Programm beenden
in.close();
out.close();
sock.close();
} catch (IOException e) {
System.err.println(e.toString());
Listing4504.java
035
System.exit(1);
036
}
037
}
038 }
Listing 45.4: Laden einer Seite von einem Web-Server
Wird das Programm beispielsweise auf einem SUSE-Linux 5.2 mit frisch installiertem Apache-Server mit
localhost und /index.html als Argument aufgerufen, so beginnt seine Ausgabe wie folgt:
HTTP/1.1 200 OK
Date: Sun, 08 Nov 1998 18:26:13 GMT
Server: Apache/1.2.5 S.u.S.E./5.1
Last-Modified: Sun, 24 May 1998 00:46:46 GMT
ETag: "e852-45c-35676df6"
Content-Length: 1116
Accept-Ranges: bytes
Connection: close
Content-Type: text/html
<HTML>
<HEAD>
<TITLE>Apache HTTP Server - Beispielseite</TITLE>
</HEAD>
<BODY bgcolor=#ffffff>
<H1> Der Apache WWW Server </H1> <BR>
Diese Seite soll nur als Beispiel dienen.
Die <A HREF="./manual/">Dokumentation zum
Apache-Server</A> finden Sie hier.
<P>
...
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
45.3 Server-Sockets
45.3 Server-Sockets
45.3.1 Die Klasse ServerSocket
45.3.2 Verbindungen zu mehreren Clients
45.3.3 Entwicklung eines einfachen Web-Servers
java.net.ServerSocket
Der Konstruktor erzeugt einen ServerSocket fr einen bestimmten Port, also einen bestimmten Typ von Serveranwendung
(siehe Abschnitt 45.1.4). Anschlieend wird die Methode accept aufgerufen, um auf einen eingehenden Verbindungswunsch zu
warten. accept blockiert so lange, bis sich ein Client bei der Serveranwendung anmeldet (also einen Verbindungsaufbau zu
unserem Host unter der Portnummer, die im Konstruktor angegeben wurde, initiiert). Ist der Verbindungsaufbau erfolgreich, liefert
accept ein Socket-Objekt, das wie bei einer Client-Anwendung zur Kommunikation mit der Gegenseite verwendet werden
kann. Anschlieend steht der ServerSocket fr einen weiteren Verbindungsaufbau zur Verfgung oder kann mit close
geschlossen werden.
Wir wollen uns die Konstruktion von Servern an einem Beispiel ansehen. Dazu soll ein einfacher ECHO-Server geschrieben werden,
der auf Port 7 auf Verbindungswnsche wartet. Alle eingehenden Daten sollen unverndert an den Client zurckgeschickt werden.
Zur Kontrolle sollen sie ebenfalls auf die Konsole ausgegeben werden:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
/* SimpleEchoServer.java */
import java.net.*;
import java.io.*;
public class SimpleEchoServer
{
public static void main(String[] args)
{
try {
System.out.println("Warte auf Verbindung auf Port 7...");
ServerSocket echod = new ServerSocket(7);
Socket socket = echod.accept();
System.out.println("Verbindung hergestellt");
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
int c;
while ((c = in.read()) != -1) {
SimpleEchoServer.java
019
out.write((char)c);
020
System.out.print((char)c);
021
}
022
System.out.println("Verbindung beenden");
023
socket.close();
024
echod.close();
025
} catch (IOException e) {
026
System.err.println(e.toString());
027
System.exit(1);
028
}
029
}
030 }
Listing 45.5: Ein ECHO-Server fr Port 7
Wird der Server gestartet, kann via Telnet oder mit dem EchoClient aus Listing 45.3 auf den Server zugegriffen werden:
telnet localhost 7
Wenn der Server luft, werden alle eingegebenen Zeichen direkt vom Server zurckgesendet und als Echo in Telnet angezeigt. Luft
er nicht, gibt es beim Verbindungsaufbau eine Fehlermeldung.
Wird das Programm unter UNIX gestartet, kann es mglicherweise Probleme geben. Einerseits kann es sein, da bereits Hinweis
ein ECHO-Server auf Port 7 luft. Er knnte ntigenfalls per Eintrag in inetd.conf oder hnlichen Konfigurationsdateien
vorbergehend deaktiviert werden. Andererseits drfen Server auf Ports kleiner 1024 nur mit Root-Berechtigung gestartet
werden. Ein normaler Anwender darf dagegen nur Server-Ports grer 1023 verwenden.
Der Server soll mehr als einen Client gleichzeitig bedienen knnen. Die Clients sollen zur besseren Unterscheidung
durchnumeriert werden.
Beim Verbindungsaufbau soll der Client eine Begrungsmeldung erhalten.
Fr jeden Client soll ein eigener Thread angelegt werden.
Um diese Anforderungen zu erfllen, verndern wir das obige Programm ein wenig. Im Hauptprogramm wird nun nur noch der
ServerSocket erzeugt und in einer Schleife jeweils mit accept auf einen Verbindungswunsch gewartet. Nach dem
Verbindungsaufbau erfolgt die weitere Bearbeitung nicht mehr im Hauptprogramm, sondern es wird ein neuer Thread mit dem
Verbindungs-Socket als Argument erzeugt. Dann wird der Thread gestartet und erledigt die gesamte Kommunikation mit dem
Client. Beendet der Client die Verbindung, wird auch der zugehrige Thread beendet. Das Hauptprogramm braucht sich nur noch
um den Verbindungsaufbau zu kmmern und ist von der eigentlichen Client-Kommunikation vollstndig befreit.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
/* EchoServer.java */
import java.net.*;
import java.io.*;
public class EchoServer
{
public static void main(String[] args)
{
int cnt = 0;
try {
System.out.println("Warte auf Verbindungen auf Port 7...");
ServerSocket echod = new ServerSocket(7);
while (true) {
Socket socket = echod.accept();
(new EchoClientThread(++cnt, socket)).start();
}
} catch (IOException e) {
System.err.println(e.toString());
System.exit(1);
}
EchoServer.java
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
}
}
class EchoClientThread
extends Thread
{
private int
name;
private Socket socket;
public EchoClientThread(int name, Socket socket)
{
this.name
= name;
this.socket = socket;
}
public void run()
{
String msg = "EchoServer: Verbindung " + name;
System.out.println(msg + " hergestellt");
try {
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
out.write((msg + "\r\n").getBytes());
int c;
while ((c = in.read()) != -1) {
out.write((char)c);
System.out.print((char)c);
}
System.out.println("Verbindung " + name + " wird beendet");
socket.close();
} catch (IOException e) {
System.err.println(e.toString());
}
}
}
Hat der Browser auf diese Weise eine HTML-Seite erhalten, interpretiert er den HTML-Code und zeigt die Seite formatiert auf dem
Bildschirm an. Enthlt die Datei IMG-, APPLET- oder hnliche Elemente, werden diese in derselben Weise vom Server angefordert
und in die Seite eingebaut. Die wichtigste Aufgabe des Servers besteht also darin, eine Datei an den Client zu bertragen. Wir
wollen uns zunchst das Listing ansehen und dann auf Details der Implementierung eingehen:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
/* ExperimentalWebServer.java */
import java.io.*;
import java.util.*;
import java.net.*;
/**
* Ein ganz einfacher Web-Server auf TCP und einem
* beliebigen Port. Der Server ist in der Lage,
* Seitenanforderungen lokal zu dem Verzeichnis,
* aus dem er gestartet wurde, zu bearbeiten. Wurde
* der Server z.B. im Verzeichnis c:\tmp gestartet, so
* wrde eine Seitenanforderung
* http://localhost:80/test/index.html die Datei
* c:\tmp\test\index.html laden. CGIs, SSIs, Servlets
* oder hnliches wird nicht untersttzt.
* <p>
* Die Dateitypen .htm, .html, .gif, .jpg und .jpeg werden
* erkannt und mit korrekten MIME-Headern bertragen, alle
* anderen Dateien werden als "application/octet-stream"
* bertragen. Jeder Request wird durch einen eigenen
* Client-Thread bearbeitet, nach bertragung der Antwort
* schliet der Server den Socket. Antworten werden mit
* HTTP/1.0-Header gesendet.
*/
public class ExperimentalWebServer
{
public static void main(String[] args)
{
if (args.length != 1) {
System.err.println(
"Usage: java ExperimentalWebServer <port>"
);
System.exit(1);
}
try {
int port = Integer.parseInt(args[0]);
System.out.println("Listening to port " + port);
int calls = 0;
ServerSocket httpd = new ServerSocket(port);
while (true) {
Socket socket = httpd.accept();
(new BrowserClientThread(++calls, socket)).start();
}
} catch (IOException e) {
System.err.println(e.toString());
System.exit(1);
}
}
}
/**
* Die Thread-Klasse fr die Client-Verbindung.
*/
class BrowserClientThread
extends Thread
{
static final String[][] mimetypes = {
{"html", "text/html"},
{"htm", "text/html"},
{"txt", "text/plain"},
{"gif", "image/gif"},
{"jpg", "image/jpeg"},
{"jpeg", "image/jpeg"},
{"jnlp", "application/x-java-jnlp-file"}
ExperimentalWebServer.java
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
};
private
private
private
private
private
private
private
Socket
int
PrintStream
InputStream
String
String
String
socket;
id;
out;
in;
cmd;
url;
httpversion;
/**
* Erzeugt einen neuen Client-Thread mit der angegebenen
* id und dem angegebenen Socket.
*/
public BrowserClientThread(int id, Socket socket)
{
this.id
= id;
this.socket = socket;
}
/**
* Hauptschleife fr den Thread.
*/
public void run()
{
try {
System.out.println(id + ": Incoming call...");
out = new PrintStream(socket.getOutputStream());
in = socket.getInputStream();
readRequest();
createResponse();
socket.close();
System.out.println(id + ": Closed.");
} catch (IOException e) {
System.out.println(id + ": " + e.toString());
System.out.println(id + ": Aborted.");
}
}
/**
* Liest den nchsten HTTP-Request vom Browser ein.
*/
private void readRequest()
throws IOException
{
//Request-Zeilen lesen
Vector request = new Vector(10);
StringBuffer sb = new StringBuffer(100);
int c;
while ((c = in.read()) != -1) {
if (c == '\r') {
//ignore
} else if (c == '\n') { //line terminator
if (sb.length() <= 0) {
break;
} else {
request.addElement(sb);
sb = new StringBuffer(100);
}
} else {
sb.append((char)c);
}
}
//Request-Zeilen auf der Konsole ausgeben
Enumeration e = request.elements();
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
while (e.hasMoreElements()) {
sb = (StringBuffer)e.nextElement();
System.out.println("< " + sb.toString());
}
//Kommando, URL und HTTP-Version extrahieren
String s = ((StringBuffer)request.elementAt(0)).toString();
cmd = "";
url = "";
httpversion = "";
int pos = s.indexOf(' ');
if (pos != -1) {
cmd = s.substring(0, pos).toUpperCase();
s = s.substring(pos + 1);
//URL
pos = s.indexOf(' ');
if (pos != -1) {
url = s.substring(0, pos);
s = s.substring(pos + 1);
//HTTP-Version
pos = s.indexOf('\r');
if (pos != -1) {
httpversion = s.substring(0, pos);
} else {
httpversion = s;
}
} else {
url = s;
}
}
}
/**
* Request bearbeiten und Antwort erzeugen.
*/
private void createResponse()
{
if (cmd.equals("GET") || cmd.equals("HEAD")) {
if (!url.startsWith("/")) {
httpError(400, "Bad Request");
} else {
//MIME-Typ aus Dateierweiterung bestimmen
String mimestring = "application/octet-stream";
for (int i = 0; i < mimetypes.length; ++i) {
if (url.endsWith(mimetypes[i][0])) {
mimestring = mimetypes[i][1];
break;
}
}
//URL in lokalen Dateinamen konvertieren
String fsep = System.getProperty("file.separator", "/");
StringBuffer sb = new StringBuffer(url.length());
for (int i = 1; i < url.length(); ++i) {
char c = url.charAt(i);
if (c == '/') {
sb.append(fsep);
} else {
sb.append(c);
}
}
try {
FileInputStream is = new FileInputStream(sb.toString());
//HTTP-Header senden
out.print("HTTP/1.0 200 OK\r\n");
System.out.println("> HTTP/1.0 200 OK");
out.print("Server: ExperimentalWebServer 0.5\r\n");
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241 }
System.out.println(
"> Server: ExperimentalWebServer 0.5"
);
out.print("Content-type: " + mimestring + "\r\n\r\n");
System.out.println("> Content-type: " + mimestring);
if (cmd.equals("GET")) {
//Dateiinhalt senden
byte[] buf = new byte[256];
int len;
while ((len = is.read(buf)) != -1) {
out.write(buf, 0, len);
}
}
is.close();
} catch (FileNotFoundException e) {
httpError(404, "Error Reading File");
} catch (IOException e) {
httpError(404, "Not Found");
} catch (Exception e) {
httpError(404, "Unknown exception");
}
}
} else {
httpError(501, "Not implemented");
}
}
/**
* Eine Fehlerseite an den Browser senden.
*/
private void httpError(int code, String description)
{
System.out.println("> ***" + code + ": " + description + "***");
out.print("HTTP/1.0 " + code + " " + description + "\r\n");
out.print("Content-type: text/html\r\n\r\n");
out.println("<html>");
out.println("<head>");
out.println("<title>ExperimentalWebServer-Error</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>HTTP/1.0 " + code + "</h1>");
out.println("<h3>" + description + "</h3>");
out.println("</body>");
out.println("</html>");
}
Hinweis
Nun wandelt der Server den angegebenen Dateinamen gem den Konventionen seines eigenen Betriebssystems um. Dazu wird das
erste "/" aus dem Dateinamen entfernt (alle Dateien werden lokal zu dem Verzeichnis geladen, aus dem der Server gestartet wurde)
und alle "/" innerhalb des Pfadnamens werden in den lokalen Pfadseparator konvertiert (unter MS-DOS ist das beispielsweise der
Backslash). Dann wird die Datei mit einem FileInputStream geffnet und der HTTP-Header und der Dateiinhalt an den Client
gesendet. Konnte die Datei nicht geffnet werden, wird eine Ausnahme ausgelst und der Server sendet eine Fehlerseite.
Der vom Server gesendete Header ist hnlich aufgebaut wie der Request-Header des Clients. Er enthlt mehrere Zeilen, die durch
CRLF-Sequenzen voneinander getrennt sind. Nach der letzten Headerzeile folgt eine Leerzeile, also zwei aufeinanderfolgende
CRLF-Sequenzen. HTTP 1.0 und 1.1 spezifizieren eine ganze Reihe von (optionalen) Headerelementen, von denen wir lediglich die
Versionskennung, unseren Servernamen und den MIME-Bezeichner mit der Typkennung der gesendeten Datei an den Browser
bertragen. Unmittelbar nach dem Ende des Headers wird der Dateiinhalt bertragen. Eine Umkodierung erfolgt dabei
normalerweise nicht, alle Bytes werden unverndert bertragen.
Unser Server kann sehr leicht getestet werden. Am einfachsten legt man ein neues Unterverzeichnis an und kopiert die bersetzten
Klassendateien und einige HTML-Dateien in dieses Verzeichnis. Nun kann der Server wie jedes andere Java-Programm gestartet
werden. Beim Aufruf ist zustzlich die Portnummer als Argument anzugeben:
java ExperimentalWebServer 80
Nun kann ein normaler Web-Browser verwendet werden, um Dateien vom Server zu laden. Befindet sich beispielsweise eine Datei
index.html im Server-Verzeichnis und luft der Server auf derselben Maschine wie der Browser, kann die Datei ber die Adresse
http://localhost/index.html im Browser geladen werden. Auch ber das lokale Netz des Unternehmens oder das
Internet knnen leicht Dateien geladen werden. Hat der Host, auf dem der Server luft, keinen Nameserver-Eintrag, kann statt
dessen auch direkt seine IP-Adresse im Browser angegeben werden.
Auf einem UNIX-System darf ein Server die Portnummer 80 nur verwenden, wenn er Root-Berechtigung hat. Ist das
nicht der Fall, kann der Server alternativ auf einem Port grer 1023 gestartet werden:
java ExperimentalWebServer 7777
Im Browser mu die Adresse dann ebenfalls um die Portnummer ergnzt werden:
http://localhost:7777/index.html.
Warnung
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt Suchen
<
>
Index
>>
DOC
API
Die Klasse URL wurde bereits in Abschnitt 40.1.1 behandelt. Neben den dort beschriebenen
Mglichkeiten besitzt sie Methoden, um Daten von der Quelle zu lesen, die durch den URL
adressiert wird:
public final InputStream openStream()
throws IOException
java.net.URL
Mit openStream wird ein InputStream geliefert, der wie die Methode getInputStream
der Klasse Socket zum Lesen der Quelldaten verwendet werden kann. getContent versucht
darber hinaus, die Daten zu interpretieren. Dazu knnen Content Handler Factories registriert
werden, die beispielsweise Text-, Image- oder Archivdateien interpretieren und ein dazu passendes
Java-Objekt liefern. Die Methode openConnection stellt eine Vorstufe von getContent dar.
Sie liefert ein Objekt des Typs URLConnection, das eine Abstraktion einer protokollspezifischen
Verbindung zwischen einem Java-Programm und einem URL darstellt.
Als einfaches Beispiel wollen wir uns das folgende Programm SaveURL ansehen. Es wird mit
einem URL und einer Datei als Argument aufgerufen. Mit Hilfe der Klasse URL stellt das Programm
eine Verbindung zur angegebenen URL her und beschafft durch Aufruf von openStream einen
InputStream. Mit seiner Hilfe wird die Quelle gelesen und das Ergebnis in die als zweites
Argument angegebene Datei geschrieben:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
SaveURL.java
/* SaveURL.java */
import java.net.*;
import java.io.*;
public class SaveURL
{
public static void main(String[] args)
{
if (args.length != 2) {
System.err.println(
"Usage: java SaveURL <url> <file>"
);
System.exit(1);
}
try {
URL url = new URL(args[0]);
OutputStream out = new FileOutputStream(args[1]);
InputStream in = url.openStream();
int len;
byte[] b = new byte[100];
while ((len = in.read(b)) != -1) {
out.write(b, 0, len);
}
out.close();
in.close();
} catch (MalformedURLException e) {
System.err.println(e.toString());
System.exit(1);
} catch (IOException e) {
System.err.println(e.toString());
System.exit(1);
}
}
}
Titel
Inhalt Suchen
Index
DOC
<<
<
>
>>
API
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
48.3 Midi
48.3 Midi
48.3.1 Was ist Midi?
48.3.2 Grundlegende Klassen des Midi-APIs
48.3.3 Alle meine Entchen - Erster Versuch
48.3.4 Alle meine Entchen mit dem Sequenzer
48.3.5 Zugriff auf Midi-Dateien
Lesen und Abspielen einer Midi-Datei
Abspeichern einer Sequenz in einer Midi-Datei
Hinweis
Zunchst war Midi ein reines Wire-Protokoll, das die bertragung von Echtzeit-Daten ber eine elektrische
Verbindung beschrieb. Spter wollte man Midi-Datenstrme auch aufzeichnen und in Dateien speichern knnen, und
man entwickelte dazu die Midi-Dateiprotokolle. Darin werden die eigentlichen Midi-Nachrichten mit Zeitstempeln
versehen, um sie spter mit Hilfe eines Sequenzers in ihrer exakten zeitlichen Abfolge wiedergeben zu knnen. Im
Sound-API werden Midi-Daten ohne Zeitstempel als Midi-Nachrichten (Midi-Messages) und solche mit Zeitstempel
als Midi-Ereignisse (Midi-Events) bezeichnet. Der Inhalt einer Midi-Datei wird blicherweise als Sequenz bezeichnet.
Eine Sequenz enthlt eine Reihe von Spuren, die ihrerseits die Midi-Events enthalten. Meist reprsentieren die Spuren
die unterschiedlichen Instrumente eines Stcks, so da etwa in Spur eins das Piano liegt, in Spur zwei der Bass usw.
Die verschiedenen Spuren werden innerhalb der Midi-Events durch Kanle reprsentiert, von denen es maximal 16 pro
Midi-Schnittstelle gibt.
Analog zur Klasse AudioSystem gibt es im Paket javax.sound.midi eine Klasse MidiSystem, mit
der Midi-Gerte und Midi-Dateien beschafft und Informationen ber sie abgefragt werden knnen.
Ein Midi-Gert wird durch die Klasse MidiDevice reprsentiert. Ein MidiDevice kann entweder geffnet
oder geschlossen sein und besitzt eine Reihe von Ein- und Ausgabeelementen. Die Eingabeelemente werden
durch das Interface Receiver, die Ausgabeelemente durch das Interface Transmitter dargestellt. Ein
MidiDevice kennt die Anzahl seiner Receiver und Transmitter und stellt diese auf Anfrage zur
Verfgung.
Die wichtigsten Unterklassen von MidiDevice sind Synthesizer und Sequencer. Gegenber seiner
Vaterklasse besitzt ein Synthesizer zustzlich Informationen ber die verfgbaren Instrumente und ihre
Zuordnung auf die verschiedenen Kanle. Ein Sequencer besitzt dagegen Methoden, um eine Sequenz
abzuspielen oder anzuhalten und Parameter wie Tempo, Synchronisierung oder Positionierung zu beeinflussen.
Eine Midi-Nachricht wird durch die Klasse MidiMessage und deren Unterklassen ShortMessage,
MetaMessage und SysexMessage reprsentiert. Ein Midi-Ereignis wird durch die Klasse MidiEvent
dargestellt, die zustzlich zur MidiMessage einen Zeitstempel enthlt. Eine Sequenz wird durch die Klasse
Sequence reprsentiert. Sie enthlt neben einigen globalen Informationen zum Timing der Daten eine Reihe
von Tracks, die ihrerseits die MidiEvents enthalten.
Weitere Details zu den genannten Klassen werden in den folgenden Abschnitten vorgestellt.
javax.sound.midi.MidiSystem
getSynthesizer liefert den Default-Synthesizer der installierten Sound-Hardware, typischerweise den auf der
Soundkarte eingebauten. Ist mehr als ein Synthesizer vorhanden, mu die Liste aller verfgbaren Synthesizer durch
Aufruf von getMidiDeviceInfo durchsucht und mit getMidiDevice der gewnschte ausgewhlt werden. Wir
wollen zunchst davon ausgehen, da ein Default-Synthesizer vorhanden ist, der unseren Ansprchen gengt.
Nachdem der Synthesizer verfgbar ist, mu er geffnet und zur bergabe von Midi-Nachrichten ein Receiver
beschafft werden:
javax.sound.midi.Synthesizer
Das ffnen und Schlieen eines Midi-Gerts wird mit open und close erledigt, und mit isOpen kann sein
aktueller Status herausgefunden werden. Ein Receiver kann durch Aufruf von getReceiver beschafft werden,
die Gesamtzahl aller vorhandenen Receiver kann mit getMaxReceivers abgefragt werden.
Um an ein Midi-Gert Daten zu senden, werden diese einfach an einen seiner Receiver geschickt. Dazu besitzt
dieser eine Methode send, an die beim Aufruf die gewnschte MidiMessage bergeben wird:
public void send(MidiMessage message, long timeStamp)
javax.sound.midi.Receiver
Das zweite Argument timeStamp ist zur Feinsynchronisierung der Midi-Nachrichten vorgesehen. Damit soll ein
Synthesizer in der Lage sein, leichte Timing-Schwankungen beim Anliefern der Daten auszugleichen. Ob ein
Gert dieses Feature untersttzt, kann durch Aufruf von getMicrosecondPosition bestimmt werden. Ist dessen
Rckgabewert -1, werden derartige Timestamps nicht untersttzt:
public long getMicrosecondPosition()
javax.sound.midi.MidiDevice
Aber auch, wenn diese Timestamps untersttzt werden, sollte man keine Wunder von ihnen erwarten. Die
Spezifikation weist ausdrcklich darauf hin, da damit nur kleinere Timing-Schwankungen ausgeglichen werden
knnen. Liegt ein Zeitstempel dagegen weit in der Zukunft (oder gar in der Vergangenheit), ist das Midi-Gert nicht
verpflichtet, diesen korrekt zu behandeln. Wird -1 an das timeStamp-Argument von send bergeben, ignoriert das
entsprechende Gert den Zeitstempel und bearbeitet die Midi-Nachricht, so schnell es kann.
Um eine MidiMessage zu konstruieren, wird diese zunchst mit new erzeugt und durch Aufruf von setMessage
mit Daten gefllt. Da wir weder Meta- noch Sysex-Daten bentigen, wollen wir uns lediglich die Konstruktion einer
ShortMessage mit Hilfe der folgenden setMessage-Methode ansehen:
public void setMessage(
int command,
int channel,
int data1,
int data2
)
throws InvalidMidiDataException
javax.sound.midi.ShortMessage
Als erstes Argument mu das gewnschte Midi-Kommando bergeben werden. Die fr uns relevanten Kommandos
sind NOTE_ON (Taste wird gedrckt), NOTE_OFF (Taste wird losgelassen) und PROGRAM_CHANGE
(Instrumentenwechsel). Sie werden als Konstanten in der Klasse ShortMessage definiert. Als zweites Argument
wird der Kanal angegeben, auf den sich das Kommando auswirken soll. Anschlieend folgen zwei Datenbytes, die
kommandospezifisch sind. Das NOTE_ON-Kommando erwartet darin beispielsweise die Tonhhe (die verfgbaren
Noten sind als Ganzzahlen durchnumeriert) und die relative Lautstrke (Anschlagsdynamik) der Note. NOTE_OFF
erwartet die Tonhhe als erstes Datenbyte und ignoriert das zweite. PROGRAM_CHANGE erwartet die gewnschte
Programmnummer und ignoriert das zweite Datenbyte.
Nach diesen Vorbemerkungen wollen wir uns nun ein Beispielprogramm ansehen:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
/* Listing4802.java */
Listing4802.java
import javax.sound.midi.*;
public class Listing4802
{
private static void playAlleMeineEntchen()
throws Exception
{
//Partitur {{Tonhoehe, DauerInViertelNoten, AnzahlWdh},...}
final int DATA[][] = {
{60, 1, 1}, //C
{62, 1, 1}, //D
{64, 1, 1}, //E
{65, 1, 1}, //F
{67, 2, 2}, //G,G
{69, 1, 4}, //A,A,A,A
{67, 4, 1}, //G
{69, 1, 4}, //A,A,A,A
{67, 4, 1}, //G
{65, 1, 4}, //F,F,F,F
{64, 2, 2}, //E,E
{62, 1, 4}, //D,D,D,D
{60, 4, 1} //C
};
//Synthesizer ffnen und Receiver holen
Synthesizer synth = MidiSystem.getSynthesizer();
synth.open();
Receiver rcvr = synth.getReceiver();
//Melodie spielen
ShortMessage msg = new ShortMessage();
for (int i = 0; i < DATA.length; ++i) {
for (int j = 0; j < DATA[i][2]; ++j) { //Anzahl Wdh. je Note
//Note an
msg.setMessage(ShortMessage.NOTE_ON, 0, DATA[i][0], 64);
rcvr.send(msg, -1);
//Pause
try {
Thread.sleep(DATA[i][1] * 400);
} catch (Exception e) {
//nothing
}
//Note aus
msg.setMessage(ShortMessage.NOTE_OFF, 0, DATA[i][0], 0);
rcvr.send(msg, -1);
}
}
//Synthesizer schlieen
synth.close();
}
public static void main(String[] args)
{
054
055
056
057
058
059
060
061
}
062 }
try {
playAlleMeineEntchen();
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
System.exit(0);
Warnung
javax.sound.midi.MidiSystem
Beim Abspielen einer Melodie mit dem Sequencer werden die Midi-Nachrichten nicht mehr direkt an den
Synthesizer geschickt, sondern zunchst in eine Sequence verpackt. Diese kann wie folgt konstruiert werden:
javax.sound.midi.Sequence
Das erste Argument gibt die Art und Weise an, wie das Timing erzeugt werden soll. Hier gibt es im wesentlichen die
Mglichkeiten, einen internen Timer zu verwenden, der auf Bruchteilen von Viertelnoten basiert, oder mit externer
Synchronisation zu arbeiten, bei der die Timing-Informationen im SMPTE-Format zur Verfgung gestellt werden. Wir
wollen die erste Variante whlen und bergeben dazu die Konstante Sequence.PPQ als erstes Argument. Das
zweite Argument resoultion bezieht sich auf das erste und gibt die Auflsung des Timers an. In unserem Fall
wrde es also die Anzahl der Zeitimpulse je Viertelnote angeben.
Um eine Sequence mit Daten zu fllen, wird mindestens ein Track bentigt, der durch Aufruf von createTrack
erzeugt werden kann:
public Track createTrack()
javax.sound.midi.Sequence
Ein Track-Objekt ist zunchst leer und wird durch wiederholte Aufrufe von add mit Midi-Ereignissen versehen:
public boolean add(MidiEvent event)
javax.sound.midi.Track
Nachdem die Sequence fertiggestellt ist, kann sie durch Aufruf von setSequence an den Sequencer bergeben
werden:
public void setSequence(Sequence sequence)
throws InvalidMidiDataException
javax.sound.midi.Sequencer
Mit setTempoInBPM kann das Abspieltempo in Beats Per Minute (kurz BPM), also in Viertelnoten pro Minute,
angegeben werden. start startet das Abspielen der Sequenz, stop beendet es. Mit isRunning kann geprft
werden, ob der Sequencer gerade luft.
Bevor eine Sequence abgespielt werden kann, mssen Synthesizer und Sequencer miteinander verbunden
werden. Dies geschieht, indem ein Transmitter des Sequenzers an einen Receiver des Synthesizers
angeschlossen wird. Der Transmitter verfgt dazu ber eine Methode setReceiver, an die der empfangende
Receiver bergeben wird:
public void setReceiver(Receiver receiver)
javax.sound.midi.Transmitter
Nach diesen Vorbemerkungen knnen wir uns nun ansehen, wie "Alle meine Entchen" mit Hilfe eines Sequenzers
wiedergegeben werden kann.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
/* Listing4803.java */
Listing4803.java
import javax.sound.midi.*;
public class Listing4803
{
private static void playAlleMeineEntchen()
throws Exception
{
//Partitur {{Tonhoehe, DauerInViertelNoten, AnzahlWdh},...}
final int DATA[][] = {
{60, 1, 1}, //C
{62, 1, 1}, //D
{64, 1, 1}, //E
{65, 1, 1}, //F
{67, 2, 2}, //G,G
{69, 1, 4}, //A,A,A,A
{67, 4, 1}, //G
{69, 1, 4}, //A,A,A,A
{67, 4, 1}, //G
{65, 1, 4}, //F,F,F,F
{64, 2, 2}, //E,E
{62, 1, 4}, //D,D,D,D
{60, 4, 1} //C
};
//Sequence bauen
final int PPQS = 16;
final int STAKKATO = 4;
Sequence seq = new Sequence(Sequence.PPQ, PPQS);
Track track = seq.createTrack();
long currentTick = 0;
ShortMessage msg;
//Kanal 0 auf "EnsembleStrings" umschalten
msg = new ShortMessage();
msg.setMessage(ShortMessage.PROGRAM_CHANGE, 0, 48, 0);
track.add(new MidiEvent(msg, currentTick));
//Partiturdaten hinzufgen
for (int i = 0; i < DATA.length; ++i) {
for (int j = 0; j < DATA[i][2]; ++j) { //Anzahl Wdh. je Note
msg = new ShortMessage();
msg.setMessage(ShortMessage.NOTE_ON, 0, DATA[i][0], 64);
track.add(new MidiEvent(msg, currentTick));
currentTick += PPQS * DATA[i][1] - STAKKATO;
msg = new ShortMessage();
msg.setMessage(ShortMessage.NOTE_OFF, 0, DATA[i][0], 0);
track.add(new MidiEvent(msg, currentTick));
currentTick += STAKKATO;
}
}
//Sequencer und Synthesizer initialisieren
Sequencer sequencer = MidiSystem.getSequencer();
Transmitter trans = sequencer.getTransmitter();
Synthesizer synth = MidiSystem.getSynthesizer();
Receiver rcvr = synth.getReceiver();
//Beide ffnen und verbinden
sequencer.open();
synth.open();
trans.setReceiver(rcvr);
//Sequence abspielen
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089 }
sequencer.setSequence(seq);
sequencer.setTempoInBPM(145);
sequencer.start();
while (true) {
try {
Thread.sleep(100);
} catch (Exception e) {
//nothing
}
if (!sequencer.isRunning()) {
break;
}
}
//Sequencer anhalten und Gerte schlieen
sequencer.stop();
sequencer.close();
synth.close();
}
public static void main(String[] args)
{
try {
playAlleMeineEntchen();
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
System.exit(0);
}
javax.sound.midi.MidiSystem
getSequence erwartet ein File-Objekt als Argument, mit dem die abzuspielende Midi-Datei angegeben wird. Es
liefert als Rckgabewert eine Sequence, die an den Sequenzer bergeben und von diesem abgespielt werden kann.
Ein Beispielprogramm zur Widergabe einer Midi-Datei ist nun sehr einfach zu konstruieren. Mit Ausnahme der
expliziten Konstruktion der Sequence entspricht es vollkommen dem Beispielprogramm des vorigen Abschnitts:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
/* Listing4804.java */
import java.io.*;
import javax.sound.midi.*;
public class Listing4804
{
private static void playMidiFile(String name)
throws Exception
{
//Sequencer und Synthesizer initialisieren
Sequencer sequencer = MidiSystem.getSequencer();
Transmitter trans = sequencer.getTransmitter();
Synthesizer synth = MidiSystem.getSynthesizer();
Receiver rcvr = synth.getReceiver();
//Beide ffnen und verbinden
sequencer.open();
synth.open();
trans.setReceiver(rcvr);
//Sequence lesen und abspielen
Sequence seq = MidiSystem.getSequence(new File(name));
sequencer.setSequence(seq);
sequencer.setTempoInBPM(145);
sequencer.start();
while (true) {
try {
Thread.sleep(100);
} catch (Exception e) {
//nothing
}
if (!sequencer.isRunning()) {
break;
}
}
//Sequencer anhalten und Gerte schlieen
sequencer.stop();
sequencer.close();
synth.close();
}
public static void main(String[] args)
{
try {
playMidiFile(args[0]);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
System.exit(0);
Listing4804.java
050
}
051 }
Listing 48.4: Abspielen einer Midi-Datei
Das Programm erwartet in der Kommandozeile den Namen der abzuspielenden Midi-Datei. Wird es mit der (ebenfalls
im Verzeichnis der Beispieldateien befindlichen) Datei ame.mid als Argument aufgerufen, ertnt das altbekannte Alle
meine Entchen.
Abspeichern einer Sequenz in einer Midi-Datei
Das Abspeichern einer Sequence in einer Midi-Datei ist fast so einfach wie ihr Abspielen. Die Klasse
MidiSystem besitzt dazu eine Methode write, die drei Parameter erwartet:
public static int write(Sequence in, int type, File out)
throws IOException
javax.sound.midi.MidiSystem
Als erstes Argument wird die zu speichernde Sequence bergeben, als zweites der Midi-Dateityp und als letztes ein
File-Objekt mit dem Namen der Ausgabedatei. Fr einfache Experimente kann als Midi-Dateityp einfach 0
bergeben werden. Ein Array mit allen untersttzten Dateitypen kann durch Aufruf von getMidiFileTypes
beschafft werden.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
1.1 Historie
1.1 Historie
Als offizieller Geburtstag der Programmiersprache Java gilt der 23. Mai 1995.
Mit dem Erscheinen dieses Buchs wird Java also fast sieben Jahre alt sein. Nach einer
wechselhaften Vorgeschichte, dem darauffolgenden Enthusiasmus und fast ebenso
vielen technischen Schwierigkeiten wie Errungenschaften hat sich Java heute
vollstndig etabliert. Nach wie vor verzeichnet die Sprache ein steigendes Interesse in
breiten Kreisen der Entwicklergemeinschaft und wird in Krze C++ als am hufigsten
genutzte Programmiersprache abgelst haben. Bevor wir uns in den nachfolgenden
Kapiteln mit den technischen Details der Sprache beschftigen, wollen wir einen
kurzen Blick auf die Entstehungsgeschichte von Java werfen.
Nach einschlgigen Berichten fing alles mit einer Mail des damals 25jhrigen
Programmierers Patrick Naughton an den SUN-Chef Scott McNealy an. Naughton
hatte angekndigt, das Unternehmen zu verlassen, um zu Next Computer, Inc. zu
gehen. Er war der Meinung, da manches bei SUN nicht gut funktionierte, und die
weitaus moderneren Technologien von Next reizten ihn sehr. McNealy, mit dem
Naughton zusammen Eishockey spielte, forderte ihn auf, seine Kritik samt mglicher
Lsungsvorschlge niederzuschreiben, ganz gleich, wie radikal sie auch sein mgen.
Naughtons Mail hat seine Wirkung nicht verfehlt! In einer fr SUN schwierigen
Periode mit internen Diskussionen um den Kurs des Unternehmens und seiner
Produkte rannte Naughton offene Tren ein. Seine Hauptkritikpunkte betrafen die
nicht zeitgemen grafischen Oberflchen, die unbersehbare Anzahl an
Programmierwerkzeugen, die hohen Kosten der Workstations und die komplizierte
Anwendung der Programme. Kurz, er warf SUN vor, auf dem besten Wege zu sein,
sich mehr und mehr von seinen potentiellen Kunden und Anwendern zu entfernen. Er
forderte Hard- und Software, die nicht nur von Akademikern und hochspezialisierten
Profis, sondern von normalen Menschen angewendet werden konnte.
Naughtons Klagen wurden erhrt, und innerhalb weniger Tage wurde ein Projekt
aufgesetzt, dessen Ziel es sein sollte, die nchsten groen Trends der Computer- und
Softwareindustrie aufzuspren. Naughton zog seine angedrohte Kndigung zurck
und begann 1991 zusammen mit James Gosling und Mike Sheridan die Arbeit an
einem geheimen, zunchst fr ein Jahr finanzierten und auerhalb des regulren
Unternehmens angesiedelten Vorhabens, das spter den Namen Green-Projekt erhielt.
Nach anfnglichen Schwierigkeiten, seine eigene Aufgabe zu definieren, entschied
sich das Team dafr, einen Prototyp zur Steuerung und Integration von Gerten zu
bauen, wie sie in normalen Haushalten in groer Zahl verwendet wurden (Toaster,
Videorecorder, Fernseher etc.). Bestandteile dieses Projekts waren ein Betriebssystem
(Green-OS), ein portabler Interpreter (Oak), ein Grafiksubsystem und diverse
Hardwarekomponenten. Bis Mitte 1992 entwickelte Naughton mit seinen Kollegen
ein Gert, das etwa heutigen Palm-Computern glich und mit einer tastaturlosen
grafischen Oberflche per drahtloser Datenbertragung zur Bedienung
unterschiedlichster Gerte der Konsumelektronik verwendet werden konnte.
Das als *7 (Star Seven) bezeichnete Gert wurde im Herbst 1992 firmenintern
prsentiert. Diese Vorstellung konnte einige der Manager - unter ihnen SUNMitbegrnder Bill Joy und Scott McNealy - so beeindrucken, da im November 1992
aus dem lockeren Team die Firma First Person, Inc. gegrndet wurde. Mit zuletzt
etwa 70 Mitarbeitern versuchte das junge Unternehmen, den Prototypen zur
Serienreife zu bringen und zu vermarkten. Trotz groer Anstrengungen scheiterten
aber alle Versuche, Vertrge und Partnerschaften zur kommerziellen Verwendung
von Star Seven unter Dach und Fach zu bringen. Nach vielen Mhen wurde die
Arbeit von First Person, Inc. im April 1994 praktisch beendet.
Fast wre die Geschichte von Java nun bereits zu Ende gewesen, ehe sie richtig
begonnen hatte.
Bei aller Euphorie ber interaktives Fernsehen und Heimelektronik hatten die
Entwickler nmlich eine andere, sehr viel realere Entwicklung bersehen.
Mittlerweile hatte das World Wide Web eine kritische Gre erreicht! Nachdem
NCSA Mosaic als erster grafischer Web-Browser im April 1993 verfgbar war,
konnte jedermann ansprechend aufbereitete Informationen im Internet ansehen und
auf einfache Weise zwischen unterschiedlichen Diensten, Medien und Anbietern
wechseln. Vor allem Bill Joy, der sich gelegentlich ber den Stand des GreenProjekts informierte, erkannte das Potential des World Wide Web und die Bedeutung
einer plattformunabhngigen Programmiersprache, mit der neben textuellen Inhalten
auch Programme transportiert und ohne Installations- oder Portierungsaufwand auf
einem beliebigen Zielrechner ausgefhrt werden konnten.
In der Annahme, damit die Bedeutung des Internet zu strken und auf diese Weise
von so prominenten Firmen wie Borland, Lotus, Oracle, IBM, Netscape und
Symantec untersttzt.
Im Laufe der nchsten Monate kam der Hype dann richtig in Fahrt, und Java wurde
mit Lorbeeren berhuft. In welcher Weise das Interesse an Java anstieg, mgen
einige Kennzahlen verdeutlichen:
Gamelan ist ein Verzeichnis von Java-Ressourcen, das Verweise auf JavaApplets oder -Applikationen verwaltet. Whrend die Zahl der Eintrge im
Januar 1996 noch deutlich unter 1000 lag, sollte sie sich innerhalb der
nchsten 6 Monate vervierfachen.
Eine hnliche Entwicklung nahmen die Java-Newsgroups im Internet.
Whrend im Januar 1996 lediglich eine einzige Gruppe comp.lang.java
existierte, gibt es heute 12 weitere Newsgroups innerhalb der
comp.lang.java-Hierarchie mit insgesamt etwa 15000 Nachrichten pro
Monat. Zudem gibt es unzhlige nationale Newsgroups, Listserver und nichtffentliche Diskussionsforen.
Ein weiteres Beispiel fr die rasante Verbreitung ist die Anzahl der bei SUN
offiziell registrierten Java-Usergruppen. Sie lag im Februar 1996 bei 17, stieg
bis Ende 1998 auf 140 an und liegt heute bei fast 900. Weitere 2600 befinden
sich in Grndung.
Die Ende 1996 von SUN ins Leben gerufene Java Developer's Connection,
die als Download- und Service-Plattform fr die Java-Entwicklungswerkzeuge
von SUN dient, verzeichnet heute mehrere Millionen registrierte Entwickler.
Nach einer Reihe von Ankndigungen im ersten Halbjahr wurden bis Ende 1996
zahlreiche Neuerungen vorgestellt. Unter ihnen waren die Datenbank-Spezifikation
JDBC, die Komponentenarchitektur Beans, das Card API, HotJava Views, die 100
% Pure Java Initiative und eine Reihe weiterer APIs. Zustzlich kamen die ersten
integrierten Entwicklungssysteme, wie Cafe und Visual Cafe von Symantec oder J++
von Microsoft, auf den Markt.
Im Dezember 1996 wurde die Version 1.1 des Java Development Kit angekndigt.
Sie sollte eine Reihe von Bugs der Vorgngerversion beheben und weitere
Funktionalitten hinzufgen. Im Februar 1997 standen die ersten Betaversionen des
JDK 1.1 zur Verfgung und konnten von interessierten Entwicklern heruntergeladen
werden. Im Mrz 1997 wurde dann HotJava 1.0 herausgegeben (alle vorigen
Versionen hatten lediglich Betacharakter), und auch das Java-Betriebssystem JavaOS
1.0 wurde in diesem Monat der ffentlichkeit vorgestellt.
Etwa zeitgleich konnte man auf der Cebit 1997 den ersten Prototypen der
JavaStation, einer diskettenlosen Workstation, die ausschlielich auf Java basierte,
bewundern. Mit der Ankndigung von Java-Prozessoren wie dem PicoJava erffnete
SUN die Perspektive, da Java-Programme mittelfristig ebenso schnell laufen werden
wie kompilierter C- oder C++-Code. Das fr Java-Entwickler herausragende Ereignis
des Jahres war die JavaOne im April 1997, die erste Konferenz, die sich
ausschlielich um Java drehte. Sie brachte eine Vielzahl von Ankndigungen,
Prototypen und neuen Produkten hervor. Die JavaOne findet seither jedes Frhjahr in
San Francisco statt und ist nach wie vor eines der wichtigsten Ereignisse der
weltweiten Java-Gemeinde.
Die folgenden Monate standen fr viele Entwickler und Tool-Hersteller im Zeichen
der Umstellung auf die Version 1.1 des JDK. Zwar gab es bereits Ende 1997 mehr als
ein Dutzend integrierte Entwicklungsumgebungen, doch Support fr die Version 1.1
war lngst nicht berall vorhanden. Auch die Browser-Hersteller taten sich schwer
und stellten erst zum Jahreswechsel 1997/98 mit den 4er Versionen ihrer Browser
erste Implementierungen des JDK 1.1 vor. Bis diese einigermaen stabil waren,
vergingen weitere Monate.
Whrend sich 1998 die meisten Entwickler mit der Version 1.1 beschftigten, wurde
bei SUN bereits am neuen JDK 1.2 gearbeitet. Im Frhjahr 1998 stand dessen erste
ffentliche Version, das JDK 1.2 Beta 2, der ffentlichkeit zur Verfgung. Wichtige
Neuerungen waren die Java Foundation Classes mit dem Swing Toolset, dem Java
2D API und dem Drag-and-Drop API, das Collection-API und das Extension
Framework. Daneben gab es viele weitere Verbesserungen bestehender Pakete. Nach
zwei weiteren Betas, die bis zum Juli erschienen, brachte SUN im Oktober und
November die Release Candidates 1 und 2 heraus. Anfang Dezember 1998 wurde
dann schlielich die erste finale Version des JDK 1.2 zur Verfgung gestellt und im
Januar 1999 in Java 2 Platform umbenannt.
Mit der Version 1.2 hatte sich der Anspruch SUNs an das JDK gendert. Whrend es
zuvor darum ging, mglichst viele Features in das JDK einzubauen, stand seit dem
JDK 1.2 offiziell die Stabilitt und Performance im Vordergrund. Ersteres sollte mit
einem rigorosen Qualittssicherungsprogramm erreicht werden, letzteres durch
Verbesserung der virtuellen Maschine. Im Mrz 1999 wurde der lange angekndigte
HotSpot-Compiler ausgeliefert. Zwar brachte er mit seiner adaptiven
Compilertechnologie, bei der interpretierte Programmteile zur Ausfhrungszeit genau
dann in direkt ausfhrbaren Maschinencode bersetzt werden, wenn sie wesentlich
zur Laufzeit des Programms beitragen, fr einige Anwendungen
Geschwindigkeitsvorteile. In vielen Fllen reichte den Entwicklern und Anwendern
die Performance der Java-Programme jedoch nicht aus. Insbesondere das SwingToolkit, die neue Grafikplattform, galt auf durchschnittlichen Arbeitspltzen als zu
trge und trug sehr zu dem Ruf Javas bei, fr echte Anwendungen zu langsam zu
sein.
Zudem wurde das JDK 1.2 nur zgernd von der Industrie angenommen. Zwar gab es
auf dem PC bald 1.2-kompatible Entwicklungswerkzeuge, doch auf anderen
Betriebssystemen (Macintosh, UNIX, LINUX) blieben die Portierungen bei den 1.1er
Versionen stehen. Die groen Browser-Hersteller haben bis heute keine brauchbaren
1.2er Java-Implementierungen vorgestellt. Dies mag einerseits durch technische
Schwierigkeiten begrndet gewesen sein, lag aber auch an der damals kaum
akzeptierten Community Source Licence von SUN. Mit dieser sollte ein offener JavaStandard etabliert werden, ohne da SUN die Kontrolle ber die Weiterentwicklung
von Java verlor. Durch diese Vereinbarung waren Unternehmen, die
Weiterentwicklungen des JDK vornahmen, unter Umstnden dazu verpflichtet, diese
offenzulegen bzw. unentgeltlich an SUN zu bergeben.
Nach den fehlerbereinigten Versionen 1.2.1 und 1.2.2 des JDK betrieb SUN die
Weiterentwicklung des JDK unter dem Codenamen Kestrel. Mit einem neuen
Garbage Collector, verbessertem Speichermanagement und einem neuen HotSpotCompiler sollte diese Version stabiler und performanter werden als alle vorherigen.
Viele kleine Verbesserungen sollten zudem bei der Swing-Oberflche deutliche
Geschwindigkeitsgewinne bringen. Die intensive Arbeit an dieser Version uerte
sich auch darin, da alle Bugfixes des JDK 1.2.2 auf die neue Version verschoben
wurden.
Im August 1999 gab es dann die erste ffentliche Betaversion des JDK 1.3.
Tatschlich schienen sich die Versprechen zu erfllen. Bereits das Beta lief recht
stabil und brachte bei der grafischen Oberflche deutliche Geschwindigkeitsgewinne.
Durch eine genderte Initialisierung der virtuellen Maschine reduzierte sich die
Startzeit von Java-Applikationen deutlich, und mit dem neuentwickelten Compiler
gab es groe Verbesserungen bei den Turnaround-Zeiten. Auch subjektiv lieferte das
JDK 1.3 den Eindruck erheblich besserer Performance. Nach drei weiteren
Vorabversionen wurde im Mai 2000 die endgltige Version des JDK 1.3 fr
Windows ausgeliefert. Versionen fr andere Betriebssysteme (namentlich SOLARIS
und LINUX) sollten etwa drei Monate spter folgen. Selbst Apple, dessen JavaImplementierungen lange Zeit bei der Version 1.1 stehen geblieben waren, liefert mit
dem Mac OS X mittlerweile ein aktuelles JDK als integralen Bestandteil des
Betriebssystems aus.
2001 folgte dann das Bug-Fix-Release 1.3.1, zu dem es mittlerweile ein Update
1.3.1_02 gibt. Nach einigen Betas und einem Release Candidate brachte SUN
Mitte Februar 2002 schlielich das JDK 1.4 heraus - diesmal zeitgleich fr alle
untersttzen Plattformen. Neben vielen Detailverbesserungen und umfangreichen
Erweiterungen der Klassenbibliotheken soll das JDK 1.4 weitere PerformanceVerbesserungen bringen. Sie werden unter anderem auch das zuvor schlechte
Laufzeitverhalten von Swing-Anwendungen auf UNIX-Betriebssystemen beseitigen.
Zu den wichtigsten funktionalen Erweiterungen der Version 1.4 zhlen die assert-
<
>
>>
1.2.1 Sprachmerkmale
Java wurde vollstndig neu entworfen. Die Designer versuchten, die Syntax der
Sprachen C und C++ soweit wie mglich nachzuahmen, verzichteten aber auf einen
Groteil der komplexen und fehlertrchtigen Merkmale beider Sprachen. Das
Ergebnis ihrer Bemhungen haben sie wie folgt zusammengefat:
Java soll eine einfache, objektorientierte, verteilte, interpretierte, robuste, sichere,
architekturneutrale, portable, performante, nebenlufige, dynamische
Programmiersprache sein.
Der Erfolg von Java hngt eng damit zusammen, da ein wesentlicher Teil dieser
Forderungen tatschlich in einer fr viele Programmierer akzeptablen Weise erfllt
wurde - obgleich wir am Ende dieses Kapitels auch einige kritische Anmerkungen
dazu geben werden.
Java ist sowohl eine objektorientierte Programmiersprache in der Tradition von
Smalltalk als auch eine klassische imperative Programmiersprache nach dem Vorbild
von C. Im Detail unterscheidet sich Java aber recht deutlich von C++, das denselben
Anspruch erhebt. Durch die Integration einer groen Anzahl anspruchsvoller
Features wie Multithreading, strukturiertem Exceptionhandling oder eingebauten
grafischen Fhigkeiten implementiert Java eine Reihe interessanter Neuerungen auf
dem Gebiet der Programmiersprachen.
Zudem profitiert Java davon, da viele der Features von C++ nicht realisiert wurden
und die Sprache dadurch schlank und bersichtlich wurde. So gibt es beispielsweise
keine expliziten Pointer, keine separaten Header-Dateien, keine Mehrfachvererbung
und keine Templates in Java. Wer allerdings glaubt, Java sei eine
Programmiersprache, die nur das Allerntigste bietet, irrt. Tatschlich ist Java eine
elegante Sprache, die auch fr grere Projekte und anspruchsvolle Aufgaben alle
erforderlichen Reserven besitzt.
In Java gibt es die meisten elementaren Datentypen, die auch C besitzt. Arrays und
Strings sind als Objekte implementiert und sowohl im Compiler als auch im
Laufzeitsystem verankert. Methodenlose Strukturtypen wie struct oder union
gibt es in Java nicht. Alle primitiven Datentypen sind vorzeichenbehaftet und in ihrer
Gre exakt spezifiziert. Java besitzt einen eingebauten logischen Datentyp
boolean.
Java bietet semidynamische Arrays, deren initiale Gre zur Laufzeit festgelegt
werden kann. Arrays werden als Objekte angesehen, die einige wohldefinierte
Eigenschaften haben. Mehrdimensionale Arrays werden wie in C dadurch realisiert,
da einfache Arrays ineinandergeschachtelt werden. Dabei knnen auch nichtrechteckige Arrays erzeugt werden. Alle Array-Zugriffe werden zur Laufzeit auf
Einhaltung der Bereichsgrenzen geprft.
Die Ausdrcke in Java entsprechen weitgehend denen von C und C++. Java besitzt
eine if-Anweisung, eine while-, do- und for-Schleife und ein switchStatement. Es gibt die von C bekannten break- und continue-Anweisungen in
normaler und gelabelter Form. Letztere ermglicht es, mehr als eine Schleifengrenze
zu berspringen. Java besitzt kein goto-Statement (obgleich es sich um ein
reserviertes Wort handelt). Variablendeklarationen werden wie in C++ als
Anweisungen angesehen und knnen an beliebiger Stelle innerhalb des Codes
auftauchen. Seit der Version 1.4 besitzt Java eine assert-Anweisung, die zur
Laufzeit an- und abgeschaltet werden kann.
Als OOP-Sprache besitzt Java alle Eigenschaften moderner objektorientierter
Sprachen. Wie C++ erlaubt Java die Definition von Klassen, aus denen Objekte
erzeugt werden knnen. Objekte werden dabei stets als Referenzdatentypen
behandelt, die wie Variablen angelegt und verwendet werden knnen. Zur
Initialisierung gibt es Konstruktoren, und es kann eine optionale Finalizer-Methode
definiert werden, die bei der Zerstrung des Objekt aufgerufen wird. Seit der Version
1.1 gibt es lokale Klassen, die innerhalb einer anderen Klasse definiert werden.
Alle Methodenaufrufe in Java sind dynamisch. Methoden knnen berladen werden,
Operatoren allerdings nicht. Anders als in C++ ist das Late-Binding standardmig
aktiviert, kann aber per Methode deaktiviert werden. Java erlaubt Einfach-, aber
keine Mehrfachvererbung von Implementierungen. Mit Hilfe von Interfaces (das sind
abstrakte Klassendefinitionen, die nur aus Methoden bestehen) ist eine restriktive
Form der Mehrfachvererbung mglich, die einen Kompromi zwischen beiden
Alternativen darstellt. Java erlaubt die Definition abstrakter Basisklassen, die neben
konkreten auch abstrakte Methoden enthalten.
Neben Instanzvariablen und -methoden knnen auch Klassenvariablen und methoden definiert werden. Alle Elemente einer Klassendefinition knnen mit Hilfe
der aus C++ bekannten Schlsselwrter public, private und protected in
ihrer Sichtbarkeit eingeschrnkt werden. Es gibt zwar keine friends, aber die
Sichtbarkeit von Methoden oder Klassen kann auf das eigene Paket beschrnkt
werden. Objektvariablen werden als Referenzen implementiert. Mit ihrer Hilfe ist
eine gegenber C/C++ eingeschrnkte Zeigerverarbeitung mglich, die das Erstellen
dynamischer Datenstrukturen ermglicht.
Das Speichermanagement in Java erfolgt automatisch. Whrend das Erzeugen von
Objekten (von wenigen Ausnahmen abgesehen) immer einen expliziten Aufruf des
new-Operators erfordert, erfolgt die Rckgabe von nicht mehr bentigtem Speicher
automatisch. Ein Garbage-Collector, der als niedrigpriorisierter Hintergrundproze
luft, sucht in regelmigen Abstnden nach nicht mehr referenzierten Objekten und
gibt den durch sie belegten Speicher an das Laufzeitsystem zurck. Viele der Fehler,
die bei der Programmierung in C oder C++ dadurch entstehen, da der Entwickler
selbst fr das Speichermanagement verantwortlich ist, knnen in Java nicht mehr
auftreten.
In Java gibt es ein strukturiertes Exceptionhandling. Damit ist es mglich,
Laufzeitfehler zu erkennen und in strukturierter Weise zu behandeln. Eine Methode
mu jeden Laufzeitfehler, der whrend ihrer Abarbeitung auftreten kann, entweder
abfangen oder durch eine geeignete Deklaration an den Aufrufer weitergeben. Dieser
hat dann seinerseits die Pflicht, sich um den Fehler zu kmmern. Exceptions sind
normale Objekte, und die zugehrigen Klassen knnen erweitert und als Grundlage
fr anwendungsspezifische Fehler-Objekte verwendet werden.
1.2.3 Grafikprogrammierung
Die Java-Laufzeitbibliothek bietet umfassende grafische Fhigkeiten. Diese sind im
Das Abstract Windowing Toolkit (kurz AWT) bietet elementare Grafik- und
Fensterfunktionen auf der Basis der auf der jeweiligen Zielmaschine
verfgbaren Fhigkeiten.
Das Swing Toolset stellt darber hinaus eine Reihe zustzlicher
Dialogelemente zur Verfgung und ermglicht die Konstruktion sehr
komplexer grafischer Oberflchen. Mit seinem Pluggable Look-and-Feel
bietet es die Mglichkeit, das Look-and-Feel eines Programmes zur Laufzeit
umzuschalten und den Bedrfnissen des jeweiligen Benutzers und den
Fhigkeiten der Systemumgebung anzupassen.
Die dritte wichtige Komponente ist das Java 2D API, das komplexe
Grafikoperationen und Bildbearbeitungsroutinen zur Verfgung stellt.
kann. Die Lnge eines Vektors ist vernderlich, und Elemente knnen am Ende oder
an einer beliebigen anderen Stelle eingefgt werden. Aufgrund dieser Flexibilitt
kann ein Vector oft da verwendet werden, wo ansonsten eine lineare Liste durch
Verkettung von Objektreferenzen manuell erstellt werden mte. Wie gewhnlich
erfolgt auch das Speichermanagement eines Vektors vollkommen automatisch.
Neben Vector gibt es weitere Container-Klassen. So bietet beispielsweise
Hashtable die Mglichkeit, Schlssel-Wert-Paare zusammenhngend zu
speichern und bei gegebenem Schlssel den zugehrigen Wert effizient wieder
aufzufinden.
Ein ntzlicher Mechanismus zum Durchlaufen von Container-Klassen ist das
Enumeration-Interface, das die Methoden hasMoreElements und
nextElement zur Verfgung stellt. Diese knnen verwendet werden, um in einer
Schleife alle Elemente des Containers sukkzessive zu durchlaufen. Alle
vordefinierten Container-Klassen stellen Methoden zur Verfgung, die EnumerationObjekte zum Durchlaufen der eigenen Elemente zurckgeben.
Seit dem JDK 1.2 gibt es in Java eine eigene Bibliothek fr Container-Klassen, das
Collection-API. Sie stellt eine umfassende Sammlung an Interfaces fr ContainerKlassen zur Verfgung und bietet unterschiedliche Implementierungen fr
verschiedene Anwendungsflle. Die zuvor erwhnte Klasse Enumeration wird
hier durch das Interface Iterator ersetzt, das einfacher zu bedienen ist. Das
Collection-API stellt daneben einige Algorithmen zur Verarbeitung von Containern
zur Verfgung (z.B. Sortieren), die es in den lteren Container-Klassen nicht gab.
Java stellt auch Zufallszahlen zur Verfgung. Das Paket java.util bietet eine
Klasse Random, die das Initialisieren von Zufallszahlengeneratoren und den Zugriff
auf ganzzahlige oder Fliekomma-Zufallszahlen ermglicht. Neben gleichverteilten
stellt die Klasse Random auch normalverteilte Zufallszahlen zur Verfgung.
Seit dem JDK 1.1 werden darber hinaus mit jedem Release weitere
hochspezialisierte (und teilweise sehr aufwendige) Bibliotheken zur Verfgung
gestellt. So bietet beispielsweise JDBC (Java Database Connectivity) den Zugriff auf
relationale Datenbanken, JavaBeans stellt eine portable Komponentenarchitektur zur
Verfgung, und mit dem Networking-API, RMI (Remote Method Invocation) und der
Java-eigenen CORBA-Implementierung javaidl kann unternehmensweit auf
Netzwerkressourcen und verteilte Objekte zugegriffen werden. Per Serialisierung
knnen Objekte persistent gemacht werden, und mit dem Reflection-API kann der
Aufbau von Objekten und Klassen zur Laufzeit untersucht und dynamisch darauf
zugegriffen werden. Wir werden in diesem Buch die wichtigsten dieser Bibliotheken
ausfhrlich erlutern.
<
>
>>
1.3 Bewertung
1.3 Bewertung
1.3.1 Einige weitverbreitete Miverstndnisse ...
These 1: Java == JavaScript
These 2: Java ist einfach zu erlernen
These 3: Java ist portabel
These 4: Java-Programme sind langsam
These 5: Java ist nicht fr ernsthafte Anwendungen geeignet
1.3.2 Ausblick
Diese gern wiederholte These aus der Sprachbeschreibung ist nur bedingt richtig.
Java ist eine objektorientierte Programmiersprache mit fortschrittlichen Eigenschaften
und mu wie eine solche erlernt werden. Sie ist einfacher zu beherrschen als C oder
C++, und es gibt weniger Raum fr Anfngerfehler. Auch die Klassenbibliothek ist
leichter zu verwenden, denn sie wurde neu entworfen und kommt ohne die Altlasten
nicht-objektorientierter Vorgnger daher. Die Kompatibilittsanforderungen von
mittlerweile vier JDK-Folgeversionen (1.1, 1.2, 1.3 und 1.4) merkt man ihr aber
durchaus an. Vor allem wegen des groen Umfangs der Klassenbibliotheken erfordert
der Umgang mit Java ein nicht zu unterschtzendes Ma an Einarbeitungszeit, bevor
man als Entwickler zu produktiven Ergebnissen kommt.
These 3: Java ist portabel
Die Quellcode-Portabilitt von Java-Programmen ist auf der reinen Sprachebene
etwas hher als die von C- oder C++-Programmen. Das ist hauptschlich dem
Verzicht auf explizite Pointer sowie maschinennahe Datentypen und Operatoren zu
verdanken. Auch die genaue Spezifikation der elementaren Datentypen trgt zu dieser
Tatsache bei.
Bezieht man dagegen die riesige Klassenbibliothek mit ihren auf allen Plattformen
standardmig vorhandenen Funktionen in die Betrachtung ein, fallen C und C++ im
Portabilittsvergleich um Lichtjahre zurck. Funktionalitten wie Datenbankzugriffe,
Netzwerkprogrammierung, Multithreading, Serialisierung, Grafikprogrammierung
und viele andere mehr sind in Java bereits in der Grundversion enthalten und stehen
auf allen Plattformen in derselben Weise zur Verfgung.
Ein weiterer Portabilittsvorteil von Java besteht zudem darin, da die kompilierten
Programme binrkompatibel sind. Ein einmal bersetztes Programm wird auf jeder
Plattform laufen, die eine Java-VM und die erforderliche Laufzeitumgebung zur
Verfgung stellt. Ein Rekompilieren, wie es bei C- oder C++-Programmen
erforderlich ist, bentigen Java-Programme daher nicht.
These 4: Java-Programme sind langsam
Da Java-Programme in Bytecode bersetzt werden, der nicht direkt vom Prozessor
ausgefhrt werden kann, mu ein Interpreter diese Aufgabe bernehmen. Dadurch
knnen CPU-lastige Java-Programme um den Faktor 10 bis 20 langsamer sein als
vergleichbare C/C++-Programme.
Dieses Argument wird auch heute noch gern von der C-/C++-Fraktion angefhrt. In
der Praxis relativiert es sich durch mehrere Faktoren. Einerseits sind nur wenige
Programme ausgesprochen CPU-intensiv. Die meisten interaktiven Programme
verbringen nmlich einen Groteil ihrer Zeit damit, auf Eingaben des Benutzers oder
langsame Datenbank- oder Festplattenzugriffe zu warten. Zudem wurde das
Laufzeitverhalten von Java-Programmen durch die Entwicklung von Just-In-TimeCompilern, Native-Code-Compilern oder Java-Prozessoren in den letzten Jahren
stetig verbessert und nhert sich zunehmend der von kompiliertem C/C++-Code an.
Die seit dem JDK 1.3 zum Umfang des JDK gehrenden HotSpot-Compiler
analysieren zur Laufzeit des Programmes den interpretierten Bytecode und bersetzen
besonders rechenintensive Teile in direkt ausfhrbaren Maschinencode.
Das Performance-Problem kann daher als temporres Problem angesehen werden falls es fr den speziellen Typ Anwendung berhaupt noch existiert. Viele
Beobachter gehen heute davon aus, da Java-Programme bald mit derselben
Geschwindigkeit laufen werden wie kompilierte C/C++-Programme. Benchmarks
zeigen diese Ergebnisse in Teilbereichen schon heute. Auch die anfnglichen
Performance-Probleme von Swing-Oberflchen wurden mit dem JDK 1.3 und den
aktuellen Entwicklungen auf dem Prozessormarkt weitgehend unschdlich gemacht.
Allerdings kann der unachtsame Entwickler in Java sehr leicht zu einer schlechten
Performance beitragen. Wer die abstrakten Designmglichkeiten von Strings,
Readern oder Writern, Collection-Klassen und vielen anderen Bestandteilen der
Klassenbibliothek bedenkenlos verwendet, kann das Laufzeitverhalten seines
Programmes stark beeintrchtigen. Schon mit der Kenntnis einiger Details ber den
Aufbau wichtiger JDK-Klassen lassen sich die vorhandenen Bibliotheken weit
effizienter nutzen, und die Performance der Programme steigt an. In Kapitel 49 gehen
wir auf einige dieser Details ein und zeigen, wie man Java-Programme schreibt, deren
Performance fr viele Anwendungen adquat ist.
These 5: Java ist nicht fr ernsthafte Anwendungen geeignet
Diese Behauptung resultiert vor allem aus drei Beobachtungen. Zum einen hatten
viele der zunchst in Java entwickelten Anwendungen Spielzeug- oder
Democharakter. Meist als Applet realisiert, hatten sie lediglich die Aufgabe, eine
Homepage zu verschnern oder die Java-Kentnisse ihrer Entwickler zu
demonstrieren. Echten Nutzwert boten dagegen nur wenige, und grere
Applikationen, die komplett in Java geschrieben wurden, waren zunchst kaum auf
dem Markt. Dies hat sich mittlerweile vollstndig gendert. Es gibt hunderte,
wahrscheinlich tausende von ausgewachsenen Java-Applikationen aus allen nur
erdenklichen Anwendungsgebieten.
Zweitens war das Look-and-Feel von Java-Programmen nicht ausgereift. Tatschlich
bildet das AWT nur einen geringen Anteil der in den jeweiligen plattformspezifischen
Fenstersystemen verfgbaren Mglichkeiten ab. Die wenigen Dialogelemente stehen
allerdings portabel zur Verfgung. Mit Hilfe des Swing-Toolsets kann dieses Problem
als gelst angesehen werden. Swing bietet einen umfassenden Satz komplexer
Dialogelemente und stellt ihn plattformbergreifend zur Verfgung. Dabei ist es
mglich, das Look-and-Feel der jeweiligen Anwendung zur Laufzeit umzustellen und
so dem Geschmack und den Kenntnissen des jeweiligen Benutzers anzupassen.
Die dritte Beobachtung besagt, da Java voller Fehler steckt. Whrend dies weniger
fr die Sprache selbst, ihre Werkzeuge oder die elementaren Eigenschaften der
Klassenbibliothek gilt, konnten die frhen Grafikbibliotheken ein gewisses Ma an
Fehlerhaftigkeit nicht verhehlen. Zwar gibt es nach wie vor Fehler im JDK (alle
bekannten Bugs sind in der als Bug Parade bezeichneten Fehlerdatenbank
gespeichert), doch sind diese in aller Regel nicht so schwerwiegend, da nicht ein
Workaround gefunden werden knnte. Zudem stecken die Fehler meist in den
Klassenbibliotheken. Der Compiler und die virtuelle Maschine, auf der die
ausfhrbaren Programme laufen, knnen fr die meisten Anwendungen als
hinreichend stabil angesehen werden. Auch hier gibt es Ausnahmen, insbesondere bei
JDK-Portierungen auf weniger gebruchliche Betriebssysteme. Auf den meisten
Systemen ist das JDK in der tglichen Praxis jedoch ein ausgesprochen stabiles,
zuverlssiges und hinreichend schnelles Entwicklungswerkzeug.
1.3.2 Ausblick
Wenn man das Anwenderinteresse zugrunde legt, ist Java schon jetzt die mit Abstand
erfolgreichste Programmiersprache der letzten Jahre. Wie oben gezeigt, hat die
Sprache in den wenigen Jahren seit Ausgabe der Version 1.0 eine immense
Verbreitung erfahren. Java war Anla fr Tausende von Bchern und
Zeitschriftenartikeln, und es entstanden eine Reihe von stark frequentierten
Newsgroups, die das Interesse an Java deutlich machen. Alle greren
Softwarehersteller untersttzen die Sprache und haben konkrete Produkte realisiert. In
den meisten einschlgigen Stellenanzeigen werden Java-Kenntnisse verlangt.
Zeitgleich zur Diskussion um die Eignung Javas als Entwicklungswerkzeug fr
grafische Oberflchen hat sich ein Wandel auf der Serverseite vollzogen. Mit dem
Servlet-API und den Java Server Pages haben sich beispielsweise im Bereich der
Generierung dynamischer Web-Seiten Techniken etabliert, die dem traditionellen
CGI-Scripting Konkurrenz machen. Daneben setzen groe Softwarehersteller auf
mehrschichtige Client-Server-Architekturen mit Java-Untersttzung (Oracle
Financials, Star Office) oder statten ihre Datenbank- oder Web-Server mit JavaFhigkeiten aus (Oracle 8, Lotus Domino, SUN Java Web Server, Apache jserv). In
vielen Unternehmen gibt es bereits verteilte Anwendungen, die auf die
plattformbergreifenden Fhigkeiten und die Binrkompatibilitt von JavaAnwendungen setzen.
Auch auf der Client-Seite ist seit einiger Zeit ein Trend zu Java zu beobachten. So
gibt es kleine Java-Anwendungen, die auf dem Palm Pilot, dem Psion 5 oder in
Mobiltelefonen laufen. Aber auch komplexe Standalone-Programme wie beispielsweise aus dem Bereich der Entwicklungswerkzeuge - JBuilder, NetBeans
(Forte), Together/J oder OptimizeIt werden mittlerweile in Java entwickelt. Viele
Unternehmen setzen Java ein, um die Produktivitt bei der Entwicklung graphischer
Oberflchen zu erhhen, oder verbinden in Java geschriebene Frontends mit den
vorhandenen batchorientierten Applikations- und Datenbankservern. Nicht selten
fhrt der Umstieg von C/C++ auf Java zu drastischen Effizienzsteigerungen bei der
Entwicklung und Portierung komplexer Anwendungssysteme.
Neben der plattformbergreifenden Portabilitt von Bytecode und grafischer
Oberflche bietet Java Dinge wie Datenbankanbindung, Multithreading oder
Netzwerkprogrammierung ohne architektonische Brche. Mit der Pflicht,
objektorientiert zu programmieren (im Gegensatz zu C++ gibt es in Java keine
legitimierte Mglichkeit, rein prozedurale Programme zu schreiben), mu der
Entwickler einen weiteren Schritt in der Evolution seiner eigenen Fhigkeiten
vollziehen. Applets, Servlets und der Austausch von Bytecode zwischen
Anwendungen wurde erst mit Java salonfhig. Mit weltweit mehreren Millionen JavaEntwicklern ist die kritische Masse zur (irreversiblen) Etablierung dieser neuen
Techniken sicher erreicht.
Natrlich kann heute niemand sagen, ob Java auch langfristig erfolgreich sein und als
Programmiersprache auch in zehn oder zwanzig Jahren eine Rolle spielen wird. Das
derzeitige und nach mittlerweile 7 Jahren immer noch steigene Entwicklerinteresse
spricht allerdings sehr dafr. Programmiersprachen und -methoden werden nicht von
heute auf morgen ausgewechselt, denn Paradigmenwechsel brauchen ihre Zeit.
Vorhandener, mit viel Mhe erstellter Quellcode wird nicht einfach weggeworfen.
Auch 20 Jahre alte COBOL-, RPG- und FORTRAN-Programme werden heute noch
gewartet, wenn auch mit sinkender Tendenz. Java hat seinen Zenit noch nicht einmal
erreicht, und neuer Code auf Basis von Java wird in nach wie vor zunehmendem
Mae entwickelt. So kann die Prognose gewagt werden, da Java auch im Jahre 2010
und darber hinaus nennenswerte Bedeutung haben wird.
Wer heute auf Erfahrungen in Java-Programmierung zurckgreifen kann, hat morgen
mglicherweise den entscheidenden Vorteil im Wettbewerb mit der Konkurrenz.
<
>
>>
1.4 Zusammenfassung
1.4 Zusammenfassung
<
>
>>
2.1.1 Hardware-Voraussetzungen
Zur Installation des JDK ist ein vernnftig ausgestatteter PC mit Windows oder
Linux oder eine Solaris-Workstation erforderlich. Alternativ kann auch eine der
vielen anderen Plattformen verwendet werden, auf die das JDK portiert wurde,
beispielsweise OS/2, Mac OS X oder eines der anderen Unix-Derivate. Die meisten
Beispiele in diesem Buch wurden auf Rechnern unterschiedlicher Konfiguration und
Ausstattung mit Windows 95, 98 oder NT entwickelt. Einige von ihnen auch unter
LINUX. Als eben noch brauchbares Minimalsystem kann ein PC folgender
Konfiguration angesehen werden:
Pentium-166
48 MB RAM
Grafik mit 800 * 600 Pixeln, 8 Bit Farbtiefe
2.1.2 Installation
Wir wollen hier nur die Installation unter Windows beschreiben, die mit Hilfe eines
InstallShield-Scripts mengesteuert erfolgt. Anschlieend mu die Dokumentation
des JDK installiert werden. Dies geschieht per Hand und erfordert ein Programm zum
Entpacken der HTML-Dateien.
Installation des JDK
Das JDK 1.4 befindet sich auf der CD-ROM im Unterverzeichnis \install\jdk14. Falls
die CD-ROM nicht vorhanden ist, kann das JDK von SUNs Java-Server
http://java.sun.com geladen werden (ca. 40 MB Download zzgl. Dokumentation). Die
Installation unter Windows ist sehr einfach und erfolgt in folgenden Schritten:
Mit dem JDK wird auch die Laufzeitumgebung JRE (Java Runtime
Hinweis
Environment) installiert. Sie besitzt einen eigenen Deinstallationseintrag
und befindet sich im Verzeichnis c:\programme\java\j2re1.4.0 (bis zur
Version 1.3 war das Basisverzeichnis c:\programme\javasoft\). In der
Installationsroutine des JDK 1.2 hatte das JRE einen separaten Lizenztext
und konnte wahlweise in ein anderes Verzeichnis installiert werden. Seit
dem JDK 1.3 gibt es diese zustzlichen Schritte nicht mehr. Fr
Arbeitspltze, die nur das JRE bentigen (auf denen Java-Programme
lediglich ausgefhrt, nicht aber entwickelt werden sollen), kann das JRE
auch alleine installiert werden. Es befindet sich ebenfalls auf der CDROM zum Buch.
Alle Dateien befinden sich nun im ausgewhlten Installationsverzeichnis, und die
Installation des JDK ist abgeschlossen. Um mit dem JDK arbeiten zu knnen, mu
noch das Verzeichnis \jdk1.4\bin in den Suchpfad fr ausfhrbare Dateien
eingetragen werden. Das kann direkt in der autoexec.bat durch Modifikation des
PATH-Statements erfolgen oder bei jedem Aufruf einer DOS-Box mit Hilfe einer
Batchdatei erledigt werden:
PATH=c:\jdk1.4\BIN;%PATH%
Unter Windows NT, 2000 und XP ist der entsprechende Eintrag in den
Umgebungsparametern der Systemkonfiguration vorzunehmen. Informationen zu
anderen Betriebssystemen und weitere Hinweise finden sich in den Installation
Notes des JDK.
Anders als die Vorgngerversionen bentigen die JDKs 1.2, 1.3 und 1.4
Hinweis
bei einer Standardinstallation unter Windows keine Umgebungsvariable
CLASSPATH mehr, denn die entsprechenden Informationen werden bei
der Installation in die Registry geschrieben (sie liegen in unterschiedlichen
Abschnitten, eine Suche nach javasoft hilft weiter). Ist jedoch eine
CLASSPATH-Variable vorhanden, wird sie auch verwendet. Wird das
JDK 1.2, 1.3 oder 1.4 also ber eine 1.0er oder 1.1er Version installiert,
mu dafr gesorgt werden, da eine eventuell gesetzte CLASSPATHVariable modifiziert oder entfernt wird. Weitere Informationen zum
Setzen der CLASSPATH-Umgebungsvariable finden sich ab Abschnitt
13.2.2.
Installation der Dokumentation
Die Dokumentation des JDK besteht aus einer Sammlung von HTML-Dateien, die
auf der CD-ROM im Verzeichnis \install\jdk14 zu finden sind. Um sie zu installieren,
mu die Datei j2sdk-1_4_0-doc.zip ausgepackt werden. Da lange Dateinamen darin
enthalten sind, mu zum Auspacken ein geeigneter Entpacker verwendet werden.
Auf der CD-ROM befindet sich im Verzeichnis \misc eine Version von WinZip95,
die dazu verwendet werden kann. Bitte beachten Sie vor der Verwendung dieses
Programms die Lizenz- und Registrierungshinweise. Die Dokumentation belegt etwa
weitere 100 MB Plattenspeicher.
Die Dokumentation wurde bereits in das Unterverzeichnis docs verpackt. Falls das
Installationsverzeichnis nicht gendert wurde, knnen die Dateien also ohne weitere
nderungen im Verzeichnis c:\jdk1.4 ausgepackt werden. Alle erforderlichen
Unterverzeichnisse werden dann automatisch angelegt. Zur Anzeige der
Dokumentation ist ein Browser wie Netscape Navigator oder Microsoft Internet
Explorer erforderlich. Um leichter auf die Dokumentation zugreifen zu knnen, ist es
sinnvoll, innerhalb des Browsers einen Verweis (Lesezeichen) auf das
Hauptdokument \jdk1.4\docs\index.html anzulegen. Diese Datei kann zur Navigation
auf alle Teile der Dokumentation verwendet werden und enthlt zustzlich eine Reihe
von Verweisen auf externe Dokumente. Alternativ kann ein Icon auf dem Desktop
angelegt werden, das den Browser mit dem Namen dieser Datei als Argument aufruft
(die korrekte Schreibweise ist file:///C|/jdk1.4/docs/index.html). Auf
dieselbe Weise kann ein Verweis auf die Datei \jdk1.4\docs\api\index.html angelegt
werden, mit dem direkt in die API-Dokumentation gesprungen werden kann.
Installation der Quelltexte
Das JDK 1.4 wird mit den vollstndigen Java-Quelltexten ausgeliefert. Nicht
enthalten sind dagegen die Quelltexte der Native Methods und der
<
>
>>
2.4 Zusammenfassung
2.4 Zusammenfassung
<
>
>>
Warnung
Dieses Buch enthlt an vielen Stellen Vorwrtsverweise auf Themen, die Hinweis
noch nicht behandelt wurden. Dadurch wird den Verflechtungen innerhalb
der Themenbereiche Rechnung getragen, und man findet beim
Nachschlagen schnell alle relevanten Textstellen. Wenn Sie beim Lesen
auf einen Vorwrtsverweis stoen, knnen Sie normalerweise zunchst
warten, bis die Erklrung nachgereicht wird (oftmals schon kurze Zeit
spter). Sie knnen den Begriff natrlich auch sofort nachschlagen, aber
aus didaktischen Grnden ist das meist nicht unbedingt notwendig.
der Vorstellung einiger weiterfhrender Themen bildet Kapitel 10 den Abschlu der
objektorientierten Programmierung. Whrend lokale und anonyme Klassen ebenso
wie Wrapper-Klassen zum Handwerkszeug jedes Java-Programmierers gehren
sollten, ist der ausfhrliche Teil ber Design-Patterns optional und kann beim ersten
Lesen ausgelassen werden.
Im nchsten Teil des Buchs werden weiterfhrende Spracheigenschaften und
Grundlagen der Klassenbibliothek behandelt. Die Kapitel 11 bis 17 sind ebenso
wichtig wie die Grundlagen der Sprache. Sie behandeln Strings, Exceptions und
Packages, erklren die Collections und stellen die wichtigsten Utility-Klassen vor.
Einzig Kapitel 15 knnte beim ersten Lesen bersprungen werden, denn dort wird das
mit dem JDK 1.2 eingefhrte Collection-API vorgestellt. Die seit der Version 1.0
vorhandenen Collections sind dagegen Pflicht und werden in Kapitel 14 behandelt.
Die nchsten vier Kapitel 18 bis 21 beschftigen sich mit Dateizugriffen. Sie erlutern
zeichen- und byteorientierte Streams, Random-Access-Dateien und den Zugriff auf
Verzeichnisse. Den Abschlu des vierten Teils bildet Kapitel 22 mit der Behandlung
des Multithreadings.
Die darauffolgenden Kapitel beschftigen sich mit dem Abstract Windowing Toolkit
und zeigen, wie man Java-Programme mit grafischer Oberflche schreibt. Whrend
Kapitel 23 bis 29 Grundlagen behandelt, die auch dann wichtig sind, wenn nur SwingProgramme geschrieben werden sollen, werden in den Kapiteln 30 und 32
ausschlielich AWT-Mens und -Dialogelemente vorgestellt. Kapitel 31 erklrt den
Umgang mit Layoutmanagern und ist sowohl fr AWT- als auch fr SwingProgramme wichtig. Den Abschlu dieses Teils bilden zwei Kapitel, in denen gezeigt
wird, wie eigene Dialogelemente entwickelt werden knnen und wie man Bitmaps
und Animationen einsetzt. Zwar sind sie weitgehend AWT-spezifisch, doch ist ihr
Inhalt auch fr entsprechende Swing-Programme und zum Verstndnis der JavaBeans-Architektur vonnten.
Teil 6 des Buchs befat sich mit der Programmierung von grafischen Oberflchen mit
Swing. Whrend in Kapitel 35 Grundlagen erklrt und Unterschiede bzw. Parallelen
zum AWT aufgezeigt werden, behandeln die nchsten drei Kapitel alle wichtigen
Dialogelemente des Swing-Toolkits. Zusammen mit den im vorigen Teil vermittelten
Grundlagen stehen nach Ende dieses Teils ausreichend Kenntnisse zur Entwicklung
eigener Swing-Applikationen zur Verfgung.
In den nachfolgenden Kapiteln 39 und 40 wird die Applet-Programmierung erlutert.
Diese Kapitel knnen bei Bedarf auch vorgezogen werden, insbesondere SwingKenntnisse sind zu ihrem Verstndnis nicht ntig. Die Grundlagen der
Grafikprogrammierung aus Kapitel 5 sollten Sie allerdings gelesen haben.
Die beiden letzten Teile des Buchs behandeln eine Reihe weiterfhrender Themen.
Die Kapitel 41 und 42 erweitern die Mglichkeiten, Daten persistent zu speichern. Sie
erlutern das Serialisieren von Objekten und beschreiben den Zugriff auf relationale
Datenbanken. In Kapitel 43 wird gezeigt, wie mit Hilfe des Reflection-APIs zur
Laufzeit auf die Interna von Klassen und Objekten zugegriffen werden kann. In
Kapitel 44 wird die zum Erstellen eigenstndiger Komponenten wichtige Java-BeansArchitektur vorgestellt.
Die nchsten beiden Kapitel 45 und 46 beschftigen sich mit der
Netzwerkprogrammierung. Whrend das erste die Grundlagen vorstellt und zeigt, wie
TCP/IP-Clients und -Server geschrieben werden, stellt das zweite verteilte
Objektarchitekturen mit RMI vor. Den Abschlu dieses Teils bildet Kapitel 47 mit
der Vorstellung des Sicherheitskonzepts von Java und der Beschreibung signierter
Applets. Hier werden auch wichtige kryptografische Grundlagen erlutert. In Kapitel
48 wird das Sound-API vorgestellt, und in den letzten beiden Kapiteln des Buchs
werden Hinweise zur Steigerung der Performance von Java-Programmen und zum
Umgang mit den Hilfsprogrammen des JDK gegeben.
und in welcher Reihenfolge Sie das tun. Die vorgegebene Kapitelaufteilung und reihenfolge mag Ihnen dabei ein Leitfaden sein. Whlen Sie die Technik, die Ihren
Neigungen und Erwartungen am meisten entgegenkommt und am besten mit Ihren
Beweggrnden in Einklang zu bringen ist. Dann werden Sie den grtmglichen
Nutzen aus dem Buch ziehen.
<
>
>>
3.3 Zusammenfassung
3.3 Zusammenfassung
<
>
>>
4.7 Zusammenfassung
4.7 Zusammenfassung
Der Unicode-Zeichensatz
Namenskonventionen fr Bezeichner
Kommentare in Java
Die primitiven Datentypen boolean, char, byte, short, int, long,
float und double
Die boolschen Literale true und false sowie die Fliekommaliterale
MAX_VALUE, MIN_VALUE, NaN, NEGATIVE_INFINITY und
POSITIVE_INFINITY
Die Standard-Escape-Sequenzen \b, \t, \n, \f, \r, \", \', \\ und \nnn
Deklaration, Lebensdauer und Sichtbarkeit von Variablen
Deklaration, Initialisierung und Zugriff auf Arrays
Referenztypen und automatisches Speichermanagement
Typkonvertierungen
<
>
>>
5.9 Zusammenfassung
5.9 Zusammenfassung
<
>
>>
6.5 Zusammenfassung
6.5 Zusammenfassung
<
>
>>
7.4 Zusammenfassung
7.4 Zusammenfassung
<
>
>>
8.5 Zusammenfassung
8.5 Zusammenfassung
Das Prinzip der Vererbung und das Schlsselwort extends zum Ableiten
einer Klasse
Methodenberlagerung, die Bedeutung des statischen und dynamischen
Bindens und das Schlsselwort super
Die Attribute private, protected, public, static, final,
transient und volatile
Die Bedeutung des Begriffs package scoped
Klassenvariablen und -methoden und statische Konstruktoren
Abstrakte Klassen und Polymorphismus
<
>
>>
9.6 Zusammenfassung
9.6 Zusammenfassung
<
>
>>
10.4 Zusammenfassung
10.4 Zusammenfassung
<
>
>>
<
>
>>
Hinweis
Titel
<<
Inhalt
<
Suchen
>
Index
>>
DOC
API
11.4.1 Konstruktoren
StringBuffer()
java.lang.StringBuffer
StringBuffer(String s)
Der parameterlose Konstruktor erzeugt einen leeren StringBuffer. Wird dagegen ein String
bergeben, erzeugt der Konstruktor ein StringBuffer-Objekt, das eine Kopie der bergebenen
Zeichenkette darstellt.
java.lang.StringBuffer
Mit append wird der String s an das Ende des StringBuffer-Objekts angehngt. Zurckgegeben
wird das auf diese Weise verlngerte StringBuffer-Objekt s. Zustzlich gibt es die Methode append
in Varianten fr das Anhngen aller Arten von primitiven Typen. Anstelle eines String-Objekts wird
hier der entsprechende primitive Typ bergeben, in einen String konvertiert und an das Ende des Objekts
angehngt.
insert fgt den String s an der Position offset in den aktuellen StringBuffer ein.
Zurckgegeben wird das auf diese Weise verlngerte StringBuffer-Objekt s. Auch diese Methode
gibt es fr primitive Typen. Der anstelle eines String bergebene Wert wird zunchst in einen String
konvertiert und dann an der gewnschten Stelle eingefgt.
java.lang.StringBuffer
Mit deleteCharAt wird das an Position index stehende Zeichen entfernt und der StringBuffer
um ein Zeichen verkrzt. delete entfernt den Teilstring, der von Position start bis end reicht, aus
dem StringBuffer und verkrzt ihn um die entsprechende Anzahl Zeichen.
java.lang.StringBuffer
Mit der Methode setCharAt wird das an Position index stehende Zeichen durch das Zeichen c ersetzt.
Falls der StringBuffer zu kurz ist (also index hinter das Ende des StringBuffer-Objekts zeigt),
lst die Methode eine Ausnahme des Typs StringIndexOutOfBoundsException aus.
Seit dem JDK 1.2 gibt es zustzlich eine Methode replace, mit der ein Teil des
JDK1.1-1.4
StringBuffer-Objekts durch einen anderen String ersetzt werden kann. Dabei wird
das von Position start bis Position end - 1 gehende Teilstck durch den String str
ersetzt. Falls erforderlich, wird der ursprngliche StringBuffer verkrzt oder
verlngert.
11.4.5 Lngeninformationen
int length()
java.lang.StringBuffer
length liefert die Lnge des Objekts, also die Anzahl der Zeichen, die im StringBuffer enthalten
sind. Mit capacity wird dagegen die Gre des belegten Pufferspeichers ermittelt. Dieser Wert ist
typischerweise grer als der von length zurckgegebene Wert.
java.lang.StringBuffer
Nachdem die Konstruktion eines StringBuffer-Objekts abgeschlossen ist, kann es mit Hilfe dieser
Methode effizient in einen String verwandelt werden. Die Methode legt dabei keine Kopie des
StringBuffer-Objekts an, sondern liefert einen Zeiger auf den internen Zeichenpuffer. Erst wenn der
StringBuffer erneut verndert werden soll, wird tatschlich eine Kopie erzeugt.
Die sinnvolle Anwendung der Klassen String und StringBuffer hat bei vielen
Programmen groen Einflu auf ihr Laufzeitverhalten. In Kapitel 49 gehen wir daher noch
einmal auf die speziellen Performanceaspekte beider Klassen ein und geben Hinweise zu ihrer
sinnvollen Anwendung.
Titel
Inhalt
Suchen
Index
DOC
<<
<
>
>>
API
Hinweis
11.5 Zusammenfassung
11.5 Zusammenfassung
<
>
>>
Mit den Exceptions besitzt Java einen Mechanismus zur strukturierten Behandlung
von Fehlern, die whrend der Programmausfhrung auftreten. Tritt etwa ein
Laufzeitfehler auf, weil ein Array-Zugriff auerhalb der definierten Grenzen erfolgte
oder weil eine Datei, die geffnet werden sollte, nicht gefunden wurde, so gibt es in
Java Sprachmittel, die eine systematische Behandlung solcher Ausnahmen
ermglichen.
Da Exceptions ein relativ neues Feature von Programmiersprachen sind, ist es
sinnvoll, zunchst die in diesem Zusammenhang verwendeten Begriffe vorzustellen.
Als Exception wird dabei die eigentliche Ausnahme bezeichnet, die durch ein
Programm zur Laufzeit verursacht werden kann. Das Auslsen einer Ausnahme wird
im Java-Sprachgebrauch als throwing bezeichnet, wir werden meist die deutsche
Bezeichnung auslsen verwenden. Das Behandeln einer Ausnahme, also die explizite
Reaktion auf das Eintreten einer Ausnahme, wird als catching bezeichnet. Schlielich
werden wir auch die Begriffe Ausnahme und Exception synonym verwenden.
Das Grundprinzip des Exception-Mechanismus in Java kann wie folgt beschrieben
werden:
Ein Laufzeitfehler oder eine vom Entwickler gewollte Bedingung lst eine
Ausnahme aus.
Diese kann nun entweder von dem Programmteil, in dem sie ausgelst wurde,
behandelt werden, oder sie kann weitergegeben werden.
Wird die Ausnahme weitergegeben, so hat der Empfnger der Ausnahme
erneut die Mglichkeit, sie entweder zu behandeln oder selbst weiterzugeben.
Wird die Ausnahme von keinem Programmteil behandelt, so fhrt sie zum
Abbruch des Programms und zur Ausgabe einer Fehlermeldung.
Die folgenden Abschnitte erlutern die Details des Auftretens, der Behandlung und
<
>
>>
12.4 Zusammenfassung
12.4 Zusammenfassung
<
>
>>
13.6 Zusammenfassung
13.6 Zusammenfassung
<
>
>>
Als Collections bezeichnet man Datenstrukturen, die dazu dienen, Mengen von Daten
aufzunehmen und zu verarbeiten. Die Daten werden gekapselt abgelegt, und der
Zugriff ist nur mit Hilfe vorgegebener Methoden mglich. Typische Collections sind
Stacks, Queues, Deques, Priority Queues, Listen und Trees. Collections gibt es in
groer Anzahl und diversen Implementierungsvarianten; jeder angehende
Informatiker kann ein Lied davon singen.
Man knnte sagen, da Collections fr Datenstrukturen so etwas sind wie
Schleifenkonstrukte fr Anweisungen. So wie Schleifen die Wiederholbarkeit und
Wiederverwendbarkeit von Code ermglichen, erlauben Collections die
Zusammenfassung und repetitive Bearbeitung von einzelnen Datenelementen.
Tatschlich werden Collections sehr oft in Zusammenhang mit Schleifen verwendet,
insbesondere, wenn das Programm von seiner Grundstruktur her nicht Einzel-,
sondern Mengendaten verarbeitet.
Ihre Hauptrolle besteht dann darin, Elementardaten zu speichern und einen der
Bearbeitung angemessenen Zugriff darauf zu ermglichen. Darber hinaus knnen
sie ganz neue Sichten auf die Daten definieren. Eine Hashtable beispielsweise stellt
Daten paarweise zusammen und ermglicht so die Definition einer
Referenzbeziehung, die ohne ihre Hilfe nur schwer herzustellen wre.
Einfache Programmiersprachen bieten Collections meist nur in Form von Arrays. In
Java und anderen objektorientierten Sprachen gibt es dagegen eine ganze Reihe
unterschiedlicher Collections mit einem viel breiter gefcherten Aufgabenspektrum.
Java kennt zwei unterschiedliche Collection-APIs. Whrend beider Funktionalitt
sich betrchtlich berschneidet, weisen ihre Schnittstellen teilweise recht groe
Unterschiede auf. Einerseits gibt es seit dem JDK 1.0 die "traditionellen" Collections
mit den Klassen Vector, Stack, Dictionary, Hashtable und BitSet.
Andererseits wurde mit dem JDK 1.2 ein neues Collection-API eingefhrt und
parallel zu den vorhandenen Klassen plaziert. Wir werden uns in diesem Kapitel mit
den traditionellen Klassen beschftigen und in Kapitel 15 auf die Neuerungen des
JDK 1.2 eingehen. Dann werden wir auch einige der Beweggrnde beleuchten, die
zur Einfhrung der neuen Klassen gefhrt haben.
<
>
>>
14.6 Zusammenfassung
14.6 Zusammenfassung
<
>
>>
Wie in Kapitel 14 erlutert, gibt es seit dem JDK 1.0 die "traditionellen" Collections
mit den Klassen Vector, Stack, Dictionary, Hashtable und BitSet.
Obwohl sie ihren Zweck durchaus erfllen, gab es einige Kritik am CollectionsKonzept des JDK 1.0. Zunchst wurde die geringe Vielseitigkeit kritisiert, etwa im
Vergleich zum mchtigen Collection-Konzept der Sprache SmallTalk. Zudem galten
die JDK-1.0-Collections als nicht sehr performant. Fast alle wichtigen Methoden sind
synchronized, und als generische Speicher von Elementen des Typs Object ist
bei jedem Zugriff ein Typecast bzw. eine Typberprfung notwendig (wir kommen
auf diese Begriffe in Kapitel 22 zurck). Zudem gab es Detailschwchen, die immer
wieder kritisiert wurden. Als Beispiel kann die Wahl der Methodennamen des
Enumeration-Interfaces genannt werden. Oder die Implementierung der Klasse
Stack, die als Ableitung von Vector weit ber ihr eigentliches Ziel hinausschiet.
Diese Kritik wurde von den Java-Designern zum Anla genommen, das CollectionKonzept im Rahmen der Version 1.2 neu zu berdenken. Herausgekommen ist dabei
eine Sammlung von gut 20 Klassen und Interfaces im Paket java.util, die das
Collections-Framework des JDK 1.2 bilden. Wer bei dieser Zahl erschreckt, sei
getrstet. Letztlich werden im wesentlichen die drei Grundformen Set, List und
Map realisiert. Die groe Anzahl ergibt sich aus der Bereitstellung verschiedender
Implementierungsvarianten, Interfaces und einiger abstrakter Basisklassen, mit denen
die Implementierung eigener Collections vereinfacht werden kann. Wir wollen uns
zunchst mit der grundlegenden Arbeitsweise der Collection-Typen vertraut machen:
Eine List ist eine beliebig groe Liste von Elementen beliebigen Typs, auf
die sowohl wahlfrei als auch sequentiell zugegriffen werden kann.
Ein Set ist eine (doublettenlose) Menge von Elementen, auf die mit
typischen Mengenoperationen zugegriffen werden kann.
Eine Map ist eine Abbildung von Elementen eines Typs auf Elemente eines
int size()
boolean isEmpty()
boolean contains(Object o)
Iterator iterator()
Object[] toArray()
Object[] toArray(Object[] a)
boolean add(Object o)
boolean remove(Object o)
boolean containsAll(Collection c)
boolean addAll(Collection c)
boolean removeAll(Collection c)
boolean retainAll(Collection c)
void clear()
boolean equals(Object o)
int hashCode()
java.util.Collection
hashCode, isEmpty, size und toArray obligatorisch sind, mssen die brigen
Methoden in einer konkreten Implementierung nicht unbedingt zur Verfgung
gestellt werden, sondern knnen weggelassen, ersetzt oder ergnzt werden.
Aus Kapitel 9 wissen wir allerdings, da alle definierten Methoden eines Interfaces
in einer konkreten Implementierung zur Verfgung gestellt werden mssen. Davon
sind natrlich auch die Collection-Klassen nicht ausgenommen. Wenn sich diese
entschlieen, eine optionale Methode nicht zu realisieren, so mu diese zwar
implementiert werden, lst aber beim Aufruf eine Exception des Typs
UnsupportedOperationException aus. Die tatschliche Schnittstelle einer
konkreten Collection-Klasse kann also nicht dem Interface entnommen werden, das
sie implementiert, sondern erfordert zustzlich die schriftliche Dokumentation der
Klasse.
ber diesen Designansatz kann man sich streiten. Da sowieso nur die nichtverndernden Methoden nicht-optional sind, htte man ebenso gut diese allein in ein
generisches Collection-Interface stecken knnen und von allen Collection-Klassen
implementieren lassen knnen. Zustzliche Methoden wren dann in abgeleiteten
Interfaces passend zur jeweiligen Collection-Klasse zu definieren.
<
>
>>
15.9 Zusammenfassung
15.9 Zusammenfassung
<
>
>>
16.6 Zusammenfassung
16.6 Zusammenfassung
<
>
>>
Die Klasse Math aus dem Paket java.lang enthlt Methoden zur FliekommaArithmetik. Alle Methoden dieser Klasse sind static und knnen daher ohne
konkretes Objekt verwendet werden. Nachfolgend wollen wir die wichtigsten von
ihnen auflisten und eine kurze Beschreibung ihrer Funktionsweise geben.
17.2.1 Winkelfunktionen
java.lang.Math stellt die blichen Winkelfunktionen und ihre Umkehrungen zur
Verfgung. Winkelwerte werden dabei im Bogenma bergeben.
public
public
public
public
public
public
static
static
static
static
static
static
double
double
double
double
double
double
sin(double x)
cos(double x)
tan(double x)
asin(double x)
acos(double x)
atan(double x)
java.lang.Math
public
public
public
public
static
static
static
static
public
public
public
public
static
static
static
static
java.lang.Math
17.2.3 Arithmetik
Die nachfolgend aufgelisteten Methoden dienen zur Berechnung der
Exponentialfunktion zur Basis e, zur Berechnung des natrlichen Logarithmus und
zur Berechnung der Exponentialfunktion zu einer beliebigen Basis. Mit sqrt kann
die Quadratwurzel berechnet werden.
public static double exp(double a)
java.lang.Math
public
public
public
public
static
static
static
static
int abs(int a)
long abs(long a)
float abs(float a)
double abs(double a)
java.lang.Math
<
>
>>
17.5 Zusammenfassung
17.5 Zusammenfassung
<
>
>>
Als Sprache, die nicht nur das Erstellen vorwiegend grafikorientierter Web-Applets
ermglicht, sondern auch zur Entwicklung von eigenstndigen Anwendungen
eingesetzt werden soll, bietet Java eine umfangreiche Bibliothek zum sequentiellen
und wahlfreien Zugriff auf Dateien und zur Verwaltung von Verzeichnissen.
Whrend der wahlfreie Zugriff hnlich wie in anderen Sprachen gelst ist, wurden bei
der sequentiellen Ein-/Ausgabe neue Wege beschritten. Die dafr verwendeten
Klassen realisieren das aus anderen Sprachen bekannte Konzept der Streams auf der
Basis objektorientierter Techniken.
Ein Stream wird dabei zunchst als abstraktes Konstrukt eingefhrt, dessen Fhigkeit
darin besteht, Zeichen auf ein imaginres Ausgabegert zu schreiben oder von diesem
zu lesen. Erst konkrete Unterklassen binden die Zugriffsroutinen an echte Ein- oder
Ausgabegerte, wie beispielsweise an Dateien, Strings oder Kommunikationskanle
im Netzwerk.
Das Stream-Klassenkonzept von Java bietet die Mglichkeit, Streams zu verketten
oder zu schachteln. Die Verkettung von Streams ermglicht es, mehrere Dateien
zusammenzufassen und fr den Aufrufer als einen einzigen Stream darzustellen. Das
Schachteln von Streams erlaubt die Konstruktion von Filtern, die bei der Ein- oder
Ausgabe bestimmte Zusatzfunktionen bernehmen, beispielsweise das Puffern von
Zeichen, das Zhlen von Zeilen oder die Interpretation binrer Daten. Beide Konzepte
sind mit normalen Sprachmitteln realisiert und knnen selbst erweitert werden. Es ist
ohne weiteres mglich, eigene Filter zu schreiben, die den Ein- oder Ausgabestrom
analysieren und anwendungsbezogene Funktionalitten realisieren.
Alle Klassen zur Dateiein- und -ausgabe befinden sich im Paket java.io. Um sie
zu verwenden, sollte daher folgende Anweisung an den Anfang eines Programms
gestellt werden:
import java.io.*;
Bis zur Version 1.0 des JDK gab es nur Byte-Streams in Java. Wesentliches Merkmal
eines Byte-Streams war es dabei, da jede einzelne Transporteinheit 8 Bit lang war
(also ein einzelnes Byte). Whrend damit die Kompatibilitt zu Textdateien, die mit
konventionellen Programmiersprachen erstellt wurden oder von diesen gelesen
werden sollten, gewhrleistet war, gab es naturgem Probleme bei der Umwandlung
in die 16 Bit langen Unicode-Zeichen, die innerhalb von Java zur Zeichendarstellung
benutzt werden. Zudem war die Abbildung zwischen Bytes und Characters eher
unsystematisch gelst und bot wenig Untersttzung fr die Anpassung an
unterschiedliche Zeichenstze und nationale Gegebenheiten.
All dies hat die JDK-Designer dazu bewogen, das Konzept der Streams in der
Version 1.1 zu berdenken und die neue Gruppe der Character-Streams einzufhren.
Character-Streams verwenden grundstzlich 16 Bit lange Unicode-Zeichen und
arbeiten daher besser mit den String- und Zeichentypen von Java zusammen. Um zu
den 8-Bit-Zeichenstzen in externen Dateien kompatibel zu bleiben, wurden explizite
Brckenklassen eingefhrt, die Character-Streams in Byte-Streams berfhren und
umgekehrt. Diese bieten nun auch die Mglichkeit der Anpassung an spezielle
Zeichenstze und lokale Besonderheiten.
Wir werden uns in diesem Kapitel ausschlielich mit den Character-Streams
beschftigen, weil sie fr die Verarbeitung von textbasierten Streams wichtig sind.
Byte-Streams und Brckenklassen werden im nchsten Kapitel erlutert. Wir werden
dort sehen, da es - selbst im UNICODE-Zeitalter - eine ganze Reihe von
Anwendungen fr byteorientierte Dateizugriffe gibt.
Die Beispiele in diesem Kapitel wurden hauptschlich unter Windows
Hinweis
entwickelt und getestet, und mitunter kommen darin windows-spezifische
Besonderheiten vor. So wird als Zeilenendezeichen "\r\n" verwendet,
obwohl unter UNIX "\n" und auf dem Mac "\r" gebruchlich ist. Am
besten ist es natrlich, das System-Property line.separator zu
verwenden (siehe Abschnitt 16.3.1). Auch bei den Pfadseparatoren gibt es
Unterschiede. Whrend unter Windows der Backslash "\" verwendet wird,
ist es unter UNIX der Slash "/". Mit dem System-Property
file.separator kann auch auf diese Eigenschaft portabel zugegriffen
werden.
Handbuch der Java-Programmierung, 3. Auflage,
Addison Wesley, Version 3.0
1998-2002 Guido Krger,
API
http://www.javabuch.de
<
>
>>
18.4 Zusammenfassung
18.4 Zusammenfassung
<
>
>>
<
>
>>
19.4 Zusammenfassung
19.4 Zusammenfassung
<
>
>>
Titel
<<
DOC
API
Neben der stream-basierten Ein- und Ausgabe bietet die Java-Klassenbibliothek auch die
Mglichkeit des wahlfreien Zugriffs auf Dateien. Dabei wird eine Datei nicht als Folge von
Bytes angesehen, die nur sequentiell durchlaufen werden kann, sondern als eine Art externes
Array, das an beliebiger Stelle gelesen oder beschrieben werden kann.
Fr den Zugriff auf Random-Access-Dateien stellt das Paket java.io die Klasse
RandomAccessFile zur Verfgung. Anders als bei der stream-orientierten Ein-/Ausgabe
kann dabei allerdings nur auf "echte Dateien" zugegriffen werden; die Verwendung von Arrays
oder Pipes als Dateiersatz wird nicht untersttzt. Auch das bei Streams realisierte Filterkonzept
ist in Random-Access-Dateien nicht zu finden.
Die Klasse RandomAccessFile stellt Methoden zum Anlegen neuer Dateien und zum
ffnen vorhandener Dateien zur Verfgung. Fr den lesenden oder schreibenden Zugriff auf die
Datei gibt es hnliche Methoden wie bei der sequentiellen Dateiverarbeitung. Zustzlich kann
der Satzzeiger wahlfrei positioniert werden, und es ist mglich, seine aktuelle Position
abzufragen.
Das ffnen von Random-Access-Dateien erfolgt mit dem Konstruktor der Klasse
RandomAccessFile, der in zwei verschiedenen Varianten zur Verfgung steht:
public RandomAccessFile(File file, String mode)
throws IOException
java.io.RandomAccessFile
Bei der bergabe des String-Parameters name wird die Datei mit diesem Namen geffnet
und steht fr nachfolgende Schreib- und Lesezugriffe zur Verfgung. Bei der bergabe eines
File-Objekts wird die durch dieses Objekt spezifizierte Datei geffnet (die Klasse File wird
in Kapitel 21 besprochen). Der zweite Parameter mode gibt die Art des Zugriffs auf die Datei
an. Er kann entweder "r" sein, um die Datei nur zum Lesen zu ffnen, oder "rw", um sie zum
Schreiben und zum Lesen zu ffnen. Ein reiner Schreibmodus, wie er beispielsweise unter
Titel
<<
>
>>
DOC
API
java.io.RandomAccessFile
getFilePointer liefert die aktuelle Position des Satzzeigers; das erste Byte einer
Datei steht dabei an Position 0. Da der Rckgabewert vom Typ long ist, untersttzt
Java den Zugriff auf Dateien, die grer als 2 GByte sind, sofern es das
Betriebssystem zult.
Das Positionieren des Satzzeigers erfolgt mit der Methode seek an die durch pos
angegebene Stelle. Anders als in C bezieht sich der Wert von pos dabei immer auf
den Anfang der Datei. Das Positionieren relativ zur aktuellen Position kann mit der
Methode skipBytes erledigt werden. Die neue Position wird dabei aus der
aktuellen Position plus dem Inhalt des Parameters n berechnet. Auch negative Werte
fr n sind dabei erlaubt und bewirken eine Rckwrtsverschiebung des Satzzeigers.
Es ist erlaubt, den Satzzeiger hinter das derzeitige Ende der Datei zu
verschieben. Allerdings wird dadurch ihre tatschliche Lnge nicht
verndert. Dies geschieht erst, wenn an der angesprungenen Position
tatschlich Zeichen geschrieben werden.
Hinweis
Mit length kann die derzeitige Lnge der Datei bestimmt werden. Nach dem
ffnen einer bestehenden Datei liefert diese Methode deren initiale Lnge.
Schreibzugriffe am Ende der Datei erhhen diesen Wert. Mit setLength kann die
Lnge der Datei verndert werden. Ist der als Argument bergebene Wert kleiner als
die bisherige Lnge, wird die Datei verkrzt. Ein eventuell dahinter stehender
Dateizeiger wird auf das neue Ende der Datei gesetzt. Ist das Argument grer als die
bisherige Lnge, wird die Datei vergrert. Der Wert der neu eingefgten Bytes
bleibt allerdings undefiniert, bis sie explizit beschrieben werden.
<
>
>>
20.5 Zusammenfassung
20.5 Zusammenfassung
<
>
>>
Im Paket java.io gibt es eine Klasse File, die als Abstraktion einer Datei oder
eines Verzeichnisses angesehen werden kann. File kann sowohl absolute als auch
relative Namen unter UNIX und DOS/Windows (UNC und Laufwerksbuchstabe)
reprsentieren. Im Gegensatz zu den bisher besprochenen Klassen, die sich mit dem
Zugriff auf den Inhalt einer Datei beschftigten, spielt dieser in der Klasse File
keine Rolle. Statt dessen abstrahiert sie den Namen und Zugriffspfad einer Datei und
die im Verzeichniseintrag zur ihr gespeicherten Eigenschaften.
Die Klasse File besitzt drei Konstruktoren:
public File(String pathname)
java.io.File
Wird lediglich der String pathname bergeben, so wird ein File-Objekt zu dem
angegebenen Datei- oder Verzeichnisnamen konstruiert. Alternativ kann der zweite
Konstruktor verwendet werden, wenn Verzeichnis- und Dateiname getrennt
bergeben werden sollen. Eine hnliche Funktion bernimmt auch der dritte
Konstruktor. Hier wird jedoch der bergebene Verzeichnisname als File-Objekt zur
Verfgung gestellt.
Bei der Konstruktion von Datei- und Verzeichnisnamen unter MS-DOS
ist zu beachten, da der Separator (Backslash) gleichzeitig EscapeZeichen fr Strings ist und daher in Verzeichnis- oder Dateiliteralen
doppelt angegeben werden mu (siehe Abschnitt 4.2.2).
Warnung
File("TestFile.java");
File("c:\\arc\\doku\\javacafe\\kap01.doc");
File(".", "TestFile.java");
File("c:\\config.sys");
<
>
>>
Nachdem ein File-Objekt konstruiert ist, knnen die Methoden zum Zugriff auf die
einzelnen Bestandteile des Dateinamens aufgerufen werden:
public String getName()
java.io.File
getName liefert den Namen der Datei oder des Verzeichnisses; eventuelle
Verzeichnisinformationen sind darin nicht enthalten. getPath liefert den
kompletten Namen inklusive darin enthaltener Verzeichnisinformationen.
Mit getAbsolutePath kann der absolute Pfadname fr das File-Objekt
ermittelt werden. Wurde das File-Objekt mit Hilfe eines absoluten Pfadnamens
konstruiert, liefert getAbsolutePath genau diesen Namen. Wurde es dagegen
mit einem relativen Namen konstruiert, so stellt getAbsolutePath den Namen
des aktuellen Verzeichnisses vor den Namen. Namensbestandteile der Art . und ..
werden dabei allerdings nicht eliminiert, und es knnen leicht Pfadangaben wie
C:\ARC\DOKU\javacafe\examples\.\TestFile.java entstehen, die in der Mitte einen
oder mehrere Punkte enthalten.
Schlielich gibt es noch die Methode getParent, die den Namen des
Vaterverzeichnisses ermittelt. Steht das File-Objekt fr eine Datei, ist dies der
Name des Verzeichnisses, in dem die Datei liegt. Handelt es sich um ein Verzeichnis,
wird der Name des darber liegenden Verzeichnisses geliefert. Gibt es kein
Vaterverzeichnis, liefert getParent den Rckgabewert null.
<
>
>>
21.6 Zusammenfassung
21.6 Zusammenfassung
<
>
>>
Kaum eine wichtige Programmiersprache der letzten Jahre hat das Konzept der
Nebenlufigkeit innerhalb der Sprache implementiert. Mit Nebenlufigkeit
bezeichnet man die Fhigkeit eines Systems, zwei oder mehr Vorgnge gleichzeitig
oder quasi-gleichzeitig ausfhren zu knnen. Lediglich ADA stellt sowohl parallele
Prozesse als auch Mechanismen zur Kommunikation und Synchronisation zur
Verfgung, die direkt in die Sprache eingebettet sind. Durch Weiterentwicklungen im
Bereich der Betriebssystemtechnologie wurde allerdings das Konzept der Threads
immer populrer und auf der Basis von Library-Routinen auch konventionellen
Programmiersprachen zur Verfgung gestellt.
Java hat Threads direkt in die Sprache integriert und mit den erforderlichen
Hilfsmitteln als Konstrukt zur Realisierung der Nebenlufigkeit implementiert. Ein
Thread ist ein eigenstndiges Programmfragment, das parallel zu anderen Threads
laufen kann. Ein Thread hnelt damit einem Proze, arbeitet aber auf einer feineren
Ebene. Whrend ein Proze das Instrument zur Ausfhrung eines kompletten
Programms ist, knnen innerhalb dieses Prozesses mehrere Threads parallel laufen.
Der Laufzeit-Overhead zur Erzeugung und Verwaltung eines Threads ist relativ
gering und kann in den meisten Programmen vernachlssigt werden. Ein wichtiger
Unterschied zwischen Threads und Prozessen ist der, da alle Threads eines
Programmes sich einen gemeinsamen Adreraum teilen, also auf dieselben Variablen
zugreifen, whrend die Adrerume unterschiedlicher Prozesse streng voneinander
getrennt sind.
Die Implementierung von Threads war eine explizite Anforderung an das Design der
Sprache. Threads sollen unter anderem die Implementierung grafischer
Anwendungen erleichtern, die durch Simulationen komplexer Ablufe oft inhrent
nebenlufig sind. Threads knnen auch dazu verwendet werden, die Bedienbarkeit
von Dialoganwendungen zu verbessern, indem rechenintensive Anwendungen im
Hintergrund ablaufen.
Threads werden in Java durch die Klasse Thread und das Interface Runnable
implementiert. In beiden Fllen wird der Thread-Body, also der parallel
auszufhrende Code, in Form der berlagerten Methode run zur Verfgung gestellt.
Die Kommunikation kann dann durch Zugriff auf die Instanz- oder Klassenvariablen
oder durch Aufruf beliebiger Methoden, die innerhalb von run sichtbar sind,
erfolgen. Zur Synchronisation stellt Java das aus der Betriebssystemtheorie bekannte
Konzept des Monitors zur Verfgung, das es erlaubt, kritische Abschnitte innerhalb
korrekt geklammerter Programmfragmente und Methoden zu kapseln und so den
Zugriff auf gemeinsam benutzte Datenstrukturen zu koordinieren.
Darber hinaus bietet Java Funktionen zur Verwaltung von Threads. Diese erlauben
es, Threads in Gruppen zusammenzufassen, zu priorisieren und Informationen ber
Eigenschaften von Threads zu gewinnen. Das Scheduling kann dabei wahlweise
unterbrechend oder nichtunterbrechend implementiert sein. Die Sprachspezifikation
legt dies nicht endgltig fest, aber in den meisten Java-Implementierungen wird dies
von den Mglichkeiten des darunter liegenden Betriebssystems abhngen.
<
>
>>
Threads knnen in Java nicht nur ausgefhrt und synchronisiert werden, sondern
besitzen auch eine Reihe administrativer Eigenschaften, die besonders dann ntzlich
sind, wenn das Thread-Konzept stark genutzt wird. Diese administrativen
Eigenschaften lassen sich in zwei Gruppen unterteilen. Zum einen gibt es
Eigenschaften, die bei den Threads selbst zu finden sind, beispielsweise die Prioritt
oder der Name eines Threads. Zum anderen gibt es Eigenschaften, die darauf
begrndet sind, da jeder Thread in Java zu einer Thread-Gruppe gehrt, die
untereinander ber Vater-Kind-Beziehungen miteinander in Verbindung stehen.
Beide Gruppen von Eigenschaften sollen im folgenden kurz erklrt werden.
java.lang.Thread
Mit Hilfe der Methode getName kann der Name eines Threads abgefragt werden:
public String getName()
java.lang.Thread
Neben einem Namen besitzt ein Thread auch eine Prioritt. Die Prioritt steuert den
Scheduler in der Weise, da bei Vorhandensein mehrerer bereiter Threads diejenigen
mit hherer Prioritt vor denen mit niedrigerer Prioritt ausgefhrt werden. Beim
Anlegen eines neuen Threads bekommt dieser die Prioritt des aktuellen Threads.
Mit Hilfe der Methode setPriority kann die Prioritt gesetzt und mit
getPriority abgefragt werden:
public void setPriority(int newPriority)
java.lang.Thread
Als Parameter kann an setPriority ein Wert bergeben werden, der zwischen
den Konstanten Thread.MIN_PRIORITY und Thread.MAX_PRIORITY liegt.
Der Normalwert wird durch die Konstante Thread.NORM_PRIORITY festgelegt.
In der aktuellen Version des JDK haben diese Konstanten die Werte 1, 5 und 10.
22.5.2 Thread-Gruppen
Thread-Gruppen dienen dazu, Informationen zu verwalten, die nicht nur fr einen
einzelnen Thread von Bedeutung sind, sondern fr eine ganze Gruppe. Weiterhin
bieten sie Methoden, die auf alle Threads einer Gruppe angewendet werden knnen,
und sie erlauben es, die Threads einer Gruppe aufzuzhlen. In der JavaKlassenbibliothek gibt es zu diesem Zweck eine eigene Klasse ThreadGroup.
Der Einstieg zur Anwendung von Objekten der Klasse ThreadGroup ist die
Methode getThreadGroup der Klasse Thread. Sie liefert das ThreadGroupObjekt, dem der aktuelle Thread angehrt. Alle Thread-Gruppen sind ber eine
Vater-Kind-Beziehung miteinander verkettet. Mit Hilfe der Methode getParent
von ThreadGroup kann die Vater-Thread-Gruppe ermittelt werden:
public ThreadGroup getParent()
java.lang.ThreadGroup
werden:
public int enumerate(Thread[] list)
java.lang.ThreadGroup
enumerate erwartet ein Array von Threads, das beim Aufruf mit allen Threads, die
zu dieser Thread-Gruppe oder einer ihrer Untergruppen gehren, gefllt wird. Das
Array sollte vor dem Aufruf bereits auf die erforderliche Gre dimensioniert
werden. Als Anhaltspunkt kann dabei das Ergebnis der Methode activeCount
dienen, die die Anzahl der aktiven Threads in der aktuellen Thread-Gruppe und allen
Thread-Gruppen, die die aktuelle Thread-Gruppe als Vorfahr haben, angibt:
public int activeCount()
<
>
>>
java.lang.ThreadGroup
22.6 Zusammenfassung
22.6 Zusammenfassung
<
>
>>
Leser, die dennoch ausschlielich Swing lernen wollen und das AWT als
unntigen Ballast ansehen, brauchen die Kapitel 30 und 32 lediglich kurz
anzusehen. Das dazwischen liegende Kapitel 31 sollte dagegen nicht
bersprungen werden, denn es beschreibt die Arbeitsweise und Anwendung
der Layoutmanager. Die brigen Kapitel in diesem Teil des Buchs behandeln
Themen, die in beiden Welten von Bedeutung sind.
<
>
>>
Tip
23.5 Zusammenfassung
23.5 Zusammenfassung
<
>
>>
24.4 Zusammenfassung
24.4 Zusammenfassung
<
>
>>
Farben werden in Java durch die Klasse Color reprsentiert. Jedes Color-Objekt
reprsentiert dabei eine Farbe, die durch ihre RGB-Werte eindeutig bestimmt ist.
Farben knnen durch Instanzieren eines Color-Objekts und bergabe des
gewnschten RGB-Wertes an den Konstruktor erzeugt werden:
public Color(int r, int g, int b)
java.awt.Color
Der Konstruktor mit den int-Parametern erwartet dabei die Farbwerte als
Ganzzahlen im Bereich von 0 bis 255. Alternativ dazu knnen die Farbanteile auch
als Fliekommazahlen bergeben werden. In diesem Fall mu jeder der Werte im
Bereich von 0.0 bis 1.0 liegen. 0.0 entspricht dem vlligen Fehlen dieses Farbanteils
und 1.0 der maximalen Intensitt (entsprechend dem ganzzahligen Farbwert 255).
Alternativ stellt die Klasse Color eine Reihe von statischen Color-Objekten zur
Verfgung, die direkt verwendet werden knnen:
public
public
public
public
public
public
public
public
public
public
public
public
public
static
static
static
static
static
static
static
static
static
static
static
static
static
Color
Color
Color
Color
Color
Color
Color
Color
Color
Color
Color
Color
Color
white
lightGray
gray
darkGray
black
red
blue
green
yellow
magenta
cyan
orange
pink
java.awt.Color
Um von einem bestehenden Farbobjekt die RGB-Werte zu ermitteln, stellt die Klasse
Color die Methoden getRed, getGreen und getBlue zur Verfgung:
public int getRed()
public int getGreen()
public int getBlue()
<
>
>>
java.awt.Color
25.5 Zusammenfassung
25.5 Zusammenfassung
Das RGB-Farbmodell
Gebruchliche Farbwerte und ihre RGB-Codes
Erzeugen von Farben mit der Klasse Color
Zugriff auf die Ausgabefarbe des Gerte-Kontexts durch Aufruf von
setColor und getColor
Verwendung der Systemfarben
<
>
>>
26.1 Einleitung
26.1 Einleitung
Das Ausdrucken von Texten und Grafiken war traditionell eine der Achillesfersen
von Java. Im JDK 1.0 waren die Verhltnisse noch klar: hier konnte berhaupt nicht
gedruckt werden! Es gab schlicht und einfach keine Klassen, die das Drucken
untersttzten. Nachdem die Java-Entwicklergemeinde bereits begonnen hatte,
proprietre Erweiterungen zu entwickeln, wurde mit der Version 1.1 ein einfaches
Druck-API in das JDK aufgenommen.
Leider war dessen Design recht unvollstndig, und das API war voll von Schwchen
und Restriktionen. Mit dem JDK 1.2 wurde ein neuer Entwurf vorgestellt, der die
Schwchen des vorigen beheben sollte. Tatschlich hatte er ein besseres Design, und
viele der Anforderungen, die zuvor unerfllt blieben, wurden jetzt erfllt.
Unglcklicherweise war die Implementierung des 1.2er-APIs wenig performant. In
der Praxis traten - selbst bei einfachen Druckjobs - mitunter Spooldateien von 20 MB
oder mehr auf, und der Ausdruck solcher Dateien dauerte nicht selten lnger als 10
Minuten. Erst mit dem JDK 1.3 wurden diese Probleme behoben, und es scheint, da
nunmehr mit einer gewissen Zuverlssigkeit und Performance gedruckt werden kann.
Wir werden in diesem Kapitel die beiden unterschiedlichen Druck-APIs vorstellen
und Beispiele fr ihre Anwendung geben. Fr den Fall, das beide ungeeignet sind,
werden wir zustzlich zeigen, wie mit Hilfe des Java Communications API direkt auf
eine serielle oder paralle Schnittstelle zugegriffen werden kann, und werden so die
Grundlagen fr die Entwicklung eigener Druckertreiber legen.
<<
<
>
>>
API
26.5 Zusammenfassung
26.5 Zusammenfassung
<
>
>>
27.6 Zusammenfassung
27.6 Zusammenfassung
<
>
>>
28.3 Zusammenfassung
28.3 Zusammenfassung
<
>
>>
29.7 Zusammenfassung
29.7 Zusammenfassung
<
>
>>
30.1 Grundlagen
30.1 Grundlagen
<
>
>>
30.2 Menleiste
30.2 Menleiste
Eine Menleiste stellt das Hauptmen eines Fensters dar. Sie befindet sich unterhalb
der Titelleiste am oberen Rand des Fensters und zeigt die Namen der darin
enthaltenen Mens an. Eine Menleiste wird durch Instanzieren der Klasse
MenuBar erzeugt:
public MenuBar()
java.awt.MenuBar
Der parameterlose Konstruktor erzeugt eine leere Menleiste, in die dann durch
Aufruf der Methode add Mens eingefgt werden knnen:
public void add(Menu m)
java.awt.MenuBar
Zum Entfernen eines bestehenden Mens kann die Methode remove verwendet
werden. Zur Auswahl des zu entfernenden Mens kann dabei entweder das Men
oder der Index desselben als Parameter bergeben werden:
public void remove(MenuComponent m)
public void remove(int index)
java.awt.MenuBar
Mit getMenu stellt die Klasse MenuBar eine Methode zum Zugriff auf ein
beliebiges Men der Menleiste zur Verfgung. getMenu liefert dabei das
Menobjekt, das sich an der Position mit dem angegebenen Index befindet:
java.awt.MenuBar
Um eine Menleiste an ein Fenster zu binden, besitzt die Klasse Frame eine
Methode setMenuBar:
public void setMenuBar(MenuBar mb)
java.awt.Frame
Durch Aufruf dieser Methode wird die angegebene Menleiste im Fenster angezeigt,
und beim Auswhlen eines Menpunkts werden Nachrichten ausgelst und an das
Fenster gesendet. Die Fensterklasse kann diese Nachrichten durch das Registrieren
eines Objekts vom Typ ActionListener bearbeiten. Wir gehen darauf spter
genauer ein.
<
>
>>
30.3 Mens
30.3 Mens
Die Mens bilden die Bestandteile einer Menleiste. Sie werden in Java durch
Instanzen der Klasse Menu reprsentiert. Im einfachsten Fall erwartet der
Konstruktor von Menu einen String-Parameter, der den Namen des Mens angibt:
public Menu(String label)
java.awt.Menu
Dieser Name wird verwendet, um das Men in der Menleiste zu verankern. hnlich
wie bei der Menleiste stehen auch bei einem Men die Methoden add und remove
zur Verfgung. Im Gegensatz zu MenuBar bearbeiten sie allerdings keine Mens,
sondern Meneintrge:
public void add(MenuItem mi)
public void add(String label)
java.awt.Menu
Die Methode remove kann entweder mit einem numerischen Index oder mit einem
MenuItem als Parameter aufgerufen werden, um den zu entfernenden Meneintrag
zu identifizieren. Wird ein numerischer Index verwendet, so beginnt die Zhlung des
ersten Elements bei 0.
add steht ebenfalls in zwei Varianten zur Verfgung. Bei der ersten mu ein Objekt
der Klasse MenuItem bergeben werden. Die zweite erwartet lediglich einen
String, der den Mennamen bezeichnet. Sie generiert automatisch eine
java.awt.Menu
java.awt.Menu
<
>
>>
30.8 Zusammenfassung
30.8 Zusammenfassung
<
>
>>
31.4 Zusammenfassung
31.4 Zusammenfassung
<
>
>>
Titel
<<
>
>>
API
32.3 Button
32.3 Button
Ein Button ist eine beschriftete Schaltflche, die dazu verwendet wird, auf
Knopfdruck des Anwenders Aktionen in der Fensterklasse auszulsen.
public Button()
java.awt.Button
Der parameterlose Konstruktor erzeugt einen Button ohne Text. blicherweise wird
ein Button gleich bei der Konstruktion beschriftet, was durch bergabe eines
String-Objekts erreicht werden kann.
public void setLabel(String label)
java.awt.Button
setLabel und getLabel erlauben den Zugriff auf die Beschriftung des Buttons.
Wird ein Button gedrckt, so sendet er ein Action-Event an seine Ereignisempfnger.
Diese mssen das Interface ActionListener implementieren und sich durch Aufruf
von addActionListener registrieren:
public void addActionListener(ActionListener l)
java.awt.Button
java.awt.Button
>
>>
Hinweis
32.12 Zusammenfassung
32.12 Zusammenfassung
<
>
>>
Bisher haben wir uns nur mit vordefinierten Dialogelementen beschftigt. Java bietet
aber auch die Mglichkeit, Dialogelemente selbst zu definieren und wie eine
vordefinierte Komponente zu verwenden. In der Java-Beans-Spezifikation wurden
die dazu erforderlichen Techniken beschrieben und die zugrunde liegenden Konzepte
definiert. Wir wollen uns an dieser Stelle zunchst mit den Grundlagen der
Entwicklung eigener Dialogelemente auf der Basis der Klasse Canvas befassen, wie
sie mit dem SystemColorViewer in Abschnitt 32.11 angedeutet werden. Eine
ausfhrliche Beschreibung von Java-Beans findet sich in Kapitel 44.
Ein Canvas ist ein frei definierbares Dialogelement, das in der Grundversion
praktisch keinerlei Funktionalitt zur Verfgung stellt. Damit ein Canvas etwas
Sinnvolles tun kann, mu daraus eine eigene Klasse abgeleitet werden, und in dieser
mssen die Methode paint und die Methoden zur Reaktion auf Nachrichten
berlagert werden.
public Canvas()
java.awt.Canvas
java.awt.Canvas
Die Standardversion von paint zeichnet nur die Ausgabeflche in der aktuellen
Hintergrundfarbe. Eine berlagernde Version kann hier natrlich ein beliebig
komplexes Darstellungsverhalten realisieren. Der Punkt (0,0) des bergebenen
<
>
>>
33.4 Zusammenfassung
33.4 Zusammenfassung
<
>
>>
34.3 Zusammenfassung
34.3 Zusammenfassung
<
>
>>
35.1.1 Einleitung
Bereits kurze Zeit nachdem mit dem JDK 1.0 das Abstract Windowing Toolkit
eingefhrt wurde, begannen die Entwickler bei SUN, ber Mglichkeiten
nachzudenken, die graphische Oberflche von Java-Anwendungen zu verbessern. Als
nachteilig sahen sie vor allem folgende Eigenschaften des AWT an:
Erstens fallen die plattformspezifischen Besonderheiten weg, und der Code zur
Implementierung der Dialogelemente vereinfacht sich deutlich.
Zweitens entfallen auch die Unterschiede in der Bedienung. Der Anwender
findet auf allen Betriebssystemen dasselbe Aussehen und dieselbe Bedienung
vor.
Drittens ist Swing nicht mehr darauf angewiesen, Features auf der Basis des
kleinsten gemeinsamen Nenners aller untersttzten Plattformen zu realisieren.
Komplexe Dialogelemente wie Bume, Tabellen oder Registerkarten knnen
plattformunabhngig realisiert werden und stehen nach der Implementierung
ohne Portierungsaufwand auf allen Systemen zur Verfgung. Tatschlich stellt
Swing eine sehr viel umfassendere und anspruchsvollere Menge an
Dialogelementen als das AWT zur Verfgung.
Der Grundstein fr Komponenten, die von Java selbst erzeugt und dargestellt werden
knnen, wurde im JDK 1.1 mit der Einfhrung der Lightweight Components
(leichtgewichtige Komponenten) gelegt. Dabei wird die paint-Methode eines
Component-Objects nicht mehr an die betriebssystemspezifische Klasse
weitergeleitet, sondern in den Komponentenklassen berlagert und mit Hilfe
grafischer Primitivoperationen selbst implementiert. Die im AWT vorhandenen
Dialogelemente werden im Gegensatz dazu als Heavyweight Components bezeichnet
(schwergewichtige Komponenten).
Mit Hilfe der leichtgewichtigen Komponenten, die fr alle Aspekte der Darstellung
Das Modell enthlt die Daten des Dialogelements und speichert seinen
Zustand.
Der View ist fr die grafische Darstellung der Komponente verantwortlich.
Der Controller wirkt als Verbindungsglied zwischen beiden. Er empfngt
Tastatur- und Mausereignisse und stt die erforderlichen Manahmen zur
nderung von Model und View an.
Das Modell enthlt praktisch die gesamte Verarbeitungslogik der Komponente. Ein
wichtiger Aspekt ist dabei, dass ein Model mehrere Views gleichzeitig haben kann.
Damit Vernderungen des Modells in allen Views sichtbar werden, wird ein
Benachrichtigungsmechanismus implementiert, mit dem das Modell die zugeordneten
Views ber nderungen informiert. Diese Vorgehensweise entspricht dem ObserverPattern, wie es in Abschnitt 10.3.9 erlutert wurde.
Bei den Swing-Dialogelementen wird eine vereinfachte Variante von MVC
verwendet, die auch als Model-Delegate-Prinzip bezeichnet wird. Hierbei wird die
Funktionalitt von View und Controller in einem UI Delegate zusammengefat.
Dadurch wird einerseits die Komplexitt reduziert (oft ist der Controller so einfach
strukturiert, da es sich nicht lohnt, ihn separat zu betrachten) und andererseits die in
der Praxis mitunter unhandliche Trennung zwischen View und Controller
aufgehoben. Es kann allerdings sein, da ein Dialogelement mehr als ein Model
besitzt. So haben beispielsweise Listen und Tabellen neben ihrem eigentlichen
Datenmodell ein weiteres, das nur fr die Selektion von Datenelementen zustndig
ist.
Aber...
Natrlich wre es zu schn, wenn die geschilderten Eigenschaften frei von
"Nebenwirkungen" wren. Dem ist (natrlich) nicht so. Neben den vielen Vorteilen
bringt die Verwendung von Swing auch Probleme mit sich:
wie das Look-and-Feel des Programms zur Laufzeit umgeschaltet werden kann.
Anschlieend geben wir einige Tips zur Umstellung von AWT-Anwendungen auf
Swing und zeigen wichtige Gemeinsamkeiten und Unterschiede zwischen den beiden
Welten auf.
<
>
>>
35.3 Zusammenfassung
35.3 Zusammenfassung
Einschrnkungen des AWT, die zur Entwicklung von Swing gefhrt haben
Das Konzept der leichtgewichtigen Komponenten und ihre Vorteile
Pluggable Look-and-Feels
Das Model-View-Controller-Prinzip
Elemente einer einfachen Swing-Anwendung:
Die Container-Klassen JFrame und JPanel
Die Klassen JLabel und JTextField
Die Verwendung von Tooltips und Umrandungen
Die Klasse JList
Automatisches Scrollen mit der Klasse JScrollPane
Die Klasse JButton
Umschalten des Look-and-Feel
<
>
>>
36.4 Zusammenfassung
36.4 Zusammenfassung
<<
<
>
>>
API
37.5 Zusammenfassung
37.5 Zusammenfassung
<<
<
>
>>
API
38.4 Zusammenfassung
38.4 Zusammenfassung
<<
<
>
>>
API
39.5 Zusammenfassung
39.5 Zusammenfassung
<
>
>>
40.4.1 Funktionsweise
Obwohl Applets einer der Hauptgrnde fr das groe Interesse an Java waren und mit
Netscape 2.0 bereits frh eine weitverbreitete Plattform fr JDK-1.0-Applets zur
Verfgung stand, stellte sich bei vielen Entwicklern bald eine gewisse Erncherung
ein. Bereits zur Version 1.1 des JDK hatten die Browser-Hersteller groe Probleme,
Schritt zu halten. Whrend Netscape den Applet-Entwicklern mit Qualittsproblemen
die frhen 4er Versionen des Communicators verhagelte, wurden beim Internet
Explorer aus unternehmenspolitischen Grnden bestimmte JDK-1.1-Eigenschaften
gar nicht erst zur Verfgung gestellt.
Selbst heute noch werden Applets nicht selten mit dem JDK 1.0 entwickelt, um
mglichen Kompatibilittsproblemen aus dem Weg zu gehen. Von einer echten
Untersttzung fr die neueren JDKs sind die Browser noch weit entfernt.
Glcklicherweise hat SUN diese Probleme erkannt und mit dem Java-Plugin eine
eigene Browser-Erweiterung zum Ausfhren von Applets entwickelt. Diese luft
sowohl mit dem Communicator als auch mit dem Internet Explorer und wird
zusammen mit dem SUN-JRE bzw. -JDK installiert. Entsprechend prparierte
Applets laufen dann nicht mehr unter den Browser-eigenen Java-Implementierungen,
sondern verwenden die VM und die Laufzeitumgebung der Original-SUNImplementierung. Der Browser stellt nur noch das Fenster zur Bildschirmausgabe zur
Verfgung. Auf diese Weise knnen Applets alle Eigenschaften der aktuellen JavaVersionen verwenden (Collections, Java-2D, Swing usw.).
Allerdings hat die Sache einen kleinen Haken. Um das Plugin zum Ausfhren eines
Applets zu bewegen, darf dieses im HTML-Quelltext nmlich nicht mehr innerhalb
eines APPLET-Tags stehen, sondern mu mit dem EMBED- bzw. OBJECT-Tag
eingebunden werden. Zwar stellt SUN ein automatisches Werkzeug zur
Konvertierung von Web-Seiten zur Verfgung, aber das kann natrlich nur auf Seiten
angewendet werden, deren Quelltext modifizierbar ist. Whrend beispielsweise
unternehmensinterne Web-Seiten im Intranet entsprechend prpariert werden knnen,
werden aus dem Internet geladene HTML-Seiten mit eingebetteten Applets nach wie
vor von der im Browser eingebauten Java-Implementierung ausgefhrt.
Des weiteren mu natrlich auf dem Client ein geeignetes JRE inklusive des Plugins
installiert sein, damit die modifizierten Applets ausgefhrt werden knnen. Zwar
knnen die HTML-Seiten so aufgebaut werden, da beim Fehlen des Plugins ein
Verweis auf eine Download-Seite angezeigt wird. In diesem Fall mu das JRE dann
aber erst vom Anwender auf dem Client installiert werden, was aus verschiedenen
Grnden problematisch sein kann (fehlende Kenntnisse des Anwenders, Programme
drfen nur vom Administrator installiert werden, Sicherheitsbedenken usw.) Einer der
Hauptvorteile von Applets, die "installationsfreie" Ausfhrbarkeit von Programmen,
ist verloren.
Dies mgen Grnde dafr sein, da sich das Plugin noch nicht fr Internet-Applets
durchsetzen konnte. Fr Intranet-Installationen, bei denen die Ausstattung der Clients
kontrollierbar ist, kann es aber eine echte Alternative gegenber dem Betrieb von
autonomen Java-Applikationen sein. Wir wollen im nchsten Abschnitt die Schritte
aufzeigen, die erforderlich sind, um ein Applet mit dem Plugin auszufhren.
Seit der Version 1.4 des JDK knnen bestimmte Web-Browser dazu
JDK1.1-1.4
gebracht werden, auch bei einem normalen APPLET-Tag auf die
Runtime der Version 1.4 zurckzugreifen, statt auf die eingebaute JavaImplementierung. Dies gilt im wesentlichen fr die 4er-, 5er- und 6erVersionen des Internet Explorers sowie fr die 6.0er- und 6.1erVersionen des Netscape Communicators. Ein entsprechendes
Verhalten der Browser kann entweder bei der Installation des JDK
festgelegt werden (siehe Abschnitt 2.1.2), oder auch nachtrglich ber
den Eintrag Java-Plug-in 1.4.0 in der Systemsteuerung.
<
>
>>
40.5 Zusammenfassung
40.5 Zusammenfassung
Das Konzept der Uniform Resource Locators, die Klasse URL und der Aufruf
eines anderen Dokuments mit showDocument
Der Applet-Kontext eines Applets
Kommunikation unterschiedlicher Applets auf einer HTML-Seite
Umwandlung einer Applikation in ein Applet
Anwendungsmglichkeiten, Installation und Verwendung des Java-Plugins
<
>
>>
41.4 Zusammenfassung
41.4 Zusammenfassung
<
>
>>
42.1 Einleitung
42.1 Einleitung
42.1.1 Grundstzliche Arbeitsweise
42.1.2 Die Architektur von JDBC
Treibertypen
Mehrstufige Client-Server-Architekturen
SQL-2 Entry Level
Wir wollen uns in diesem Abschnitt zunchst mit der grundstzlichen Architektur
von JDBC und datenbankbasierten Java-Anwendungen vertraut machen.
Anschlieend werden die wichtigsten Bestandteile der JDBC-Schnittstelle vorgestellt
und ihre jeweilige Funktionsweise kurz erlutert. Der nchste Abschnitt illustriert
und verfeinert die Konzepte dann an einem praktischen Beispiel, bei dem eine
Datenbank zur Speicherung von Verzeichnissen und Dateien entworfen, mit Daten
gefllt und abgefragt wird. Zum Abschlu werden wir einige spezielle Probleme
besprechen und auf die Besonderheiten gebruchlicher JDBC-Treiber eingehen.
Dieses Kapitel ist typisch fr die weiterfhrenden Themen in diesem Buch. JDBC ist
ein sehr umfassendes Gebiet, das auf den zur Verfgung stehenden Seiten nicht
vollstndig behandelt werden kann. Wir verfolgen statt dessen einen pragmatischen
Ansatz, bei dem wichtige Grundlagen erlutert werden. Mit Hilfe von Beispielen
wird ihre praktische Anwendbarkeit demonstriert. Insgesamt mssen aber viele
Fragen offen bleiben, die durch die Lektre weiterer Dokumentationen geschlossen
werden knnen. Dazu zhlen beispielsweise Trigger, Blobs und Stored Procedures,
die Erweiterungen in JDBC 2.0 oder die Entwicklung eigener JDBC-Treiber.
verfolgten die Designer dabei das primre Ziel, die groe Zahl vorhandener SQLDatenbanken problemlos anzubinden. In konzeptioneller Anlehnung an die
weitverbreitete ODBC-Schnittstelle wurde daraufhin mit JDBC (Java Database
Connectivity) ein standardisiertes Java-Datenbank-Interface entwickelt, das mit der
Version 1.1 fester Bestandteil des JDK wurde.
JDBC stellt ein Call-Level-Interface zur SQL-Datenbank dar. Bei einer solchen
Schnittstelle werden die SQL-Anweisungen im Programm als Zeichenketten
bearbeitet und zur Ausfhrung an parametrisierbare Methoden bergeben.
Rckgabewerte und Ergebnismengen werden durch Methodenaufrufe ermittelt und
nach einer geeigneten Typkonvertierung im Programm weiterverarbeitet.
Dem gegenber steht ein zweites Verfahren, das als Embedded SQL (ESQL)
bezeichnet wird. Hierbei werden die SQL-Anweisungen mit besonderen
Schlsselwrtern direkt in den Java-Quelltext eingebettet, und die Kommunikation
mit dem Java-Programm erfolgt durch speziell deklarierte Host-Variablen. Damit der
Java-Compiler durch die eingebetteten SQL-Anweisungen nicht
durcheinandergebracht wird, mssen sie zunchst von einem Prprozessor in
geeigneten Java-Code bersetzt werden. Whrend Embedded-SQL insbesondere bei
Datenbankanwendungen, die in C oder C++ geschrieben sind, sehr verbreitet ist,
spielt es in Java praktisch keine Rolle und konnte sich gegenber JDBC nicht
durchsetzen.
Steht bereits ein ODBC-Treiber zur Verfgung, so kann er mit Hilfe der im
Lieferumfang enthaltenen JDBC-ODBC-Bridge in Java-Programmen
verwendet werden. Diese Konstruktion bezeichnet man als Typ-1-Treiber. Mit
seiner Hilfe knnen alle Datenquellen, fr die ein ODBC-Treiber existiert, in
Java-Programmen genutzt werden.
Zu vielen Datenbanken gibt es neben ODBC-Treibern auch spezielle Treiber
des jeweiligen Datenbankherstellers. Setzt ein JDBC-Treiber auf einem
solchen proprietren Treiber auf, bezeichnet man ihn als Typ-2-Treiber.
Wenn ein JDBC-Treiber komplett in Java geschrieben und auf dem Client
keine spezielle Installation erforderlich ist, der Treiber zur Kommunikation
Mehrstufige Client-Server-Architekturen
Whrend die Typ-1- und Typ-2-Treiber lokal installierte und konfigurierte Software
erfordern (die jeweiligen ODBC- bzw. herstellerspezifischen Treiber), ist dies bei
Typ-3- und Typ-4-Treibern normalerweise nicht der Fall. Hier knnen die zur
Anbindung an die Datenbank erforderlichen Klassendateien zusammen mit der
Applikation oder dem Applet aus dem Netz geladen und ggfs. automatisch
aktualisiert werden. Nach der Verffentlichung von JDBC gab es zunchst gar keine
Typ-3- oder Typ-4-Treiber. Mittlerweile haben sich aber alle namhaften
Datenbankhersteller zu Java bekannt und stellen auch Typ-3- oder Typ-4-Treiber zur
Verfgung. Daneben gibt es eine ganze Reihe von Fremdherstellern, die JDBCTreiber zur Anbindung bekannter Datenbanksysteme zur Verfgung stellen.
Mit JDBC knnen sowohl zwei- als auch drei- oder hherstufige Client-ServerSysteme aufgebaut werden (Multi-Tier-Architekturen). Whrend bei den
zweistufigen Systemen eine Aufteilung der Applikation in Datenbank (Server) und
Arbeitsplatz (Client) vorgenommen wird, gibt es bei den dreistufigen Systemen noch
eine weitere Schicht, die zwischen beiden Komponenten liegt. Sie wird gemeinhin als
Applikations-Server bezeichnet und dient dazu, komplexe Operationen vom
Arbeitsplatz weg zu verlagern. Der Applikations-Server ist dazu mit dem DatenbankServer verbunden und kommuniziert mit diesem ber ein standardisiertes Protokoll
(z.B. JDBC). Den Arbeitspltzen stellt er dagegen hherwertige Dienste (z.B.
komplette Business-Transaktionen) zur Verfgung und kommuniziert mit ihnen ber
ein spezielles Anwendungsprotokoll (z.B. HTTP, RMI, CORBA oder andere).
SQL-2 Entry Level
JDBC bernimmt die Aufgabe eines Transportprotokolls zwischen Datenbank und
Anwendung und definiert damit zunchst noch nicht, welche SQL-Kommandos
bertragen werden drfen und welche nicht. Tatschlich verwendet heute jede
relationale Datenbank ihren eigenen SQL-Dialekt, und eine Portierung auf eine
andere Datenbank ist nicht selten aufwendiger als ein Wechsel des Compilers.
Um einen minimalen Anspruch an Standardisierung zu gewhrleisten, fordert SUN
von den JDBC-Treiberherstellern, mindestens den SQL-2 Entry-Level-Standard von
1992 zu erfllen. Mit Hilfe einer von SUN erhltlichen Testsuite knnen die
<
>
>>
42.5 Zusammenfassung
42.5 Zusammenfassung
<
>
>>
43.1 Einleitung
43.1 Einleitung
Bei der Entwicklung des JDK 1.1 sahen sich die Entwickler mit einer Schwche von
Java konfrontiert, die die Entwicklung bestimmter Typen von Tools und
Bibliotheken unmglich machte: der statischen Struktur von Klassen und Objekten.
Um ein Objekt anzulegen, eine seiner Methoden aufzurufen oder auf eine seiner
Membervariablen zuzugreifen, mute der Code der Klasse zur Compilezeit bekannt
sein. Whrend dies fr die meisten gewhnlichen Anwendungen kein Problem
darstellt, ist es fr die Entwicklung generischer Werkzeuge und hochkonfigurierbarer
Anwendungen, die durch PlugIns erweiterbar sind, unzureichend. Insbesondere die
Entwicklung der Beans- und Serialisierungs-APIs war mit den in der Version 1.0
verfgbaren Spracheigenschaften nicht mglich.
Bentigt wurde vielmehr die Mglichkeit, Klassen zu laden und zu instanzieren
(auch mit parametrisierten Konstruktoren), ohne da bereits zur Compilezeit ihr
Name bekannt sein mute. Weiterhin sollten statische oder instanzbasierte Methoden
aufgerufen und auf Membervariablen auch dann zugegriffen werden knnen, wenn
ihr Name erst zur Laufzeit des Programmes bekannt ist.
Gesucht wurde also ein Mechanismus, der diese normalerweise vom Compiler
angeforderten Fhigkeiten des Laufzeitsystems auch "normalen" Anwendungen zur
Verfgung stellte. Mit dem Reflection-API des JDK 1.1 wurde eine LibrarySchnittstelle geschaffen, die alle erwhnten Fhigkeiten (und noch einige mehr)
implementiert und beliebigen Anwendungen als integralen Bestandteil der JavaKlassenbibliothek zur Verfgung stellt. Erweiterungen am Sprachkern waren dazu
nicht ntig.
Wir wollen uns in diesem Kapitel die wichtigsten Eigenschaften des Reflection-APIs
ansehen und ein paar ntzliche Anwendungen vorstellen. Die als Introspection
bezeichneten Erweiterungen fr die Beans-Behandlung werden in Kapitel 44
behandelt.
<
>
>>
43.6 Zusammenfassung
43.6 Zusammenfassung
Das Reflection-API ist ohne Zweifel ein wichtiger Bestandteil von Java. Es ist nicht
nur Grundlage der Beans- und Serialisierungs-APIs, sondern erffnet dem ToolEntwickler auch eine Flle von Mglichkeiten und Freiheiten bei der Entwicklung
allgemein verwendbarer Werkzeuge. Zu beachten ist allerdings, da Zugriffe auf
Methoden und Membervariablen deutlich langsamer als bei direktem Zugriff
ausgefhrt werden. Im experimentellen Vergleich ergaben sich typischerweise
Unterschiede um den Faktor 10 bis 100. Whrend auf der verwendeten Testmaschine
der Aufruf einer Methode mit einem double-Parameter beispielsweise 120 ns.
dauerte, wurden fr denselben Aufruf per Reflection etwa 7.0 s. bentigt. Bei
deaktiviertem Just-In-Time-Compiler erhhte sich die Zeit fr den direkten Aufruf
auf 620 ns., whrend der Reflection-Wert mit etwa 7.2 s. nahezu unverndert blieb.
Beim Zugriff auf Membervariablen wurden hnliche Unterschiede gemessen.
Bemerkenswert ist, da der JIT bei Verwendung des Reflection-APIs praktisch keine
Geschwindigkeitsvorteile bringt, denn alle nennenswerten Methoden sind native und
knnen daher kaum optimiert werden.
In diesem Kapitel wurden folgende Themen behandelt:
<
>
>>
Bevor wir uns den wichtigsten Eigenschaften einer Bean zuwenden, wollen wir uns
kurz mit einigen grundlegenden Begriffen vertraut machen.
Bei der Entwicklung einer Anwendung mit grafischen Komponenten wird
zweckmigerweise zwischen Designzeitpunkt und Ausfhrungszeitpunkt
unterschieden. Beide bezeichnen eigentlich Zeitrume und stehen fr die beiden
unterschiedlichen Phasen der Entwicklung einer Anwendung und ihrer spteren
Ausfhrung. Zum Designzeitpunkt werden die Komponenten in einem grafischen
Editor des Entwicklungswerkzeugs ausgewhlt, in einem Formular plaziert und an
die Erfordernisse der Anwendung angepat. Diesen Editor wollen wir fortan als GUIDesigner bezeichnen, synonym werden aber auch Begriffe wie Grafik-Editor, GUIPainter usw. verwendet.
Ein groer Teil der bei den Beans realisierten Konzepte spielt nur fr den
Designzeitraum eine Rolle. Spielt dieser keine Rolle (beispielsweise weil
die Komponenten nicht in einem GUI-Designer, sondern - wie in diesem
Buch bisher stets gezeigt - per Source-Code-Anweisungen eingebunden
und konfiguriert werden), verlieren Beans einiges von ihrer Ntzlichkeit,
und herkmmliche Techniken der Wiederverwendung in
objektorientierten Programmen knnen an ihre Stelle treten.
Hinweis
Wir wollen uns die wichtigsten Eigenschaften von Beans zunchst berblicksartig
ansehen:
1. Eine Bean wird im Programm durch ein Objekt (also eine instanzierte Klasse)
reprsentiert. Das gilt sowohl fr den Designzeitpunkt, bei dem es vom GUIDesigner instanziert wird, als auch fr den Ausfhrungszeitpunkt, bei dem die
Anwendung dafr verantwortlich ist, das Objekt zu konstruieren. Eine Bean
bentigt dazu notwendigerweise einen parameterlosen Konstruktor.
2. Sie besitzt konfigurierbare Eigenschaften, die ihr Verhalten festlegen. Diese
Eigenschaften werden ausschlielich ber Methodenaufrufe modifiziert, deren
Namen und Parameterstrukturen genau definierten Designkonventionen
folgen. Damit knnen sie vom GUI-Designer automatisch erkannt und dem
Entwickler in einem Editor zur Auswahl angeboten werden.
3. Eine Bean ist serialisierbar und deserialisierbar. So knnen die zum
Designzeitpunkt vom Entwickler vorgenommenen Einstellungen gespeichert
und zum Ausfhrungszeitpunkt rekonstruiert werden. (Serialisierung wurde in
Kapitel 41 behandelt).
4. Eine Bean sendet Events, um registrierte Listener ber nderungen ihres
internen Zustands oder ber sonstige wichtige Ereignisse zu unterrichten.
5. Einer Bean kann optional ein Informations-Objekt zugeordnet werden, mit
dessen Hilfe dem GUI-Designer weitere Informationen zur Konfiguration
<
>
>>
44.7 Zusammenfassung
44.7 Zusammenfassung
<
>
>>
45.5 Zusammenfassung
45.5 Zusammenfassung
<
>
>>
46.3 Zusammenfassung
46.3 Zusammenfassung
<
>
>>
47.2.1 Sprachsicherheit
Java wurde von Anfang mit hheren Sicherheitsansprchen entworfen, als dies
blicherweise bei Programmiersprachen der Fall ist. Einer der Hauptgrnde dafr
war der Wunsch, den Aufruf von Applets, die aus dem Internet geladen wurden, so
sicher wie mglich zu machen. Selbst bsartige Applets sollten nicht in der Lage
sein, ernsthafte Angriffe auf den Computer, das Betriebssystem oder die Ressourcen
des Anwenders auszufhren.
Sicherheit beginnt in Java schon bei der Implementierung der Sprache. Anders als in
C oder C++ gibt es beispielsweise keine direkten Zugriffe auf den Hauptspeicher und
keine Pointerarithmetik. Das Memory-Management arbeitet vollautomatisch.
Sicherheitslcken, die durch (provozierte) Speicherberlufe verursacht werden, sind
damit nicht ohne weiteres mglich.
Alle Typkonvertierungen werden zur Laufzeit geprft und unerlaubte
Umwandlungen von vorne herein ausgeschlossen. Auch Zugriffe auf Array-Elemente
und Strings werden grundstzlich auf Einhaltung der Bereichsgrenzen geprft.
Zugriffe, die auerhalb des erlaubten Bereichs liegen, fhren nicht zu undefiniertem
Verhalten, sondern werden definiert abgebrochen und lsen eine Ausnahme aus.
Beim Laden von Bytecode ber das Netz wird dieser vor der Ausfhrung von einem
Bytecode-Verifier untersucht. Auf diese Weise wird beispielsweise sichergestellt, da
nur gltige Opcodes verwendet werden, da alle Sprunganweisungen auf den Anfang
einer Anweisung zeigen, da alle Methoden mit korrekten Signaturen versehen sind
der lesende und schreibende Zugriff auf Dateien des lokalen Computers,
das ffnen von TCP/IP-Verbindungen zu einem anderen als dem Host, von
dem das Applet geladen wurde,
das Akzeptieren von TCP/IP-Verbindungen auf privilegierten Portnummern,
das Lesen benutzerbezogener System-Properties wie "user.name",
"user.home", "user.dir" oder "java.home",
das Erzeugen eines Top-Level-Fensters ohne Warnhinweis,
das Ausfhren externer Programme,
das Laden von System-Libraries,
das Beenden der virtuellen Maschine.
Bentigte ein Applet Zugriff auf diese Ressourcen, gab es im JDK 1.0 die Hinweis
Mglichkeit, die zugehrigen Dateien auf der lokalen Festplatte
abzulegen. Denn wenn das Applet zur Ausfhrung nicht aus dem Netz,
sondern aus den lokalen Dateien gestartet wurde, galt es automatisch als
vertrauenswrdig und hatte Zugriff auf alle ansonsten verbotenen
Ressourcen.
bekanntgemacht wurde), kann das Applet die Sandbox verlassen und hat Zugriff auf
alle geschtzten Ressourcen.
Mit dem JDK 1.2 wurde dieses Konzept weiter verfeinert. Whrend es im JDK 1.1
schwierig war, die Zugriffsbeschrnkungen schrittweise zu lockern, ist das nun viel
einfacher. Die Zugriffsbeschrnkungen sind konfigurierbar und knnen mit Hilfe
einer Policy-Datei auch ohne Programmnderungen angepat werden. Sie knnen
wahlweise davon abhngig gemacht werden, von wem die Signatur stammt, als auch
davon, woher das Applet geladen wurde. Zudem wurde die prinzipielle
Unterscheidung zwischen lokalem und netzwerkbasiertem Code aufgegeben. Obwohl
die Sicherheitseinstellungen so konfiguriert werden knnten, da lokalem Code
voller Zugriff auf alle Ressourcen gewhrt wird, ist das standardmig nicht mehr der
Fall.
<
>
>>
47.4 Zusammenfassung
47.4 Zusammenfassung
<
>
>>
Seit der Version 1.3 besitzt das JDK Sound-Fhigkeiten, die weit ber die in
Abschnitt 39.3 erluterten Mglichkeiten hinausgehen. Mit Hilfe des Sound-APIs
knnen Samples abgespielt oder aufgenommen werden. Es knnen Midi-Dateien
erzeugt oder wiedergegeben werden, und es ist mglich, direkt auf angeschlossene
oder eingebaute Tonerzeuger zuzugreifen. Das API abstrahiert alle fr das Erzeugen
und Bearbeiten von Sounds wesentlichen Konzepte und untersttzt die Erkennung
und Verwendung unterschiedlichster Hardware-Komponenten.
Das Sound-API ist allerdings nicht ganz leicht zu bedienen und wird in der Literatur
sehr stiefmtterlich behandelt. Die Schwierigkeiten haben mehrere Ursachen:
Einerseits handelt es sich um ein Low-Level-API, also eines, bei dem schon
zur Erzielung relativ einfacher Effekte recht viel Programmieraufwand ntig
ist.
Andererseits kann das API wenig Annahmen ber standardmig verfgbare
Hardware machen. Soll beispielsweise eine Fltenmelodie erklingen, mu das
Programm erst einmal herausfinden, ob ein Synthesizer eingebaut oder ber
einen der Midi-Ports erreichbar ist. Zudem mu er das entsprechende
Instrument zur Verfgung stellen knnen. Erst dann kann dieser konfiguriert
und mit den entsprechenden Tonerzeugungs-Kommandos beschickt werden.
Zu guter Letzt erfordert der Umgang mit dem Sound-API ein grundlegendes
Verstndnis fr viele der auf diesem Gebiet relevanten Konzepte. Das API ist
sehr eng an Begriffe angelehnt, die direkte Pendants in der elektronischen
oder elektronikuntersttzten Musik haben. Ohne ein Basiswissen in
Themenbereichen wie Samples, Midi, Tracks, Timecodes, Sequencer,
Synthesizer Soundbanks oder Mixer sind die korrespondierenden Klassen und
Interfaces schwerlich korrekt zu verwenden.
Wir wollen deshalb in diesem Kapitel einen sehr pragmatischen Ansatz whlen.
Erforderliche Begriffe werden, wo ntig, lediglich kurz erklrt, denn wir gehen
davon aus, da beim Leser bereits ein Grundstock an einschlgigen
Grundkenntnissen vorhanden ist. Oder wenigstens die Bereitschaft, sich diese
whrend des Lesens und Experimentierens anzueignen. Auch werden wir die APIs
nur ansatzweise erlutern, denn mehr ist aus Platzgrnden nicht mglich.
Die Beispielprogramme wurden so gewhlt, da sie einen unmittelbaren Praxisnutzen
haben. Sie stellen leicht einzusetzende Routinen zum Abspielen von Samples sowie
zum Erzeugen einfacher Midi-Sequenzen und zum Abspielen von Midi-Files zur
Verfgung. Damit werden die wichtigsten Standardflle beim Einsatz von Sound
abgedeckt.
Das Sound-API dient als Basis fr alle Arten von Sound-Support in Java. Seine
Anwendungsgebiete reichen von interaktiven Applikationen oder Spielen mit SoundUntersttzung ber Media-Player und Musik-Tools bis hin zu Telefonie- und
Konferenz-Applikationen. Des weiteren ist das Sound-API Basis hherer
Programmierschnittstellen, wie etwa des Java Media Framework, das eine
Schnittstelle zum Abspielen und Erzeugen einer ganzen Reihe von Audio- und
Videoformaten zur Verfgung stellt.
Die Beispiele in diesem Buch funktionieren natrlich nur dann, wenn auf Warnung
dem Computer, an dem sie nachvollzogen werden sollen, eine geeignete
(und vom Java-Sound-API untersttzte) Sound-Hardware vorhanden ist.
Dabei handelt es sich typischerweise um eine Sound-Karte, es kann aber
(wenigstens beim Midi-API) auch eine Midi-Schnittstelle mit
angeschlossenem Synthesizer verwendet werden. Ist eine solche
Hardware nicht verfgbar, erklingt beim Ausfhren der
Beispielprogramme entweder gar nichts (oder das Falsche) oder es wird
eine Ausnahme ausgelst.
Handbuch der Java-Programmierung, 3. Auflage,
Addison Wesley, Version 3.0
1998-2002 Guido Krger,
API
http://www.javabuch.de
<
>
>>
48.4 Zusammenfassung
48.4 Zusammenfassung
<
>
>>
49.1 Einleitung
49.1 Einleitung
Java gilt gemeinhin als Sprache, die mit Performance-Problemen zu kmpfen hat.
Nicht nur die Ablaufgeschwindigkeit des Compilers und anderer
Entwicklungswerkzeuge, sondern vor allem die der eigenen Programme lt oft zu
wnschen brig. Aufgrund der Plattformunabhngigkeit des vom Compiler
generierten Bytecodes kann dieser normalerweise nicht direkt auf dem jeweiligen
Betriebssystem ausgefhrt werden. Er verwendet statt dessen einen eigenen
Interpreter, die Virtuelle Maschine (kurz: VM), zur Ausfhrung der erstellten
Programme.
Interpretierter Code wird naturgem langsamer ausgefhrt als kompilierter, selbst
wenn er in Form von Bytecodes vorliegt. Zwar ist es prinzipiell mglich, auch JavaProgramme in Native-Code zu bersetzen (es gibt sogar einige kommerzielle Tools,
die das tun), aber dann ist es mit der Plattformunabhngigkeit aus, und das fertige
Programm luft nur noch auf einem Betriebssystem. Whrend das fr Applikationen
in bestimmten Fllen akzeptabel sein mag, verbietet sich diese Vorgehensweise fr
Applets, die im Internet auf vielen verschiedenen Browsern und Betriebssystemen
laufen mssen, von selbst. Zudem konterkarieren native-kompilierte Programme die
Grundidee der plattformbergreifenden Binrkompatibilitt, die eine der
herausragenden Eigenschaften von Java ist.
Eine Alternativlsung bieten Just-In-Time-Compiler (kurz: JIT), deren Entwicklung
in groen Schritten voranschreitet. Ein JIT ist ein Programm, das den Bytecode von
Methoden whrend der Ausfhrung des Programms in Maschinencode der aktuellen
Plattform bersetzt und so beim nchsten Aufruf wesentlich schneller ausfhren kann.
Vorteilhaft ist dabei, da die Klassendateien mit dem Bytecode unverndert
ausgeliefert werden knnen und das Programm seinen plattformbergreifenden
Charakter erhlt. Nur der Just-In-Time-Compiler ist plattformspezifisch und an ein
bestimmtes Betriebssystem gebunden. Nahezu alle Hersteller kommerzieller JavaProdukte haben mittlerweile einen JIT als festen Bestandteil ihres Java-
Entwicklungssystems eingebunden. Auch SUN liefert seit dem JDK 1.1.6 den Just-InTime-Compiler von Symantec als festen Bestandteil des JDK aus.
Leider ist auch ein Just-In-Time-Compiler kein Allheilmittel gegen
Performanceprobleme. Zwar ist er in der Lage, bestimmte Codeteile so stark zu
beschleunigen, da ihre Ablaufgeschwindigkeit der von kompiliertem C-Code
nahekommt. Andererseits gibt es nach wie vor gengend Mglichkeiten, Programme
zu schreiben, die inhrent langsamen Code enthalten, der auch von einem Just-InTime-Compiler nicht entscheidend verbessert werden kann. Zudem ensteht durch den
Einsatz des JIT ein gewisser Overhead, der mglicherweise einen NettoPerformancegewinn verhindert, denn das Kompilieren des Bytecodes kostet Zeit und
zustzlichen Speicher.
Des weiteren ist zu bedenken, da zur Laufzeit eine Vielzahl von Checks
durchgefhrt werden mssen, die die Ablaufgeschwindigkeit von Java-Programmen
vermindert:
Am besten ist es daher, bereits whrend der Entwicklung der Programme auf die
Ablaufgeschwindigkeit des erzeugten Codes zu achten. Wir wollen uns in diesem
Kapitel einige typische Java-Konstrukte ansehen, die bei unachtsamer Verwendung
zu Performance-Problemen fhren knnen. Gleichzeitig wollen wir Mglichkeiten
aufzeigen, wie man mit alternativem Code den Engpa umgehen und die
Ablaufgeschwindigkeit des Programms verbessern kann. Wenn man diese Regeln
beachtet, ist es durchaus mglich, in Java grere Programme zu schreiben, deren
Laufzeitverhalten auf aktuellen Rechnern absolut akzeptabel ist.
Wir wollen uns in diesem Kapitel nicht mit grundlegenden Techniken der
Codeoptimierung beschftigen. Auch wenn sie zeitweilig kurz angedeutet
werden, knnen diese Themen besser in Bchern ber
Programmiersprachen, Algorithmen oder Optimierungstechniken fr
Compiler nachgelesen werden. Auch Tips & Tricks, die in aller Regel nur
marginale Verbesserungen bringen, oder langsamer Code, fr den keine
einfach anzuwendenden Alternativen bekannt sind, sollen hier nicht
behandelt werden. Statt dessen wollen wir uns auf einige groe
Themenkomplexe konzentrieren, die leicht umzusetzen sind und in der
Praxis schnell zu Verbesserungen fhren.
Hinweis
<
>
>>
49.4 Zusammenfassung
49.4 Zusammenfassung
<
>
>>
50.9.1 Aufruf
keytool [ commands ]
50.9.2 Beschreibung
keytool ist ein Hilfsprogramm zum Erzeugen und Verwalten von Schlsseln fr
Public-Key-Kryptosysteme. Das Programm bietet eine groe Anzahl von Funktionen,
von denen die wichtigsten bereits in Abschnitt 47.1.6 erklrt wurden. Weitere Details
knnen in der Tool-Dokumentation des JDK nachgelesen werden.
<
>
>>
50.10.1 Aufruf
policytool
50.10.2 Beschreibung
policytool ist ein interaktives Programm zum Bearbeiten von Policy-Dateien
(siehe Abschnitt 47.3.4). Mit seiner Hilfe knnen neue Policy-Dateien erstellt oder
vorhandene gendert werden. Es versucht zunchst, die benutzerbezogene PolicyDatei zu ffnen (unter Windows c:\windows\.java.policy, unter UNIX
$HOME/.java.policy), kann aber auch fr beliebige andere Policy-Dateien verwendet
werden. In einer Listbox zeigt das Programm alle vorhandenen Eintrge an. Sie
knnen editiert oder gelscht oder es knnen neue hinzugefgt werden.
Wenn ein Policy-Eintrag bearbeitet wird, werden alle Berechtigungen auf dem
Bildschirm angezeigt. Auch sie knnen bearbeitet, gelscht oder hinzugefgt werden.
Beim Hinzufgen knnen die mglichen Varianten - ohne die Gefahr von
Tippfehlern, wie sie beim manuellen Editieren leicht entstehen knnen - bequem im
Dialog ausgewhlt werden. Nach dem Bearbeiten knnen die nderungen in die
ursprngliche oder eine andere Policy-Datei geschrieben werden.
<
>
>>
50.13.1 Aufruf
rmiregistry [port]
50.13.2 Beschreibung
rmiregistry ist der Standard-Namensservice fr RMI. Er dient als Hintergrund
dazu, RMI-Objekte zu registrieren und auf Anfrage Clients zur Verfgung zu stellen.
rmiregistry wurde in Abschnitt 46.2.4 erlutert. Seine einzige Option ist die
TCP-Portnummer. Wird sie nicht angegeben, gilt 1099 als Voreinstellung.
<
>
>>