16.11.2013 Aufrufe

Programmieren in Java - HostFiXX.de

Programmieren in Java - HostFiXX.de

Programmieren in Java - HostFiXX.de

MEHR ANZEIGEN
WENIGER ANZEIGEN

Erfolgreiche ePaper selbst erstellen

Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.

<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Prof. Jürgen Sauer<br />

<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Skriptum zur Vorlesung im WS 2004/2005<br />

1


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Inhaltsverzeichnis<br />

0. ÜBERSICHT.....................................................................................................................................11<br />

0.1 Ziel <strong>de</strong>r Vorlesung "<strong>Programmieren</strong>" ...................................................................................................... 11<br />

0.2 <strong>Java</strong>-Masch<strong>in</strong>e und Programmiersystem ................................................................................................. 11<br />

0.3 Der erste Versuch........................................................................................................................................ 13<br />

0.3.1 Das erste Programm............................................................................................................................... 13<br />

0.3.2 Bestandteile e<strong>in</strong>es Programms ............................................................................................................... 19<br />

1. EINFÜHRUNG IN DIE JAVA-PROGRAMMIERUNG......................................................................21<br />

1.1 Übersicht zur Entwicklung <strong>de</strong>r Programmiersprache <strong>Java</strong> ................................................................... 21<br />

1.2 Was ist <strong>Java</strong>?............................................................................................................................................... 22<br />

1.3 E<strong>in</strong>stieg <strong>in</strong> die <strong>Java</strong>-Programmierung ...................................................................................................... 25<br />

1.3.1 Die Software für die <strong>Java</strong>-Programmierung .......................................................................................... 25<br />

1.3.2 Applets und Anwendungen.................................................................................................................... 27<br />

1.3.2.1 Entwicklung von <strong>Java</strong>-Anwendungen ............................................................................................ 27<br />

1.3.2.2 Entwicklung von <strong>Java</strong>-Applets ....................................................................................................... 34<br />

1.3.3 Programmablaufpläne und Strukturierte Programmierung .................................................................... 43<br />

1.3.3.1 Darstellung von Algorithmen durch Programmablaufpläne ........................................................... 43<br />

1.3.3.2 Darstellung von Algorithmen mit Struktogrammen........................................................................ 46<br />

1.3.4 Datentyp und Datenstruktur................................................................................................................... 52<br />

1.4 Die Objektorientierung von <strong>Java</strong> .............................................................................................................. 55<br />

1.4.1 Grundlegen<strong>de</strong> Konzepte......................................................................................................................... 55<br />

1.4.1.1 Zustand und Verhalten von Objekten, Klassen, Instanz- und Klassen-Variable bzw. -Metho<strong>de</strong>n . 55<br />

1.4.1.1.1 Die Abbildung von Zustand bzw. Verhalten <strong>in</strong> Instanzvariable bzw. Instanzmetho<strong>de</strong>n ......... 55<br />

1.4.1.1.2 E<strong>in</strong>fache Typen, Referenztypen, Automatisches Boxen.......................................................... 59<br />

1.4.1.1.3 Konstruktoren .......................................................................................................................... 61<br />

1.4.1.1.4 Zugriffsrechte auf Klassen, Variable und Metho<strong>de</strong>n ............................................................... 62<br />

1.4.1.1.5 Klassenvariable und Klassenmetho<strong>de</strong>n.................................................................................... 64<br />

1.4.1.1.6 Lokale Variable und Konstanten.............................................................................................. 65<br />

1.4.1.1.7 Überla<strong>de</strong>n von Metho<strong>de</strong>n......................................................................................................... 66<br />

1.4.1.2 Superklassen und Subklassen, Vererbung und Klassenhierarchie.................................................. 67<br />

1.4.1.3 Referenzen, Referenztypen und Aufzählungstyp............................................................................72<br />

1.4.1.4 Konvertieren von Objekten und Primitivtypen ............................................................................... 75<br />

1.4.1.5 Manipulieren von Objekten ............................................................................................................ 76<br />

1.4.1.6 Innere (lokale) und anonyme Klassen............................................................................................. 80<br />

1.4.1.7 Schnittstellen und Pakete ................................................................................................................ 82<br />

1.4.1.7.1 Schnittstellen............................................................................................................................ 82<br />

1.4.1.7.2 Pakete....................................................................................................................................... 84<br />

1.4.1.8 Polymorphismus und B<strong>in</strong><strong>de</strong>n .......................................................................................................... 88<br />

1.4.2 Klassen <strong>de</strong>s Pakets java.lang.................................................................................................................. 90<br />

1.4.2.1 Die Klasse Object............................................................................................................................ 90<br />

1.4.2.3 Die Klasse System .......................................................................................................................... 93<br />

1.4.2.4 Multithread<strong>in</strong>g................................................................................................................................. 95<br />

1.4.2.4.1. Die Klasse Thread................................................................................................................... 95<br />

1.4.2.4.2 Das Interface Runnable ...................................................................................................... 102<br />

1.4.2.4.3. Prozesse und Threads <strong>in</strong> <strong>Java</strong>................................................................................................ 104<br />

1.4.2.4.4 Synchronisation nebenläufiger Prozesse................................................................................ 106<br />

1.4.2.5 Die Klassen Str<strong>in</strong>g und Str<strong>in</strong>gBuffer ............................................................................................ 112<br />

3


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

1.4.2.5.1 Die Klasse Str<strong>in</strong>g............................................................................................................... 112<br />

1.4.2.5.2 Die Klasse Str<strong>in</strong>gBuffer ......................................................................................................... 116<br />

1.4.2.6 Die Math-Klasse ........................................................................................................................... 117<br />

1.4.2.7 Object-Wrapper-Klassen............................................................................................................... 118<br />

1.4.3 Ausnahmen und Ausnahmenbehandlung............................................................................................. 121<br />

1.4.4 E<strong>in</strong>-, Ausgaben..................................................................................................................................... 128<br />

1.4.4.1 E<strong>in</strong>- und Ausgabeströme <strong>de</strong>r Klasse System................................................................................. 128<br />

1.4.4.2 Datenströme .................................................................................................................................. 129<br />

1.4.4.3 E<strong>in</strong>gabe und Ausgabe mit Dateien................................................................................................ 131<br />

1.4.4.4 Verwalten von Dateien und Verzeichnissen durch die Klasse File............................................... 137<br />

1.4.4.5 Filtern mit <strong>de</strong>n Klasse FilterRea<strong>de</strong>r und FilterWriter ................................................................... 137<br />

1.4.4.6 Der Dateiauswahl-Dialog.............................................................................................................. 141<br />

2. HAUPTBESTANDTEILE DER SPRACHE ................................................................................... 143<br />

2.1 Token.......................................................................................................................................................... 143<br />

2.1.1 Schlüsselworte ..................................................................................................................................... 144<br />

2.1.2 Bezeichner und Namenskonventionen................................................................................................. 144<br />

2.1.3 Literale ................................................................................................................................................. 145<br />

2.1.3.1 Ganzzahlige Literale ..................................................................................................................... 145<br />

2.1.3.2 Gleitpunktliterale .......................................................................................................................... 145<br />

2.1.3.3 Boolesche Literale......................................................................................................................... 146<br />

2.1.3.4 Zeichenliterale............................................................................................................................... 146<br />

2.1.3.5 Zeichenkettenliterale..................................................................................................................... 147<br />

2.1.4 Trennzeichen........................................................................................................................................ 147<br />

2.1.5 Operatoren ........................................................................................................................................... 148<br />

2.1.5.1 Arithmetische Operatoren ............................................................................................................. 148<br />

2.1.5.1.1 E<strong>in</strong>stellige arithmetische Operatoren ..................................................................................... 148<br />

2.1.5.1.2 Arithmetische Zuweisungsoperatoren.................................................................................... 148<br />

2.1.5.2 Inkrement- / Dekrement-Operatoren............................................................................................. 149<br />

2.1.5.3 Bitweise arithmetische Operatoren ............................................................................................... 149<br />

2.1.5.4 Bitweise Verschiebungsoperatoren............................................................................................... 149<br />

2.1.5.5 Bitweise Zuweisungsoperatoren ................................................................................................... 151<br />

2.1.5.6 Vergleichsoperatoren .................................................................................................................... 152<br />

2.1.5.7 Logische Vergleichsoperatoren..................................................................................................... 152<br />

2.1.6 Kommentare, e<strong>in</strong>gebettete Dokumentation.......................................................................................... 153<br />

2.2 Typen.......................................................................................................................................................... 155<br />

2.2.1 Primitive Datentypen ........................................................................................................................... 155<br />

2.2.2 Operationen mit primitiven Datentypen............................................................................................... 156<br />

2.2.2.1 Operationen mit booleschen Variablen......................................................................................... 156<br />

2.2.2.2 Operationen mit Zeichenvariablen................................................................................................ 156<br />

2.2.2.3 Operationen mit Gleitpunktzahlen ................................................................................................ 157<br />

2.2.2.4 Operationen mit ganzzahligen Variablen (bzw. ganzzahligen Ausdrücken) ................................ 158<br />

2.2.3 Datenfel<strong>de</strong>r (Arrays) ............................................................................................................................ 159<br />

2.2.3.1 Deklarieren, Erstellen von Array-Objekten .................................................................................. 159<br />

2.2.3.2 Zugriff auf Datenfeld-Elemente, Än<strong>de</strong>rn von Datenfeld-Elementen............................................ 161<br />

2.2.3.3 Anwendungen mit e<strong>in</strong>dimensionaler Datenfel<strong>de</strong>rn....................................................................... 162<br />

2.2.3.4 Mehrdimensionale Datenfel<strong>de</strong>r..................................................................................................... 164<br />

2.2.3.5 Die Klasse Arrays ......................................................................................................................... 170<br />

2.3 Ausdrücke.................................................................................................................................................. 171<br />

2.3.1 Arithmetische Ausdrücke..................................................................................................................... 171<br />

2.3.2 Bewertung von Ausdrücken................................................................................................................. 171<br />

2.3.3 Typkonvertierungen............................................................................................................................. 172<br />

2.3.4 Vergleichsoperatoren ........................................................................................................................... 175<br />

2.3.5 Logische Ausdrücke............................................................................................................................. 175<br />

2.4 Anweisungen.............................................................................................................................................. 176<br />

2.4.1 Blöcke und Anweisungen .................................................................................................................... 176<br />

4


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

2.4.2 Leere Anweisungen ............................................................................................................................. 176<br />

2.4.3 Benannte Anweisungen ....................................................................................................................... 176<br />

2.4.4 Deklarationen....................................................................................................................................... 176<br />

2.4.5 Ausdrucksanweisungen........................................................................................................................ 176<br />

2.4.6 Auswahlanweisungen .......................................................................................................................... 177<br />

2.4.6.1 if-Anweisungen............................................................................................................................. 177<br />

2.4.6.2 if-else-Anweisungen ..................................................................................................................... 177<br />

2.4.6.3 switch-Anweisungen..................................................................................................................... 179<br />

2.4.7 Iterationsanweisungen.......................................................................................................................... 180<br />

2.4.7.1 while-Anweisung .......................................................................................................................... 180<br />

2.4.7.2 do-Anweisung............................................................................................................................... 181<br />

2.4.7.3 for-Anweisung .............................................................................................................................. 182<br />

2.4.7.4 Die erweiterte Form <strong>de</strong>r for-Schleife <strong>in</strong> <strong>Java</strong> 1.5.......................................................................... 183<br />

2.4.8 Sprung-Anweisungen........................................................................................................................... 184<br />

2.4.8.1 break-Anweisung .......................................................................................................................... 184<br />

2.4.8.2 cont<strong>in</strong>ue-Anweisung ..................................................................................................................... 184<br />

2.4.8.3 Marken (labels) ............................................................................................................................. 185<br />

2.4.8.4 return-Anweisung ......................................................................................................................... 187<br />

2.4.8.5 throw-Anweisung.......................................................................................................................... 187<br />

2.4.9 Synchronisationsanweisungen ............................................................................................................. 187<br />

2.4.10 Schutzanweisungen............................................................................................................................ 188<br />

2.4.11 Unerreichbare Anweisungen.............................................................................................................. 188<br />

2.5 Klassen ....................................................................................................................................................... 188<br />

2.5.1 Deklaration........................................................................................................................................... 188<br />

2.5.2 Generische Klassen und generische Schnittstellen .............................................................................. 189<br />

2.5.2.1 Generische Typen ......................................................................................................................... 189<br />

2.5.2.1.1 Typvariable ............................................................................................................................ 189<br />

2.5.2.1.2 Vererbung .............................................................................................................................. 192<br />

2.5.2.1.3 E<strong>in</strong>schränkung <strong>de</strong>r Typvariablen ........................................................................................... 193<br />

2.5.2.2 Generische Schnittstellen.............................................................................................................. 194<br />

2.6 Metho<strong>de</strong>n ................................................................................................................................................... 196<br />

2.6.1 Die Deklaration.................................................................................................................................... 196<br />

2.6.2 Die Zugriffsspezifizierung ................................................................................................................... 196<br />

2.6.3 Die Metho<strong>de</strong>nmodifizierer................................................................................................................... 197<br />

2.6.4 Rückgabewerte von Metho<strong>de</strong>n............................................................................................................. 197<br />

2.6.5 Metho<strong>de</strong>nname und Parameterliste ...................................................................................................... 197<br />

2.6.6 Variable Parameteranzahl .................................................................................................................... 198<br />

2.6.7 Rekursion ............................................................................................................................................. 198<br />

2.6.7.1 Rekursive Funktionen ................................................................................................................... 198<br />

2.6.7.2 Rekursion und Iteration................................................................................................................. 199<br />

3. GRAFISCHE BENUTZEROBERFLÄCHEN UND APPLETS ...................................................... 201<br />

3.1 Ereignisse <strong>in</strong> grafischen Benutzeroberflächen ....................................................................................... 201<br />

3.1.1 Gestaltung von GUI mit Hilfe <strong>de</strong>r AWT-Klassen................................................................................ 201<br />

3.1.2 Ereignisbehandlung unter grafischen Benutzeroberflächen................................................................. 205<br />

3.1.3 Anwendung lokaler Klassen für die Ereignisbehandlung.................................................................... 208<br />

3.1.5 Low-Level-Events................................................................................................................................ 213<br />

3.1.5.1 Component-Events........................................................................................................................ 213<br />

3.1.5.2 W<strong>in</strong>dow-Events............................................................................................................................. 213<br />

3.1.5.3 Mouse-Events ............................................................................................................................... 214<br />

3.1.5.4 MouseMotion-Events.................................................................................................................... 215<br />

3.1.5.5 Fokus-Events................................................................................................................................. 215<br />

3.1.5.6 Key-Events.................................................................................................................................... 216<br />

3.1.6 Der Weg e<strong>in</strong>es Ereignisses................................................................................................................... 219<br />

3.2 Kommunikation Anwen<strong>de</strong>r – Programm über Dialoge bzw. Menüs................................................... 221<br />

3.2.1 Dialoge................................................................................................................................................. 221<br />

5


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

3.2.2 Menüs................................................................................................................................................... 228<br />

3.2.3 Popup-Menüs ....................................................................................................................................... 230<br />

3.3 Grundlagen <strong>de</strong>r Applet-Erstellung.......................................................................................................... 232<br />

3.3.1 HTML-Grundlagen.............................................................................................................................. 232<br />

3.3.2 Die <strong>in</strong>terne Arbeitsweise e<strong>in</strong>es Applets ............................................................................................... 234<br />

3.3.3 „Multithread<strong>in</strong>g“-fähige Applets ......................................................................................................... 240<br />

3.3.4 Animation <strong>in</strong> Applets........................................................................................................................... 243<br />

3.3.5 Das La<strong>de</strong>n und Anzeigen von Bil<strong>de</strong>rn ................................................................................................. 248<br />

3.3.7 Die Klasse JApplet............................................................................................................................... 250<br />

3.3.8 Das Interface AppletContext................................................................................................................ 251<br />

3.4 Grafische Benutzeroberfläche mit Observable-Observer ..................................................................... 252<br />

3.5 Sw<strong>in</strong>g .......................................................................................................................................................... 255<br />

3.6 <strong>Java</strong> Beans ................................................................................................................................................. 256<br />

3.6.1 Wie<strong>de</strong>rverwendbare Softwarekomponenten ........................................................................................ 256<br />

3.6.3 Enterprise <strong>Java</strong> Beans .......................................................................................................................... 258<br />

4. GRAFIK UND BILDVERARBEITUNG ......................................................................................... 259<br />

4.1 Allgeme<strong>in</strong>e Zeichenvorgänge ................................................................................................................... 260<br />

4.1.1 Punkte, L<strong>in</strong>ien, Kreise, Bögen, Polygone ............................................................................................ 260<br />

4.1.2 Farbangaben......................................................................................................................................... 266<br />

4.1.3 Textausgabe über <strong>de</strong>n Zeichen-Modus ................................................................................................ 267<br />

4.1.4 Grössenangaben ................................................................................................................................... 270<br />

4.1.5 Clipp<strong>in</strong>g-Operationen .......................................................................................................................... 271<br />

4.2 Bildverarbeitung ....................................................................................................................................... 271<br />

4.2.1 Klassen zur Bildverarbeitung............................................................................................................... 272<br />

4.2.2 Bildproduzenten und Bildkonsumenten............................................................................................... 276<br />

4.2.3 Bildfilter............................................................................................................................................... 277<br />

4.2.4 Kopieren von Speicher <strong>in</strong> e<strong>in</strong> Bild bzw. von Bil<strong>de</strong>rn <strong>in</strong> e<strong>in</strong>en Speicher.............................................. 281<br />

4.2.4.1 Kopieren von Speicher <strong>in</strong> e<strong>in</strong> Bild................................................................................................ 281<br />

4.2.4.2 Kopieren von Bil<strong>de</strong>rn <strong>in</strong> e<strong>in</strong>en Speicher ....................................................................................... 282<br />

4.3 <strong>Java</strong> 2D....................................................................................................................................................... 284<br />

4.3.1 Das Zeichnen unter <strong>Java</strong>2D API.......................................................................................................... 285<br />

4.3.3 Koord<strong>in</strong>atensysteme und Transformation ............................................................................................ 295<br />

4.3.4 <strong>Java</strong>2D-Pipel<strong>in</strong>e ................................................................................................................................... 300<br />

4.3.5 Farbmanagement unter <strong>Java</strong> 2D........................................................................................................... 301<br />

4.3.6 Text und Fonts unter <strong>Java</strong>2D ............................................................................................................... 307<br />

4.3.7 2D Bildverarbeitung............................................................................................................................. 309<br />

4.3.8 Animationen mit <strong>Java</strong>2D ..................................................................................................................... 310<br />

5. AWT............................................................................................................................................... 311<br />

5.1 Bestandteile <strong>de</strong>s AWT............................................................................................................................... 311<br />

5.2 Die AWT-Komponenten........................................................................................................................... 311<br />

5.2.1 Schaltflächen (Buttons)........................................................................................................................ 312<br />

5.2.2 Labels................................................................................................................................................... 315<br />

5.2.3 Kontrollkästchen und Optionsfel<strong>de</strong>r .................................................................................................... 315<br />

5.2.4 Auswahlmenüs..................................................................................................................................... 319<br />

5.2.5 Listenfel<strong>de</strong>r .......................................................................................................................................... 320<br />

5.2.6 Textbereiche und Textfel<strong>de</strong>r ................................................................................................................ 321<br />

5.2.7 Schieber und Bildlaufleisten ................................................................................................................ 324<br />

5.2.8 ScrollPane ............................................................................................................................................ 327<br />

6


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

5.2.9 Zeichenbereiche ................................................................................................................................... 328<br />

5.3 Conta<strong>in</strong>er ................................................................................................................................................... 331<br />

5.3.1 Panels ................................................................................................................................................... 331<br />

5.3.2 Frames.................................................................................................................................................. 331<br />

5.3.3 Menüs................................................................................................................................................... 332<br />

5.3.4 Dialoge................................................................................................................................................. 336<br />

5.4 Die Layout-Manager................................................................................................................................. 338<br />

5.4.1 Layout-Regeln...................................................................................................................................... 338<br />

5.4.2 Die e<strong>in</strong>zelnen Layout-Manager............................................................................................................ 339<br />

5.5 Die Event-Mo<strong>de</strong>lle 1.0 und 1.1 ................................................................................................................. 347<br />

5.5.1 Der AWT-Handler 1.0 ......................................................................................................................... 347<br />

5.5.2 Das Event-Handl<strong>in</strong>g <strong>in</strong> <strong>Java</strong> 1.1 bzw. <strong>Java</strong> 1.2.................................................................................... 349<br />

5.6 Benutzerschnittstellen mit Sw<strong>in</strong>g............................................................................................................. 355<br />

6. UTILITIES...................................................................................................................................... 421<br />

6.1 Kollektionen (Collections) ........................................................................................................................ 421<br />

6.1.1 Durchwan<strong>de</strong>rn von Datenstrukturen mit Iteratoren ............................................................................. 421<br />

6.1.2 Die Klasse Vector ................................................................................................................................ 422<br />

6.1.3 Die Klasse Stack .................................................................................................................................. 424<br />

6.1.4 Die Klasse Bitset für Bitmengen.......................................................................................................... 427<br />

6.1.5 Die Klasse Hashtable und assoziative Speicher................................................................................... 428<br />

6.1.6 Die abstrakte Klasse Dictionary........................................................................................................... 432<br />

6.1.7 Die Klasse Properties........................................................................................................................... 433<br />

6.2 Collection API ........................................................................................................................................... 434<br />

6.2.1 Die Schnittstellen Collection, Iterator, Comparator............................................................................. 434<br />

6.2.2 Die Behälterklassen und Schnittstellen <strong>de</strong>s Typs List ......................................................................... 436<br />

6.2.3 Behälterklassen <strong>de</strong>s Typs Set............................................................................................................... 440<br />

6.2.4 Behälterklassen <strong>de</strong>s Typs Map............................................................................................................. 443<br />

6.2.5 Algorithmen ......................................................................................................................................... 445<br />

6.2.5.1 Datenmanipulation ........................................................................................................................ 445<br />

6.2.5.2 Größter und kle<strong>in</strong>ster Wert e<strong>in</strong>er Collection................................................................................. 446<br />

6.2.5.3 Sortieren........................................................................................................................................ 446<br />

6.2.5.4 Suchen von Elementen.................................................................................................................. 448<br />

6.2.6 Generics ............................................................................................................................................... 449<br />

6.2.6.1 Sammlungsklassen ........................................................................................................................ 449<br />

6.2.6.2 Generische Metho<strong>de</strong>n ................................................................................................................... 450<br />

6.2.6.3 Iteration......................................................................................................................................... 452<br />

6.3 Die Klasse Str<strong>in</strong>gTokenizer...................................................................................................................... 455<br />

6.4 Die Klasse Random................................................................................................................................... 456<br />

6.5 Datumsberechnungen ............................................................................................................................... 457<br />

6.5.1 Die Klassen Date, Calendar, GregorianCalendar................................................................................. 457<br />

6.5.2 Formatieren <strong>de</strong>r Datumsangaben ......................................................................................................... 461<br />

6.6 Formatieren mit Format-Objekten ......................................................................................................... 463<br />

6.6.1 Die Klasse Numberformat für die Ausgabe von Prozenten, Zahlen, Währungen ............................... 464<br />

6.6.2 Ausgabe formatieren mit MessageFormat ........................................................................................... 464<br />

6.6.3 Dezimalzahlenformatierung mit DecimalFormat................................................................................. 464<br />

6.6.4 Formatieren mit format()...................................................................................................................... 465<br />

7. EIN- UND AUSGABE ................................................................................................................... 467<br />

7


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

7.1 Die abstrakten Klassen InputStream und OutputStream..................................................................... 469<br />

7.1.1 InputStream.......................................................................................................................................... 469<br />

7.1.2 OutputStream ....................................................................................................................................... 471<br />

7.2 Gefilterte Ströme....................................................................................................................................... 472<br />

7.3 Die Klasse File ........................................................................................................................................... 475<br />

7.4 Die RandomAccessFile-Klasse................................................................................................................. 477<br />

7.5 Die Klasse StreamTokenizer .................................................................................................................... 479<br />

7.6 Klassen für spezielle nützliche Ströme.................................................................................................... 482<br />

7.7 <strong>Java</strong> 1.1 IO-Ströme ................................................................................................................................... 483<br />

7.7.1 Grundlagen........................................................................................................................................... 483<br />

7.7.2 Die abstrakte Klassen Rea<strong>de</strong>r und ihre Ableitungen............................................................................ 484<br />

7.7.3 Die abstrakte Klasse Writer und ihre Ableitungen .............................................................................. 486<br />

7.7.4 Demonstrationsprogramm zur E<strong>in</strong>-/ Ausgabe ab <strong>Java</strong> Version 1.1 ..................................................... 489<br />

8. SERIALISIERUNG........................................................................................................................ 491<br />

8.1 Grundlagen................................................................................................................................................ 491<br />

8.1.1 Das Interface Serializable .................................................................................................................... 491<br />

8.1.2 Die Klasse ObjectOutputStream .......................................................................................................... 492<br />

8.1.3 Die Klasse ObjectInputStream............................................................................................................. 494<br />

8.2 Tiefe Objektkopien ................................................................................................................................... 496<br />

9. NETZWERKPROGRAMMIERUNG .............................................................................................. 497<br />

9.1 Adressen, Ressourcen und URLs............................................................................................................. 497<br />

9.1.1 Die Klasse InetAddress........................................................................................................................ 497<br />

9.1.2 Die Klasse URL ................................................................................................................................... 498<br />

9.1.3 Die Klasse URLConnection und abgeleitete Klassen.......................................................................... 502<br />

9.2 Kommunikation <strong>in</strong> Netzwerken............................................................................................................... 503<br />

8


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Literaturverzeichnis<br />

Alexan<strong>de</strong>r Newman u.a.: JAVA, Referenz und Anwendungen, 1996, Haar bei München : Que<br />

Ralph Steyer: JAVA 1.2, Kompendium, 1998, Markt & Technik, Haar bei München<br />

Laura Lemay u. Roger Ca<strong>de</strong>nhead; JAVA 2 <strong>in</strong> 21 Tagen, 1999, Markt & Technik, Haar bei<br />

München<br />

Gui<strong>de</strong> Krüger: Go To <strong>Java</strong> 2, 1999, Addison-Wesley, München ...<br />

David Flanagan: <strong>Java</strong> <strong>in</strong> a Nutshell, 1996, O’Reilly&Associates Inc, Bonn ...<br />

Florian Hawlitzek: <strong>Java</strong> 2, 2000, Addison-Wesley, München ...<br />

Christian Ullenboom: <strong>Java</strong> ist auch e<strong>in</strong>e Insel, GalileoPress, Bonn 2002<br />

9


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

0. Übersicht<br />

0.1 Ziel <strong>de</strong>r Vorlesung "<strong>Programmieren</strong>"<br />

„Vermittlung grundlegen<strong>de</strong>r Kenntnisse, die zum Lösen von Problemen mit e<strong>in</strong>er<br />

Rechenanlage (Computer, Rechner) nötig s<strong>in</strong>d.“<br />

Die Rechenanlage ist „irgen<strong>de</strong><strong>in</strong>e“ (vom „Jumbo“ bis zum Personal Computer) und<br />

muß<br />

- e<strong>in</strong>e E<strong>in</strong>gabemöglichkeit für Ziffern, Buchstaben und Son<strong>de</strong>rzeichen besitzen (Tastatur)<br />

- programmierbar se<strong>in</strong><br />

- e<strong>in</strong>e Ausgabemöglichkeit haben (Bildschirm)<br />

Zum Erwerb grundlegen<strong>de</strong>r Kenntnisse für das Lösen von Problemen auf e<strong>in</strong>em<br />

Universalrechner ist<br />

- zu zeigen, wie man von <strong>de</strong>r Problemstellung zum Lösungsverfahren (Algorithmus) kommt. Alle<br />

Probleme, für die e<strong>in</strong> Lösungweg genügend präzise formuliert wer<strong>de</strong>n kann, s<strong>in</strong>d über <strong>de</strong>n Rechner<br />

lösbar.<br />

- zu üben: Der Umgang mit <strong>de</strong>n Algorithmen. Dazu wird die Programmiersprache <strong>Java</strong> benutzt, <strong>in</strong> <strong>de</strong>r<br />

die Algorithmen formuliert wer<strong>de</strong>n. Der <strong>in</strong> <strong>Java</strong> formulierte Lösungsweg (Algorithmus) ist das <strong>Java</strong>-<br />

Programm (Anwendung, Applet), <strong>de</strong>r Rechner auf <strong>de</strong>m das Programm ausführbar ist, ist die <strong>Java</strong>-<br />

Masch<strong>in</strong>e. <strong>Java</strong> ist e<strong>in</strong>e Sprache, die die Arbeitsanweisungen e<strong>in</strong>es Programmierers e<strong>in</strong>fach und<br />

anschaulich darstellen kann.<br />

<strong>Java</strong> wur<strong>de</strong> ab 1991 bei Sun Microsystems entwickelt. Verantwortlich für <strong>Java</strong> ist<br />

<strong>Java</strong>Soft, e<strong>in</strong>e Tochterfirma von Sun Microsystems. <strong>Java</strong> und <strong>Java</strong>Soft 1 halten<br />

permanent die aktuellsten Informationen im Internet bereit.<br />

0.2 <strong>Java</strong>-Masch<strong>in</strong>e und Programmiersystem<br />

Die <strong>Java</strong>-Masch<strong>in</strong>e 2 nutzt die Möglichkeiten <strong>de</strong>s Universalrechners.<br />

Die E<strong>in</strong>gabemöglichkeit dient zum E<strong>in</strong>schreiben von Daten und Programmen <strong>in</strong> <strong>de</strong>n<br />

Speicher <strong>de</strong>r Zentrale<strong>in</strong>heit (central process<strong>in</strong>g unit, CPU). Die Zentrale<strong>in</strong>heit<br />

verarbeitet die e<strong>in</strong>gegebenen Daten nach <strong>de</strong>n Anweisungen <strong>de</strong>s e<strong>in</strong>gelesenen<br />

Programms, das zur Ausführung <strong>in</strong> b<strong>in</strong>äre Form gebracht wur<strong>de</strong>. Das Steuerwerk<br />

überwacht die Ausführung <strong>de</strong>s Programms (Entschlüsseluung <strong>de</strong>r b<strong>in</strong>är dargestellten<br />

Anweisungen). Das eigentliche Abarbeiten <strong>de</strong>r Programmanweisungen<br />

(arithmetische, logische Operatoren) <strong>in</strong> <strong>de</strong>r gegebenen Reihenfolge übernimmt das<br />

Rechenwerk. Daten, Programme, die im Hauptspeicher ke<strong>in</strong>en Platz f<strong>in</strong><strong>de</strong>n, s<strong>in</strong>d auf<br />

externen Speichermedien ausgelagert (Magnetband, Magnetplatte, Floppy Disc).<br />

1 http://java.sun.com<br />

2 Die virtuelle Masch<strong>in</strong>e von <strong>Java</strong> (<strong>Java</strong> VM) ist wirklich e<strong>in</strong>e Masch<strong>in</strong>e, d.h. Hardware, die bestimmte<br />

Operationsco<strong>de</strong>s (Masch<strong>in</strong>enanweisungen) <strong>in</strong>terpretieren kann. Es wur<strong>de</strong>n bzw. wer<strong>de</strong>m Chips entwickelt, die<br />

diese Masch<strong>in</strong>e <strong>in</strong> Hardware implementieren. Im Regelfall geschieht aber e<strong>in</strong>e Emulation durch das<br />

Laufzeitsystem auf <strong>de</strong>r jeweiligen Zielplattform.<br />

11


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

E<strong>in</strong>gabee<strong>in</strong>heit Externer Speicher Ausgabee<strong>in</strong>heit<br />

Hauptspeicher Steuerwerk Rechenwerk<br />

Zentrale<strong>in</strong>heit<br />

Abb. 0.2-1: Pr<strong>in</strong>zipieller Aufbau e<strong>in</strong>es Universalrechners<br />

Der Universalrechner ist vielfältig anwendbar. Welche Anwendungsmöglichkeit (z.B.<br />

<strong>Java</strong>-Masch<strong>in</strong>e) gewünscht wird, gibt <strong>de</strong>r Benutzer durch Systemkommandos<br />

bekannt. Diese Kommandos wer<strong>de</strong>n von Systemprogrammen entschlüsselt und<br />

anschließend <strong>in</strong>terpretiert. E<strong>in</strong>es dieser Kommandos verwan<strong>de</strong>lt, falls die dazu<br />

nötigen Programme vorhan<strong>de</strong>n s<strong>in</strong>d, <strong>de</strong>n Universalrechner <strong>in</strong> e<strong>in</strong>e <strong>Java</strong>-Masch<strong>in</strong>e.<br />

Für die <strong>Java</strong>-Masch<strong>in</strong>e s<strong>in</strong>d danach die <strong>Java</strong>-Programme <strong>de</strong>s Benutzers die Quelle<br />

aller Erkenntnisse.<br />

Quellprogramme wer<strong>de</strong>n über die Tastatur e<strong>in</strong>gegeben und auf externen Speicher<br />

abgelegt. Zur E<strong>in</strong>gabe, Korrektur und Än<strong>de</strong>rung stellt das System e<strong>in</strong> Programm mit<br />

<strong>de</strong>m Namen „Editor" zur Verfügung. Das Speichern <strong>de</strong>r e<strong>in</strong>gegebenen bzw.<br />

geän<strong>de</strong>rten Programme auf Externspeicher übernimmt das Systemprogramm<br />

„Dateiverwaltung“.<br />

<strong>Java</strong>-Programm<br />

Daten<br />

E<strong>in</strong>gabe (Tastatur)<br />

Externer Speicher<br />

Externer Speicher<br />

<strong>Java</strong>-Compiler<br />

System,<br />

<strong>Java</strong>-Quellpro-<br />

System- <strong>Java</strong>-Masch<strong>in</strong>e gramme<br />

programme<br />

<strong>Java</strong>-Laufzeitsystem<br />

<strong>Java</strong>-Byteco<strong>de</strong>-<br />

Dateien<br />

Ausgabe (Bildschirm)<br />

12


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

0.3 Der erste Versuch<br />

0.3.1 Das erste Programm<br />

Quelltext:<br />

import java.lang.*;<br />

/* ErstesProgramm ist e<strong>in</strong>e Applikation, die <strong>de</strong>n e<strong>in</strong>fachen<br />

Gebrauch von Zeichenketten aufzeigt */<br />

public class ErstesProgramm extends Object<br />

{<br />

// Beg<strong>in</strong>n <strong>de</strong>r Ausfuehrung vom Programm<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

// Ausgabe auf das Standard-Ausgabegeraet<br />

System.out.pr<strong>in</strong>tln(<br />

"Das erste Programm <strong>de</strong>r Vorlesung <strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong>.");<br />

}<br />

}<br />

Das Programm umfaßt zwei Teile: e<strong>in</strong>e Klassen<strong>de</strong>f<strong>in</strong>ition und e<strong>in</strong>en<br />

Programmabschnittt, <strong>de</strong>r durch die Metho<strong>de</strong> ma<strong>in</strong>() bestimmt ist.<br />

Je<strong>de</strong> <strong>Java</strong>-Anwendung besteht aus mehreren Klassen. Die Klasse, die die<br />

Ausgangsbasis für <strong>Java</strong>-Programme ist, muß die ma<strong>in</strong>()-Metho<strong>de</strong> benutzen:<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[]) { ... }.<br />

Es be<strong>de</strong>uten:<br />

public ... Die Metho<strong>de</strong> ist für an<strong>de</strong>re Klassen und Objekte verfügbar<br />

static ... Es han<strong>de</strong>lt sich um e<strong>in</strong>e Klassenmetho<strong>de</strong><br />

ma<strong>in</strong>() ... Die Funktion ma<strong>in</strong>() hat e<strong>in</strong>en Parameter vom Typ Zeichenkette<br />

(Str<strong>in</strong>g). Dieser Parameter dient zur Aufnahme von<br />

Befehlszeilenargumente. Argumente, die an <strong>Java</strong>-Programme<br />

übergeben wer<strong>de</strong>n, wer<strong>de</strong>n zu Zeichenketten konvertiert. In <strong>Java</strong>-<br />

Programmen ist ma<strong>in</strong>(), wie <strong>in</strong> C/C++-Programmen, die erste<br />

Rout<strong>in</strong>e <strong>de</strong>s Programms, die ausgeführt wird.<br />

Durch die „import“-Anweisung können Entwickler Klassen verwen<strong>de</strong>n, die <strong>in</strong><br />

an<strong>de</strong>ren Dateien <strong>de</strong>f<strong>in</strong>iert s<strong>in</strong>d. Compiler bzw. Interpreter greifen auf die Dateien zu.<br />

Über „import“ wird bestimmt, wo diese Dateien liegen. <strong>Java</strong> importiert immer das<br />

Paket „java.lang“, <strong>de</strong>nn hier ist die Klasse Object enthalten, von <strong>de</strong>r alle <strong>Java</strong>-<br />

Klassen abgeleitet s<strong>in</strong>d. Das Paket „java.lang“, die unmittelbare Ableitung von <strong>de</strong>r<br />

Klasse Object wer<strong>de</strong>n <strong>in</strong> <strong>Java</strong> per <strong>de</strong>fault bereitgestellt. Die diesbezüglichen<br />

Angaben im Programm können entfallen, <strong>de</strong>r Quelltext kann <strong>de</strong>shalb auch folgen<strong>de</strong><br />

Gestalt annehmen:<br />

/* ErstesProgramm ist e<strong>in</strong>e Applikation, die <strong>de</strong>n e<strong>in</strong>fachen<br />

Gebrauch von Zeichenketten aufzeigt */<br />

public class ErstesProgramm<br />

{<br />

// Beg<strong>in</strong>n <strong>de</strong>r Ausfuehrung vom Programm<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

// Ausgabe auf das Standard-Ausgabegeraet<br />

System.out.pr<strong>in</strong>tln(<br />

"Das erste Programm <strong>de</strong>r Vorlesung <strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong>.");<br />

}<br />

}<br />

13


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Token<br />

Die Ausdrucksformen <strong>de</strong>r Sprache <strong>Java</strong> wer<strong>de</strong>n Token genannt. In <strong>de</strong>rartige für die<br />

Sprache <strong>Java</strong> s<strong>in</strong>nvolle E<strong>in</strong>heiten muß sich <strong>de</strong>r Quelltext e<strong>in</strong>es <strong>Java</strong>-Programms<br />

zerlegen lassen. Es gibt <strong>in</strong> <strong>Java</strong> fünf Arten von Token: Bezeichner o<strong>de</strong>r I<strong>de</strong>ntifizierer,<br />

Schlüsselworte, Literale, Operatoren, Trennzeichen.<br />

Bezeichner, I<strong>de</strong>ntifizierer: Darunter versteht man die Benennungen (Namen) für<br />

Klassen, Objekte, Variable, Metho<strong>de</strong>n. Namen s<strong>in</strong>d zusammengesetzt aus Unico<strong>de</strong>-<br />

Zeichen. <strong>Java</strong> benutzt <strong>de</strong>n 16-Bit-Unico<strong>de</strong>-Zeichensatz, <strong>de</strong>ssen erste 256 Zeichen<br />

<strong>de</strong>m normalen ASCII-Zeichensatz entsprechen (Byte 1 ist immer auf 0 gesetzt). Im<br />

vorliegen<strong>de</strong>n Programm s<strong>in</strong>d z.B. Bezeichner: ErstesProgramm, ma<strong>in</strong>, System.<br />

<strong>Java</strong> unterschei<strong>de</strong>t Groß-/ Kle<strong>in</strong>schreibung. Namen bestehen <strong>in</strong> <strong>de</strong>r Regel aus<br />

alphabetischen Zeichen und Dezimalziffern. An <strong>de</strong>r ersten Stelle darf ke<strong>in</strong>e Zahl<br />

stehen.<br />

Schlüsselwörter: Das s<strong>in</strong>d Wörter, die e<strong>in</strong> wesentlicher Teil <strong>de</strong>r <strong>Java</strong>-<br />

Sprach<strong>de</strong>f<strong>in</strong>ition s<strong>in</strong>d, z.B.: public, class, void, Str<strong>in</strong>g.<br />

Literale: mit e<strong>in</strong>em Literal können Variablen und Konstanten bestimmte Werte<br />

zugewiesen wer<strong>de</strong>n. Literale können annehmen: numerische Werte (z.B. 13),<br />

boolesche Werte (true bzw. false), Zeichen (z.B. ‘A‘) und Zeichenketten (z.B.<br />

"Das erste Programm <strong>de</strong>r Vorlesung <strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong>.").<br />

Operatoren: Das s<strong>in</strong>d Zeichen bzw. Zeichenkomb<strong>in</strong>ationen zur Angabe e<strong>in</strong>er<br />

auszuführen<strong>de</strong>n Operation mit e<strong>in</strong>er o<strong>de</strong>r mehreren Variablen o<strong>de</strong>r Konstanten<br />

(Operan<strong>de</strong>n), z.B. + - / >.<br />

Variable s<strong>in</strong>d Arbeitsspeicherstellen, an <strong>de</strong>nen Informationen gespeichert wer<strong>de</strong>n<br />

können. Zur Deklaration erhält e<strong>in</strong>e Variable Namen (Bezeichner) und Typ<br />

zugeordnet. Der Typ kennzeichnet die Art <strong>de</strong>r Information, die die Variable<br />

aufnehmen kann, z.B.:<br />

<strong>in</strong>t i; // zur Aufnahme ganzer Zahlen<br />

Str<strong>in</strong>g s; // zur Aufnahme von Zeichenketten<br />

In <strong>Java</strong> unterschei<strong>de</strong>t man: elementare (primitive Typen) und benutzer<strong>de</strong>f<strong>in</strong>ierte<br />

Typen (e<strong>in</strong>schl. <strong>de</strong>r vom System bereitgestellten Klassen: Str<strong>in</strong>g und Array). Es<br />

gibt acht primitive Typen zum Speichern von ganzen Zahlen (byte, short, <strong>in</strong>t,<br />

long), Gleitpunktzahlen (float, double), Zeichen (char) und booleschen Werten<br />

(boolean).<br />

Sobald die Variable <strong>de</strong>klariert wur<strong>de</strong>, kann ihr über <strong>de</strong>n Zuweisungsoperator („=“) e<strong>in</strong><br />

Wert zugewiesen wer<strong>de</strong>n, z.B. <strong>in</strong>nerhalb <strong>de</strong>r ma<strong>in</strong>()-Metho<strong>de</strong><br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

Str<strong>in</strong>g s; // Deklaration <strong>de</strong>r Variablen s zur Aufnahme von Zeichenketten<br />

// Zuweisung<br />

s = "Das erste Programm <strong>de</strong>r Vorlesung <strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong>."<br />

// Aufruf <strong>de</strong>r Metho<strong>de</strong> pr<strong>in</strong>tln() <strong>de</strong>r Klasse System, die sich im Paket<br />

// java.lang bef<strong>in</strong><strong>de</strong>t<br />

System.out.pr<strong>in</strong>tln(s);<br />

}<br />

14


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Vorrang Operator Operan<strong>de</strong>ntyp Assoziation Operation<br />

1 ++<br />

--<br />

+, -<br />

~<br />

!<br />

(type)<br />

Arithmetisch<br />

Arithmetisch<br />

Arithmetisch<br />

Integral<br />

boolean<br />

irgen<strong>de</strong><strong>in</strong><br />

R<br />

R<br />

R<br />

R<br />

R<br />

R<br />

Pre- o<strong>de</strong>r Post-Inkrement<br />

(unär)<br />

Pre- o<strong>de</strong>r Post-<br />

Dekrement (unär)<br />

Unäres Plus, unäres<br />

M<strong>in</strong>us<br />

Bitweises Komplement<br />

(unär)<br />

Logisches Komplement<br />

(unär)<br />

cast<br />

2 *, /, % arithmetisch L Multiplikation, Division,<br />

Rest<br />

3 +, -<br />

+<br />

Arithmetisch<br />

Str<strong>in</strong>g<br />

L<br />

L<br />

Addition, Subtraktion<br />

Verkettung<br />

4 ><br />

>>><br />

Integral<br />

Integral<br />

Integral<br />

L<br />

L<br />

L<br />

L<strong>in</strong>ks-Shift<br />

Rechts-Shift mit<br />

Vorzeichen<br />

Rechts-Shift mit Nullen-<br />

5 =<br />

<strong>in</strong>stanceof<br />

6 ==<br />

!=<br />

==<br />

!=<br />

7 &<br />

&<br />

8 ^<br />

^<br />

Arithmetisch<br />

Arithmetisch<br />

Objekt, Typ<br />

Primitiver Typ<br />

Primitiver Typ<br />

Objekt<br />

Objekt<br />

Integral<br />

boolean<br />

Integral<br />

boolean<br />

L<br />

L<br />

L<br />

L<br />

L<br />

L<br />

L<br />

L<br />

L<br />

L<br />

L<br />

Nachziehen<br />

Kle<strong>in</strong>er als, kle<strong>in</strong>er als<br />

o<strong>de</strong>r gleich<br />

Größer als, größer als<br />

o<strong>de</strong>r gleich<br />

Objekt?, Instanz?<br />

Gleich (i<strong>de</strong>ntische Werte)<br />

Ungleich (verschie<strong>de</strong>ne<br />

Werte)<br />

Gleich (Referenz auf das<br />

gleiche Objekt)<br />

Ungleich (Referenz auf<br />

verschie<strong>de</strong>ne Objekte)<br />

Bitweises Und<br />

Logisches Und<br />

Bitweises O<strong>de</strong>r<br />

Logisches O<strong>de</strong>r<br />

(exklusiv)<br />

9 |<br />

|<br />

Integral<br />

boolean<br />

L<br />

L<br />

Bitweises O<strong>de</strong>r<br />

Logisches O<strong>de</strong>r (<strong>in</strong>klusiv)<br />

10 && boolean L Konditionelles Und<br />

11 || boolean L Konditionelles O<strong>de</strong>r<br />

12 ?= boolean,<br />

irgen<strong>de</strong><strong>in</strong>,<br />

irgen<strong>de</strong><strong>in</strong><br />

R<br />

Konditioneller (ternärer)<br />

Operator<br />

13 =<br />

*=, /=,<br />

+=, -=<br />

=,<br />

>>>=,<br />

&=, ^=,<br />

|=<br />

Abb. 0.3-1: <strong>Java</strong>-Operatoren<br />

Variable, irgen<strong>de</strong><strong>in</strong><br />

Variable, irgen<strong>de</strong><strong>in</strong><br />

R<br />

R<br />

Zuweisung<br />

Zuweisung mit Operation<br />

15


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

System.out.pr<strong>in</strong>tln(s); ist e<strong>in</strong>e Anweisung. Anweisungen stehen für e<strong>in</strong>e<br />

e<strong>in</strong>zelne Aktion, die <strong>in</strong> e<strong>in</strong>em <strong>Java</strong>-Programm ausgeführt wird. Je<strong>de</strong> Anweisung wird<br />

mit e<strong>in</strong>em Strichpunkt „;“ abgeschlossen. E<strong>in</strong>ige Anweisungen erzeugen e<strong>in</strong>en Wert,<br />

wie das bspw. beim Addieren zweier Zahlen <strong>de</strong>r Fall ist. Derartige Anweisungen<br />

wer<strong>de</strong>n als Ausdrücke bezeichnet.<br />

E<strong>in</strong> Ausdruck ist e<strong>in</strong>e Anweisung, die als Ergebnis e<strong>in</strong>en (Rückgabe-) Wert<br />

produziert. Dieser Wert kann zur späteren Verwendung im Programm gespeichert,<br />

direkt <strong>in</strong> e<strong>in</strong>er an<strong>de</strong>ren Anweisung verwen<strong>de</strong>t o<strong>de</strong>r überhaupt nicht beachtet wer<strong>de</strong>n.<br />

Ausdrücke können Konstanten, Variablen, Operatoren be<strong>in</strong>halten. Ausdrücke s<strong>in</strong>d<br />

vielfach mit unären und b<strong>in</strong>ären Operatoren verknüpft.<br />

Trennzeichen: Das s<strong>in</strong>d Symbole und Zusammenfassungen von Quellco<strong>de</strong>: ( ) {<br />

} [ ] ; . , .<br />

„(“: wird sowohl zum Öffnen e<strong>in</strong>er Parameterliste für e<strong>in</strong>e Metho<strong>de</strong> als auch zur<br />

Festlegung <strong>de</strong>r Priorität für Operationen <strong>in</strong> e<strong>in</strong>em Ausdruck benutzt.<br />

„)“: wird sowohl zum Schließen e<strong>in</strong>er Parameterliste für e<strong>in</strong>e Metho<strong>de</strong> als auch zur<br />

Festlegung <strong>de</strong>r Priorität für Operationen <strong>in</strong> e<strong>in</strong>em Ausdruck benutzt.<br />

Bsp.: public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

„{“: wird zu Beg<strong>in</strong>n e<strong>in</strong>es Blocks mit Anweisungen o<strong>de</strong>r e<strong>in</strong>er Initialisierungsliste<br />

gesetzt.<br />

„}“:wird an das En<strong>de</strong> e<strong>in</strong>es Blocks mit Anweisungen o<strong>de</strong>r e<strong>in</strong>er Initialisierungsliste<br />

gesetzt.<br />

Bsp.: Metho<strong>de</strong>n wer<strong>de</strong>n <strong>in</strong> <strong>Java</strong> durch e<strong>in</strong>en Anweisungsblock bestimmt. E<strong>in</strong> Anweisungsblock<br />

besteht <strong>in</strong> <strong>de</strong>r Regel aus e<strong>in</strong>er Reihe von Anweisungen, die von geschweiften Klammern umschlossen<br />

s<strong>in</strong>d.<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

Str<strong>in</strong>g s;<br />

s = "Das erste Programm <strong>de</strong>r Vorlesung <strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong>.";<br />

System.out.pr<strong>in</strong>tln(s);<br />

}<br />

„[“: steht für e<strong>in</strong>e Ausdruck, <strong>de</strong>r als In<strong>de</strong>x für e<strong>in</strong> Datenfeld (Array) steht.<br />

„]“: folgt e<strong>in</strong>em Ausdruck, <strong>de</strong>r als In<strong>de</strong>x für e<strong>in</strong> Datenfeld steht.<br />

Bsp.: Str<strong>in</strong>g args[] = {"Juergen", "Hubert", "Josef", "Liesel", "Christian"};<br />

<strong>de</strong>f<strong>in</strong>iert e<strong>in</strong>en Array mit 5 Komponenten, die alle <strong>de</strong>n gleichen Typ besitzen.<br />

Datenfel<strong>de</strong>r (Arrays) dienen zum Speichern von Elementen, die alle <strong>de</strong>nselben Typ aufweisen.<br />

Je<strong>de</strong>m Element wird <strong>in</strong>nerhalb <strong>de</strong>s Datenfelds e<strong>in</strong> eigener Speicherplatz zugewiesen. Dieser<br />

Speicherplatz ist für <strong>de</strong>n leichten Zugriff durchnumeriert.<br />

[0] "Juergen"<br />

[1] "Hubert"<br />

[2] "Josef"<br />

[3] "Liesel"<br />

[4] "Christian"<br />

Auf <strong>de</strong>n Wert e<strong>in</strong>er Komponenten kann über <strong>de</strong>n Array-Namen (z.B. args), gefolgt von e<strong>in</strong>em In<strong>de</strong>x <strong>in</strong><br />

eckigen Klammern (z.B. [0]) zugegriffen wer<strong>de</strong>n. Mit<br />

args[0] = "Roland";<br />

kann <strong>de</strong>m ersten Element <strong>de</strong>s Array args e<strong>in</strong> neuer Wert zugewiesen wer<strong>de</strong>n.<br />

„;“: dient zum Been<strong>de</strong>n e<strong>in</strong>er Anweisung.<br />

„,“: wird häufig als Begrenzer (z.B. <strong>in</strong> e<strong>in</strong>er Parameterliste) benutzt.<br />

„.“: wird als Dezimalpunkt als auch zum Trennen solcher D<strong>in</strong>ge wie Paketnamen von<br />

Klassennamen o<strong>de</strong>r Variablennamen benutzt (z.B. System.out.pr<strong>in</strong>tln(s);).<br />

16


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Standardmäßig haben Klassen Zugriff auf die Klassen im Paket „java.lang“. Zur<br />

Bezugnahme auf e<strong>in</strong>e Klasse, die sich nicht <strong>in</strong> java.lang bef<strong>in</strong><strong>de</strong>t, s<strong>in</strong>d alle Pakete,<br />

<strong>in</strong> <strong>de</strong>nen sich die Klasse bef<strong>in</strong><strong>de</strong>t, anzugeben, z.B.<br />

java.awt.Color // bezieht sich auf die Klasse Color im Paket awt,<br />

// das sich im Paket java bef<strong>in</strong><strong>de</strong>t.<br />

Leerraumzeichen: Sie können <strong>in</strong> beliebiger Anzahl und an je<strong>de</strong>m Ort zwischen Token<br />

(die e<strong>in</strong>e Funktion besitzen) zur übersichtlichen Gestaltung <strong>de</strong>s Quellco<strong>de</strong>s plaziert<br />

wer<strong>de</strong>n. Solche Zeichen s<strong>in</strong>d bspw.: Space, Tab, Zeilenen<strong>de</strong>, Formularvorschub<br />

Kommentar: Er wird vom Compiler ignoriert. Man unterschei<strong>de</strong>t <strong>de</strong>n Kommentar bis<br />

zum Zeilenen<strong>de</strong> „//“ und <strong>de</strong>n e<strong>in</strong>gebetteten Kommentar „/* ... */“, z.B.:<br />

/* ErstesProgramm ist e<strong>in</strong>e Applikation, die <strong>de</strong>n e<strong>in</strong>fachen<br />

Gebrauch von Zeichenketten aufzeigt */<br />

Übersetzung und Ausführung.<br />

Bef<strong>in</strong><strong>de</strong>t sich <strong>de</strong>r Quelltext zum Programm <strong>in</strong> e<strong>in</strong>er Datei mit <strong>de</strong>m Namen<br />

ErstesProgramm.java, dann kann dieses Programm durch Aufruf <strong>de</strong>s <strong>Java</strong>-<br />

Compilers javac <strong>in</strong> ablauffähigen Byteco<strong>de</strong> übersetzt wer<strong>de</strong>n. Das geschieht über<br />

das folgen<strong>de</strong> Systemkommando:<br />

javac ErstesProgramm.java<br />

Den Byte-Co<strong>de</strong>, <strong>de</strong>n <strong>de</strong>r <strong>Java</strong>-Compiler <strong>in</strong> <strong>de</strong>r Datei ErstesProgramm.class<br />

h<strong>in</strong>terlegt hat, kann über das Kommando<br />

java ErstesProgramm<br />

zur Ausführung gebracht wer<strong>de</strong>n.<br />

17


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Konsole: Arbeitsspeicher: Externspeicher<br />

javac ErstesProgramm.java<br />

ErstesProgramm.java<br />

ErstesProgramm.class<br />

java ErstesProgramm<br />

ErstesProgramm.class<br />

<strong>Java</strong> (-Interpreter)<br />

"Das erste Programm <strong>de</strong>r Vorlesung <strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong>"<br />

Abb. 0.3-2: Ablaufplan ErstesProgramm<br />

18


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

0.3.2 Bestandteile e<strong>in</strong>es Programms<br />

Wesentliche Programmelemente <strong>in</strong> <strong>Java</strong> s<strong>in</strong>d:<br />

- Anweisungen<br />

Anweisungen 3 gehören zu <strong>de</strong>n elementaren ausführbaren Programmelementen. E<strong>in</strong>e Anweisung<br />

kann e<strong>in</strong>e Deklaration enthalten, e<strong>in</strong>en Ausdruck 4 auswerten o<strong>de</strong>r <strong>de</strong>n Programmablauf 5 (Auswahl-,<br />

Iterations-, Sprung-Anweisungen und return-, throw-Anweisung) steuern.<br />

- Blöcke<br />

E<strong>in</strong> Block 6 ist e<strong>in</strong>e Zusammenstellung von Anweisungen, die nache<strong>in</strong>an<strong>de</strong>r ausgeführt wer<strong>de</strong>n. E<strong>in</strong><br />

Block kann eigene Variable <strong>de</strong>f<strong>in</strong>ieren, die nur <strong>in</strong>nerhalb <strong>de</strong>s Blocks sichtbar s<strong>in</strong>d. Sie wer<strong>de</strong>n beim<br />

Aufruf <strong>de</strong>s Blocks angelegt und beim Verlassen <strong>de</strong>s Blocks zerstört. Innerhalb e<strong>in</strong>es Blocks s<strong>in</strong>d nur<br />

die lokalen Variablen <strong>de</strong>s Blocks und die lokalen Variablen <strong>de</strong>s umgeben<strong>de</strong>n Blocks bzw. <strong>de</strong>r<br />

umgeben<strong>de</strong>n Metho<strong>de</strong> sichtbar. Nach außen stellt sich <strong>de</strong>r Block als e<strong>in</strong>e e<strong>in</strong>zige Anweisung dar.<br />

- Metho<strong>de</strong>n<br />

Metho<strong>de</strong>n 7 unterschei<strong>de</strong>n sich von Blöcken folgen<strong>de</strong>rmaßen:<br />

-- Sie haben e<strong>in</strong>en Namen und können von verschie<strong>de</strong>nen Stellen <strong>de</strong>s Programms aufgerufen<br />

wer<strong>de</strong>n.<br />

-- Sie s<strong>in</strong>d parametrisierbar<br />

-- Sie können e<strong>in</strong>en Rückgabewert besitzen.<br />

Metho<strong>de</strong>n wer<strong>de</strong>n <strong>in</strong> <strong>Java</strong> immer lokal zu e<strong>in</strong>er Klasse <strong>de</strong>f<strong>in</strong>iert.<br />

- Klassen<br />

Sie 8 enthalten Variablen zur Beschreibung <strong>de</strong>s Zustands von Objekten und Metho<strong>de</strong>n zur<br />

Beschreibung <strong>de</strong>s Verhaltens von Objekten.<br />

- Schnittstellen<br />

E<strong>in</strong>e Schnittstelle (Interface) ist e<strong>in</strong>e Sammlung von Metho<strong>de</strong>n, die e<strong>in</strong>en Namen besitzen, aber<br />

nicht implementiert s<strong>in</strong>d. E<strong>in</strong> Klasse kann beliebig viele Schnittstellen implementieren. Dadurch wird<br />

die Klasse zur Implementierung <strong>de</strong>r Metho<strong>de</strong> gezwungen, <strong>de</strong>ren Namen von <strong>de</strong>r Schnittstelle<br />

<strong>de</strong>f<strong>in</strong>iert wur<strong>de</strong>n. Falls zwei unterschiedliche Klassen, dieselbe Schnittstelle implementieren, können<br />

bei<strong>de</strong> auf Aufrufe <strong>de</strong>r Metho<strong>de</strong>, die <strong>in</strong> <strong>de</strong>r Schnittstelle <strong>de</strong>f<strong>in</strong>iert s<strong>in</strong>d, reagieren. Allerd<strong>in</strong>gs kann die<br />

Reaktion auf diese Metho<strong>de</strong>naufrufe bei e<strong>in</strong>zelnen Klassen total unterschiedlich se<strong>in</strong>.<br />

- Pakete<br />

E<strong>in</strong> Paket ist e<strong>in</strong>e Sammlung von Klassen. Je<strong>de</strong> Klasse <strong>in</strong> <strong>Java</strong> gehört zu e<strong>in</strong>em Paket. Pakete<br />

ermöglichen, daß Sammlungen von Klassen bei Bedarf verfügbar s<strong>in</strong>d. Die Klassenbibliotheken<br />

bef<strong>in</strong><strong>de</strong>n sich <strong>in</strong> e<strong>in</strong>em Paket mit <strong>de</strong>m Namen „java“. Dieses Paket be<strong>in</strong>haltet Pakete, die spezielle<br />

Bestandteile <strong>de</strong>r Sprache <strong>Java</strong>, z.B. Dateie<strong>in</strong>gabe und Datenausgabe, Multimedia, etc. <strong>de</strong>f<strong>in</strong>ieren.<br />

Standardmäßig haben Klassen <strong>de</strong>r Anwen<strong>de</strong>r nur Zugrifff auf Klassen im Paket „java.lang“<br />

(Standard-Feature). Klassen irgen<strong>de</strong><strong>in</strong>es an<strong>de</strong>ren Pakets müssen importiert wer<strong>de</strong>n.<br />

- Applikationen (Anwendungen)<br />

Anwendungen (Applikationen) bil<strong>de</strong>n die eigenständigen Programme. Sie benötigen zur Ausführung<br />

ke<strong>in</strong>en Browser, son<strong>de</strong>rn nur <strong>de</strong>n <strong>Java</strong>-Interpreter und die .class-Dateien <strong>de</strong>r verwen<strong>de</strong>ten<br />

Klassen.<br />

- Applets<br />

Applets s<strong>in</strong>d ebenfalls lauffähige <strong>Java</strong>-Programme. Sie wer<strong>de</strong>n aus e<strong>in</strong>er HTML-Seite aufgerufen<br />

und benötigen zur Ausführung e<strong>in</strong>en Web-Browser (o<strong>de</strong>r e<strong>in</strong> Werkzeug wie <strong>de</strong>n Appletviewer).<br />

Applets müssen von <strong>de</strong>r Klasse Applet abgeleitet und nach <strong>de</strong>n Regeln dieser Klasse aufgebaut<br />

se<strong>in</strong>. Zum Starten <strong>de</strong>s Programms erzeugt <strong>de</strong>r Browser e<strong>in</strong>e Instanz <strong>de</strong>r abgeleiteten Klasse und<br />

ruft e<strong>in</strong>e Reihe vor<strong>de</strong>f<strong>in</strong>ierter Callback-Metho<strong>de</strong>n 9 auf.<br />

3 vgl. 2.4<br />

4 vgl. 2.4.5<br />

5 vgl. 2.4.6<br />

6 vgl. 2.4.1<br />

7 vgl. 2.6<br />

8 vgl. 2.5<br />

9 CallBack-Metho<strong>de</strong>n s<strong>in</strong>d von <strong>de</strong>r abgeleiteteten Klasse zur Verfügung gestellte Metho<strong>de</strong>n, die vom Browser<br />

bzw. Appletviewer aufgerufen wer<strong>de</strong>n<br />

19


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

20


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

1. E<strong>in</strong>führung <strong>in</strong> die <strong>Java</strong>-Programmierung<br />

1.1 Übersicht zur Entwicklung <strong>de</strong>r Programmiersprache <strong>Java</strong><br />

<strong>Java</strong> ist <strong>in</strong> <strong>de</strong>n Entwicklungslaboren <strong>de</strong>r amerikanischen Firma Sun Microsystems 10<br />

entstan<strong>de</strong>n. Man entschied sich bei Sun zur Realisierung e<strong>in</strong>es im Jahre 1990<br />

begonnenen Projekts 11 für e<strong>in</strong>e neue Programmiersprache, da bisher entwickelte<br />

Programme mit vorliegen<strong>de</strong>n Programmiersprachen zu große Schwächen zeigten.<br />

Der erste Versuch war nur bed<strong>in</strong>gt erfolgreich. Lediglich <strong>de</strong>r damals im Internet<br />

verbreitete Mosaic-Browser 12 wur<strong>de</strong> zu e<strong>in</strong>er Zielplattform <strong>de</strong>r neuen<br />

Programmiersprache 13 , die En<strong>de</strong> 1994 für das Internet umgearbeitet wur<strong>de</strong> und über<br />

das Netz frei und umsonst verteilt wur<strong>de</strong>.<br />

1995 wur<strong>de</strong> die neue Programmiersprache mit <strong>de</strong>m Namen <strong>Java</strong> 14 <strong>de</strong>r Internet-<br />

Öffentlichkeit <strong>in</strong> Komb<strong>in</strong>ation mit e<strong>in</strong>em Browser, Hot<strong>Java</strong>, präsentiert. Hot<strong>Java</strong><br />

war die erste komplexe und vollständig <strong>in</strong> <strong>Java</strong> geschriebene Anwendung, <strong>de</strong>r erste<br />

<strong>Java</strong>-fähige Browser und damit die Präsentationsform für die ersten <strong>Java</strong>-Applets.<br />

Außer<strong>de</strong>m war dieser Browser e<strong>in</strong>e wesentliche Ergänzung <strong>de</strong>s ersten <strong>Java</strong>-<br />

Entwicklungstools von Sun – das <strong>Java</strong> Develelopment Kit (JDK 1.0). E<strong>in</strong><br />

kommerzielles Produkt, <strong>de</strong>r <strong>Java</strong> Workshop 15 , wur<strong>de</strong> kurz nach <strong>de</strong>r Präsentation von<br />

JDK 1.0 bereitgestellt.<br />

Natürlich gab es im JDK noch diverse K<strong>in</strong><strong>de</strong>rkrankheiten. Im zweiten Quartal 1997<br />

folgte <strong>de</strong>shalb nach e<strong>in</strong>igen Zwischenversionen die Version 1.1 <strong>de</strong>s JDK. Parallel zur<br />

10 Sun ist e<strong>in</strong>e <strong>de</strong>r führen<strong>de</strong>n Hersteller von Workstations<br />

11 Entwicklung e<strong>in</strong>es vollkommen neuen, plattformunabhängigen Betriebssystems für <strong>de</strong>n „Consumerbereich<br />

<strong>de</strong>r allgeme<strong>in</strong>en Elektronik (Telefone, Vi<strong>de</strong>orecor<strong>de</strong>r, Waschmasch<strong>in</strong>en, Kaffemasch<strong>in</strong>en; eigentlich alle<br />

elektrischen Masch<strong>in</strong>en, die Daten benötigen)<br />

12 <strong>de</strong>r erste WWW-Browser mit e<strong>in</strong>er grafischen Benutzeroberfläche. WWW steht für World Wi<strong>de</strong> Web und ist<br />

<strong>in</strong>zwischen die wichtigste Stütze im Internet. Das WWW ist im wesentlichen durch sog. Hypertexte aufgebaut,<br />

die mit <strong>de</strong>r Sprache HTML entwickelt wur<strong>de</strong>n und wer<strong>de</strong>n. E<strong>in</strong> Hypertext ist im wesentlichen e<strong>in</strong> ASCII-Text,<br />

<strong>de</strong>r durch maskierte Wörter (sog. Hyperl<strong>in</strong>ks) zu weiteren Seiten führt. Hypertext ist eigentlich nur e<strong>in</strong> Text mit<br />

Verweisen auf an<strong>de</strong>re Texte. Der Verweis auf <strong>de</strong>n weiterführen<strong>de</strong>n Text kann aktiviert wer<strong>de</strong>n (z.B. durch<br />

Mausklick), und es wird zu <strong>de</strong>m gewünschten Text verzweigt.<br />

Das Hypertext Transfer Protocol (HTTP) dient zur Übertragung von Informationen aus <strong>de</strong>m WWW. HTTP ist<br />

e<strong>in</strong> objektorientiertes Protokoll (TCP/IP-Programm) zur e<strong>in</strong>fachen Übertragung von Hypertext-Dokumenten<br />

zwischen Client und Server. Client-Programme, die HTTP benutzen, wer<strong>de</strong>n (<strong>in</strong> <strong>de</strong>r Regel) als Web-Browser,<br />

Server-Programme als Web-Server bezeichnet. Der Browser schickt an <strong>de</strong>n Server die Auffor<strong>de</strong>rung e<strong>in</strong>e<br />

bestimmte HTML-Seite zu übertragen. Falls er <strong>in</strong> dieser Seite weitere Verweise (z.B. auf Bil<strong>de</strong>r) ent<strong>de</strong>ckt,<br />

schickt er weitere Übertragungswünsche h<strong>in</strong>terher. Das Besorgen <strong>de</strong>r gewünschten Dokumente erfolgt über e<strong>in</strong><br />

e<strong>in</strong>heitliches Adressierungsschema, <strong>de</strong>m Uniform Resource Locater (URL), durch <strong>de</strong>n Internet-Standort und die<br />

Art <strong>de</strong>r zu übertragen<strong>de</strong>n Information i<strong>de</strong>ntifiziert wer<strong>de</strong>n.<br />

13 Dem WWW mit <strong>de</strong>m bis zu diesem Zeitpunkt realisierten Stand <strong>de</strong>r HTML fehlten: dreidimensionale<br />

Darstellung <strong>de</strong>r Objekte, e<strong>in</strong>e bewegte Animation und e<strong>in</strong>e Möglichkeit zur vernünftigen Interaktion mit <strong>de</strong>m<br />

Anwen<strong>de</strong>r. Deshalb waren hier die Multimedia- und Interaktionseigenschaften <strong>de</strong>r neuen Programmiersprache<br />

beson<strong>de</strong>rs erfolgreich.<br />

14 verantwortlich für <strong>Java</strong> ist die Firma <strong>Java</strong>Soft – e<strong>in</strong>e Tochterfirma von Sun Microsystems. Sun bzw. <strong>Java</strong>Soft<br />

halten im Internet permanent die aktuellste Information von <strong>Java</strong> bereit. E<strong>in</strong>ige <strong>de</strong>r Informationen f<strong>in</strong><strong>de</strong>t man<br />

bereits auf <strong>de</strong>r E<strong>in</strong>stiegseite von Sun (http://java.sun.com), an<strong>de</strong>re Informationen bekommt man von <strong>de</strong>r<br />

Neuigkeitenseite (http://java.sun.com/nav/new/<strong>in</strong><strong>de</strong>x.html)<br />

15 mit Test- und Debug-Möglichkeiten, e<strong>in</strong>em Referenzcompiler, e<strong>in</strong>er <strong>in</strong>tegrierten Entwicklungsumgebung mit<br />

Editor, Browser, Project-, Portfolio- und Build-Manager, Debugger, Project-Tester und Onl<strong>in</strong>e-Hilfe. Der<br />

Workshop geht mit <strong>de</strong>r Version 2.0 <strong>in</strong>zwischen <strong>in</strong> e<strong>in</strong>e neue Phase zur Unterstützung <strong>de</strong>s neuen <strong>Java</strong>.<br />

21


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

1.1.x-Version gab es auch e<strong>in</strong>en neuen Hot-<strong>Java</strong>-Browser zur Unterstützung <strong>de</strong>r<br />

neuen 1.1-API-Funktionen.<br />

<strong>Java</strong> 1.2 ist die neueste Version. In Verb<strong>in</strong>dung mit <strong>de</strong>m JDK 1.2 wur<strong>de</strong> <strong>de</strong>r Begriff<br />

<strong>Java</strong> 2 16 e<strong>in</strong>geführt. Inzwischen gibt es schon die Version 1.3, die nur wenige neue<br />

Programmierschnittstellen bietet, dafür aber e<strong>in</strong>e komfortablere und schnellere<br />

Laufzeitumgebung.<br />

Seit Version 1.2 ist <strong>Java</strong> <strong>in</strong> verschie<strong>de</strong>nen Ausgaben (Editionen) erhältlich. Sun hat<br />

drei verschie<strong>de</strong>ne Editionen mit unterschiedlicher Ausrichtung <strong>de</strong>f<strong>in</strong>iert:<br />

1. Standard-Edition für <strong>de</strong>n Desktop Client o<strong>de</strong>r PC<br />

2. Enterprise-Edition für Application Server<br />

3. Micro-Edition für Kle<strong>in</strong>geräte.<br />

Am weitesten entwickelt ist die Standard-Edition.<br />

Neben <strong>de</strong>m JDK gibt es zahlreiche kommerzielle Entwicklungstools für <strong>Java</strong>-<br />

Programmierer, z. B.: Symantec Visual Café, Borland JBuil<strong>de</strong>r, SuperCa<strong>de</strong>, National<br />

Intelligence Roaster, SunSoft <strong>Java</strong> Workshop.<br />

1.2 Was ist <strong>Java</strong>?<br />

<strong>Java</strong> ist 17 e<strong>in</strong>e e<strong>in</strong>fache, objektorientierte, <strong>de</strong>zentrale, <strong>in</strong>terpretierte, stabil laufen<strong>de</strong>,<br />

sichere, architekturneutrale, portierbare und dynamische Sprache, die<br />

Hochleistungsgeschw<strong>in</strong>digkeitsanwendungen und Multithread<strong>in</strong>g unterstützt.<br />

- <strong>Java</strong> ist e<strong>in</strong>fach, obwohl es sehr nahe an <strong>de</strong>r ziemlich komplizierten C/C++-Syntax entworfen wur<strong>de</strong>.<br />

Die komplexen Teile von C/C++ wur<strong>de</strong>n jedoch aus <strong>Java</strong> ausgeschlossen. In <strong>Java</strong> gibt es ke<strong>in</strong>e<br />

Zeiger (Po<strong>in</strong>ter) und auch ke<strong>in</strong>e Zeiger-Arithmetik. Str<strong>in</strong>gs und Arrays s<strong>in</strong>d echte Objekte. Die<br />

Speicherverwaltung erfolgt weitgehend automatisch.<br />

Die Ausdrücke <strong>in</strong> <strong>Java</strong> 18 entsprechen aber weitgehend <strong>de</strong>nen von C/C++. <strong>Java</strong> besitzt e<strong>in</strong>e „if“-<br />

Anweisung, e<strong>in</strong>e „while“-, „do“- und „for“-Schleife und e<strong>in</strong>e „switch“-Anweisung. Es gibt die von C<br />

bekannten „break“- und „cont<strong>in</strong>ue“-Anweisungen (<strong>in</strong> normaler und mit e<strong>in</strong>em „Label“ versehenen<br />

Form) 19 .<br />

- <strong>Java</strong> ist kle<strong>in</strong>. E<strong>in</strong>e <strong>de</strong>r ursprünglichen Ziele von <strong>Java</strong> war die Erleichterung <strong>de</strong>r Software-Entwicklung für<br />

kle<strong>in</strong>e Rechner.<br />

- <strong>Java</strong> ist objektorientiert. Es gehört zu e<strong>in</strong>er Familie von Sprachen, die Daten als Objekte <strong>de</strong>f<strong>in</strong>ieren<br />

und Metho<strong>de</strong>n zur Bearbeitung dieser Objekte verwen<strong>de</strong>n. Das objektorientierte Konzept von <strong>Java</strong><br />

hat viel von C++ geerbt, aber auch Konzepte an<strong>de</strong>rer objektorientierter Sprachen wur<strong>de</strong>n<br />

übernommen. Wie die meisten objektorientierten Sprachen umfaßt <strong>Java</strong> e<strong>in</strong>e umfangreiche<br />

Klassenbibliothek, die grundlegen<strong>de</strong> Datentypen, Systeme<strong>in</strong>- und Systemausgabe und an<strong>de</strong>re<br />

Hilfsmittel (utilities) bietet. Die grundlegen<strong>de</strong>n Klassen s<strong>in</strong>d Teil <strong>de</strong>s JDK, das darüber h<strong>in</strong>aus noch<br />

Klassen besitzt, die Funktionen im Zusammenhang mit <strong>in</strong> Netzwerken üblichen Internet-Protokollen<br />

und Benutzeroberflächen unterstützen. Da diese Klassenbibliotheken <strong>in</strong> <strong>Java</strong> geschrieben s<strong>in</strong>d, s<strong>in</strong>d<br />

sie, wie alle <strong>Java</strong>-Anwendungen, auf alle Plattformen portierbar.<br />

- <strong>Java</strong> ist <strong>de</strong>zentral und erfüllt e<strong>in</strong>e wesentliche Eigenschaft von Client/Server-Anwendungen. Die<br />

Fähigkeit <strong>de</strong>r Verteilung von Informationen für die Berechnung <strong>de</strong>r Daten. „Dezentral“ beschreibt die<br />

Beziehung von Systemobjekten: Es ist gleichgültig, ob die Objekte sich auf lokalen o<strong>de</strong>r entfernten<br />

16 Die exakte Bezeichnung für das JDK 1.2 ist <strong>Java</strong> 2 JDK v1.2. Nähere Information enthält die Webseite:<br />

http://java.sun.com/products/jdk/1.2/java2.html<br />

17 Offizielle Def<strong>in</strong>ition von Sun Microsystems<br />

18 vgl. 2.3<br />

19 vgl. 2.4.<br />

22


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Systemen bef<strong>in</strong><strong>de</strong>n. Objekte können mit <strong>Java</strong>-Programmen über URLs 20 vom gesamten Web<br />

genauso wie auf <strong>de</strong>m lokalen Rechner geöffnet und bearbeitet wer<strong>de</strong>n. Wichtige Teile <strong>de</strong>r<br />

Anwendung bzw. <strong>de</strong>r Daten können lokal vorhan<strong>de</strong>n se<strong>in</strong>, an<strong>de</strong>re wer<strong>de</strong>n bei Bedarf gela<strong>de</strong>n.<br />

- <strong>Java</strong> ist <strong>in</strong>terpretiert. E<strong>in</strong> gewisser Teil <strong>de</strong>s <strong>Java</strong>-Co<strong>de</strong>s (ca. 20 %) wer<strong>de</strong>n vom Conta<strong>in</strong>er, <strong>de</strong>m<br />

Browser <strong>in</strong>terpretiert. Der <strong>Java</strong>-Quellco<strong>de</strong> wird mit <strong>de</strong>m <strong>Java</strong>-Compiler <strong>in</strong> Byteco<strong>de</strong><br />

(architekturneutrales Object-Co<strong>de</strong>-Format) kompiliert. Byteco<strong>de</strong> ist nicht lauffähig, bis er von <strong>de</strong>r<br />

<strong>Java</strong>-Laufzeitumgebung 21 <strong>in</strong>terpretiert wird.<br />

<strong>Java</strong>-Compiler (Pentium)<br />

<strong>Java</strong>-Interpreter (Pentium)<br />

<strong>Java</strong>-Co<strong>de</strong><br />

<strong>Java</strong>-Compiler (SPARC)<br />

<strong>Java</strong>-Interpreter (SPARC)<br />

Abb. 1.2-1:<br />

- <strong>Java</strong> ist stabil, d.h. zuverlässig. Die Stabilität e<strong>in</strong>er Programmiersprache zeigt sich dar<strong>in</strong>, daß<br />

während <strong>de</strong>r Kompilierungsphase <strong>de</strong>r gößte Teil <strong>de</strong>r Datenüberprüfung ausgeführt wer<strong>de</strong>n kann.<br />

<strong>Java</strong> ist stark typisiert. Damit können Fehler früh gefun<strong>de</strong>n wer<strong>de</strong>n. E<strong>in</strong> weiteres Stabilitätskriterium<br />

von <strong>Java</strong>-Programmen ist die e<strong>in</strong>gebaute Begrenzung <strong>de</strong>r Zugriffsmöglichkeiten auf <strong>de</strong>n<br />

Speicherbereich <strong>de</strong>s Rechners 22 . H<strong>in</strong>zu kommt auch noch die anschließen<strong>de</strong><br />

Sicherheitsüberprüfung durch <strong>de</strong>n „L<strong>in</strong>ker“. Der L<strong>in</strong>ker ist e<strong>in</strong> Teil <strong>de</strong>r Laufzeitumgebung, das die im<br />

System e<strong>in</strong>gehen<strong>de</strong>n Daten überprüft.<br />

20 Rechner s<strong>in</strong>d über IP-Nummern bzw. über Alias-Namen (Doma<strong>in</strong>-Name-System, DNS) im Internet e<strong>in</strong><strong>de</strong>utig<br />

adressiert. Innerhalb <strong>de</strong>s Internet müssen aber auch alle Daten und Programme über unverwechselbare Internet-<br />

Adressen bestimmt se<strong>in</strong>. Der Name dieser Internet-Adressen für konkrete Adressanfragen an Dokumente im<br />

Internet lautet URL und steht für Uniform Resource Locator („e<strong>in</strong>heitliches Adressierungsschema für<br />

Objekte“ im Internet). „E<strong>in</strong>heitlich“ <strong>de</strong>shalb, weil mit e<strong>in</strong>er URL sowohl die verschie<strong>de</strong>nen Dienste (WWW,<br />

FTP, Gopher usw.) als auch Rechner o<strong>de</strong>r Dokumente beschrieben wer<strong>de</strong>n können. Der Begriff „Objekt“ steht<br />

bspw. für Datei, Text, Vi<strong>de</strong>os, Sounds usw., also für ziemlich alles, was sich im Netz bef<strong>in</strong><strong>de</strong>t. Die exakte<br />

Schreibweise e<strong>in</strong>er URL ist je nach Dienstprotokoll etwas unterschiedlich, sieht jedoch <strong>in</strong> <strong>de</strong>r Regel so aus:<br />

Dienstprotokoll://host.doma<strong>in</strong>:port/pfad/datei<br />

Dienstprotokoll ist bspw.: http, ftp, gopher, news, mailto, wais. Danach folgen fast immer Doppelpunkt und<br />

zwei Slashes (Ausnahme: mailto).<br />

Mit hosts.doma<strong>in</strong> wird e<strong>in</strong> Rechner im Internet adressiert. Dabei kann die IP-Nummer <strong>de</strong>s Rechner angegeben<br />

wer<strong>de</strong>n (unüblich). Häufiger nimmt man dafür <strong>de</strong>n DNS-Namen (<strong>in</strong> <strong>de</strong>r Form<br />

host.{localDoma<strong>in</strong>}.SecondLevelDoma<strong>in</strong>.TopLevelDoma<strong>in</strong>.<br />

Auf e<strong>in</strong>em Internet-Rechner kann unter e<strong>in</strong>em Namen e<strong>in</strong>e ganze Reihe von verschie<strong>de</strong>nen Diensten parallel<br />

betrieben wer<strong>de</strong>n (z.B. FTP-Server, HTTP-Server). Zum Erreichen <strong>de</strong>s gewünschten Dienstes auf <strong>de</strong>m<br />

ausgewählten Rechner benötigt man <strong>de</strong>n sog. Port. Ports s<strong>in</strong>d numerische Werte (zwischen 0 und 1023). In <strong>de</strong>r<br />

Regel hat je<strong>de</strong>r Internet-Dienst e<strong>in</strong>en Default-Wert, <strong>de</strong>r immer verwen<strong>de</strong>t wird, wenn ke<strong>in</strong> Port explizit<br />

angegeben ist. Der Port für e<strong>in</strong>en HTTP-Server ist immer „80“, e<strong>in</strong> FTP-Server hat immer <strong>de</strong>n Port „21“.<br />

Das genaue Objekt verbirgt sich h<strong>in</strong>ter <strong>de</strong>r Angabe /pfad/datei.<br />

21 Normalerweise durch e<strong>in</strong>en <strong>Java</strong>-Browser<br />

22 Ungeprüfte Zugriffe auf Speicherbereiche <strong>de</strong>s Rechners ermöglicht C/C++ (Zeigerarithmetik, implizite<br />

Deklaration)<br />

23


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

- <strong>Java</strong> gilt als sicher. In dieser H<strong>in</strong>sicht muß sich <strong>Java</strong> allerd<strong>in</strong>gs noch bewähren 23 .<br />

- <strong>Java</strong> ist auf verschie<strong>de</strong>n Systemen mit unterschiedlichen Prozessoren und Betriebssystemarchitekturen<br />

lauffähig (architekturneutral). Der komplette <strong>Java</strong>-Byteco<strong>de</strong> kann auf je<strong>de</strong>m Prozessor<br />

ausgeführt wer<strong>de</strong>n, <strong>de</strong>r e<strong>in</strong>en javafähigen Browser (bspw. die virtuelle Masch<strong>in</strong>e von <strong>Java</strong> 24 )<br />

unterstützt. Plattformunabhängiger B<strong>in</strong>ärco<strong>de</strong> wird allerd<strong>in</strong>gs nicht erzeugt, son<strong>de</strong>rn <strong>Java</strong>-Byteco<strong>de</strong><br />

wird während <strong>de</strong>r Laufzeit <strong>in</strong> systemeigenen Masch<strong>in</strong>enco<strong>de</strong> übertragen (<strong>in</strong>terpretiert).<br />

- <strong>Java</strong> unterstützt „Multithread<strong>in</strong>g“. „Multthread<strong>in</strong>g be<strong>de</strong>utet: Mehrere Aufgaben o<strong>de</strong>r Prozesse können<br />

gleichzeitig ausgeführt wer<strong>de</strong>n. Nicht die quasi gleichzeitige Ausführung mehrerer Programme<br />

(Multitask<strong>in</strong>g), son<strong>de</strong>rn die gleichzeitige, parallele Ausführung von e<strong>in</strong>zelnen Programmschritten<br />

(o<strong>de</strong>r zusammenhängen<strong>de</strong>n Prozessen) ist „Multithread<strong>in</strong>g“. Das kann be<strong>de</strong>uten: Innerhalb e<strong>in</strong>es<br />

Programms können mehrere D<strong>in</strong>ge gleichzeitig geschehen, mehrere Fa<strong>de</strong>n / Threads e<strong>in</strong>es<br />

Programms können gleichzeitig verfolgt und abgearbeitet wer<strong>de</strong>n.<br />

23 Verschie<strong>de</strong>ne Sicherheitslücken wur<strong>de</strong>n aufge<strong>de</strong>ckt und wur<strong>de</strong>n <strong>in</strong>zwischen beseitigt.<br />

24 Die virtuelle Masch<strong>in</strong>e wird auch als <strong>Java</strong> Interpreter o<strong>de</strong>r <strong>Java</strong> Environment (<strong>Java</strong>-Laufzeitumgebung)<br />

bezeichnet<br />

24


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

1.3 E<strong>in</strong>stieg <strong>in</strong> die <strong>Java</strong>-Programmierung<br />

1.3.1 Die Software für die <strong>Java</strong>-Programmierung<br />

Das JAVA Development Kit (JDK) enthält die Entwicklungsumgebung zum Schreiben<br />

von <strong>Java</strong> Programmen. Das JDK ist für Sun-SPARC-Systeme mit Solaris 2.2 (o<strong>de</strong>r<br />

höher) sowie für W<strong>in</strong>dows NT und W<strong>in</strong>dows 95 über das Internet 25 erhältlich.<br />

Nach <strong>de</strong>r Installation hat das <strong>Java</strong>-Verzeichnis 26 folgen<strong>de</strong>n Inhalt 27 :<br />

Abb. 1.3-1: Inhalt <strong>de</strong>s <strong>Java</strong>-Verzeichnisses<br />

Innerhalb <strong>de</strong>s <strong>Java</strong>-Verzeichnisses bef<strong>in</strong><strong>de</strong>n sich Unterverzeichnisse 28 mit folgen<strong>de</strong>m<br />

Inhalt:<br />

Verzeichnis<br />

\b<strong>in</strong><br />

\lib<br />

\<strong>in</strong>clu<strong>de</strong><br />

\<strong>de</strong>mo<br />

\src<br />

Inhalt<br />

In diesem Verzeichnis bef<strong>in</strong><strong>de</strong>n sich die JDK-Programme<br />

In diesem Verzeichnis bef<strong>in</strong><strong>de</strong>n sich <strong>de</strong>faultmäßig die Standard-<strong>Java</strong>-Klassen<br />

<strong>de</strong>s JDK 29<br />

In diesem Verzeichnis bef<strong>in</strong><strong>de</strong>n sich diverse Hea<strong>de</strong>r-Dateien für die geme<strong>in</strong>same<br />

Verwendung von <strong>Java</strong> und C/C++<br />

Das Demo-Verzeichnis e<strong>in</strong>thält Beispielprogramme<br />

Falls „<strong>Java</strong>-Source“ im InstallShield ausgewählt wur<strong>de</strong>, wird dieses Verzeichnis<br />

mit angelegt. Dar<strong>in</strong> bef<strong>in</strong><strong>de</strong>n sich die entkomprimierten <strong>Java</strong>-Dateien, die sonst<br />

nur im gepackten Zustand (Datei SRC.ZIP) vorhan<strong>de</strong>n s<strong>in</strong>d.<br />

Es ist s<strong>in</strong>nvoll, die <strong>in</strong> <strong>de</strong>r JDK-Umgebung verfügbaren Werkzeuge von allen<br />

Verzeichnissen aus zugänglich zu machen. Für W<strong>in</strong>dows-NT und W<strong>in</strong>dows-95-<br />

25 Das jeweils aktuelle JDK (aber auch ältere Versionen) können von <strong>de</strong>n Sun-Microsystem-Webseiten bzw. <strong>de</strong>r<br />

<strong>Java</strong>Soft-Homepage gela<strong>de</strong>n wer<strong>de</strong>n (http://www.sun.com bzw. http://www.javasoft.com/ ). Genutzt wer<strong>de</strong>n<br />

kann auch die <strong>Java</strong>Soft-WWW-Download-Seite für das jeweilige JDK (http://java.sun.com.products/jdk/x.x ,<br />

die Angabe x.x ist durch die gewünschte Versionsnummer zu ersetzen). Auch e<strong>in</strong>e FTP-Download, z.B. vom<br />

Sun-FTP-Server (ftp.javasoft.com) ist möglich.<br />

26 Erzeugt von <strong>de</strong>r Entpackungsrout<strong>in</strong>e <strong>de</strong>s JDK<br />

27 Das Verzeichnis „me<strong>in</strong>epr“ dient zur Aufnahme von Projekten und zählt nicht standardmäßig zum Inhalt<br />

e<strong>in</strong>es <strong>Java</strong>-Verzeichnisses<br />

28 unter W<strong>in</strong>dows<br />

29 Im wesentlichen ist das die Datei classes.zip<br />

25


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Anwen<strong>de</strong>r wird das Verzeichnis mit <strong>de</strong>n Werkzeugen <strong>in</strong> <strong>de</strong>r Pfadangabe <strong>de</strong>r Datei<br />

„autoexec.bat“ e<strong>in</strong>getragen, z.B.:<br />

PATH c:\;c:\w<strong>in</strong>dows;c:\w<strong>in</strong>dows\command;c:\jdk1.1.6\b<strong>in</strong>;c:\jdk1.1.6\<strong>in</strong>clu<strong>de</strong>;<br />

Die CLASSPATH-Umgebungsvariable 30 sollte unter W<strong>in</strong>dows <strong>in</strong> <strong>de</strong>r Datei<br />

„autoexec.bat“ so vorliegen:<br />

set classpath=c:\jdk1.1.6\lib\classes.zip;<br />

Im JDK 1.2 wird die CLASSPATH-Umgebungsvariable nicht mehr benötigt. Ist<br />

allerd<strong>in</strong>gs e<strong>in</strong>e CLASSPATH-Variable gesetzt, so wird sie auch verwen<strong>de</strong>t.<br />

Nach <strong>de</strong>r Installation <strong>de</strong>s JDK 1.2 bzw. JDK 1.3 muß das Verzeichnis<br />

\jdk1.3\b<strong>in</strong> <strong>in</strong> <strong>de</strong>n Suchpfad ausführbarer Dateien e<strong>in</strong>getragen wer<strong>de</strong>n. Das kann<br />

direkt <strong>in</strong> <strong>de</strong>r autoexec.bat durch Modifikation <strong>de</strong>r PATH-Anweisung, mit Hilfe e<strong>in</strong>er<br />

Batch-Datei o<strong>de</strong>r direkt erledigt wer<strong>de</strong>n, z.B.:<br />

set PATH=d:\jdk1.3\b<strong>in</strong>;%PATH%<br />

30 Umgebungsvariable s<strong>in</strong>d E<strong>in</strong>stellungen, mit <strong>de</strong>nen die Hot<strong>Java</strong>- und <strong>Java</strong>-Interpreter-Umgebungen <strong>de</strong>s<br />

Systems spezifiziert wer<strong>de</strong>n. Sie wer<strong>de</strong>n z.B. unter W<strong>in</strong>dows im allg. auf Befehlszeilenebene o<strong>de</strong>r <strong>in</strong> <strong>de</strong>r<br />

„autoexec.bat“ mit <strong>de</strong>r Anweisung „SET [Umgebungsvariable]= ....“ gesetzt.<br />

CLASSPATH ist die wichtigste <strong>de</strong>r Umgebungsvariablen von <strong>Java</strong>. Mit dieser Umgebungsvariablen wird<br />

bestimmt, woher die Systemklassen importiert wer<strong>de</strong>n.<br />

26


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

1.3.2 Applets und Anwendungen<br />

<strong>Java</strong> Programme wer<strong>de</strong>n <strong>in</strong> zwei Hauptanwendungs-Gruppen geglie<strong>de</strong>rt: Applets<br />

und Anwendungen.<br />

Applets s<strong>in</strong>d <strong>Java</strong>-Programme, die über das WWW heruntergela<strong>de</strong>n und von e<strong>in</strong>em<br />

Web-Browser auf <strong>de</strong>m Rechner <strong>de</strong>s Anwen<strong>de</strong>rs ausgeführt wer<strong>de</strong>n. Applets können<br />

nur auf e<strong>in</strong>em javafähigen Browser ausgeführt wer<strong>de</strong>n bzw. mit e<strong>in</strong>em Tool <strong>de</strong>s JDK,<br />

<strong>de</strong>m Appletviewer, gesichtet wer<strong>de</strong>n.<br />

<strong>Java</strong>-Anwendungen s<strong>in</strong>d allgeme<strong>in</strong>e, <strong>in</strong> <strong>de</strong>r <strong>Java</strong>-Sprache geschriebene Programme.<br />

Zum Ausführen von <strong>Java</strong>-Anwendungen ist ke<strong>in</strong> Browser nötig.<br />

1.3.2.1 Entwicklung von <strong>Java</strong>-Anwendungen<br />

1. Aufgabe: Erstelle e<strong>in</strong>e Anwendung, die <strong>de</strong>n Text „Willkommen <strong>in</strong> <strong>de</strong>r <strong>Java</strong>-<br />

Welt“ ausgibt.<br />

Lösungsschritte:<br />

1) Erstelle die folgen<strong>de</strong> Datei mit <strong>de</strong>m Namen „Willkommen.java“ mit Hilfe e<strong>in</strong>es<br />

Dateiaufbereiters (Editor):<br />

class Willkommen<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

System.out.pr<strong>in</strong>tln("Willkommen <strong>in</strong> <strong>de</strong>r <strong>Java</strong>-Welt!");<br />

}<br />

}<br />

Das Programm umfaßt zwei Teile: e<strong>in</strong>e Klassen<strong>de</strong>f<strong>in</strong>ition und e<strong>in</strong><br />

Programmabschnitt, <strong>de</strong>r unter ma<strong>in</strong>() angegeben ist.<br />

2) Speichern <strong>de</strong>r Datei mit <strong>de</strong>m Namen „Willkommen.java“ unter e<strong>in</strong>em beliebigen<br />

Verzeichnis 31 .<br />

3) Aufruf <strong>de</strong>s <strong>Java</strong>-Übersetzers über die folgen<strong>de</strong> Befehlszeilene<strong>in</strong>gabe:<br />

javac [optionen] date<strong>in</strong>ame.<br />

optionen .... bestimmen das Verhalten vom Compiler<br />

date<strong>in</strong>ame ... Name <strong>de</strong>r Datei mit <strong>de</strong>m <strong>Java</strong>-Quellco<strong>de</strong>. "javac" for<strong>de</strong>rt, daß<br />

<strong>de</strong>r Quelltext <strong>in</strong> Dateien steht, <strong>de</strong>ren Date<strong>in</strong>ame die Extension ".java"<br />

besitzt.<br />

„javac“ kompiliert <strong>de</strong>n Quellco<strong>de</strong> <strong>in</strong> <strong>Java</strong>-Byteco<strong>de</strong> und speichert se<strong>in</strong>e Ausgabe <strong>in</strong><br />

e<strong>in</strong>er Datei mit <strong>de</strong>m Namen „date<strong>in</strong>ame.class“. Standardmäßig wer<strong>de</strong>n .class-<br />

Dateien im gleichen Verzeichnis wie die .java-Quelldatei erzeugt 32 . Im vorliegen<strong>de</strong>n<br />

Fall ist <strong>de</strong>r Aufruf: javac Willkommen.java.<br />

4) Ausführen <strong>de</strong>r Byteco<strong>de</strong>-Datei „Willkommen.class“ mit <strong>de</strong>m <strong>Java</strong>-Interpreter<br />

java: „java Willkommen“. Der im JDK enthaltene Interpreter heißt „java“. Falls<br />

alles richtig gelaufen ist, ersche<strong>in</strong>t <strong>in</strong> <strong>de</strong>r letzten (Ausgabe-) Zeile: Willkommen <strong>in</strong><br />

<strong>de</strong>r <strong>Java</strong>-Welt!.<br />

31 Vgl. pr13210<br />

32 Mit <strong>de</strong>r Option "-d" im javac-Aufruf kann die .class-Datei an e<strong>in</strong>em an<strong>de</strong>ren Ort gespeichert wer<strong>de</strong>n.<br />

27


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

2. Aufgabe: Erstelle e<strong>in</strong>e Anwendung, die <strong>de</strong>n Text „Herzlich Willkommen<br />

Juergen Hubert Josef Liesel“ ausgibt. Die angebenen Namen<br />

sollen auf Befehlszeilenebene als Parameter e<strong>in</strong>geben wer<strong>de</strong>n.<br />

Lösungsschritte:<br />

1) Erstellen e<strong>in</strong>er Quelle (Datei), die folgen<strong>de</strong>n Quellco<strong>de</strong> 33 enthält:<br />

class WillkommensGruss<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

System.out.pr<strong>in</strong>t("Herzlich Willkommen ");<br />

System.out.pr<strong>in</strong>t(args[0]);<br />

}<br />

}<br />

2) Speichern <strong>de</strong>r Datei, Übersetzen und Aufruf <strong>de</strong>s Programm mit <strong>de</strong>m Parameter<br />

Juergen führt zu: „Herzlich Willkommen Juergen“.<br />

Argumente auf Befehlszeilenebene wer<strong>de</strong>n <strong>in</strong> <strong>de</strong>n Zeichenketten-Array „Str<strong>in</strong>g<br />

args[]“ aufgenommen. „args“ ist <strong>de</strong>r Name <strong>de</strong>s Zeichenketten-Arrays, das die<br />

Argumentenliste enthält und <strong>de</strong>m Programm immer zur Verfügung steht. Hier wur<strong>de</strong><br />

nur das erste Argument <strong>de</strong>r Liste (args[0])ausgegeben. Sollen alle Argumente, die<br />

auf <strong>de</strong>r Befehlszeilenebene e<strong>in</strong>gegeben wur<strong>de</strong>n, ausgegeben wer<strong>de</strong>n, dann ist die<br />

Liste komponentenweise mit e<strong>in</strong>er Zählschleife abzuarbeiten. Die Zählschleife (for-<br />

Schleife) sieht <strong>in</strong> <strong>Java</strong> so aus:<br />

for (Initialisierung; Test; Inkrement)<br />

{<br />

// Anweisungen<br />

}<br />

Initialisierung ist e<strong>in</strong> Ausdruck, <strong>de</strong>r <strong>de</strong>n Beg<strong>in</strong>n <strong>de</strong>r Zählschleife e<strong>in</strong>leitet, z.B.<br />

Initialisierung e<strong>in</strong>es Schleifen<strong>in</strong><strong>de</strong>x (z.B. <strong>in</strong>t i = 0;). Die Variablen, die <strong>in</strong> diesem<br />

Teil <strong>de</strong>r Schleife <strong>de</strong>klariert wer<strong>de</strong>n, s<strong>in</strong>d lokal (<strong>in</strong> Bezug auf die Schleife). Das<br />

be<strong>de</strong>utet: Sie gehören zur Schleife und existieren nicht mehr nach <strong>de</strong>r vollständigen<br />

Ausführung <strong>de</strong>r Schleife. Man kann <strong>in</strong> diesem Bereich mehr als e<strong>in</strong>e Variable<br />

<strong>in</strong>itialisieren (durch Angabe mehrerer durch Kommas getrennte Ausdrücke, z.B.<br />

<strong>in</strong>t i = 0, <strong>in</strong>t j = 10).<br />

Test ist e<strong>in</strong> Ausdruck, <strong>de</strong>r nach je<strong>de</strong>r Iteration <strong>de</strong>r Schleife ausgeführt wird. Der Test<br />

muß e<strong>in</strong> boolescher Ausdruck o<strong>de</strong>r e<strong>in</strong>e Funktion se<strong>in</strong>, die e<strong>in</strong>en booleschen Wert<br />

zurückgibt (true o<strong>de</strong>r false). Ergibt <strong>de</strong>r Test true, wird die Schleife ausgeführt. Sobald<br />

er false ergibt, wird die Schleifenausführung angehalten.<br />

Inkrement ist e<strong>in</strong> beliebiger Audruck o<strong>de</strong>r Funktionsaufruf. Üblicherweise wird er<br />

verwen<strong>de</strong>t, um <strong>de</strong>n Wert <strong>de</strong>s Schleifen<strong>in</strong><strong>de</strong>x näher an <strong>de</strong>n Endwert zu br<strong>in</strong>gen und<br />

damit für Beendigung <strong>de</strong>r Schleife zu sorgen. Wie im Initialisierungsbereich kann im<br />

Inkrement-Bereich mehr als e<strong>in</strong> Ausdruck untergebracht se<strong>in</strong>, falls die e<strong>in</strong>zelnen<br />

Ausdrücke mit Kommas vone<strong>in</strong>an<strong>de</strong>r getrennt s<strong>in</strong>d.<br />

Auf <strong>de</strong>n Wert e<strong>in</strong>es Elements <strong>in</strong> e<strong>in</strong>em Array wird über <strong>de</strong>n Namen <strong>de</strong>s Array, gefolgt<br />

von e<strong>in</strong>em In<strong>de</strong>x <strong>in</strong> eckigen Klammern zugegriffen (z.B. args[i]). Array-Indizes<br />

beg<strong>in</strong>nen mit 0. Alle Array-Indizes wer<strong>de</strong>n geprüft, um sicher zu stellen, daß sie sich<br />

<strong>in</strong>nerhalb <strong>de</strong>r Grenzen <strong>de</strong>s Array bef<strong>in</strong><strong>de</strong>n, wie sie bei <strong>de</strong>r Erzeugung <strong>de</strong>s Arrays<br />

festgelegt wur<strong>de</strong>n. Die Länge <strong>de</strong>s Array kann im Programm mit <strong>de</strong>r Instanzvariablen<br />

length getestet wer<strong>de</strong>n (z.B. args.length).<br />

33 Vgl. pr13210<br />

28


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

3) Erweitern <strong>de</strong>r Quellco<strong>de</strong>-Datei um e<strong>in</strong>e Zählschleife, die die Argumentenliste<br />

abarbeitet:<br />

class WillkommensGruss<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

<strong>in</strong>t i; // Lokale Variable<br />

System.out.pr<strong>in</strong>t("Herzlich Willkommen ");<br />

for (i=0; i < args.length;i++)<br />

{<br />

System.out.pr<strong>in</strong>t(args[i] + " ");<br />

}<br />

}<br />

}<br />

„args.length“ bestimmt die Länge <strong>de</strong>r Argumentenliste.<br />

4) Speichern <strong>de</strong>r Datei, Übersetzen und Aufruf <strong>de</strong>s Programm mit Parametern führt<br />

zu: „Herzlich Willkommen Juergen Liesel Vera Christian Hubert“.<br />

Auffällig ist: i spielt hier eigentlich gar ke<strong>in</strong>e große Rolle, es hat lediglich als In<strong>de</strong>x<br />

se<strong>in</strong>e Berechtigung. Nur damit lässt sich das Element an e<strong>in</strong>er bestimmten Stelle im<br />

Array ansprechen. Da dieses Durchlaufen von Arrays häufig ist , haben die<br />

Entwickler von Sun seit <strong>Java</strong> 1.5 e<strong>in</strong>e Abkürzung für soche Iterationen <strong>in</strong> die Sprache<br />

e<strong>in</strong>geführt:<br />

for (Typ bezeichner : Feld)<br />

...<br />

Diese erweiterte for-Schleife löst sich vom In<strong>de</strong>x und erfragt je<strong>de</strong>s Elemment <strong>de</strong>s<br />

Fel<strong>de</strong>s. "WillkommensGruss" lässt sich dann so formulieren:<br />

class WillkommensGruss<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

System.out.pr<strong>in</strong>t("Herzlich Willkommen ");<br />

for (Str<strong>in</strong>g arg : args)<br />

{<br />

System.out.pr<strong>in</strong>t(arg + " ");<br />

}<br />

System.out.pr<strong>in</strong>tln();<br />

}<br />

}<br />

Die "for"-Schleife ist folgen<strong>de</strong>rmaßen zu lesen: "Für je<strong>de</strong>s Element arg vom Typ<br />

Str<strong>in</strong>g <strong>in</strong> args tue ... ". E<strong>in</strong> Variablenname wie i für <strong>de</strong>n Scheifen<strong>in</strong><strong>de</strong>x ist nicht mehr<br />

nötig, <strong>de</strong>nn <strong>de</strong>r In<strong>de</strong>x ist nicht sichtbar. Intern setzt <strong>de</strong>r Compiler diese erweiterte<br />

for-Schleife ganz klassisch um, sodass <strong>de</strong>r Byteco<strong>de</strong> unter bei<strong>de</strong>n Varianten gleich<br />

ist.<br />

3. Aufgabe: Die auf Befehlszeilenebene e<strong>in</strong>gegebenen Namen sollen sortiert<br />

wer<strong>de</strong>n. So soll die E<strong>in</strong>gabe <strong>de</strong>r Befehlszeile „java<br />

WillkommensGruss Juergen Hubert Josef Liesel<br />

Christian“ zu folgen<strong>de</strong>r Ausgabe führen: „Herzlich Willkommen<br />

Christian Hubert Josef Juergen Liesel“.<br />

Lösungsschritte:<br />

1) Gesucht ist e<strong>in</strong> Algorithmus, <strong>de</strong>r die über die Befehlszeile e<strong>in</strong>gegebenen Namen<br />

sortiert. E<strong>in</strong> e<strong>in</strong>facher Sortieralgorithmus ist unter <strong>de</strong>m Namen „Bubble-Sort“<br />

29


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

bekannt. Man vergleicht dabei zunächst <strong>de</strong>n ersten unter args[0] abgelegten Namen<br />

gegen alle weiteren im Datenfeld args abgelegten Namen.<br />

args<br />

[0] "Juergen"<br />

[1] "Hubert"<br />

[2] "Josef"<br />

[3] "Liesel"<br />

[4] "Christian"<br />

Nach 4 Vergleichen hat das Datenfeld args folgen<strong>de</strong> Gestalt angenommen:<br />

args<br />

[0] "Christian"<br />

[1] "Juergen"<br />

[2] "Josef"<br />

[3] "Liesel"<br />

[4] "Hubert"<br />

Die erste Position (args[0]) ist im Datenfeld damit schon richtig e<strong>in</strong>geordnet.<br />

Danach muß jetzt <strong>de</strong>r Name an <strong>de</strong>r 2. Position ([1]) mit <strong>de</strong>n übrigen Namen <strong>de</strong>s<br />

Datenfelds verglichen wer<strong>de</strong>n. Nach 3 Vergleichen zeigt sich folgen<strong>de</strong>s Bild:<br />

args<br />

[0] "Christian"<br />

[1] "Hubert"<br />

[2] "Juergen"<br />

[3] "Liesel"<br />

[4] "Josef"<br />

„Christian“ und „Hubert“ s<strong>in</strong>d jetzt richtig e<strong>in</strong>geordnet. Nun muß <strong>de</strong>r Name an <strong>de</strong>r<br />

dritten Position mit allen noch verbliebenen Namen auf Position 4 und 5 noch<br />

verglichen wer<strong>de</strong>n. Das Resultat <strong>de</strong>r Vergleiche zeigt:<br />

args<br />

[0] "Christian"<br />

[1] "Hubert"<br />

[2] "Josef"<br />

[3] "Liesel"<br />

[4] "Juergen"<br />

Der nächste Durchgang führt dann zum sortierten Datenfeld:<br />

args<br />

[0] "Christian"<br />

[1] "Hubert"<br />

[2] "Josef"<br />

[3] "Juergen"<br />

[4] "Liesel"<br />

2) Der soeben beschriebene Sortieralgorithmus muß <strong>in</strong> <strong>Java</strong>-Programmco<strong>de</strong><br />

abgebil<strong>de</strong>t wer<strong>de</strong>n. Es ist leicht erkennbar, daß hier zwei verschachtelte for-<br />

Schleifen die Sortierung erreichen können.<br />

for (i = 0; i < args.length; i++)<br />

{<br />

for (j = i + 1; j < args.length; j++)<br />

{<br />

30


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

if (args[i].compareTo(args[j]) > 0)<br />

{ // Tauschen<br />

Str<strong>in</strong>g temp = args[i]; // lokale Variable<br />

args[i] = args[j];<br />

args[j] = temp;<br />

}<br />

}<br />

}<br />

Zeichenketten wer<strong>de</strong>n über die Metho<strong>de</strong> „compareTo“ <strong>de</strong>r Klasse Str<strong>in</strong>g<br />

verglichen. Generell stehen die Vergleichsoperatoren (== != < >=) nur für<br />

das Vergleichen von Zahlen zur Verfügung.<br />

Die Vergleichsbed<strong>in</strong>gung wird durch das Schlüsselwort if erzeugt:<br />

if (Ausdruck)<br />

{<br />

// Anweisung(en)<br />

}<br />

else {<br />

// Anweisung(en)<br />

}<br />

E<strong>in</strong>e if-Bed<strong>in</strong>gung verwen<strong>de</strong>t e<strong>in</strong>en boolschen Ausdruck für die Entscheidung, ob<br />

e<strong>in</strong>e Anweisung ausgeführt wer<strong>de</strong>n soll. Die Anweisung wird ausgeführt, wenn <strong>de</strong>r<br />

Ausdruck <strong>de</strong>n Wert true zurückliefert. Falls gewünscht wird, daß e<strong>in</strong>e bestimmte<br />

Anweisung bzw. Anweisungen ausgeführt wer<strong>de</strong>n, wenn <strong>de</strong>r boolsche Ausdruck<br />

false zurückliefert, ist dieser Anweisungsblock durch das Schlüsselwort else<br />

e<strong>in</strong>zuleiten.<br />

3) Das vollständige Programm umfaßt folgen<strong>de</strong>n Quelltext:<br />

class WillkommensGruss<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

<strong>in</strong>t i, j; // Lokale Variable<br />

System.out.pr<strong>in</strong>t("Herzlich Willkommen ");<br />

for (i = 0; i < args.length; i++)<br />

{<br />

for (j = i + 1; j < args.length; j++)<br />

{<br />

if (args[i].compareTo(args[j]) > 0)<br />

{<br />

Str<strong>in</strong>g temp = args[i];<br />

args[i] = args[j];<br />

args[j] = temp;<br />

}<br />

}<br />

}<br />

for (i = 0; i < args.length; i++)<br />

{<br />

System.out.pr<strong>in</strong>t(args[i] + " ");<br />

}<br />

System.out.pr<strong>in</strong>tln();<br />

}<br />

}<br />

4) Speichern <strong>de</strong>r Datei, Übersetzen und Aufruf <strong>de</strong>s Programms mit Parametern führt<br />

zu: „Herzlich Willkommen Christian Hubert Josef Juergen Liesel“.<br />

4. Aufgabe: Die Argumentenliste args soll auf folgen<strong>de</strong> Weise angezeigt wer<strong>de</strong>n:<br />

31


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

args[0] = "Christian"<br />

args[1] = "Hubert"<br />

args[2] = "Josef"<br />

args[3] = "Juergen"<br />

args[4] = "Liesel"<br />

Lösungsschritte:<br />

1) System.out.pr<strong>in</strong>tln() bzw. System.out.pr<strong>in</strong>t() erwarten e<strong>in</strong> e<strong>in</strong>ziges<br />

Argument <strong>in</strong>nerhalb <strong>de</strong>r Klammern. Sollen, wie hier gewünscht, mehrere Variablen,<br />

vom Typ Str<strong>in</strong>g o<strong>de</strong>r Zeichenkettenliterale, Argument für pr<strong>in</strong>tln() se<strong>in</strong>, dann<br />

können diese Elemente mit <strong>de</strong>m Verkettungsoperator „+“ zu e<strong>in</strong>em e<strong>in</strong>zigen Str<strong>in</strong>g<br />

o<strong>de</strong>r Zeichenkettenliteral verknüpft wer<strong>de</strong>n. Der Umgang mit <strong>de</strong>m<br />

Verkettungsoperator ist <strong>in</strong> <strong>Java</strong> e<strong>in</strong>fach, da er alle Variablentypen und Objektwerte<br />

wie Str<strong>in</strong>gs behan<strong>de</strong>lt. Sobald e<strong>in</strong> Teil e<strong>in</strong>er Verkettung e<strong>in</strong> Str<strong>in</strong>g o<strong>de</strong>r e<strong>in</strong> Str<strong>in</strong>g-<br />

Literal ist, wer<strong>de</strong>n alle Operatoren wie Str<strong>in</strong>gs behan<strong>de</strong>lt, z.B.:<br />

System.out.pr<strong>in</strong>tln("1 + 2 = " + 3);.<br />

2) Die Ausgabe kann über die folgen<strong>de</strong> Anweisungen so erfolgen<br />

for (i = 0; i < args.length; i++)<br />

{<br />

System.out.pr<strong>in</strong>tln("args[" + i + "] = \"" + args[i] + "\"");<br />

}<br />

System.out.pr<strong>in</strong>tln();<br />

Die Zeichen für doppelte Anführungszeichen(") und Backslash(\) müssen durch die<br />

Verwendung von sog. Escape-Sequenzen (\" und \\) dargestellt wer<strong>de</strong>n. Die<br />

bei<strong>de</strong>n Anführungszeichen, die das Literal umschliessen, müssen <strong>in</strong> <strong>de</strong>rselben Zeile<br />

<strong>de</strong>s Quellco<strong>de</strong>s stehen. E<strong>in</strong>e neue Zeile kann <strong>in</strong>nerhalb e<strong>in</strong>es Literals durch die<br />

Escape-Sequenz \n erreicht wer<strong>de</strong>n.<br />

5. Aufgabe: Zwei Gleitpunktzahlen sollen über die Befehlszeile e<strong>in</strong>gelesen wer<strong>de</strong>n.<br />

Anschließend sollen mit diesen bei<strong>de</strong>n Zahlen alle zugelassenen,<br />

b<strong>in</strong>ären arithmetischen Operationen für Gleitpunktzahlen (<strong>de</strong>s primitiven<br />

Datentyps float) und unäre Inkrement- bzw. Dekrement-Operationen<br />

ausgeführt wer<strong>de</strong>n.<br />

Lösungsschritte:<br />

1) Die Übergabe <strong>de</strong>r bei<strong>de</strong>n Zahlen über die Befehlsargumentenliste erfor<strong>de</strong>rt die<br />

Konvertierung <strong>de</strong>s Typs Str<strong>in</strong>g <strong>in</strong> e<strong>in</strong>en Typ <strong>de</strong>r Klasse Float. Das konvertierte<br />

Datum kann anschließend e<strong>in</strong>er Variablen <strong>de</strong>s primitiven Typs float zugewiesen<br />

wer<strong>de</strong>n.<br />

float x = Float.valueOf(args[0]).floatValue();<br />

float y = Float.valueOf(args[1]).floatValue();<br />

2) Für ganze Zahlen und Gleitpunktzahlen s<strong>in</strong>d Additions- (+), Subtraktions- (-),<br />

Multiplikations- (*) und Divisionsopertoren (/) <strong>de</strong>f<strong>in</strong>iert. Es gibt außer<strong>de</strong>m für unäre<br />

Arithmetik noch Inkrement- und Dekrementoperatoren zur Manipulation <strong>de</strong>s Werts<br />

von Variablen.<br />

3) Der Quelltext zur Programmlösung 34 ist dann:<br />

public class FloatDemo<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

34 FloatDemo.java aus pr13210<br />

32


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

}<br />

}<br />

// Konvertieren<br />

float x = Float.valueOf(args[0]).floatValue();<br />

float y = Float.valueOf(args[1]).floatValue();<br />

// Ausgabe <strong>de</strong>r ueber die Befehlszeile e<strong>in</strong>gegebenen Zahlen<br />

System.out.pr<strong>in</strong>tln("x = " + x);<br />

System.out.pr<strong>in</strong>tln("y = " + y);<br />

// B<strong>in</strong>aere Arithmetik mit <strong>de</strong>n Operatoren + - * /<br />

float z;<br />

z = x + y;<br />

System.out.pr<strong>in</strong>tln("z = x + y = " + z);<br />

z = x - y;<br />

System.out.pr<strong>in</strong>tln("z = x - y = " + z);<br />

z = x * y;<br />

System.out.pr<strong>in</strong>tln("z = x * y = " + z);<br />

z = x / y;<br />

System.out.pr<strong>in</strong>tln("z = x / y = " + z);<br />

// Unaere Arithmetik mit Inkrement- / Dekrementoperator<br />

x++;<br />

System.out.pr<strong>in</strong>tln("Nach x++: x = " + x);<br />

y--;<br />

System.out.pr<strong>in</strong>tln("Nach y--: y = " + y);<br />

z = x++;<br />

System.out.pr<strong>in</strong>tln("Nach z = x++: z = " + z + ", x = " + x);<br />

z = ++x;<br />

System.out.pr<strong>in</strong>tln("Nach z = ++x: z = " + z + ", x = " + x);<br />

System.out.pr<strong>in</strong>tln("x = " + x);<br />

System.out.pr<strong>in</strong>tln("y = " + y);<br />

z = ++x + y--;<br />

System.out.pr<strong>in</strong>tln("nach z = ++x + y--: z = " + z + ", x = "<br />

+ x + " y = " + y);<br />

System.out.pr<strong>in</strong>tln("x = " + x);<br />

System.out.pr<strong>in</strong>tln("y = " + y);<br />

z = x + y * ++y;<br />

System.out.pr<strong>in</strong>tln("nach z = x + y * ++y: z = " + z + ", x = "<br />

+ x + " y = " + y);<br />

z = (float) (1.0f / 0.0f);<br />

System.out.pr<strong>in</strong>tln("z = (float) (1.0f / 0.0f) = " + z);<br />

4) Der Aufruf java FloatDemo 17 4 führt dann zu folgen<strong>de</strong>n Ausgabe:<br />

x = 17.0<br />

y = 4.0<br />

z = x + y = 21.0<br />

z = x - y = 13.0<br />

z = x * y = 68.0<br />

z = x / y = 4.25<br />

Nach x++: x = 18.0<br />

Nach y--: y = 3.0<br />

Nach z = x++: z = 18.0, x = 19.0<br />

Nach z = ++x: x = 20.0, x = 20.0<br />

x = 20.0<br />

y = 3.0<br />

Nach z = ++x + y--: z = 24.0, x = 21.0 y = 2.0<br />

x = 21.0<br />

y = 2.0<br />

Nach z = x + y * ++y: z = 27.0, x = 21.0 y = 3.0<br />

z = (float) (1.0f / 0.0f) = Inf<strong>in</strong>ity<br />

Das zuletzt angegebenen Resultat zeigt die Zuordnung "Inf<strong>in</strong>ity" falls e<strong>in</strong>e Zahl<br />

ungleich Null durch Null geteilt wird. 1.0f und 0.0f s<strong>in</strong>d Gleitpunkt-Literale. Da<br />

"Inf<strong>in</strong>ity" als sehr groß <strong>in</strong>terpretiert (Typ double) wird, muß hier <strong>in</strong> <strong>de</strong>n Typ<br />

float konvertiert wer<strong>de</strong>n.<br />

33


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Die Anwendung zeigt außer<strong>de</strong>m die Anwendungsweise <strong>de</strong>r Inkrement- und<br />

Dekrementoperatoren <strong>in</strong> Präfix- und Postfix-Schreibweise. Steht <strong>de</strong>r Operator vor <strong>de</strong>r<br />

Variablen (z.B. ++x), dann wird er angewen<strong>de</strong>t, bevor <strong>de</strong>r Wert <strong>de</strong>r Variablen <strong>in</strong> e<strong>in</strong>e<br />

Anweisung e<strong>in</strong>gesetzt wird. Im umgekehrten Fall (z.B. x++) wird die Variable zuerst<br />

<strong>in</strong> e<strong>in</strong>er Anweisung benutzt und dann erhöht.<br />

6. Aufgabe: In <strong>de</strong>r 5. Aufgabe führt <strong>de</strong>r Aufruf java FloatDemo zu e<strong>in</strong>em Fehler.<br />

Dieser Fehler soll aufgefangen wer<strong>de</strong>n.<br />

Lösungsschritte:<br />

1) <strong>Java</strong> verfügt zur Behandlung von Fehlern e<strong>in</strong>e spezielle<br />

Ausnahmenbehandlungsrout<strong>in</strong>e (exception handl<strong>in</strong>g) <strong>de</strong>n „try-catch“-Block. Im<br />

„try“-Block wird <strong>de</strong>r normale Ablauf behan<strong>de</strong>lt. Im „catch“-Block erfolgt die<br />

Behandlung <strong>de</strong>r Ausnahmen. Die Verzweigung <strong>in</strong> <strong>de</strong>n „catch“-Block erfolgt beim<br />

Auftreten e<strong>in</strong>er Ausnahme automatisch, <strong>de</strong>r „try“-Block bleibt dabei unberührt.<br />

2) Der Quelltext nimmt nach <strong>de</strong>m E<strong>in</strong>fügen e<strong>in</strong>es „try-catch“-Blocks folgen<strong>de</strong><br />

Gestalt an:<br />

public class FloatDemo<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

try {<br />

// Konvertieren<br />

................<br />

}<br />

catch(Exception e)<br />

{<br />

System.out.pr<strong>in</strong>tln("Fehler bei <strong>de</strong>r E<strong>in</strong>gabe: java FloatDemo a b");<br />

System.out.pr<strong>in</strong>tln(e.getMessage());<br />

}<br />

}<br />

}<br />

1.3.2.2 Entwicklung von <strong>Java</strong>-Applets<br />

Die Entwicklung e<strong>in</strong>es Applets unterschei<strong>de</strong>t sich von e<strong>in</strong>er Anwendung, weil <strong>Java</strong>-<br />

Applets <strong>in</strong> e<strong>in</strong>er Web-Seite mit an<strong>de</strong>ren Seitenelementen zusammen ausgeführt<br />

wer<strong>de</strong>n. Zur Ausführung e<strong>in</strong>es Applets ist es nötig, das Applet <strong>in</strong> e<strong>in</strong>em HTML-<br />

Dokument 35 e<strong>in</strong>zubetten. In diesem HTML-Dokument wer<strong>de</strong>n <strong>de</strong>m javafähigen<br />

Browser die Informationen mitgeteilt, die er zur Ausführung <strong>de</strong>s Applets benötigt. Der<br />

Browser lädt die Klassendatei und führt das Applet automatisch aus.<br />

Aus <strong>de</strong>r Sicht <strong>de</strong>s Programmierers ist das Applet e<strong>in</strong>e Klasse, die von <strong>de</strong>r Applet-<br />

Klasse abgeleitet wird.<br />

1. Aufgabe: Erstelle e<strong>in</strong> Applet, das <strong>in</strong> e<strong>in</strong>em Fenster <strong>de</strong>n Text „Herzlich<br />

Willkommen <strong>in</strong> <strong>de</strong>r <strong>Java</strong>-Welt“ ausgibt<br />

Lösungsschritte:<br />

1) Erstelle die folgen<strong>de</strong> Datei mit <strong>de</strong>m Namen „WillkommenApplet.java“ mit Hilfe<br />

e<strong>in</strong>es Dateiaufbereiters (Editor):<br />

35 e<strong>in</strong>e entsprechen<strong>de</strong> Referenz <strong>in</strong>nerhalb e<strong>in</strong>er HTML-Seite mit e<strong>in</strong>em speziellen Tag, <strong>de</strong>m -Tag<br />

erledigt die E<strong>in</strong>bettung <strong>in</strong> <strong>de</strong>n Browser<br />

34


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

/* Das erste <strong>Java</strong>-Applet */<br />

import java.awt.Graphics;<br />

public class WillkommenApplet extends java.applet.Applet<br />

{<br />

public void pa<strong>in</strong>t (Graphics g)<br />

{<br />

g.drawStr<strong>in</strong>g("Herzlich willkommen <strong>in</strong> <strong>de</strong>r <strong>Java</strong> Welt!",5,25);<br />

}<br />

}<br />

Durch die „import“-Anweisung können Entwickler Klassen verwen<strong>de</strong>n, die <strong>in</strong><br />

an<strong>de</strong>ren Dateien <strong>de</strong>f<strong>in</strong>iert s<strong>in</strong>d. Compiler bzw. Interpreter greifen auf die class-<br />

Dateien zu. Über „import“ wird bestimmt, wo diese Dateien liegen. <strong>Java</strong> importiert<br />

immer das Paket „java.lang“, <strong>de</strong>nn hier ist die Klasse Object enthalten, von <strong>de</strong>r<br />

alle <strong>Java</strong>-Klassen abgeleitet s<strong>in</strong>d. Die Graphics-Klasse enthält Metho<strong>de</strong>n zum<br />

Zeichnen von Textzeichen und Zeichenketten. Mit <strong>de</strong>r „drawstr<strong>in</strong>g“-Metho<strong>de</strong> <strong>de</strong>r<br />

Graphics-Klasse können Textzeichen auf <strong>de</strong>n Bildschirm gemalt wer<strong>de</strong>n.<br />

Die folgen<strong>de</strong> Abbildung zeigt die Mo<strong>de</strong>llierung 36 <strong>de</strong>s vorliegen<strong>de</strong>n Quellco<strong>de</strong>s:<br />

WillkommenApplet<br />

pa<strong>in</strong>t()<br />

g.drawStr<strong>in</strong>g<br />

(“Herzlich Willkommen <strong>in</strong> <strong>de</strong>r <strong>Java</strong>-Welt!“,5,25)<br />

Abb. 1.3-2: Klassendiagramm zu WillkommenApplet<br />

Die Klasse WillkommenApplet läßt sich grafisch als rechteckiges Symbol<br />

darstellen. Die pa<strong>in</strong>t()-Metho<strong>de</strong> wird ohne formale Parameter beschrieben, ihre<br />

Implementierung wird durch die beigefügte Notiz gezeigt.<br />

Die unmittelbare Superklasse wird im Quelltext direkt <strong>in</strong> <strong>de</strong>r „extends“-Klausel<br />

angegeben. „extends java.applet.Applet“ bestimmt das die angegebene<br />

Applet-Klasse von <strong>de</strong>r Applet-Klasse <strong>de</strong>s Abstract W<strong>in</strong>dow<strong>in</strong>g Toolkit (AWT)<br />

abgeleitet ist. Der an<strong>de</strong>re Teil <strong>de</strong>r Klassen<strong>de</strong>f<strong>in</strong>ition enthält das Schlüsselwort<br />

„public“ mit <strong>de</strong>r Be<strong>de</strong>utung: Die Klasse ist nach <strong>de</strong>m La<strong>de</strong>n für das gesamte <strong>Java</strong>-<br />

System verfügbar. Applets müssen „public“ <strong>de</strong>klariert wer<strong>de</strong>n.<br />

Die folgen<strong>de</strong> Abbildung zeigt die Beziehungen <strong>de</strong>r Klasse WillkommenApplet zu<br />

ihren unmittelbaren Nachbarn:<br />

Applet<br />

WillkommenApplet<br />

pa<strong>in</strong>t()<br />

Graphics<br />

36 Die Mo<strong>de</strong>llierung erfolgt nach <strong>de</strong>n Regeln <strong>de</strong>r Unified Mo<strong>de</strong>ll<strong>in</strong>g Language (UML). Die UML ist e<strong>in</strong>e<br />

grafische, standardisierte Sprache zum Spezifizieren, Konstruieren, Visualisieren und Dokumentieren.<br />

35


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Abb. 1.3-3: Die unmittelbare Umgebung von WillkommenApplet<br />

Die gerichtete L<strong>in</strong>ie mit <strong>de</strong>r unausgefüllten Pfeilspitze von WillkommenApplet zu<br />

Applet repräsentiert e<strong>in</strong>e Generalisierung, d.h.: WillkommenApplet ist e<strong>in</strong>e<br />

Unterklasse von Applet. Der gestrichelte Pfeil repräsentiert e<strong>in</strong>e<br />

Abhängigkeitbeziehung: WillkommenApplet verwen<strong>de</strong>t Graphics.<br />

E<strong>in</strong> eigenes, vom Benutzer erstelltes Applet überschreibt gewöhnlich Metho<strong>de</strong>n, die<br />

<strong>in</strong> <strong>de</strong>r Superklasse Applet <strong>de</strong>f<strong>in</strong>iert s<strong>in</strong>d. Diese Metho<strong>de</strong>n übernehmen Aufgaben zur<br />

Initialisierung <strong>de</strong>s Applet vor <strong>de</strong>r Ausführung (public void start()), zur<br />

Reaktion auf Mause<strong>in</strong>gaben, zum Anhalten <strong>de</strong>s Applet (public void stop())<br />

und zu Aufräumungsarbeiten (public void <strong>de</strong>stroy()), wenn das Applet<br />

been<strong>de</strong>t wird. E<strong>in</strong>e dieser Metho<strong>de</strong>n ist pa<strong>in</strong>t(), die sich um die Anzeige <strong>de</strong>s<br />

Applet <strong>in</strong> e<strong>in</strong>er Webseite kümmert. Die pa<strong>in</strong>t()-Metho<strong>de</strong> besitzt e<strong>in</strong> e<strong>in</strong>ziges<br />

Argument, e<strong>in</strong>e Instanz <strong>de</strong>r Klasse Graphics. Die Klasse Graphics stellt<br />

Verhaltensweisen zur Darstellung von Schriften, Farben, zum Zeichnen von L<strong>in</strong>ien 37 ,<br />

von Ellipsen bzw. Kreisen 38 , von Rechtecken 39 und an<strong>de</strong>ren Formen zur Verfügung.<br />

In <strong>de</strong>r pa<strong>in</strong>t()-Metho<strong>de</strong> wur<strong>de</strong> hier <strong>de</strong>r Str<strong>in</strong>g "Herzlich Willkommen <strong>in</strong> <strong>de</strong>r<br />

<strong>Java</strong> Welt!" bei <strong>de</strong>n (x,y)-Koord<strong>in</strong>aten (5,25) ausgegeben. Der Ursprung <strong>de</strong>s<br />

Koord<strong>in</strong>atensystems liegt <strong>in</strong> <strong>de</strong>r l<strong>in</strong>ken oberen Ecke <strong>de</strong>s Darstellungsbereichs. Der<br />

Str<strong>in</strong>g wird <strong>in</strong> e<strong>in</strong>er <strong>de</strong>faultmäßig festgelegten Schrift und Farbe angezeigt.<br />

Die Untersuchung <strong>de</strong>r <strong>Java</strong>-Bibliotheken zu Applet und Graphics zeigt: Die bei<strong>de</strong>n<br />

Klassen s<strong>in</strong>d Teil e<strong>in</strong>er größeren Hierarchie. Verfolgt man die von Applet erweiterten<br />

und implementierten Klassen, dann kann man das folgen<strong>de</strong> Klassendiagramm<br />

erhalten:<br />

Object<br />

ImageObserver<br />

Component<br />

Conta<strong>in</strong>er<br />

Panel<br />

Applet<br />

WillkommenApplet<br />

37 public void drawL<strong>in</strong>e(<strong>in</strong>t x1, <strong>in</strong>t y1, <strong>in</strong>t x2, <strong>in</strong>t y2); (x1,y1) bestimmt <strong>de</strong>n<br />

Anfangspunkt, (x2,y2) bestimmt <strong>de</strong>n Endpunkt <strong>de</strong>r L<strong>in</strong>ie.<br />

38 public void drawOval(<strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t width, <strong>in</strong>t height); (x,y) gibt die<br />

Koord<strong>in</strong>aten <strong>de</strong>r oberen l<strong>in</strong>ken Ecke <strong>de</strong>s die Ellipse umschreiben<strong>de</strong>n Rechtecks mit <strong>de</strong>r Höhe height und <strong>de</strong>r<br />

Breite width am<br />

39 public void drawRect(<strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t width, <strong>in</strong>t height); bestimmt die obere<br />

l<strong>in</strong>ke Ecke <strong>de</strong>s Rechtecks, (width, height) legen Breite und Höhe <strong>de</strong>s Rechtecks fest.<br />

36


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Abb. 1.3-4: Vererbungshierarchie von WillkommenApplet<br />

Die Beziehung zwischen ImageObserver und Component ist e<strong>in</strong>e Schnittstelle.<br />

ImageObserver wird von Component implementiert.<br />

WillkommenApplet arbeitet mit <strong>de</strong>n Klassen Applet und Graphics unmittelbar<br />

zusammen. Diese bei<strong>de</strong>n Klassen bil<strong>de</strong>n lediglich e<strong>in</strong>en kle<strong>in</strong>en Ausschnitt aus <strong>de</strong>r<br />

Bibliothek mit vor<strong>de</strong>f<strong>in</strong>ierten <strong>Java</strong>-Klassen. Die Verwaltung dieser Klassen und<br />

Schnittstellen organisiert <strong>Java</strong> <strong>in</strong> mehreren verschie<strong>de</strong>nen Paketen. Das Wurzelpaket<br />

<strong>in</strong> <strong>de</strong>r <strong>Java</strong>-Umgebung heißt java. In dieses Paket s<strong>in</strong>d mehrere weitere Pakete<br />

geschachtelt, die wie<strong>de</strong>rum an<strong>de</strong>re Pakete, Schnittstellen und Klassen enthalten.<br />

Object existiert im Paket lang, Panel, Conta<strong>in</strong>er, Component existieren im<br />

Paket awt, und die Klasse Applet im Paket applet. Die Schnittstelle<br />

ImageObserver existiert im Paket image, das liegt wie<strong>de</strong>rum im Paket awt<br />

(qualifizierter Name: java.awt.ImageObeserver). Die Paketstruktur 40 kann <strong>in</strong><br />

e<strong>in</strong>em Klassendiagramm visualisiert wer<strong>de</strong>n:<br />

java<br />

WillkommenApplet<br />

applet<br />

awt<br />

lang<br />

Abb. 1.3-5: Paketstruktur im WillkommenApplet<br />

2) Speichern <strong>de</strong>r Datei mit <strong>de</strong>m Namen „WillkommenApplet.java“ unter e<strong>in</strong>em<br />

beliebigen Verzeichnis.<br />

3) Aufruf <strong>de</strong>s <strong>Java</strong>-Übersetzers über die folgen<strong>de</strong> Befehlszeilene<strong>in</strong>gabe: „javac<br />

WillkommenApplet.java“. Der Compiler gibt bei erfolgreicher Übersetzung ke<strong>in</strong>e<br />

Rückmeldung. Zwei Dateien müssen nach erfolgreicher Übersetzung vorliegen:<br />

„WillkommenApplet.java“ und „WillkommenApplet.class“.<br />

4) Erstellen <strong>de</strong>r folgen<strong>de</strong>n HTML-Datei „WillkommenApplet.html“, anschließend<br />

Speichern dieser Datei.<br />

<br />

<br />

Seid gegruesst!<br />

<br />

<br />

Me<strong>in</strong> <strong>Java</strong> Applet sagt:<br />

<br />

<br />

<br />

<br />

Der Bezugspunkt <strong>in</strong> HTML-Dateien auf e<strong>in</strong> Applet erfolgt mit <strong>de</strong>m -Tag. Das<br />

Co<strong>de</strong>-Attribut dient zur Angabe von <strong>de</strong>m Namen <strong>de</strong>r Klasse, die das Applet enthält.<br />

40 Pakete wer<strong>de</strong>n <strong>in</strong> <strong>de</strong>r UML als Akten mit Reitern dargestellt. Die gestrichelten Pfeile repräsentieren die<br />

Abhängigkeiten zwischen <strong>de</strong>n Paketen.<br />

37


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Die Attribute WIDTH und HEIGHT dienen zum Bestimmen <strong>de</strong>r Größe <strong>de</strong>s Applets.<br />

Der Browser benutzt diese Werte zur Zuteilung <strong>de</strong>s Raums, <strong>de</strong>r für das Applet auf<br />

<strong>de</strong>r Seite freigehalten wer<strong>de</strong>n muß. Hier wur<strong>de</strong> e<strong>in</strong>e Box mit e<strong>in</strong>er Breite von 200 und<br />

e<strong>in</strong>er Höhe von 50 Pixeln <strong>de</strong>f<strong>in</strong>iert.<br />

WillkommenApplet ist als Applet implementiert. Es kann nie isoliert stehen, son<strong>de</strong>rn<br />

ist normalerweise Teil e<strong>in</strong>er Webseite.<br />

WillkommenApplet.java<br />

WillkommenApplet.html<br />

WillkommenApplet.class<br />

Abb. 1.3-6: Die Komponenten von WillkommenApplet<br />

5) Ausführung <strong>de</strong>s Applet mit e<strong>in</strong>em javafähigen Web-Browser bzw. <strong>de</strong>m<br />

Appletviewer, z.B. mit <strong>de</strong>m Aufruf „appletviewer WillkommenApplet.html“. Das<br />

Resultat müßte so aussehen:<br />

Abb. 1.3-7: Darstellung <strong>de</strong>s Fensters mit <strong>de</strong>m Appletviewer<br />

In e<strong>in</strong>em Browser wür<strong>de</strong> zusätzlich <strong>de</strong>r Text rund um das Applet („Me<strong>in</strong> <strong>Java</strong><br />

Applet sagt:“) gezeigt wer<strong>de</strong>n.<br />

2. Aufgabe: Verän<strong>de</strong>rn von Schrift und Farbe für <strong>de</strong>n Text „Herzlich Willkommen<br />

<strong>in</strong> <strong>de</strong>r <strong>Java</strong>-Welt“.<br />

Lösungsschritte:<br />

1) Erweitere die Datei mit <strong>de</strong>m Namen „WillkommenApplet.java“ mit Hilfe e<strong>in</strong>es<br />

Dateiaufbereites (Editor).<br />

Die erste Erweiterung soll die Schrift verän<strong>de</strong>rn, <strong>in</strong> <strong>de</strong>r <strong>de</strong>r Text ausgegeben wird. Es<br />

wird e<strong>in</strong> Objekt <strong>de</strong>r Klasse java.awt.Font über folgen<strong>de</strong> Anweisung erzeugt: Font f<br />

= new Font("TimesRoman",Font.BOLD,12);<br />

Objekte <strong>de</strong>r Klasse Font dienen zum Bereitstellen verschie<strong>de</strong>ner Schriftarten für die<br />

Metho<strong>de</strong> drawStr<strong>in</strong>g() und repräsentieren <strong>de</strong>n Namen, <strong>de</strong>n Stil und die Größe<br />

e<strong>in</strong>er Schrift. Mit e<strong>in</strong>em spezifischen Objekt <strong>de</strong>r Klasse Font kann man e<strong>in</strong>e Schrift<br />

aufrufen, die sich von <strong>de</strong>r standardmäßig <strong>in</strong> Applets benutzten Schrift unterschei<strong>de</strong>t.<br />

Dem Font-Objekt wird hier die Schrift „TimesRoman“, fett <strong>in</strong> „12-Punkt“ Größe<br />

38


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

zugewiesen. Das neue Objekt wird anschließend <strong>de</strong>r Instanzvariablen f zugewiesen<br />

und ist so <strong>de</strong>r Metho<strong>de</strong> pa<strong>in</strong>t() zugänglich:<br />

public void pa<strong>in</strong>t (Graphics g)<br />

{<br />

// Mitteilung: Die Schrift zur Anzeige von Text bef<strong>in</strong><strong>de</strong>t sich <strong>in</strong> <strong>de</strong>r<br />

// Instanzvariablen f<br />

g.setFont(f);<br />

// Mitteilung: Die Farbe fuer die Ausgabe ist gelb<br />

g.setColor(Color.yellow);<br />

// Die glebe Farbe dient zum Fuellen e<strong>in</strong>es Rahmens fuer <strong>de</strong>n Text, <strong>de</strong>r<br />

// aus e<strong>in</strong>em Rechteck mit abgerun<strong>de</strong>ten Ecken besteht<br />

g.fillRoundRect(0,0,225,30,10,10);<br />

// Mitteilung: die Farbe fuer Textausgaben ist<br />

// e<strong>in</strong>e Instanz <strong>de</strong>r Klasse Color fuer die Farbe rot<br />

g.setColor(Color.red);<br />

// Mitteilung: Text wird <strong>in</strong> festgelegter Schrift und Farbe bei <strong>de</strong>n<br />

// (x,y)-Koord<strong>in</strong>aten (5,25) ausgegeben.<br />

g.drawStr<strong>in</strong>g("Herzlich Willkommen <strong>in</strong> <strong>de</strong>r <strong>Java</strong> Welt!",5,25);<br />

}<br />

Die Klassen Font und Color wer<strong>de</strong>n über die folgen<strong>de</strong>n import-Anweisungen<br />

bereitgestellt:<br />

import java.awt.Graphics;<br />

import java.awt.Font;<br />

import java.awt.Color;<br />

Dafür kann man auch „import java.awt.*;“ 1 schreiben.<br />

2) Kompiliere die Datei „WillkommenApplet.java“ <strong>in</strong> e<strong>in</strong>e „.class“-Datei.<br />

3) Ausführung <strong>de</strong>s Applet, z.B. über <strong>de</strong>n Aufruf appletviewer<br />

WillkommenApplet.html.<br />

3. Aufgabe: Erweitere das Applet <strong>de</strong>r 1. Aufgabe um e<strong>in</strong>en Dokumentationskommentar<br />

<strong>Java</strong> kennt verschie<strong>de</strong>ne Kommentarzeichen<br />

- für die <strong>in</strong>terne Dokumentation: // und die Komb<strong>in</strong>ation /* ... */<br />

- für die externe Dokumentation: //** ... */<br />

Grundsätzlich steht <strong>de</strong>r Dokumentationskommentar vor Klassen, (Interfaces,<br />

Konstruktoren,) Metho<strong>de</strong>n, z.B. 42 :<br />

/* Das erste <strong>Java</strong> Applet */<br />

import java.awt.Graphics;<br />

/** Dieses Applet <strong>de</strong>monstriert <strong>de</strong>n Grundaufbau von Applets.<br />

*** @author Juergen Sauer<br />

*** @version WS 2001/2002<br />

*** @see WillkommenApplet01<br />

*/<br />

public class WillkommenApplet01 extends java.applet.Applet<br />

{<br />

/** Die Metho<strong>de</strong> pa<strong>in</strong>t ist von Component geerbt<br />

*** Sie wird aufgerufen, wenn das Applet neu gezeichnet wer<strong>de</strong>n muss;.<br />

41 Diese Anweisung stellt alle Klassen <strong>de</strong>s Pakets java.awt zur Verfügung<br />

42 pr13210<br />

39


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

*** @param g E<strong>in</strong>e Referenz e<strong>in</strong>es Grafikobjekts<br />

*/<br />

public void pa<strong>in</strong>t (Graphics g)<br />

{<br />

g.drawStr<strong>in</strong>g("Herzlich Willkommen <strong>in</strong> <strong>de</strong>r <strong>Java</strong> Welt!",5,25);<br />

}<br />

}<br />

Das Programm, das e<strong>in</strong>en <strong>de</strong>rartigen Quellco<strong>de</strong> zu e<strong>in</strong>er HTML-Dokumentation<br />

verarbeitet, heißt javadoc.exe und ist im <strong>Java</strong>-SDK von Sun 43 enthalten.<br />

Aufrufsyntax: javadoc [flags] [klasse|package]<br />

Für „flags“ kann * o<strong>de</strong>r folgen<strong>de</strong> Schalter angegeben wer<strong>de</strong>n:<br />

-version<br />

-author<br />

-sourcepath<br />

-classpath<br />

-doctype<br />

-no<strong>in</strong><strong>de</strong>x<br />

-notree<br />

@version berücksichtigen<br />

@author berücksichtigen<br />

Suchpfad für Klassen<br />

Synonym für sourcepath<br />

Ausgabetyp (Standard: html)<br />

Ke<strong>in</strong> Verzeichnis aller Konstruktoren und Metho<strong>de</strong>n erzeugen<br />

Ke<strong>in</strong>e <strong>Java</strong>-Klassenhierarchie erzeugen<br />

Falls das vorliegen<strong>de</strong> Beispiel mit javadoc –author –version –notree –<br />

no<strong>in</strong><strong>de</strong>x WillkommenApplet01.java aufgerufen wird, erzeugt das Programm<br />

e<strong>in</strong>e Datei WillkommenApplet01.html, die Auswertungen <strong>de</strong>r <strong>Java</strong>-API zeigt und<br />

<strong>de</strong>n Kommentar, z. B.:<br />

43 siehe auch: http://java.sun.com/javadoc/<br />

40


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Abb. 1.3-8: Ausschnitt zum über javadoc erzeugten Dokument<br />

4. Aufgabe: Überwachen <strong>de</strong>s Lebenszyklus e<strong>in</strong>es Applet.<br />

E<strong>in</strong> Applet führt ke<strong>in</strong>e Aktionen aus eigener Initiative aus, son<strong>de</strong>rn empfängt<br />

Ereignisse vom System und liefert Ergebnisse zurück. Beim Start e<strong>in</strong>es Applet ruft<br />

<strong>de</strong>r Webbrowser die Metho<strong>de</strong> <strong>in</strong>it() <strong>de</strong>s Appletobjekts auf. Beim Been<strong>de</strong>n e<strong>in</strong>es<br />

Applets wird die Metho<strong>de</strong> <strong>de</strong>stroy() aufgerufen. <strong>in</strong>it() und <strong>de</strong>stroy() wer<strong>de</strong>n im<br />

Lebenszyklus e<strong>in</strong>es Applet genau e<strong>in</strong>mal ausgeführt, nämlich beim Erzeugen bzw.<br />

beim Been<strong>de</strong>n e<strong>in</strong>es Objekts. Zusätzlich s<strong>in</strong>d zwei weitere Metho<strong>de</strong>n start() und<br />

stop() vorgesehen, die an verschie<strong>de</strong>nen Stellen zur Aktivierung aufgerufen wer<strong>de</strong>n<br />

können. Wählt e<strong>in</strong> Anwen<strong>de</strong>r bei gestartetem Applet z.B. im Browser e<strong>in</strong>e an<strong>de</strong>re<br />

Internetseite an, so ruft <strong>de</strong>r Browser die stop()-Rout<strong>in</strong>e auf und hält das Applet an,<br />

bis <strong>de</strong>r Anwen<strong>de</strong>r wie<strong>de</strong>r auf die Appletseite zurückkehrt. Dann ruft <strong>de</strong>r Browser die<br />

start()-Rout<strong>in</strong>e zum ordnungsgemäßen Fortsetzen <strong>de</strong>s Applet auf.<br />

import java.awt.*;<br />

import java.applet.*;<br />

41


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

public class AppletDemo extends Applet<br />

{<br />

// Instanzvariable<br />

Str<strong>in</strong>g s;<br />

<strong>in</strong>t <strong>in</strong>itialisierungen = 0;<br />

<strong>in</strong>t startpunkte = 0;<br />

<strong>in</strong>t haltepunkte = 0;<br />

// Metho<strong>de</strong>n<br />

public void <strong>in</strong>it() { <strong>in</strong>itialisierungen++; }<br />

public void start() { startpunkte++; }<br />

public void stop() { haltepunkte++; }<br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

// Zur Ausgabe e<strong>in</strong>er geeigneten Nachricht über das Verhalten<br />

// <strong>de</strong>s Applet wird <strong>de</strong>r Str<strong>in</strong>g s mit Hilfe <strong>de</strong>s Opertors + aus<br />

// Teilstr<strong>in</strong>gs zusammengesetzt. Ganze Zahlen wer<strong>de</strong>n dabei <strong>in</strong> Zei-<br />

// chenketten konvertiert.<br />

s = "Initialisierungen: " + <strong>in</strong>itialisierungen +<br />

", Startpunkte: " + startpunkte +<br />

", Haltepunkte: " + haltepunkte;<br />

g.drawStr<strong>in</strong>g(s,10,10);<br />

}<br />

}<br />

Browser<br />

öffnet<br />

Webseite<br />

verlässt<br />

Browser<br />

schliesst<br />

Browser<br />

<br />

<strong>in</strong>it()<br />

start()<br />

pa<strong>in</strong>t()<br />

stop()<br />

<strong>de</strong>stroy()<br />

:Applet<br />

Abb.: Lebenszyklus e<strong>in</strong>es Applets<br />

5. Aufgabe: Entwickle aus <strong>de</strong>r vorliegen<strong>de</strong>n Aufgabe e<strong>in</strong>e allgeme<strong>in</strong>e Applet-<br />

Schablone, die als Vorlage für das Erstellen von Applets dienen kann.<br />

Das folgen<strong>de</strong> Gerüst beschreibt e<strong>in</strong> Muster für alle Applets:<br />

// Name <strong>de</strong>r Klasse:<br />

// Beschreibung:<br />

// Import <strong>de</strong>r Pakete<br />

// import java.lang.*;<br />

// import java.applet.*;<br />

// import java.awt.*;<br />

// Top-Level-Klassen-Deklaration bzw. Def<strong>in</strong>ition <strong>de</strong>s Applets<br />

public Klassenname extends java.applet.Applet<br />

42


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

{<br />

// Variablen-Deklarationen bzw. Def<strong>in</strong>itionen<br />

// ...<br />

// Eigene Metho<strong>de</strong>n<br />

// ...<br />

// Metho<strong>de</strong>n, die ueberschrieben wer<strong>de</strong>n<br />

//<br />

public void <strong>in</strong>it()<br />

{<br />

// ...<br />

}<br />

public void start()<br />

{<br />

// ...<br />

}<br />

public void stop()<br />

{<br />

// ...<br />

}<br />

public void <strong>de</strong>stroy()<br />

{<br />

// ...<br />

}<br />

// Optional: die Ausgabemetho<strong>de</strong><br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

// ..<br />

}<br />

}<br />

E<strong>in</strong> Applet erbt Metho<strong>de</strong>n und Variablen <strong>de</strong>r Applet-Klasse. Die Applet-Klasse erbt<br />

wie<strong>de</strong>rum von e<strong>in</strong>er Reihe an<strong>de</strong>rer Klassen. E<strong>in</strong>e Klasse, die<br />

java.applet.Applet erweitert, kann Variablen und Metho<strong>de</strong>n aus<br />

java.lang.Object, java.awt.Component, java.awt.Conta<strong>in</strong>er sowie<br />

java.awt.Panel verwen<strong>de</strong>n.<br />

1.3.3 Programmablaufpläne und Strukturierte Programmierung<br />

1.3.3.1 Darstellung von Algorithmen durch Programmablaufpläne<br />

Komposition<br />

Aus e<strong>in</strong>er Anweisungsfolge 44 (mit n ∈ N ) „anweisung 1 anweisung 2 ... anweisung n “<br />

konstuiert man durch Klammerung mit { und } e<strong>in</strong>e Anweisung, die Komposition <strong>de</strong>r<br />

Anweisungen <strong>de</strong>r Folge<br />

{ anweisung 1 anweisung 2 ... anweisung n }<br />

Die Anweisungen wer<strong>de</strong>n nache<strong>in</strong>an<strong>de</strong>r <strong>in</strong> <strong>de</strong>r angegebenen Reihenfolge ausgeführt.<br />

Die Klammern umschließen e<strong>in</strong>en Block, <strong>in</strong> <strong>de</strong>m Variablen neu bzw. lokal vere<strong>in</strong>bart<br />

wer<strong>de</strong>n können.<br />

44 vgl. 2.4.1<br />

43


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

In Programmabläufplänen wer<strong>de</strong>n Anweisungen <strong>in</strong> Anweisungsknoten (Symbol:<br />

Kästchen) dargestellt.<br />

Abb.: Anweisungsknoten<br />

Neben Anweisungsknoten gibt es <strong>in</strong> Prgrammablaufplänen Testknoten. Sie wer<strong>de</strong>n<br />

mit e<strong>in</strong>em Rhombus symbolisiert. Testknoten enthalten Tests. Ist die Bed<strong>in</strong>gung<br />

erfüllt bestimmt <strong>de</strong>r mit „true“ bewertete Pfeil <strong>de</strong>n nächsten Knoten. An<strong>de</strong>renfalls <strong>de</strong>r<br />

mit „false“ bewertete.<br />

Abb.: Testknoten<br />

Daneben gibt es noch <strong>in</strong>itale und f<strong>in</strong>ale Knoten:<br />

Abb.: Initialer bzw. f<strong>in</strong>aler Knoten<br />

Bed<strong>in</strong>gte Anweisung 45<br />

<strong>Java</strong>-Notation: if (bed<strong>in</strong>gung) anweisung<br />

Programmablaufplan:<br />

true<br />

false<br />

Abb.: Bed<strong>in</strong>gte Anweisung<br />

Verzweigungsanweisung<br />

<strong>Java</strong>-Notation: if (bed<strong>in</strong>gung) anweisung1 else anweisung2<br />

45 vgl. 2.4.6<br />

44


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Programmablaufplan:<br />

true<br />

false<br />

Abb.: Verzweigungsannweisung<br />

Endgeprüfte Schleife 46<br />

<strong>Java</strong>-Notation: // Vorbed<strong>in</strong>gung<br />

do<br />

anweisung<br />

// Invariante<br />

while (bed<strong>in</strong>gung)<br />

// Nachbed<strong>in</strong>gung<br />

Programmablaufplan:<br />

true<br />

false<br />

Abb.: Endgeprüfte Schleife<br />

Anfangsgeprüfte Schleife 47<br />

<strong>Java</strong>-Notation: // Vorbed<strong>in</strong>gung<br />

while (bed<strong>in</strong>gung)<br />

// Invariante<br />

anweisung<br />

// Nachbed<strong>in</strong>gung<br />

46 vgl. 2.4.7<br />

47 vgl. 2.4.7<br />

45


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Programmablaufplan:<br />

false<br />

true<br />

Abb.: Anfangsgeprüfte Schleife<br />

Bsp.: Flussdiagramm zur Berechnung <strong>de</strong>s größten geme<strong>in</strong>samen Teilers von m und<br />

n.<br />

Start<br />

x = m<br />

y = n<br />

T<br />

x == y<br />

F<br />

z = x<br />

x > y<br />

En<strong>de</strong> x = x – y y = y - x<br />

1.3.3.2 Darstellung von Algorithmen mit Struktogrammen<br />

Je<strong>de</strong>s Programm kann mit drei Grundstrukturen realisiert wer<strong>de</strong>n:<br />

- Sequenz (Komposition)<br />

Bei e<strong>in</strong>er Sequenz wer<strong>de</strong>n die Anweisungen nache<strong>in</strong>an<strong>de</strong>r, <strong>de</strong>r Reihe nach, durchgeführt.<br />

- Fallunterscheidung<br />

Hier wird aufgrund e<strong>in</strong>er Bed<strong>in</strong>gung e<strong>in</strong>er von 2 Fällen ausgewertet.<br />

- Schleife<br />

46


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Bei e<strong>in</strong>er Schleife wird e<strong>in</strong>e Verarbeitung wie<strong>de</strong>rholt durchgeführt, solange e<strong>in</strong>e Bed<strong>in</strong>gung erfüllt ist.<br />

Programme, die aus diesen Grundstrukturen aufgebaut s<strong>in</strong>d, nennt man strukturierte<br />

Programme 48 . Zum Programmentwurf nach <strong>de</strong>r Metho<strong>de</strong> <strong>de</strong>r "strukturierten<br />

Programmierung" gehört das Pr<strong>in</strong>zip <strong>de</strong>r schrittweisen Verfe<strong>in</strong>erung (stepwise<br />

ref<strong>in</strong>ement). Man beg<strong>in</strong>nt mit e<strong>in</strong>em Grobansatz und verfe<strong>in</strong>ert schrittweise 49 . Dabei<br />

wird die Gesamtaufgabe <strong>in</strong> Teilaufgaben aufgeteilt, die unabhängig vone<strong>in</strong>an<strong>de</strong>r<br />

entwickelt wer<strong>de</strong>n können (Modularisierung).<br />

1. Die Sequenz (Komposition)<br />

Aus e<strong>in</strong>er Anweisungsfolge 50 (mit n ∈ N ) „anweisung 1 anweisung 2 ... anweisung n “<br />

konstuiert man durch Klammerung mit { und } e<strong>in</strong>e Anweisung, die Komposition <strong>de</strong>r<br />

Anweisungen <strong>de</strong>r Folge<br />

{ anweisung 1 anweisung 2 ... anweisung n }<br />

Die Anweisungen wer<strong>de</strong>n nache<strong>in</strong>an<strong>de</strong>r <strong>in</strong> <strong>de</strong>r angegebenen Reihenfolge ausgeführt.<br />

Die Klammern umschließen e<strong>in</strong>en Block, <strong>in</strong> <strong>de</strong>m Variablen neu bzw. lokal vere<strong>in</strong>bart<br />

wer<strong>de</strong>n können.<br />

In Programmabläufplänen wer<strong>de</strong>n Anweisungen <strong>in</strong> Anweisungsknoten (Symbol:<br />

Kästchen) dargestellt.<br />

Abb.: Anweisungsknoten<br />

2. Die Selektion (Fallunterscheidung)<br />

Bei e<strong>in</strong>er Sektion wird aufgrund e<strong>in</strong>er Bed<strong>in</strong>gung entschie<strong>de</strong>n 51 , welches <strong>de</strong>r bei<strong>de</strong>n<br />

Anweisungen durchgeführt wird, z.B.:<br />

if (n > 0)<br />

System.out.pr<strong>in</strong>tln("positiv");<br />

else<br />

System.out.pr<strong>in</strong>tln("kle<strong>in</strong>er o<strong>de</strong>r gleich 0");<br />

48 Entschei<strong>de</strong>n<strong>de</strong>r Punkt: Verbannung <strong>de</strong>s Sprungbefehls "goto" aus <strong>de</strong>n Programmen (zentrale Ursache für<br />

undurchsichtige Programme).<br />

49 Top Down Pr<strong>in</strong>zip<br />

50 vgl. 2.4.1<br />

51 vgl. 2.4.6<br />

47


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Allgeme<strong>in</strong>es Format<br />

if (bed<strong>in</strong>gung)<br />

true false statementA;<br />

else<br />

statemebtB;<br />

statementA<br />

statementB<br />

Abb.: Graf. Darstellung e<strong>in</strong>es Struktugramms für e<strong>in</strong>e Selektion<br />

In e<strong>in</strong>em if-Statement, kann <strong>de</strong>r else-Teil wegfallen. Falls <strong>in</strong> if- o<strong>de</strong>r im else-<br />

Zweig mehrere Anweisungen durchzuführen s<strong>in</strong>d, müssen diese mit geschweiften<br />

Klammern als zusammengesetztes Statement geschrieben wer<strong>de</strong>n.<br />

Aufbau von Bed<strong>in</strong>gungen (Boolesche Ausdrücke) 52 : Die <strong>in</strong> Fallunterscheidungen<br />

auftreten<strong>de</strong> Bed<strong>in</strong>gung ist e<strong>in</strong> Boolescher Ausdruck, d.h. e<strong>in</strong> Ausdruck mit <strong>de</strong>n<br />

möglichen Werten true o<strong>de</strong>r false. Boolesche Ausdrücke s<strong>in</strong>d aufgebaut aus:<br />

- Vergleichsoperatoren<br />

< kle<strong>in</strong>er<br />

groesser<br />

>= groesser o<strong>de</strong>r gleich<br />

== gleich<br />

!= ungleich<br />

- logischen Verknüpfungen<br />

&& Und-Verknüpfung<br />

|| O<strong>de</strong>r-Verknüpfung<br />

! Negation<br />

- Klammern ()<br />

Typen-Konversionen bei Vergleichen: Bei Vergleichen wer<strong>de</strong>n (wie <strong>in</strong> algebraischen<br />

Ausdrücken) automatisch erfor<strong>de</strong>rliche Typen-Konversionen durchgeführt, sodass<br />

"<strong>in</strong>t" mit "float" usw. mite<strong>in</strong>an<strong>de</strong>r verglichen wer<strong>de</strong>n kann.<br />

Verschachtelte if-Statements: In verschachtelten if-Anweisungen bezieht sich e<strong>in</strong><br />

"else" immer auf das nächste "if".<br />

true n > 0 false <strong>in</strong>t n;<br />

if ( n > 0)<br />

n == 0<br />

System.out.pr<strong>in</strong>tln("positiv");<br />

true false else<br />

"positiv" if (n == 0)<br />

System.out.pr<strong>in</strong>tln("null");<br />

"null" "negativ" else<br />

System.out.pr<strong>in</strong>tln("negativ");<br />

Das switch-Statement: Für Mehrfach-Fallunterscheidungen, welche aufgrund e<strong>in</strong>es<br />

ganzzahligen Werts erfolgen, stellt <strong>Java</strong> e<strong>in</strong> übersichtliches Statement zur<br />

Verfügung, das switch-Statement 53 , z.B.:<br />

52 vgl. 2.3.5<br />

53 vgl. 2.4.6<br />

48


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

<strong>in</strong>t wochentag;<br />

switch (wochentag)<br />

{<br />

case 0: System.out.pr<strong>in</strong>tln("Sonntag");<br />

break;<br />

case 1: System.out.pr<strong>in</strong>tln("Montag");<br />

break;<br />

case 2: System.out.pr<strong>in</strong>tln("Dienstag"),<br />

break;<br />

…<br />

case 6: System.out.pr<strong>in</strong>tln("Samstag");<br />

break;;<br />

<strong>de</strong>fault: System.out.pr<strong>in</strong>tln("ungueltiger Tag");<br />

}<br />

3. Schleifen<br />

Man unterschei<strong>de</strong>t drei Arten von Schleifen.<br />

- "while"-Schleife 54<br />

Bei e<strong>in</strong>er while-Schleife wird die Aktion wie<strong>de</strong>rholt durchgeführt, solange e<strong>in</strong>e bestimmte Bed<strong>in</strong>gung<br />

erfüllt ist (Fortsetzungsbed<strong>in</strong>gung). Die Bed<strong>in</strong>gung wird bei je<strong>de</strong>m Durchgang vor <strong>de</strong>r Durchführung<br />

<strong>de</strong>r Aktion geprüft.<br />

- Zählschleifen ("for"-Schleifen)<br />

Bei e<strong>in</strong>er Zählschleife wird e<strong>in</strong> Zähler (die sog. Laufvariable) verwen<strong>de</strong>t. Die Aktion <strong>de</strong>r Schleife wird<br />

für je<strong>de</strong>n Wert <strong>de</strong>r Laufvariablen von e<strong>in</strong>em Anfangswert bis (<strong>in</strong>klusive) e<strong>in</strong>em Endwert durchgeführt.<br />

- "do-while"-Schleifen<br />

Diese Schleifen funktionieren wie die "while"-Schleifen mit <strong>de</strong>m Unterschied, dass die<br />

Fortsetzungsbed<strong>in</strong>gung bei je<strong>de</strong>m Durchgang nach <strong>de</strong>r Aktion geprüft wird.<br />

1. while-Schleifen (Anfangsgeprüfte Schleifen)<br />

solange bed<strong>in</strong>gung erfuellt<br />

while (bed<strong>in</strong>gung)<br />

{<br />

statement;<br />

statement<br />

…<br />

.... …<br />

statement<br />

statement;<br />

.... ….<br />

statement<br />

…<br />

}<br />

2. for-Schleifen 55 , z.B.:<br />

<strong>in</strong>t i;<br />

for (i = 0; i < 100; i++)<br />

{<br />

54 vgl. 2.4.7<br />

55 vgl. 2.4.7<br />

49


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

System.out.pr<strong>in</strong>t("Durchlauf: ");<br />

System.out.pr<strong>in</strong>tln(i);<br />

}<br />

Lokale Laufvariablen: Im Initialisierungs-Statement e<strong>in</strong>er for-Anweisung kann e<strong>in</strong>e<br />

Laufvariable direkt durch e<strong>in</strong>e vorangestellte Typangabe <strong>de</strong>f<strong>in</strong>iert wer<strong>de</strong>n.<br />

Bsp.: Berechnung <strong>de</strong>r ersten 100000 Glie<strong>de</strong>r <strong>de</strong>r Harmonischen Reihe<br />

double summe = 0;<br />

for (<strong>in</strong>t i = 0; i < 100000; i++)<br />

summe += 1.0 / i;<br />

System.out.pr<strong>in</strong>tln("Summe: " + summe);<br />

3. do-while-Schleifen 56 (Endgeprüfte Scheifen)<br />

do<br />

statement; {<br />

…<br />

statement;<br />

… ....<br />

statement;<br />

statement;<br />

} while (bed<strong>in</strong>gung)<br />

solange bed<strong>in</strong>gung erfuellt<br />

Bei e<strong>in</strong>er do-while-Anweisung wer<strong>de</strong>n die Anweisungen <strong>de</strong>r Schleife m<strong>in</strong><strong>de</strong>stens<br />

e<strong>in</strong>mal durchgeführt.<br />

Bsp.: Ausgabe <strong>de</strong>r Ziffern e<strong>in</strong>er nicht negativen, ganzen Zahl<br />

do<br />

{<br />

System.out.pr<strong>in</strong>tln(n % 10);<br />

n = n / 10;<br />

} while (n > 0);<br />

4. Been<strong>de</strong>n e<strong>in</strong>es Programms<br />

Mit <strong>de</strong>m Statement System.exit(0) kann e<strong>in</strong> Programm an e<strong>in</strong>er beliebigen Stelle<br />

been<strong>de</strong>t wer<strong>de</strong>n. Dabei wird e<strong>in</strong> Return-Co<strong>de</strong> ausgegeben (0 für normale En<strong>de</strong>).<br />

Dieser Co<strong>de</strong> kann bei Bedarf auf Betriebssystem-Ebene verwertet wer<strong>de</strong>n.<br />

5. Bsp.:<br />

1. BubbleSort<br />

56 vgl. 2.4.7<br />

50


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

swapped = false<br />

swapped = false<br />

Schleife über Arrayposition i von 0 bis Arraylänge - 1<br />

swap(a,i,i+1)<br />

array[i] > array[i+1]<br />

swapped?<br />

swapped = true<br />

2. Suche nach <strong>de</strong>m ersten Vorkommen von Musterzeichenfolge pattern[0..m-1]<br />

<strong>in</strong> Textzeichenfolge text[0..n-1]<br />

Ansatz: Ab je<strong>de</strong>r Position i prügfen, ob <strong>de</strong>r Test text[i..i + (m – 1)] mit <strong>de</strong>m<br />

Muster pattern übere<strong>in</strong>stimmt.<br />

letztes-bearbeitungswürdiges-Zeichen = textLaenge - musterLaenge<br />

Schleife über alle bearbeitungswürdigen Zeichenpositionen i im Text<br />

Str<strong>in</strong>g substr = Substr<strong>in</strong>g im Text von Postion i bis i + Musterlänge<br />

substr stimmt mit Muster übere<strong>in</strong><br />

return: i<br />

return: -1<br />

Implementierung:<br />

public static <strong>in</strong>t search(Str<strong>in</strong>g text, Str<strong>in</strong>g pattern)<br />

{<br />

f<strong>in</strong>al <strong>in</strong>t last = text.length() - pattern.length();<br />

for (<strong>in</strong>t i = 0; i


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

1.3.4 Datentyp und Datenstruktur<br />

Zum Umgang e<strong>in</strong>es Programms mit Werten benötigt man Variable. E<strong>in</strong>e Variable ist<br />

e<strong>in</strong> Bezeichnung, unter <strong>de</strong>r Daten im Speicher gehalten wer<strong>de</strong>n. Variable müssen vor<br />

<strong>de</strong>r ersten Verwendung vere<strong>in</strong>bart (<strong>de</strong>klariert) wer<strong>de</strong>n und e<strong>in</strong>en bestimmten<br />

Datentyp besitzen, z.B.:<br />

double a = 2.7;<br />

double b = 3.5;<br />

"a" und "b" tragen jeweils ihren eigenen Wert <strong>in</strong> sich und s<strong>in</strong>d Variable von e<strong>in</strong>em primitiven<br />

(elementaren, skalaren) Datentyp.<br />

Werte von e<strong>in</strong>em primitiven Typ<br />

- wer<strong>de</strong>n bereits bei <strong>de</strong>r Variablen<strong>de</strong>f<strong>in</strong>tion angelegt<br />

- haben e<strong>in</strong>e Literaldarstellung<br />

- können Parameter- und Rückgabewert bei Metho<strong>de</strong>n se<strong>in</strong><br />

- können mit Operatoren verknüpft wer<strong>de</strong>n.<br />

<strong>Java</strong> besitzt acht primitive Datentypen 57 :<br />

- vier Ganzzahltypen mit unterschiedlichen Wertebereichen<br />

- zwei Gleitpunktzahlentypen mit unterschiedlichen Wertebereichen nach IEEE Standard of B<strong>in</strong>ary Float<strong>in</strong>g<br />

Po<strong>in</strong>t Arithmetic.<br />

- e<strong>in</strong>en Zeichentyp<br />

Primitiver Typ Größe M<strong>in</strong>imum Maximum Wrapper-Klasse<br />

boolean 1-Bit - - Boolean<br />

char 16-Bit Unico<strong>de</strong> 0 Unico<strong>de</strong> 2 16 -1 Character<br />

byte 8-Bit -128 +128 Byte<br />

short 16-Bit -2 15 +2 15 -1 Short<br />

<strong>in</strong>t 32-Bit -2 31 +2 31 -1 Integer<br />

long 64-Bit -2 63 +2 63 -1 Long<br />

float 32-Bit IEEE 754 IEEE 754 Float<br />

double 64-Bit IEEE 754 IEEE 754 Double<br />

void - - - Void<br />

Abb.:<br />

Datentyp<br />

ist bestimmt durch<br />

- e<strong>in</strong>e Menge von Daten (Werte)<br />

- e<strong>in</strong>e Menge von Operationen auf diesen Daten<br />

Datenstruktur<br />

ist e<strong>in</strong> Datentyp mit folgen<strong>de</strong>n Eigenschaften<br />

1. Die besteht aus mehreren Datenelementen. Diese können<br />

57 vgl. 2.2.1<br />

52


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

- atomare Datentypen o<strong>de</strong>r<br />

- selbst Datenstrukturen se<strong>in</strong><br />

2. Sie setzt die Elemente durch e<strong>in</strong>e Menge von Regeln (e<strong>in</strong>e Struktur) <strong>in</strong> e<strong>in</strong>e Beziehung (Relation).<br />

Exemplare (Instanzen, Objekte) von e<strong>in</strong>em Referenzdatentyp müssen (fast immer)<br />

explizit mit new und e<strong>in</strong>em Konstruktor angefor<strong>de</strong>rt wer<strong>de</strong>n.<br />

Elementare Strukturrelationen:<br />

Menge<br />

l<strong>in</strong>eare Struktur (gerichtete 1:1-Relation)<br />

Baum (hierarchisch)<br />

(gerichtete 1 : n – Relation)<br />

Graph (Netzwerk)<br />

( n : m Relation)<br />

Datenstrukturen dienen zur Organisation von Daten, um bestimmte Operationen<br />

(effizient) zu unterstützen. E<strong>in</strong>fache Datenstrukturen, die von Programmiersprachen<br />

unterstützt wer<strong>de</strong>n, s<strong>in</strong>d vor allem: Arrays (Fel<strong>de</strong>r), Records (Datensätze), Sets, Files<br />

(Dateien). In <strong>Java</strong> s<strong>in</strong>d es: Array und File.<br />

Array: E<strong>in</strong> Array (Reihung) ist e<strong>in</strong>e Datenstruktur fester Größe, die aus Elementen<br />

gleichen Typs aufgebaut ist. Diese s<strong>in</strong>d über Indizes zugreifbar. Indizes stehen <strong>in</strong><br />

e<strong>in</strong>er 1:1 Relation zu <strong>de</strong>n Elementen: Je<strong>de</strong>r In<strong>de</strong>xwert i<strong>de</strong>ntifiziert genau e<strong>in</strong>e<br />

Komponente.<br />

Für Arrays <strong>in</strong> <strong>Java</strong> 58 gilt bzgl.<br />

- Deklaration von Referenzvariablen auf Arrays, z.B.: <strong>in</strong>t e<strong>in</strong>Feld[]; <strong>in</strong>t[]<br />

auche<strong>in</strong>Feld;<br />

- Speicheranfor<strong>de</strong>rung. Es gibt mehrere Möglichkeiten<br />

- mit new, z.B. <strong>in</strong>t [] feld = new <strong>in</strong>t[20]<br />

- Initialisierung mit Literalen, z.B.: <strong>in</strong>t [] feld = {5,23,2};<br />

- Zuweisung (ohne erneutes Bereitstellen von Speicher), z.B. <strong>in</strong>t [] e<strong>in</strong>An<strong>de</strong>resFeld = feld;<br />

- Zugriff auf Fel<strong>de</strong>lement, z.B feld[<strong>in</strong><strong>de</strong>x]<br />

Files (Dateien): E<strong>in</strong>e Datei ist e<strong>in</strong>e geordnete endliche Folge e<strong>in</strong>es gegebenen<br />

Datentyps, wobei auf alle Elemente sequentiell zugegriffen wer<strong>de</strong>n kann:<br />

- alle Elemente s<strong>in</strong>d vom gleichen Typ<br />

58 vgl. 2.2.3<br />

53


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

- Struktur zwischen <strong>de</strong>n Elementenm ist l<strong>in</strong>ear<br />

Das Lesen aus e<strong>in</strong>er Datei und Schreiben <strong>in</strong> e<strong>in</strong>e Datei erfolgt <strong>in</strong> <strong>Java</strong> 59 mit <strong>de</strong>n<br />

Klassen FileRea<strong>de</strong>r bzw. FileWriter. FileRea<strong>de</strong>r ist e<strong>in</strong> Datei-Strom und e<strong>in</strong>e<br />

Subklasse von InputStreamRea<strong>de</strong>r. Die Angabe von Dateien kann über Objekte<br />

<strong>de</strong>r Klasse File 60 o<strong>de</strong>r (plattformspezifische) Date<strong>in</strong>amen als Str<strong>in</strong>gs erfolgen.<br />

Bsp. 61 :<br />

import java.io.*;<br />

public class Kopie<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g [] args) throws IOException<br />

{<br />

File e<strong>in</strong>gabeDatei = new File(".","Kopie.java");<br />

File ausgabeDatei = new File("Kopie.txt");<br />

FileRea<strong>de</strong>r e<strong>in</strong> = new FileRea<strong>de</strong>r(e<strong>in</strong>gabeDatei);<br />

FileWriter aus = new FileWriter(ausgabeDatei);<br />

<strong>in</strong>t c;<br />

while ((c = e<strong>in</strong>.read()) != -1) aus.write(c);<br />

e<strong>in</strong>.close(); aus.close();<br />

}<br />

}<br />

Beliebige Rea<strong>de</strong>r-Objekte können <strong>in</strong> e<strong>in</strong>en BufferedRea<strong>de</strong>r gesteckt wer<strong>de</strong>n,<br />

z.B. auch FileRea<strong>de</strong>r-Objekte. (anolog gilt das für Writer bzw. Byte-Ströme):<br />

Bsp. 62 :<br />

import java.io.*;<br />

public class BufferedDemo<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g [] args) throws IOException<br />

{<br />

File e<strong>in</strong>gabeDatei = new File(".","BufferedDEmo.java");<br />

File ausgabeDatei = new File("BufferedDemo.txt");<br />

BufferedRea<strong>de</strong>r e<strong>in</strong> =<br />

new BufferedRea<strong>de</strong>r(new FileRea<strong>de</strong>r(e<strong>in</strong>gabeDatei));<br />

BufferedWriter aus =<br />

new BufferedWriter(new FileWriter(ausgabeDatei));<br />

Str<strong>in</strong>g s;<br />

while ((s = e<strong>in</strong>.readL<strong>in</strong>e()) != null)<br />

{<br />

aus.write(s);<br />

aus.write('\n');<br />

}<br />

e<strong>in</strong>.close(); aus.close();<br />

}<br />

}<br />

Strukturierte zusammengesetze Datentypen s<strong>in</strong>d <strong>in</strong> <strong>Java</strong> fast ausschließlich<br />

Referenzdatentypen (Klassen, Interfaces, Fel<strong>de</strong>r (Array <strong>in</strong> <strong>Java</strong>)). Objekte von e<strong>in</strong>em<br />

Referenzdatentyp<br />

- müssen (fast immer) explizit mit new angefor<strong>de</strong>rt wer<strong>de</strong>n<br />

- können Parameter- und Rückgabewert bei Metho<strong>de</strong>n se<strong>in</strong><br />

- können „Trägerobjekt“ e<strong>in</strong>es Methodcenaufrufs se<strong>in</strong><br />

59 vgl. 7.7 bzw. 1.4.4.3<br />

60 vgl. 1.4.4.4<br />

61 pr13400<br />

62 pr13400<br />

54


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Wird e<strong>in</strong> Objekt e<strong>in</strong>es Referenzdatentyps als Metho<strong>de</strong>nparameter verwen<strong>de</strong>t, wird<br />

e<strong>in</strong>e Kopie <strong>de</strong>r Referenz übergeben. In <strong>de</strong>r Metho<strong>de</strong> kann das Objekt im Gegendatz<br />

zu primitiven Datentypen 63 ) geän<strong>de</strong>rt wer<strong>de</strong>n, diese Än<strong>de</strong>rung wirkt auch nach<br />

außen.<br />

Basis-Datenstrukturen <strong>in</strong> <strong>Java</strong>: <strong>Java</strong> bietet mit <strong>de</strong>m <strong>Java</strong> Collection Framework 64<br />

e<strong>in</strong>e Bibliothek mit <strong>in</strong> <strong>de</strong>r Praxis häufig verwen<strong>de</strong>ten Basis-Datenstrukturen an.<br />

1.4 Die Objektorientierung von <strong>Java</strong><br />

1.4.1 Grundlegen<strong>de</strong> Konzepte<br />

1.4.1.1 Zustand und Verhalten von Objekten, Klassen, Instanz- und Klassen-Variable<br />

bzw. -Metho<strong>de</strong>n<br />

1.4.1.1.1 Die Abbildung von Zustand bzw. Verhalten <strong>in</strong> Instanzvariable bzw.<br />

Instanzmetho<strong>de</strong>n<br />

Objekte s<strong>in</strong>d die Schlüssel zum Verständnis <strong>de</strong>r objektorientierten Technologie.<br />

Objekte s<strong>in</strong>d Gegenstän<strong>de</strong> <strong>de</strong>s täglichen Lebens: <strong>de</strong>r Schreibtisch, das Skript, die<br />

Vorlesung. All diese Objekte <strong>de</strong>r realen Welt haben Zustand und Verhalten. Auch<br />

Software-Objekte haben Zustand und Verhalten. Der Zustand wird <strong>in</strong> Variablen<br />

festgehalten, das Verhalten <strong>de</strong>r Objekte beschreiben Metho<strong>de</strong>n. Die Variablen<br />

bil<strong>de</strong>n <strong>de</strong>n Kern <strong>de</strong>r Objekte. Metho<strong>de</strong>n schirmen <strong>de</strong>n Objektkern von an<strong>de</strong>ren<br />

Objekten <strong>de</strong>s Programms ab (Kapselung). Software-Objekte kommunizieren und<br />

verkehren über Nachrichen (Botschaften) mite<strong>in</strong>an<strong>de</strong>r. Das sen<strong>de</strong>n<strong>de</strong> Objekt schickt<br />

<strong>de</strong>m Zielobjekt e<strong>in</strong>e Auffor<strong>de</strong>rung, e<strong>in</strong>e bestimmte Metho<strong>de</strong> auszuführen. Das<br />

Zielobjekt versteht (hoffentlich) die Auffor<strong>de</strong>rung und reagiert mit <strong>de</strong>r zugehörigen<br />

Metho<strong>de</strong>. Die genaue formale Schreibweise solcher Botschaften <strong>in</strong> objektorientierten<br />

Sprachen ist im Detail verschie<strong>de</strong>n, jedoch wird meistens folgen<strong>de</strong> Form verwen<strong>de</strong>t:<br />

Empfänger.Metho<strong>de</strong>nname(Argument). „Argument“ ist <strong>in</strong> <strong>de</strong>m<br />

Botschaftsausdruck e<strong>in</strong> Übergabeparameter für die Metho<strong>de</strong>.<br />

In <strong>de</strong>r realen Welt existieren häufig Objekte <strong>de</strong>r gleichen Art. Sie wer<strong>de</strong>n über e<strong>in</strong>en<br />

Prototyp, e<strong>in</strong>e Klasse, zusammengefaßt. E<strong>in</strong>e Klassen<strong>de</strong>f<strong>in</strong>ition <strong>in</strong> <strong>Java</strong> wird durch<br />

das Schlüsselwort „class“ e<strong>in</strong>geleitet. Anschließend folgt <strong>in</strong>nerhalb von<br />

geschweiften Klammern e<strong>in</strong>e beliebige Anzahl an Variablen- und<br />

Metho<strong>de</strong>n<strong>de</strong>f<strong>in</strong>itionen. Zum Anlegen e<strong>in</strong>es Objekts e<strong>in</strong>er Klasse (Instanziierung 65 )<br />

muß e<strong>in</strong>e Variable vom Typ <strong>de</strong>r Klasse <strong>de</strong>klariert und mit Hilfe <strong>de</strong>s new-Operators<br />

e<strong>in</strong> neu erzeugtes Objekt zugewiesen wer<strong>de</strong>n. Das Speicher-Management <strong>in</strong> <strong>Java</strong><br />

erfolgt automatisch. Während das Erzeugen von Objekten immer e<strong>in</strong>en expliziten<br />

63 Wird <strong>in</strong> <strong>de</strong>r Metho<strong>de</strong> <strong>de</strong>r Wert <strong>de</strong>s Parameters mit primitivem Datentyp geän<strong>de</strong>rt, dann dr<strong>in</strong>gt dies nicht nach<br />

außen<br />

64 vgl. 6.2<br />

65 E<strong>in</strong>e Instanz e<strong>in</strong>er Klasse ist e<strong>in</strong> (tatsächliches) Objekt (konkrete Darstellung)<br />

55


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Aufruf <strong>de</strong>s new-Operators erfor<strong>de</strong>rt 66 , erfolgt die Rückgabe von nicht mehr<br />

benötigtem Speicher automatisch 67 .<br />

Das Schreiben e<strong>in</strong>es Programms besteht damit aus Entwurf und Zusammenstellung<br />

von Klassen. Klassenbibliotheken (Sammlung von Klassen) stellen Lösungen für<br />

grundlegen<strong>de</strong> Programmieraufgaben bereit.<br />

Zustand, Aussehen und an<strong>de</strong>re Qualitäten e<strong>in</strong>es Objekts (Attribute) wer<strong>de</strong>n durch<br />

Variable <strong>de</strong>f<strong>in</strong>iert. Da je<strong>de</strong> Instanz e<strong>in</strong>er Klasse verschie<strong>de</strong>ne Werte für ihre<br />

Variablen haben kann, spricht man von Instanzvariablen. Zusätzlich gibt es noch<br />

Klassenvariable, die die Klasse selbst und alle ihre Instanzen betreffen. Werte von<br />

Klassenvariablen wer<strong>de</strong>n direkt <strong>in</strong> <strong>de</strong>r Klasse gespeichert. Der Zustand wird <strong>in</strong><br />

Variablen festgehalten und zeigt <strong>de</strong>n momentanen Stand <strong>de</strong>r Objektstruktur an, d.h.<br />

die <strong>in</strong> <strong>de</strong>n e<strong>in</strong>zelnen Bestandteilen <strong>de</strong>s Objekts enthaltenen Informationen und<br />

Daten. Abhängig vom Detaillierungsgrad kann die Notation für e<strong>in</strong>e Variable <strong>de</strong>n<br />

Namen, <strong>de</strong>n Datentyp und <strong>de</strong>n vore<strong>in</strong>gestellten Wert zeigen:<br />

Sichtbarkeit Typ Name = vore<strong>in</strong>gestellter_Wert;<br />

Sichtbarkeit: öffentlich (public), geschützt (protected) o<strong>de</strong>r privat (private)<br />

Typ: Datentyp<br />

Name: e<strong>in</strong>e nach bestimmten Regeln 68 gebil<strong>de</strong>te Zeichenkette<br />

Nach <strong>de</strong>r Initialisierung haben alle Variablen <strong>de</strong>s Objekts zunächst Standardwerte<br />

(vore<strong>in</strong>gestellte Werte). Der Zugriff auf sie erfolgt mit Hilfe <strong>de</strong>r Punktnotation:<br />

Objekt.Variable.<br />

Zur Bezugnahme auf das aktuelle Objekt dient das Schlüsselwort this. Es kann an<br />

je<strong>de</strong>r beliebigen Stelle angegeben wer<strong>de</strong>n, an <strong>de</strong>r das Objekt ersche<strong>in</strong>en kann, z.B.<br />

<strong>in</strong> e<strong>in</strong>er Punktnotation zum Verweis auf Instanzvariablen <strong>de</strong>s Objekts o<strong>de</strong>r als<br />

Argument für e<strong>in</strong>e Metho<strong>de</strong> o<strong>de</strong>r als Ausgabewert <strong>de</strong>r aktuellen Metho<strong>de</strong>n. In vielen<br />

Fällen kann das Schlüsselwort this entfallen. Das hängt davon ab, ob es Variablen<br />

mit gleichem Namen im lokalen Bereich gibt.<br />

Zur Def<strong>in</strong>ition <strong>de</strong>s Verhaltens von Objekten dienen Metho<strong>de</strong>n. Metho<strong>de</strong>n s<strong>in</strong>d<br />

Funktionen, die <strong>in</strong>nerhalb von Klassen <strong>de</strong>f<strong>in</strong>iert wer<strong>de</strong>n und auf Klassen<strong>in</strong>stanzen<br />

angewandt wer<strong>de</strong>n. Metho<strong>de</strong>n wirken sich aber nicht nur auf e<strong>in</strong> Objekt aus. Objekte<br />

kommunizieren auch mite<strong>in</strong>an<strong>de</strong>r durch Metho<strong>de</strong>n. E<strong>in</strong>e Klasse o<strong>de</strong>r e<strong>in</strong> Objekt kann<br />

Metho<strong>de</strong>n e<strong>in</strong>er an<strong>de</strong>ren Klasse o<strong>de</strong>r e<strong>in</strong>es an<strong>de</strong>ren Objekts aufrufen, um<br />

Än<strong>de</strong>rungen <strong>in</strong> <strong>de</strong>r Umgebung mitzuteilen o<strong>de</strong>r e<strong>in</strong> Objekt aufzufor<strong>de</strong>rn, se<strong>in</strong>en<br />

Zustand zu än<strong>de</strong>rn. Instanzmetho<strong>de</strong>n (Operationen, Services) wer<strong>de</strong>n auf e<strong>in</strong>e<br />

Instanz angewandt, Klassenmetho<strong>de</strong>n beziehen sich auf e<strong>in</strong>e Klasse.<br />

Klassenmetho<strong>de</strong>n können nur mit Klassenvariablen arbeiten.<br />

Die Beschreibung <strong>de</strong>r Operationen (Nachrichten, Metho<strong>de</strong>n) erfolgt nach <strong>de</strong>m<br />

folgen<strong>de</strong>n Schema:<br />

Sichbarkeit Rückgabetypausdruck Name(Parameterliste)<br />

Sichtbarkeit: öffentlich (public), geschützt (protected), privat (private) 69<br />

Rückgabetypausdruck: Je<strong>de</strong> Metho<strong>de</strong> ist typisiert. Der Typ e<strong>in</strong>er Metho<strong>de</strong> bestimt <strong>de</strong>n Typ <strong>de</strong>s<br />

Rückgabewerts. Dieser kann von e<strong>in</strong>em beliebigen primitiven Typ 70 , e<strong>in</strong>em Objekttyp o<strong>de</strong>r vom Typ<br />

void se<strong>in</strong>. Metho<strong>de</strong>n vom Typ void haben ke<strong>in</strong>en Rückgabewert und dürfen nicht <strong>in</strong> Ausdrücken<br />

66 Ausnahmen: Str<strong>in</strong>g-, Array-Literale<br />

67 E<strong>in</strong> Garbage-Collector (niedrigpriorisierte H<strong>in</strong>tergrundprozeß) sucht <strong>in</strong> regelmäßigen Abstän<strong>de</strong>n nach nicht<br />

mehr referenzierten Objekten und gibt <strong>de</strong>n durch sie belegten Speicher an das Laufzeitsystem zurück<br />

68 vgl. 2.1.2 Bezeichner und Namenskonventionen<br />

69 vgl. 2.6.2<br />

70 vgl. 2.2<br />

56


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

verwen<strong>de</strong>t wer<strong>de</strong>n. Hat e<strong>in</strong>e Metho<strong>de</strong> e<strong>in</strong>en Rückgabewert, dann kann sie mit <strong>de</strong>r „return“-<br />

Anweisung 71 e<strong>in</strong>en Wert an <strong>de</strong>n Aufrufer zurückgeben.<br />

Parameterliste enthält optional Argumente und hat folgen<strong>de</strong> Struktur: Datentyp<br />

variablenname, ..... Die Anzahl <strong>de</strong>r Parameter ist beliebig und kann Null se<strong>in</strong>.<br />

In <strong>Java</strong> wird je<strong>de</strong> selbst<strong>de</strong>f<strong>in</strong>ierte Klasse mit Hilfe <strong>de</strong>s Operators new <strong>in</strong>stanziert. Mit<br />

Ausnahme von Zeichenketten (Str<strong>in</strong>gs) und Datenfel<strong>de</strong>rn (Arrays), bei <strong>de</strong>nen <strong>de</strong>r<br />

Compiler auch Literale zur Objekterzeugung bereitstellt, gilt dies für alle<br />

vor<strong>de</strong>f<strong>in</strong>ierten Klassen <strong>de</strong>r <strong>Java</strong>-Bibliothek.<br />

Der Aufruf e<strong>in</strong>er Metho<strong>de</strong> erfolgt ähnlich <strong>de</strong>r Verwendung e<strong>in</strong>er Instanzvariablen <strong>in</strong><br />

„Punktnotation“. Zur Unterscheidung von e<strong>in</strong>em Variablenzugriff müssen zusätzlich<br />

die Parameter <strong>in</strong> Klammern angegeben wer<strong>de</strong>n, selbst wenn die Parameter-Liste leer<br />

ist.<br />

E<strong>in</strong> praktisches Beispiel:<br />

1. Erstellen <strong>de</strong>r Klasse Rechentafel zum Rechnen mit ganzen Zahlen<br />

Die grundlegen<strong>de</strong> Klassen<strong>de</strong>f<strong>in</strong>ition ist: class Rechentafel{ }<br />

Das ist e<strong>in</strong>e <strong>Java</strong>-Klasse (<strong>in</strong> e<strong>in</strong>fachster Form), selbstverständlich passiert hier noch<br />

nicht viel. Damit etwas Aktion stattf<strong>in</strong><strong>de</strong>n kann, müssen Zustand und Verhalten<br />

diverser Rechentafel-Objekte festgehalten wer<strong>de</strong>n können.<br />

Der Zustand wird <strong>in</strong> Variablen gespeichert. In Rechentafel-Objekten gibt es zwei<br />

Operan<strong>de</strong>n zur Aufnahme ganzzahliger Werte für die Berechnung und e<strong>in</strong>e Variable<br />

zur Aufnahme <strong>de</strong>s Resultats nach <strong>de</strong>r Berechnung. Operan<strong>de</strong>n und Resultat s<strong>in</strong>d <strong>in</strong><br />

<strong>de</strong>r Klasse Rechentafel Instanzvariable, da je<strong>de</strong>s Rechentafel-Objekt dafür<br />

verschie<strong>de</strong>ne Werte haben kann. Folgen<strong>de</strong> Instanzvariable (mit Datentyp <strong>in</strong>t)<br />

wer<strong>de</strong>n <strong>in</strong> <strong>de</strong>n durch „{ }“markierten Klassenkörper e<strong>in</strong>getragen:<br />

// Instanzvariable<br />

<strong>in</strong>t ersterOperand = 0;<br />

<strong>in</strong>t zweiterOperand = 0;<br />

Da ke<strong>in</strong>e spezifische Angabe zur Sichtbarkeit vorliegen, haben Objekte auf die<br />

Variablen Zugriff, die aus Klassen <strong>in</strong>nerhalb <strong>de</strong>s Klassenverzeichnisses gebil<strong>de</strong>t<br />

wer<strong>de</strong>n.<br />

Rechentafel-Objekte sollten „Rechnen“ können. Dieses Verhalten be<strong>de</strong>utet: Die<br />

vorliegen<strong>de</strong>n Instanzvariablen s<strong>in</strong>d durch Instanzmetho<strong>de</strong>n zu ergänzen, z.B.:<br />

// Operationen<br />

<strong>in</strong>t addiere()<br />

{<br />

return ersterOperand + zweiterOperand;<br />

}<br />

<strong>in</strong>t subtrahiere()<br />

{<br />

return ersterOperand - zweiterOperand;<br />

}<br />

<strong>in</strong>t multipliziere()<br />

{<br />

return ersterOperand * zweiterOperand;<br />

}<br />

<strong>in</strong>t dividiere()<br />

{<br />

// ganzzahlige Division<br />

return ersterOperand / zweiterOperand;<br />

71 vgl. 2.6.4<br />

57


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

}<br />

Die Klasse ist damit für Rechentafel-Objekte vollständig bereitgestellt. Allerd<strong>in</strong>gs wird<br />

erst nach <strong>de</strong>m H<strong>in</strong>zufügen <strong>de</strong>r ma<strong>in</strong>()-Metho<strong>de</strong> aus dieser Klasse e<strong>in</strong>e <strong>Java</strong>-<br />

Anwendung 1 :<br />

2. Die Klasse Rechentafeltest zum Überprüfen <strong>de</strong>r Klasse Rechentafel<br />

Die Klasse Rechentafeltest ist durch die Anwendung <strong>de</strong>r Klasse Rechentafel<br />

bestimmt. Sie enthält die ma<strong>in</strong>()-Metho<strong>de</strong>. Im Mittelpunkt <strong>de</strong>r Anwendung steht die<br />

Anweisung:<br />

Rechentafel e<strong>in</strong>Rechenobjekt = new Rechentafel();<br />

Sie erzeugt e<strong>in</strong>e Instanz <strong>de</strong>r Klasse Rechentafel und speichert e<strong>in</strong>e Referenz<br />

darauf <strong>in</strong> <strong>de</strong>r Variablen „e<strong>in</strong>Rechenobjekt“. In je<strong>de</strong>r objektorientierten<br />

Programmiersprache lassen sich spezielle Metho<strong>de</strong>n <strong>de</strong>f<strong>in</strong>ieren, die bei <strong>de</strong>r<br />

Initialisierung e<strong>in</strong>es Objekts aufgerufen wer<strong>de</strong>n. In <strong>Java</strong> wer<strong>de</strong>n Konstruktoren als<br />

Metho<strong>de</strong>n ohne Rückgabewert <strong>de</strong>f<strong>in</strong>iert, die <strong>de</strong>n Namen <strong>de</strong>r Klasse erhalten, zu <strong>de</strong>r<br />

sie gehören. Falls e<strong>in</strong>e Klasse ke<strong>in</strong>en expliziten Konstruktor besitzt, wird e<strong>in</strong><br />

parameterloser „<strong>de</strong>fault“-Konstruktor aufgerufen (, <strong>de</strong>r hier zusammen mit <strong>de</strong>m<br />

Operator new verwen<strong>de</strong>t wird).<br />

import java.lang.*;<br />

class Rechentafeltest extends Object<br />

{<br />

// Zur Ausführung mit <strong>de</strong>m <strong>Java</strong>-Interpreter wird e<strong>in</strong>e ma<strong>in</strong>()-Metho<strong>de</strong><br />

// benoetigt<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g argv[])<br />

{<br />

// Erzeugen e<strong>in</strong>er Instanz <strong>de</strong>r Klasse Rechentafel<br />

Rechentafel e<strong>in</strong>Rechenobjekt = new Rechentafel();<br />

// Setzen <strong>de</strong>r Instanzvariablen<br />

e<strong>in</strong>Rechenobjekt.ersterOperand = 3;<br />

e<strong>in</strong>Rechenobjekt.zweiterOperand = 2;<br />

// Test mit e<strong>in</strong>em Rechenobjekt <strong>de</strong>r Klasse Rechentafel<br />

System.out.pr<strong>in</strong>tln("Test mit e<strong>in</strong>em Rechentafel-Objekt");<br />

// Aufruf <strong>de</strong>r Metho<strong>de</strong>n und Ausgabe <strong>de</strong>s jeweiligen Resultats<br />

System.out.pr<strong>in</strong>t("Addition ");<br />

System.out.pr<strong>in</strong>tln("von " + e<strong>in</strong>Rechenobjekt.ersterOperand + " und "<br />

+ e<strong>in</strong>Rechenobjekt.zweiterOperand + " ist "<br />

+ e<strong>in</strong>Rechenobjekt.addiere());<br />

System.out.pr<strong>in</strong>t("Subtraktion ");<br />

System.out.pr<strong>in</strong>tln("von " + e<strong>in</strong>Rechenobjekt.ersterOperand + " und "<br />

+ e<strong>in</strong>Rechenobjekt.zweiterOperand + " ist "<br />

+ e<strong>in</strong>Rechenobjekt.subtrahiere());<br />

System.out.pr<strong>in</strong>t("Multiplikation ");<br />

System.out.pr<strong>in</strong>tln("von " + e<strong>in</strong>Rechenobjekt.ersterOperand + " und "<br />

+ e<strong>in</strong>Rechenobjekt.zweiterOperand + " ist "<br />

+ e<strong>in</strong>Rechenobjekt.multipliziere());<br />

System.out.pr<strong>in</strong>t("Division ");<br />

System.out.pr<strong>in</strong>tln("von " + e<strong>in</strong>Rechenobjekt.ersterOperand + " und "<br />

+ e<strong>in</strong>Rechenobjekt.zweiterOperand + " ist "<br />

+ e<strong>in</strong>Rechenobjekt.dividiere());<br />

}<br />

}<br />

72 vgl. pr14101<br />

58


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

1.4.1.1.2 E<strong>in</strong>fache Typen 74 , Referenztypen, Automatisches Boxen<br />

E<strong>in</strong>fache Typen und Wrapper-Klassen<br />

Je<strong>de</strong>s <strong>Java</strong>-Programm besteht aus e<strong>in</strong>er Sammlung von Klassen. Der vollständige<br />

Co<strong>de</strong> von <strong>Java</strong> wird <strong>in</strong> Klassen e<strong>in</strong>geteilt. Es gibt davon nur e<strong>in</strong>e Ausnahme:<br />

Boolesche Operatoren, Zahlen und an<strong>de</strong>re e<strong>in</strong>fache Typen 75 s<strong>in</strong>d <strong>in</strong> <strong>Java</strong> erst e<strong>in</strong>mal<br />

ke<strong>in</strong>e Objekte. <strong>Java</strong> hat für alle e<strong>in</strong>fachen Typen sog. Wrapper-Klassen 76<br />

implementiert. E<strong>in</strong> Wrapper-Klasse ist e<strong>in</strong>e spezielle Klasse, die e<strong>in</strong>e<br />

Objektschnittstelle für die <strong>in</strong> <strong>Java</strong> verschie<strong>de</strong>nen primitiven Typen darstellt. Über<br />

Wrapper-Objekte können alle e<strong>in</strong>fachen Typen wie Klassen behan<strong>de</strong>lt wer<strong>de</strong>n.<br />

Den primitiven Typen s<strong>in</strong>d „Wrapper“-Klassen zugeordnet, die e<strong>in</strong> nicht primitives<br />

Objekt auf <strong>de</strong>m „Heap“ zur Darstellung <strong>de</strong>s primitiven Typs erzeugen, z.B.:<br />

char zeichen = 'x';<br />

Character zeichenDarstellung = new Character(zeichen)<br />

bzw.<br />

Character zeichenDarstellung = new Character('x');<br />

PrimitivTyp Wrapper-Typ<br />

boolean Boolean<br />

byte Byte<br />

short Short<br />

<strong>in</strong>t<br />

Integer<br />

long Long<br />

float Float<br />

double Double<br />

void 77 Void 78<br />

Wrapper-Klassen erlauben es, Daten e<strong>in</strong>es primitiven Typs als Objekt zu speichern.<br />

Objekte dieser Klassen s<strong>in</strong>d nicht modifizierbar. Ist e<strong>in</strong>mal e<strong>in</strong> Integer-Objekt mit<br />

e<strong>in</strong>er bestimmten Zahl erzeugt, lässt sich die <strong>in</strong> diesem Objekt erzeugte Zahl nicht<br />

mehr modifizieren.<br />

Alle Wrapper-Typen bieten bestimmte Service-Metho<strong>de</strong>n an.<br />

Wichtige und gebrächliche Servive-Metho<strong>de</strong>n e<strong>in</strong>schl. weiterer Merkmale:<br />

- Alle Wrapper-Typen s<strong>in</strong>d f<strong>in</strong>al <strong>de</strong>klariert und können nicht weiter spezialisiert wer<strong>de</strong>n.<br />

- ...Value(), z.B. booleanValue(), byteValue(), shortValue(), <strong>in</strong>tValue(),<br />

longValue(), floatValue(), doubleValue(). All diese Metho<strong>de</strong>n geben <strong>de</strong>n <strong>in</strong>tern<br />

gekapselten Wert unverän<strong>de</strong>rt zurück. Darüber h<strong>in</strong>aus existieren Metho<strong>de</strong>n zur Ausgabe<br />

e<strong>in</strong>es numerischen Wrapper-Typs <strong>in</strong> e<strong>in</strong>en beliebigen numerischen Primitivtyp 79 .<br />

- Konstruktoren erlauben die Erzeugung e<strong>in</strong>es Wrappertyps aus e<strong>in</strong>em Ausdruck vom Typ <strong>de</strong>s<br />

gekapselten Typs o<strong>de</strong>r aus e<strong>in</strong>em Str<strong>in</strong>g.<br />

- compareTo() vergleicht <strong>de</strong>n gekapselten zweier Wrappertyp-Objekte.<br />

74 vgl. 2.2.1<br />

75 vgl. 2.2.1<br />

76 vgl. 1.4.2.7<br />

77 Dieser Typ ist nicht als Datentyp für Variable und Attribute verfügbar, kann nur als Rückgabetyp von<br />

Operationen spezifiziert wer<strong>de</strong>n.<br />

78 kann nicht <strong>in</strong>stanziiert wer<strong>de</strong>n<br />

79 ersetzen die explizite Typumwandlung<br />

59


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

- parse...() Metho<strong>de</strong>n, z.B. parseByte(Str<strong>in</strong>g s), parseByte(Str<strong>in</strong>g s, <strong>in</strong>t<br />

radix), parseInt(Str<strong>in</strong>g s), parseInt(Str<strong>in</strong>g s, <strong>in</strong>t radix), erzeugen aus<br />

Zeichenketten <strong>de</strong>n jeweiligen Primitivtyp.<br />

- valueOf(). Für alle Wrapper-Typen <strong>in</strong>tegraler Primitivtypen (byte, char, long, <strong>in</strong>t, short) ist<br />

mit <strong>de</strong>r valueOf()-Metho<strong>de</strong> e<strong>in</strong>e (Factory-) Metho<strong>de</strong> zur Erzeugung e<strong>in</strong>es neuen Wrapper-<br />

Objekts aus e<strong>in</strong>er Zeichenkette <strong>de</strong>f<strong>in</strong>iert.<br />

- die üblichen arithmetischen Operationen stehen zur Verknüpfung von Wrapperobjekten nicht<br />

zur Verfügung (ke<strong>in</strong> Operator overload<strong>in</strong>g).<br />

Referenztypen<br />

Neben <strong>de</strong>n primitiven Typen gibt es die Referenztypen. Dazu gehören: Objekte <strong>de</strong>r<br />

benutzer<strong>de</strong>f<strong>in</strong>ierten und aus vom System bereitgestellten Klassen, <strong>de</strong>r Klassen<br />

Str<strong>in</strong>g und Array (Datenfeld). Weiterh<strong>in</strong> gibt es die vor<strong>de</strong>f<strong>in</strong>ierte Konstante<br />

„null“, die e<strong>in</strong>e leere Referenz bezeichnet.<br />

„Str<strong>in</strong>g“ und „Array“ weisen e<strong>in</strong>ige Beson<strong>de</strong>rheiten aus:<br />

- Für Str<strong>in</strong>gs und Arrays kennt <strong>de</strong>r Compiler Literale, die e<strong>in</strong>en expliziten Aufruf <strong>de</strong>s Operator new<br />

überflüssig machen<br />

- Arrays s<strong>in</strong>d klassenlose Objekte. Sie können ausschließlich vom Compiler erzeugt wer<strong>de</strong>n, besitzen<br />

aber ke<strong>in</strong>e explizite Klassen<strong>de</strong>f<strong>in</strong>ition. Sie wer<strong>de</strong>n <strong>de</strong>nnoch vom Laufzeitsystem wie normale<br />

Objeklte behan<strong>de</strong>lt.<br />

- Die Klasse Str<strong>in</strong>g ist zwar im JDK vorhan<strong>de</strong>n. Der Compiler hat aber Kenntnis über <strong>de</strong>n <strong>in</strong>neren<br />

Aufbau von Str<strong>in</strong>gs und generiert bei Anzeigeoperationen Co<strong>de</strong>, <strong>de</strong>r auf Metho<strong>de</strong>n <strong>de</strong>r Klassen<br />

Str<strong>in</strong>g und Str<strong>in</strong>gBuffer zugreift.<br />

Automatisches Boxen<br />

Box<strong>in</strong>g. Beim Speichern e<strong>in</strong>es Datums von e<strong>in</strong>em primitiven Typ <strong>in</strong> e<strong>in</strong>er Variablen,<br />

die nur Objekte speichern kann, ist kapseln <strong>in</strong> e<strong>in</strong>em Objekt <strong>de</strong>r entsprechen<strong>de</strong>n<br />

(Wrapper-) Klasse nötig (box<strong>in</strong>g). Es kam zu Konstruktoraufrufen dieser Klassen.<br />

Unbox<strong>in</strong>g. Sollte später mit Operatoren auf <strong>de</strong>n Zahlen, die durch gekapselte Objekte<br />

ausgedrückt wur<strong>de</strong>n, gerechnet wer<strong>de</strong>n, so war <strong>de</strong>r primitive Wert mit e<strong>in</strong>em<br />

Metho<strong>de</strong>naufruf aus <strong>de</strong>m Objekt wie<strong>de</strong>r zu extrahieren (unbox<strong>in</strong>g). Es kam zu<br />

Aufrufen wie <strong>in</strong>tValue() im Co<strong>de</strong> 80 .<br />

Bsp.: Manuelles Verpacken und Auspacken primitiver Daten<br />

public class ManuellesBoxen<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g [] args)<br />

{<br />

<strong>in</strong>t i1 = 42;<br />

Object o = new Integer(i1);<br />

System.out.pr<strong>in</strong>tln(o);<br />

Integer i2 = new Integer(17);<br />

Integer i3 = new Integer(4);<br />

<strong>in</strong>t i4 = 21;<br />

System.out.pr<strong>in</strong>tln((i2.<strong>in</strong>tValue() + i3.<strong>in</strong>tValue()) * 14);<br />

}<br />

}<br />

Mit <strong>Java</strong> 1.5 können die primitiven Typen mit ihren entsprechen<strong>de</strong>n KLassen<br />

synonym verwen<strong>de</strong>t wer<strong>de</strong>n. Nach außen h<strong>in</strong> wer<strong>de</strong>n die primitiven Typen auch<br />

Objekttypen. Der Übersetzer nimmt die notwendigen box<strong>in</strong>g- und unbox<strong>in</strong>g-<br />

Operationen vor.<br />

80 vgl. 1.4.2.7<br />

60


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Bsp. 81 : Automatic Box<strong>in</strong>g<br />

public class AutomatischesBoxen<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g [] args)<br />

{<br />

<strong>in</strong>t i1 = 42;<br />

Object o = i1;<br />

System.out.pr<strong>in</strong>tln(o);<br />

Integer i2 = 17;<br />

Integer i3 = 4;<br />

<strong>in</strong>t i4 = 21;<br />

System.out.pr<strong>in</strong>tln((i2 + i3) * 14);<br />

}<br />

}<br />

1.4.1.1.3 Konstruktoren<br />

E<strong>in</strong>e „Constructor“-Metho<strong>de</strong> bestimmt, wie e<strong>in</strong> Objekt <strong>in</strong>itialisiert wird. Konstruktoren<br />

haben immer <strong>de</strong>n gleichen Namen wie die Klasse und besitzen ke<strong>in</strong>e „return“-<br />

Anweisung. <strong>Java</strong> erledigt beim Aufruf e<strong>in</strong>es Konstruktors folgen<strong>de</strong> Aufgaben:<br />

- Speicherzuweisung für das Objekt<br />

- Initialisieung <strong>de</strong>r Instanzvariablen <strong>de</strong>s Objekts auf ihre Anfangswerte o<strong>de</strong>r e<strong>in</strong>en Default-Wert (0 bei<br />

Zahlen, „null“ bei Objekten, „false“ bei booleschen Operatoren.<br />

- Aufruf <strong>de</strong>r Konstruktor-Metho<strong>de</strong> <strong>de</strong>r Klasse<br />

Gewöhnlich stellt man „explizit“ e<strong>in</strong>en „<strong>de</strong>fault“-Konstruktor zur Verfügung. Dieser<br />

parameterlose Konstruktor überlagert <strong>de</strong>n implizit bereitgestellten „<strong>de</strong>fault“-<br />

Konstruktor. Er wird dann bei allen parameterlosen Instanzierungen verwen<strong>de</strong>t.<br />

Konstruktoren können aber auch – wie normale Dateien – Parameter übergeben<br />

bekommen, z.B. 82 :<br />

public Rechentafel(<strong>in</strong>t ersterOperand, <strong>in</strong>t zweiterOperand)<br />

{<br />

super(); // Aufruf <strong>de</strong>s Default-Konstruktors <strong>de</strong>r Superklasse<br />

this.ersterOperand = ersterOperand;<br />

this.zweiterOperand = zweiterOperand;<br />

}<br />

super() bestimmt e<strong>in</strong>en Aufruf <strong>de</strong>s „<strong>de</strong>fault“-Konstruktors <strong>de</strong>r e<strong>in</strong><strong>de</strong>utig<br />

bestimmten „Superklasse“. „super(...)“ darf nur als erste Anweisung e<strong>in</strong>es<br />

Konstruktors auftreten. Generell bezeichnet das reservierte Wort „super“, die nach<br />

„extends“ benannte Superklasse.<br />

Häufig s<strong>in</strong>d die Parameter Anfangswerte für Instanzvariablen und haben oft <strong>de</strong>n<br />

gleichen Namen wie die entsprechen<strong>de</strong>n Instanzvariablen. In diesen Fällen löst die<br />

Verwendung von bsp. this.ersterOperand = ersterOperand; <strong>de</strong>rartige<br />

Namenskonflikte auf.<br />

Bei „this“ han<strong>de</strong>lt es sich um e<strong>in</strong>en Zeiger, <strong>de</strong>r beim Anlegen e<strong>in</strong>es Objekts<br />

automatisch generiert wird. „this“ ist e<strong>in</strong>e Referenzvariable, die auf das aktuelle<br />

Objekt zeigt und zum Ansprechen <strong>de</strong>r eigenen Metho<strong>de</strong>n und Instanzvariablen dient.<br />

Der „this“-Zeiger ist auch explizit verfügbar und kann wie e<strong>in</strong>e ganz normale<br />

81 vgl. pr14112<br />

82 pr14102<br />

61


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Objektvariable verwen<strong>de</strong>t wer<strong>de</strong>n. Er wird als versteckter Parameter an je<strong>de</strong> nicht<br />

statische Metho<strong>de</strong> übergeben.<br />

Konstruktoren können <strong>in</strong> <strong>Java</strong> verkettet aufgerufen wer<strong>de</strong>n, d.h. sie können sich<br />

gegenseitig aufrufen. Der aufrufen<strong>de</strong> Konstruktor wird dabei als normale Metho<strong>de</strong><br />

angesehen, die über this e<strong>in</strong>en weiteren Konstruktor <strong>de</strong>r aktuellen Klasse aufrufen<br />

kann, z.B.:<br />

public Rechentafel()<br />

{<br />

this(0,1);<br />

}<br />

1.4.1.1.4 Zugriffsrechte auf Klassen, Variable und Metho<strong>de</strong>n<br />

Es gibt <strong>in</strong> <strong>Java</strong> <strong>in</strong>sgesamt 4 Zugriffsrechte:<br />

private<br />

Ohne Schlüsselwort<br />

protected<br />

public<br />

Zugriff nur <strong>in</strong>nerhalb e<strong>in</strong>er Klasse<br />

Zugriff <strong>in</strong>nerhalb e<strong>in</strong>es Pakets<br />

Zugriff <strong>in</strong>nerhalb e<strong>in</strong>es Pakets o<strong>de</strong>r von Subklassen <strong>in</strong> e<strong>in</strong>em an<strong>de</strong>ren<br />

Paket<br />

Zugriff von überall<br />

Mit Voranstellen <strong>de</strong>s Schlüsselworts „public“ können alle Klassen, Variablen /<br />

Konstanten und Metho<strong>de</strong>n für e<strong>in</strong>en beliebigen Zugriff e<strong>in</strong>gerichtet wer<strong>de</strong>n. E<strong>in</strong>e<br />

<strong>de</strong>rartige Möglichkeit, die <strong>in</strong> etwa <strong>de</strong>r Zugriffsmöglichkeit globaler Variablen <strong>in</strong><br />

konventionellen Programmiersprachen entspricht, ist <strong>in</strong>sbeson<strong>de</strong>re bei komplexen<br />

Programm-Systemen gefährlich. Es ist nicht sichergestellt, daß zu je<strong>de</strong>m Zeitpunkt<br />

die Werte <strong>de</strong>r Instanzvariablen von Objekten bestimmte Bed<strong>in</strong>gungen erfüllen.<br />

Möglich ist bspw. die Anweisung<br />

e<strong>in</strong>Rechenobjekt.zweiterOperand = 0;<br />

<strong>in</strong> <strong>de</strong>r Klasse „Rechentafeltest“. Mit Sicherheit führt diese Anweisung bei <strong>de</strong>r<br />

Ausführung e<strong>in</strong>er Division auf e<strong>in</strong>en Divisionsfehler.<br />

Abhilfe verspricht hier das Pr<strong>in</strong>zip <strong>de</strong>r Datenkapselung (data hid<strong>in</strong>g, data<br />

encapsulation). Instanzvariable wer<strong>de</strong>n als „private“ erklärt, d.h.: Zugriff auf diese<br />

Instanzvariable nur <strong>in</strong>nerhalb <strong>de</strong>r Klasse. Von außerhalb kann nur <strong>in</strong>direkt über das<br />

Aufrufen von Metho<strong>de</strong>n, die als „public“ erklärt s<strong>in</strong>d, auf die Instanzvariablen<br />

zugegriffen wer<strong>de</strong>n. Deshalb sollte auch pr<strong>in</strong>zipiell für je<strong>de</strong> Instanzvariable e<strong>in</strong>e<br />

entsprechen<strong>de</strong> „get“- und e<strong>in</strong>e „set“-Metho<strong>de</strong> („hole“ bzw. „setze“) zur Verfügung<br />

gestellt wer<strong>de</strong>n, die jeweils „public“ erklärt wer<strong>de</strong>n.<br />

Bsp.: Die Klasse „Rechentafel“ nimmt dann folgen<strong>de</strong>n Gestalt an:<br />

class Rechentafel extends Object<br />

{<br />

// Instanzvariable<br />

private <strong>in</strong>t ersterOperand = 0;<br />

private <strong>in</strong>t zweiterOperand = 0;<br />

// Konstruktoren<br />

public Rechentafel()<br />

{<br />

this(0,1);<br />

}<br />

public Rechentafel(<strong>in</strong>t ersterOperand, <strong>in</strong>t zweiterOperand)<br />

62


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

{<br />

super();<br />

this.ersterOperand = ersterOperand;<br />

this.zweiterOperand = zweiterOperand;<br />

}<br />

// Operationen<br />

public <strong>in</strong>t holersterOperand()<br />

{<br />

return ersterOperand;<br />

}<br />

public void setzersterOperand(<strong>in</strong>t ersterOperand)<br />

{<br />

this.ersterOperand = ersterOperand;<br />

}<br />

public <strong>in</strong>t holzweiterOperand()<br />

{<br />

return zweiterOperand;<br />

}<br />

public void setzweiterOperand(<strong>in</strong>t zweiterOperand)<br />

{<br />

this.zweiterOperand = zweiterOperand;<br />

}<br />

public <strong>in</strong>t addiere()<br />

{<br />

return ersterOperand + zweiterOperand;<br />

}<br />

public <strong>in</strong>t subtrahiere()<br />

{<br />

return ersterOperand - zweiterOperand;<br />

}<br />

public <strong>in</strong>t multipliziere()<br />

{<br />

return ersterOperand * zweiterOperand;<br />

}<br />

public <strong>in</strong>t dividiere()<br />

{<br />

// ganzzahlige Division<br />

if (zweiterOperand == 0)<br />

{<br />

System.out.pr<strong>in</strong>tln("Fehler: Division durch Null");<br />

System.out.pr<strong>in</strong>tln("Divisor wird auf 1 gesetzt!");<br />

zweiterOperand = 1;<br />

}<br />

return ersterOperand / zweiterOperand;<br />

}<br />

}<br />

Das Beispiel zeigt folgen<strong>de</strong> Empfehlungen für die Vergabemöglichkeit von Zugriffsrechten:<br />

Klassen<br />

Instanzvariable<br />

Instanzkonstanten<br />

Instanzmetho<strong>de</strong>n<br />

public<br />

private<br />

public<br />

public, falls e<strong>in</strong> Zugriff von außen erfor<strong>de</strong>rlich und s<strong>in</strong>nvoll ist.<br />

private, falls es sich um klasen<strong>in</strong>terne Hilfsmetho<strong>de</strong>n han<strong>de</strong>lt.<br />

Analoge Überlegungen gelten auch für Klassenvariable und -metho<strong>de</strong>n.<br />

„Freundliche“ Klassen und „freundliche“ Metho<strong>de</strong>n<br />

„Rechentafel“ ist e<strong>in</strong>e „freundliche“ Klasse. Der vore<strong>in</strong>gestellte „Defaultstatus“<br />

e<strong>in</strong>er Klasse ist immer „freundlich“ und wird dann verwen<strong>de</strong>t, wenn ke<strong>in</strong>e Angaben<br />

63


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

über die Sichtbarkeit (Spezifizierer, Modifizierer) am Beg<strong>in</strong>n <strong>de</strong>r Klassen<strong>de</strong>f<strong>in</strong>ition<br />

vorliegen.<br />

Die freundliche Grun<strong>de</strong><strong>in</strong>stellung aller Klassen be<strong>de</strong>utet: Diese Klasse kann von<br />

an<strong>de</strong>ren Klassen nur <strong>in</strong>nerhalb <strong>de</strong>sselben Pakets benutzt wer<strong>de</strong>n. Das Paket-<br />

Konzept von <strong>Java</strong> faßt mehrere Klassen zu e<strong>in</strong>em Paket über die Anweisung<br />

„package“ zusammen. Durch die Anweisung „import“ wer<strong>de</strong>n e<strong>in</strong>zelne Pakete<br />

dann <strong>in</strong> e<strong>in</strong>em Programm verfügbar gemacht. Klassen, die ohne „package“-<br />

Anweisung <strong>de</strong>f<strong>in</strong>iert wer<strong>de</strong>n, wer<strong>de</strong>n vom Compiler <strong>in</strong> e<strong>in</strong> Standardpaket gestellt. Die<br />

„.java“- und „.class“-Dateien dieses Pakets bef<strong>in</strong><strong>de</strong>n sich im aktuellen<br />

Verzeichnis o<strong>de</strong>r im darunterliegen<strong>de</strong>n Verzeichnis.<br />

Mit <strong>de</strong>m Voranstellen von „public“ vor die Klassen<strong>de</strong>klaration wird e<strong>in</strong>e Klasse als<br />

„öffentlich“ <strong>de</strong>klariert. Dies be<strong>de</strong>utet: Alle Objekte haben Zugriff auf „public“-<br />

Klassen (nicht nur die <strong>de</strong>s eigenen Pakets).<br />

Der vore<strong>in</strong>gestellte Defaultstaus e<strong>in</strong>er Metho<strong>de</strong> ist immer freundlich und wird immer<br />

dann verwen<strong>de</strong>t, wenn ke<strong>in</strong>e explizite Angabe zur Sichtbarkeit am Anfang <strong>de</strong>r<br />

Metho<strong>de</strong>n<strong>de</strong>klaration vorliegt. Die freundliche Grun<strong>de</strong><strong>in</strong>stellung aller Metho<strong>de</strong>n<br />

be<strong>de</strong>utet: Die Metho<strong>de</strong>n können sowohl <strong>in</strong>nerhalb <strong>de</strong>r Klasse als auch <strong>in</strong>nerhalb <strong>de</strong>s<br />

zugehörigen Pakets benutzt wer<strong>de</strong>n.<br />

1.4.1.1.5 Klassenvariable und Klassenmetho<strong>de</strong>n<br />

Die Klasse „Rechentafel" 83 wur<strong>de</strong> zusätzlich erweitert um<br />

// Klassenvariable<br />

static <strong>in</strong>t anzRechenobjekte = 0;<br />

Das reservierte Wort static macht Variable und Metho<strong>de</strong>n (wie bspw. ma<strong>in</strong>()) zu<br />

Klassenvariablen bzw. Klassenmetho<strong>de</strong>n.<br />

Klassenvariable wer<strong>de</strong>n <strong>in</strong> <strong>de</strong>r Klasse <strong>de</strong>f<strong>in</strong>iert und gespeichert. Deshalb wirken sich<br />

ihre Werte auf die Klasse und all ihre Instanzen aus. Je<strong>de</strong> Instanz hat Zugang zu <strong>de</strong>r<br />

Klassenvariablen, jedoch gibt es für alle Instanzen dieser Variablen nur e<strong>in</strong>en Wert.<br />

Durch Än<strong>de</strong>rung <strong>de</strong>s Werts än<strong>de</strong>rn sich die Werte aller Instanzen <strong>de</strong>r betreffen<strong>de</strong>n<br />

Klasse.<br />

Die folgen<strong>de</strong> Erweiterung <strong>de</strong>s Konstruktors <strong>de</strong>r Klasse „Rechentafel“ berücksichtigt<br />

das spezielle Verhalten von Klassenvariablen:<br />

public Rechentafel(<strong>in</strong>t ersterOperand, <strong>in</strong>t zweiterOperand)<br />

{<br />

super();<br />

this.ersterOperand = ersterOperand;<br />

this.zweiterOperand = zweiterOperand;<br />

nummer = ++anzRechenobjekte;<br />

}<br />

„nummer“ ist e<strong>in</strong>e Instanzvariable, „anzRechenObjekte“ ist die Klassenvariable.<br />

Das reservierte Wort f<strong>in</strong>al macht Variablen zu Größen, <strong>de</strong>nen nur e<strong>in</strong>mal bei <strong>de</strong>r<br />

Deklaration e<strong>in</strong> Wert zugewiesen wer<strong>de</strong>n kann, d.h. sie wer<strong>de</strong>n dadurch zu<br />

Konstanten, z.B.: public static f<strong>in</strong>al double PI = 3.1415926535897932846;<br />

83 pr14103<br />

64


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Klassenmetho<strong>de</strong>n wirken sich wie Klassenvariable auf die ganze Klasse, nicht auf<br />

e<strong>in</strong>zelne Instanzen aus. Klassenmetho<strong>de</strong>n s<strong>in</strong>d nützlich zum Zusammenfassen<br />

allgeme<strong>in</strong>er Metho<strong>de</strong>n an e<strong>in</strong>er Stelle <strong>de</strong>r Klasse. So umfaßt die Math-Klasse<br />

zahlreiche mathematische Funktionen <strong>in</strong> <strong>de</strong>r Form von Klassenmetho<strong>de</strong>n. Es gibt<br />

ke<strong>in</strong>e Instanzen <strong>de</strong>r Klasse Math.<br />

Auch rekurvive Programme benutzen Klassenmetho<strong>de</strong>n, z.B.:<br />

// Berechnung <strong>de</strong>r Fakultaet<br />

public static long fakultaet(<strong>in</strong>t n)<br />

{<br />

if (n < 0)<br />

{ return -1; }<br />

else if (n == 0)<br />

{ return 1; }<br />

else<br />

{ return n * fakultaet(n-1); }<br />

}<br />

Der Aufruf e<strong>in</strong>er Klassenmetho<strong>de</strong> kann im Rahmen <strong>de</strong>r Punktnotation aber auch<br />

direkt erfolgen, z.B.:<br />

long resultat = fakultaet(i);<br />

bzw.<br />

long resultat = Rechentafel.fakultaet(i);<br />

unter <strong>de</strong>r Voraussetzung: Die Klassenmetho<strong>de</strong> „fakultaet“ bef<strong>in</strong><strong>de</strong>t sich <strong>in</strong> <strong>de</strong>r Klasse<br />

„Rechentafel“.<br />

1.4.1.1.6 Lokale Variable und Konstanten<br />

Lokale Variable wer<strong>de</strong>n <strong>in</strong>nerhalb von Metho<strong>de</strong>n<strong>de</strong>f<strong>in</strong>itionen <strong>de</strong>klariert und können<br />

nur dort verwen<strong>de</strong>t wer<strong>de</strong>n. Es gibt auch auf Blöcke beschränkte lokale Variablen.<br />

Die Deklaration e<strong>in</strong>er lokalen Variablen gilt <strong>in</strong> <strong>Java</strong> als ausführbare Anweisung. Sie<br />

darf überall dort erfolgen, wo e<strong>in</strong>e Anweisung verwen<strong>de</strong>t wer<strong>de</strong>n darf. Die<br />

Sichtbarkeit e<strong>in</strong>er lokalen Variablen erstreckt sich von <strong>de</strong>r Deklaration bis zum En<strong>de</strong><br />

<strong>de</strong>s umschließen<strong>de</strong>n Blocks.<br />

Lokale Variablen existieren nur solange im Speicher, wie die Metho<strong>de</strong> o<strong>de</strong>r <strong>de</strong>r Block<br />

existiert. Lokale Variable müssen unbed<strong>in</strong>gt e<strong>in</strong> Wert zugewiesen bekommen, bevor<br />

sie benutzt wer<strong>de</strong>n können. Instanz- und Klassenvariable haben e<strong>in</strong>en<br />

typspezifischen Defaultwert. Auf lokale Variable kann direkt und nicht über die<br />

Punktnotation zugegriffen wer<strong>de</strong>n. Lokale Variable können auch nicht als<br />

Konstanten 84 gesetzt wer<strong>de</strong>n.<br />

Beim Bezug e<strong>in</strong>er Variablen <strong>in</strong> e<strong>in</strong>er Metho<strong>de</strong>n<strong>de</strong>f<strong>in</strong>ition sucht <strong>Java</strong> zuerst e<strong>in</strong>e<br />

Def<strong>in</strong>ition dieser Variablen im aktuellen Bereich, dann durchsucht es die äußeren<br />

Bereiche bis zur Def<strong>in</strong>ition <strong>de</strong>r aktuellen Metho<strong>de</strong>. Ist die gesuchte Größe ke<strong>in</strong>e<br />

lokale Variable, sucht <strong>Java</strong> nach e<strong>in</strong>er Def<strong>in</strong>ition dieser Variablen als Instanzvariable<br />

<strong>in</strong> <strong>de</strong>r aktuellen Klasse und zum Schluß <strong>in</strong> <strong>de</strong>r Superklasse.<br />

Konstanten s<strong>in</strong>d Speicherbereiche mit Werten, die sich nie än<strong>de</strong>rn. In <strong>Java</strong> können<br />

solche Konstanten ausschließlich für Instanz- und Klassenvariable erstellt wer<strong>de</strong>n.<br />

Konstante bestehen aus e<strong>in</strong>er Variablen<strong>de</strong>klaration, <strong>de</strong>r das Schlüsselwort f<strong>in</strong>al<br />

vorangestellt ist, und e<strong>in</strong> Anfangswert zugewiesen ist, z.B.: f<strong>in</strong>al <strong>in</strong>t LINKS =<br />

0;<br />

84 Das be<strong>de</strong>utet: das Schlüsselwort f<strong>in</strong>al ist bei lokalen Variablen nicht erlaubt<br />

65


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

1.4.1.1.7 Überla<strong>de</strong>n von Metho<strong>de</strong>n<br />

In <strong>Java</strong> ist es erlaubt, Metho<strong>de</strong>n zu überla<strong>de</strong>n, d.h. <strong>in</strong>nerhalb e<strong>in</strong>er Klasse zwei<br />

unterschiedliche Metho<strong>de</strong>n mit <strong>de</strong>nselben Namen zu <strong>de</strong>f<strong>in</strong>ieren. Der Compiler<br />

unterschei<strong>de</strong>t die verschie<strong>de</strong>nen Varianten anhand Anzahl und Typisierung <strong>de</strong>r<br />

Parameter. Es ist nicht erlaubt, zwei Metho<strong>de</strong>n mit exakt <strong>de</strong>mselben Namen und<br />

i<strong>de</strong>ntischer Parameterliste zu <strong>de</strong>f<strong>in</strong>ieren. Es wer<strong>de</strong>n auch zwei Metho<strong>de</strong>n, die sich<br />

nur durch <strong>de</strong>n Typ ihres Rückgabewerts unterschei<strong>de</strong>n als gleich angesehen.<br />

Der Compiler kann die Namen <strong>in</strong> allen drei Fällen unterschei<strong>de</strong>n, <strong>de</strong>nn er arbeitet mit<br />

<strong>de</strong>r Signatur <strong>de</strong>r Metho<strong>de</strong>. Darunter versteht man ihren <strong>in</strong>ternen Namen. Dieser setzt<br />

sich aus <strong>de</strong>m nach außen sichtbaren Namen und zusätzlich kodierter Information<br />

über Reihenfolge und die Typen <strong>de</strong>r formalen Parameter zusammen.<br />

Überla<strong>de</strong>n kann be<strong>de</strong>uten, daß bei Namensgleichheit von Metho<strong>de</strong>n <strong>in</strong> „Superklasse“<br />

und abgeleiteter Klasse die Metho<strong>de</strong> <strong>de</strong>r abgeleitetem Klasse, die <strong>de</strong>r Superklasse<br />

über<strong>de</strong>ckt. Es wird aber vorausgesetzt, daß sich die Parameter signifikant<br />

unterschei<strong>de</strong>n, sonst han<strong>de</strong>lt es sich bei <strong>de</strong>r Konstellation Superklasse – Subklasse<br />

um <strong>de</strong>n Vorgang <strong>de</strong>s Überschreibens. Soll die Orig<strong>in</strong>almetho<strong>de</strong> aufgerufen wer<strong>de</strong>n,<br />

dann wird das Schlüsselwort „super“ benutzt. Damit wird <strong>de</strong>r Metho<strong>de</strong>naufruf <strong>in</strong> <strong>de</strong>r<br />

Hierarchie nach oben weitergegeben.<br />

Überschreiben e<strong>in</strong>er Oberklassen-Metho<strong>de</strong>: Zum Überschreiben e<strong>in</strong>er Metho<strong>de</strong> wird<br />

e<strong>in</strong>e Metho<strong>de</strong> erstellt, die <strong>de</strong>n gleichen Namen, Ausgabetyp und die gleiche<br />

Parameterliste wie e<strong>in</strong>e Metho<strong>de</strong> <strong>de</strong>r Superklasse besitzt. Da <strong>Java</strong> die erste<br />

Metho<strong>de</strong>n<strong>de</strong>f<strong>in</strong>ition ausführt, die es f<strong>in</strong><strong>de</strong>t und die <strong>in</strong> Namen, Ausgabetyp und<br />

Parameterliste übere<strong>in</strong>stimmt, wird die ursprüngliche Metho<strong>de</strong>n<strong>de</strong>f<strong>in</strong>ition dadurch<br />

verborgen. Klassen-Metho<strong>de</strong>n wer<strong>de</strong>n nicht überschrieben, son<strong>de</strong>rn über<strong>de</strong>ckt, da<br />

sie (z. B. Klasse.metho<strong>de</strong>(), Superklasse.metho<strong>de</strong>()) <strong>in</strong> je<strong>de</strong>m Fall verschie<strong>de</strong>ne<br />

Namen haben.<br />

Konstruktor-Metho<strong>de</strong>n können „technisch“ nicht überschrieben wer<strong>de</strong>n. Da sie <strong>de</strong>n<br />

gleichen Namen wie die aktuelle Klasse haben, wer<strong>de</strong>n Konstruktoren nicht vererbt,<br />

son<strong>de</strong>rn immer neu erstellt. Wird e<strong>in</strong> Konstruktor e<strong>in</strong>er bestimmten Klasse<br />

aufgerufen, wird gleichzeitig auch <strong>de</strong>r Konstruktor aller Superklassen aktiviert, so daß<br />

alle Teile e<strong>in</strong>er Klasse <strong>in</strong>itialisiert wer<strong>de</strong>n. E<strong>in</strong> spezielles Überschreiben kann über<br />

super(arg1, arg2, ...) ermöglicht wer<strong>de</strong>n.<br />

Die Metho<strong>de</strong> toStr<strong>in</strong>g() ist e<strong>in</strong>e <strong>in</strong> <strong>Java</strong> häufig verwen<strong>de</strong>te Metho<strong>de</strong>.<br />

„toStr<strong>in</strong>g()“ wird als weitere Instanzmetho<strong>de</strong> <strong>de</strong>r Klasse Rechentafel <strong>de</strong>f<strong>in</strong>iert und<br />

überschreibt die Metho<strong>de</strong> gleichen Namens, die <strong>in</strong> <strong>de</strong>r Oberklasse Object <strong>de</strong>f<strong>in</strong>iert<br />

ist, z.B. 85 :<br />

public Str<strong>in</strong>g toStr<strong>in</strong>g()<br />

{<br />

return " Rechenobjekt# " + nummer + " Erster Operand: "<br />

+ ersterOperand + " Zweiter Operand: " + zweiterOperand;<br />

}<br />

}<br />

85 pr14103<br />

66


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

1.4.1.2 Superklassen und Subklassen, Vererbung und Klassenhierarchie<br />

Def<strong>in</strong>itionen<br />

Klassen können sich auf an<strong>de</strong>re Klassen beziehen. Ausgangspunkt ist e<strong>in</strong>e<br />

Superklasse, von <strong>de</strong>r Subklassen (Unter-) abgeleitet se<strong>in</strong> können. Je<strong>de</strong> Subklasse<br />

erbt <strong>de</strong>n Zustand (über die Variablen-Deklarationen) und das Verhalten <strong>de</strong>r<br />

Superklasse. Darüber h<strong>in</strong>aus können Subklassen eigene Variable und Metho<strong>de</strong>n<br />

h<strong>in</strong>zufügen. Superklassen, Subklassen bil<strong>de</strong>n e<strong>in</strong>e mehrstufige Hierarchie. In <strong>Java</strong><br />

s<strong>in</strong>d alle Klassen Ableitungen e<strong>in</strong>er e<strong>in</strong>zigen obersten Klasse – <strong>de</strong>r Klasse Object.<br />

Sie stellt die allgeme<strong>in</strong>ste Klasse <strong>in</strong> <strong>de</strong>r Hierarchie dar und legt die Verhaltensweisen<br />

und Attribute, die an alle Klassen <strong>in</strong> <strong>de</strong>r <strong>Java</strong>-Klassenbibliothek vererbt wer<strong>de</strong>n, fest.<br />

Vererbung be<strong>de</strong>utet, daß alle Klassen <strong>in</strong> e<strong>in</strong>e strikte Hierarchie e<strong>in</strong>geordnet s<strong>in</strong>d und<br />

etwas von übergeordneten Klassen erben. Was kann von übergeordneten Klassen<br />

vererbt wer<strong>de</strong>n? Beim Erstellen e<strong>in</strong>er neuen Instanz erhält man e<strong>in</strong> Exemplar je<strong>de</strong>r<br />

Variablen, die <strong>in</strong> <strong>de</strong>r aktuellen Klasse <strong>de</strong>f<strong>in</strong>iert ist. Zusätzlich wird e<strong>in</strong> Exemplar für<br />

je<strong>de</strong> Variable bereitgestellt, die sich <strong>in</strong> <strong>de</strong>n Superklassen <strong>de</strong>r aktuellen Klasse<br />

bef<strong>in</strong><strong>de</strong>t. Bei <strong>de</strong>r Metho<strong>de</strong>nauswahl haben neue Objekte Zugang zu allen<br />

Metho<strong>de</strong>nnamen ihrer Klasse und <strong>de</strong>ren Superklasse. Metho<strong>de</strong>nnamen wer<strong>de</strong>n<br />

dynamisch beim Aufruf e<strong>in</strong>er Metho<strong>de</strong> gewählt. Das be<strong>de</strong>utet: <strong>Java</strong> prüft zuerst die<br />

Klasse <strong>de</strong>s Objekts auf Def<strong>in</strong>ition <strong>de</strong>r betreffen<strong>de</strong>n Metho<strong>de</strong>. Ist sie nicht <strong>in</strong> <strong>de</strong>r<br />

Klasse <strong>de</strong>s Objekts <strong>de</strong>f<strong>in</strong>iert, sucht <strong>Java</strong> <strong>in</strong> <strong>de</strong>r Superklasse dieser Klasse usw.<br />

aufwärts <strong>in</strong> <strong>de</strong>r Hierarchie bis die Def<strong>in</strong>ition <strong>de</strong>r Metho<strong>de</strong> gefun<strong>de</strong>n wird.<br />

Ist <strong>in</strong> e<strong>in</strong>er Subklasse e<strong>in</strong>e Metho<strong>de</strong> mit gleichem Namen, gleicher Anzahl und<br />

gleichem Typ <strong>de</strong>r Argumente wie <strong>in</strong> <strong>de</strong>r Superklasse <strong>de</strong>f<strong>in</strong>iert, dann wird die<br />

Metho<strong>de</strong>n<strong>de</strong>f<strong>in</strong>ition, die zuerst (von unten nach oben <strong>in</strong> <strong>de</strong>r Hierarchie) gefun<strong>de</strong>n<br />

wird, ausgeführt. Metho<strong>de</strong>n <strong>de</strong>r abgeleiteten Klasse über<strong>de</strong>cken die Metho<strong>de</strong>n <strong>de</strong>r<br />

Superklasse (Overrid<strong>in</strong>g beim Überschreiben / Über<strong>de</strong>f<strong>in</strong>ieren).<br />

Zum Ableiten e<strong>in</strong>er Klasse aus e<strong>in</strong>er bestehen<strong>de</strong>n Klasse ist im Kopf <strong>de</strong>r Klasse mit<br />

Hilfe <strong>de</strong>s Schlüsselworts „extends“ e<strong>in</strong> Verweis auf die Basisklasse anzugeben.<br />

Wird <strong>de</strong>r Name e<strong>in</strong>er Klasse nach <strong>de</strong>m Schlüsselwort extends angegeben, dann<br />

wird damit diese Klasse als Superklasse spezifiziert, auf <strong>de</strong>r e<strong>in</strong>e neue Klasse<br />

aufbaut. Durch Erweiterung <strong>de</strong>r Superklasse entsteht e<strong>in</strong>e neue Kopie dieser Klasse,<br />

an <strong>de</strong>r Verän<strong>de</strong>rungen möglich s<strong>in</strong>d. Die neue Klasse erbt alle Daten und Metho<strong>de</strong>n,<br />

die <strong>in</strong> <strong>de</strong>r Orig<strong>in</strong>alklasse <strong>de</strong>f<strong>in</strong>iert wur<strong>de</strong>n. Durch H<strong>in</strong>zufügen neuer Elemente o<strong>de</strong>r<br />

Überla<strong>de</strong>n <strong>de</strong>r vorhan<strong>de</strong>nen kann die Funktionalität <strong>de</strong>r abgeleiteten Klasse erweitert<br />

wer<strong>de</strong>n.<br />

Die Vererbung e<strong>in</strong>er Klasse kann beliebig tief geschachtelt wer<strong>de</strong>n. E<strong>in</strong>e abgeleitete<br />

Klasse erbt dabei jeweils die Eigenschaften <strong>de</strong>r unmittelbaren „Superklasse“, die<br />

ihrerseits die Eigenschaften ihrer unmittelbaren „Superklasse“ erbt.<br />

Superklassen können abstrakte Klassen mit generischem Verhalten se<strong>in</strong>. Von e<strong>in</strong>er<br />

abstrakten Klasse wird nie e<strong>in</strong>e direkte Instanz benötigt. Sie dient nur zu<br />

Verweiszwecken. Abstrakte Klassen dürfen ke<strong>in</strong>e Implementierung e<strong>in</strong>er Metho<strong>de</strong><br />

enthalten und s<strong>in</strong>d damit auch nicht <strong>in</strong>stanziierbar. <strong>Java</strong> charakterisiert abstrakte<br />

Klassen mit <strong>de</strong>m Schlüsselwort abstract. Abstrakte Klassen wer<strong>de</strong>n zum Aufbau<br />

e<strong>in</strong>er Klassenhierarchie verwen<strong>de</strong>t, z.B. für die Zusammenfassung von zwei o<strong>de</strong>r<br />

mehr Klassen.<br />

Auch e<strong>in</strong>e abstrakte Metho<strong>de</strong> ist zunächst e<strong>in</strong>mal durch das reservierte Wort<br />

"abstract" erklärt. E<strong>in</strong>e abstrakte Metho<strong>de</strong> besteht nur aus <strong>de</strong>m Metho<strong>de</strong>nkopf,<br />

67


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

anstelle <strong>de</strong>s Metho<strong>de</strong>nrumpfs (<strong>de</strong>r Metho<strong>de</strong>n<strong>de</strong>f<strong>in</strong>ition) steht nur das "Semikolon",<br />

z.B. public abstract Str<strong>in</strong>g toStr<strong>in</strong>g();.<br />

Enthält e<strong>in</strong>e Klasse m<strong>in</strong><strong>de</strong>stens e<strong>in</strong>e abstrakte Metho<strong>de</strong>, so wird automatisch die<br />

gesamte Klasse zu e<strong>in</strong>er abstrakten Klasse. Abstrakte Klassen enthalten ke<strong>in</strong>e<br />

Konstruktoren. Sie können zwar Konstruktoren aufnehmen, allerd<strong>in</strong>gs führt je<strong>de</strong>r<br />

explizite Versuch zur Erzeugung e<strong>in</strong>es Objekts e<strong>in</strong>er abstrakten Klasse zu e<strong>in</strong>er<br />

Fehlermeldung. Abstrakte Metho<strong>de</strong>n stellen e<strong>in</strong>e Schnittstellenbeschreibung dar, die<br />

<strong>de</strong>r Programmierer e<strong>in</strong>er Subklasse zu <strong>de</strong>f<strong>in</strong>ieren hat. E<strong>in</strong> konkrete Subklasse e<strong>in</strong>er<br />

abstrakten Klasse muß alle abstrakten Metho<strong>de</strong>n <strong>de</strong>r Superklasse(n)<br />

implementieren.<br />

Beispiel: E<strong>in</strong>e Klassenhierarchie für "geometrische" Objekte 86<br />

Das folgen<strong>de</strong> Klassendiagramm zeigt die hierarchische Struktur von Klassen, die<br />

"geometrische Objekte" beschreiben. All diesen geometrischen Objekten ist<br />

geme<strong>in</strong>sam: Sie wer<strong>de</strong>n durch e<strong>in</strong>en Bezugspunkt (x,y) fixiert. DieGeme<strong>in</strong>samkeiten<br />

s<strong>in</strong>d <strong>in</strong> <strong>de</strong>r abstrakten Klasse "Geomobjekt" zusammengefaßt.<br />

86 vgl. pr14131<br />

68


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

GeomObjekt<br />

private double x;<br />

private double y;<br />

public double holeX();<br />

public void setzeX(double x);<br />

public double holeY();<br />

public void setzeY(double y);<br />

Kreis<br />

Rechteck<br />

private double r;<br />

public double holeR();<br />

public void setzeR(double r);<br />

public double umfang();<br />

public double flaeche();<br />

public Str<strong>in</strong>g toStr<strong>in</strong>g();<br />

private double breite;<br />

private double hoehe;<br />

public double holeBreite();<br />

public void setzeBreite(double breite)<br />

public double holeHoehe(double hoehe);<br />

public void setzeHoehe(double hoehe);<br />

public double umfang();<br />

public double flaeche();<br />

public Str<strong>in</strong>g toStr<strong>in</strong>g();<br />

Ausgangspunkt ist die abstrakte Klasse "GeomObjekt". Dort ist <strong>de</strong>r allen Objekten<br />

zugeordnete Bezugspunkt fixiert. Subklassen (z.B. Kreis und Rechteck) erben diesen<br />

Bezugspunkt und die zum Zugriff auf ihn bereitgestellte Metho<strong>de</strong>n. Außer<strong>de</strong>m führen<br />

die Subklassen die nötigen Spezialisierungen auf das jeweilige geometrische Objekt<br />

durch.<br />

import java.lang.*;<br />

public abstract class GeomObjekt extends Object<br />

{<br />

// Instanzvariable<br />

private double x, y; // Bezugspunkt geom. Objekte<br />

// Instanzmetho<strong>de</strong>n<br />

public double holeX()<br />

{<br />

return x;<br />

}<br />

public void setzeX(double x)<br />

{<br />

this.x = x;<br />

}<br />

public double holeY()<br />

{<br />

return y;<br />

}<br />

public void setzeY(double y)<br />

{<br />

this.y = y;<br />

}<br />

// Abstrakte Metho<strong>de</strong>n<br />

public abstract Str<strong>in</strong>g toStr<strong>in</strong>g();<br />

69


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

public abstract double umfang();<br />

public abstract double flaeche();<br />

}<br />

import java.lang.*;<br />

public class Kreis extends GeomObjekt<br />

{<br />

// Instanzvariable<br />

private double r; // Radius<br />

// Konstruktoren<br />

public Kreis()<br />

{<br />

this(0.0,0.0,1.0);<br />

}<br />

public Kreis(double r)<br />

{<br />

this(0.0,0.0,r);<br />

}<br />

public Kreis(double x, double y, double r)<br />

{<br />

super();<br />

super.setzeX(x);<br />

super.setzeY(y);<br />

this.setzeR(r);<br />

}<br />

// Instanzmetho<strong>de</strong>n<br />

public double holeR()<br />

{<br />

return r;<br />

}<br />

public void setzeR(double r)<br />

{<br />

this.r = (r >= 0.0 ? r : -r);<br />

}<br />

public double umfang()<br />

{<br />

double u = 2 * r * Math.PI;<br />

return u;<br />

}<br />

public double flaeche()<br />

{<br />

return r * r * Math.PI;<br />

}<br />

public Str<strong>in</strong>g toStr<strong>in</strong>g()<br />

{<br />

return "Kreis: (" + holeX() + ", " + holeY() +<br />

") " + "r = " + r;<br />

}<br />

}<br />

import java.lang.*;<br />

public class Rechteck extends GeomObjekt<br />

{<br />

// Instanzvariable<br />

private double breite, hoehe;<br />

// Konstruktoren<br />

public Rechteck()<br />

{<br />

this(0.0,0.0,1.0,1.0);<br />

}<br />

public Rechteck(double breite, double hoehe)<br />

{<br />

this(0.0,0.0,breite,hoehe);<br />

}<br />

70


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

public Rechteck(double x, double y, double breite, double hoehe)<br />

{<br />

super();<br />

super.setzeX(x);<br />

super.setzeY(y);<br />

this.setzeBreite(breite);<br />

this.setzeHoehe(hoehe);<br />

}<br />

// Instanzmetho<strong>de</strong>n<br />

public double holeBreite()<br />

{<br />

return breite;<br />

}<br />

public void setzeBreite(double breite)<br />

{<br />

this.breite = ( breite >= 0 ? breite : -breite);<br />

}<br />

public double holeHoehe()<br />

{<br />

return hoehe;<br />

}<br />

public void setzeHoehe(double hoehe)<br />

{<br />

this.hoehe = ( hoehe >= 0 ? hoehe : -hoehe);<br />

}<br />

public double umfang()<br />

{<br />

return 2 * breite + 2 * hoehe;<br />

}<br />

public double flaeche()<br />

{<br />

return breite * hoehe;<br />

}<br />

public Str<strong>in</strong>g toStr<strong>in</strong>g()<br />

{<br />

return "Rechteck: (" + holeX() + ", " + holeY()<br />

+ ") " + "breite = " + breite +<br />

" hoehe = " + hoehe;<br />

}<br />

}<br />

Die Klasse "Test" überprüft die vorliegen<strong>de</strong> Klassenhierarchie auf<br />

Funktionsfähigkeit:<br />

import java.lang.*;<br />

public class Test extends Object<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

// Erzeuge e<strong>in</strong>en Kreis<br />

Kreis k1 = new Kreis();<br />

System.out.pr<strong>in</strong>tln(k1.toStr<strong>in</strong>g());<br />

double umfang;<br />

umfang = k1.umfang();<br />

System.out.pr<strong>in</strong>tln("Umfang = " + umfang);<br />

System.out.pr<strong>in</strong>tln("Flaeche = " + k1.flaeche());<br />

// Erzeuge e<strong>in</strong>en an<strong>de</strong>ren Kreises<br />

Kreis k2 = new Kreis(1.0,1.0,2.0);<br />

System.out.pr<strong>in</strong>tln(k2.toStr<strong>in</strong>g());<br />

umfang = k2.umfang();<br />

System.out.pr<strong>in</strong>tln("Umfang = " + umfang);<br />

System.out.pr<strong>in</strong>tln("Flaeche = " + k2.flaeche());<br />

// Erzeuge e<strong>in</strong>en dritten Kreis<br />

Kreis k3 = new Kreis(-2.0);<br />

71


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

System.out.pr<strong>in</strong>tln(k3.toStr<strong>in</strong>g());<br />

umfang = k3.umfang();<br />

System.out.pr<strong>in</strong>tln("Umfang = " + umfang);<br />

System.out.pr<strong>in</strong>tln("Flaeche = " + k3.flaeche());<br />

// Erzeugen e<strong>in</strong>es Rechteck<br />

Rechteck r1 = new Rechteck(2.0,2.0);<br />

System.out.pr<strong>in</strong>tln(r1.toStr<strong>in</strong>g());<br />

umfang = r1.umfang();<br />

System.out.pr<strong>in</strong>tln("Umfang = " + umfang);<br />

System.out.pr<strong>in</strong>tln("Flaeche = " + r1.flaeche());<br />

// Erzeugen e<strong>in</strong>es weiteren Rechtecks<br />

Rechteck r2 = new Rechteck(2.0,2.0,2.0,3.0);<br />

System.out.pr<strong>in</strong>tln(r2.toStr<strong>in</strong>g());<br />

umfang = r2.umfang();<br />

System.out.pr<strong>in</strong>tln("Umfang = " + umfang);<br />

System.out.pr<strong>in</strong>tln("Flaeche = " + r2.flaeche());<br />

}<br />

}<br />

1.4.1.3 Referenzen, Referenztypen und Aufzählungstyp<br />

Referenzen<br />

<strong>Java</strong> erlaubt die Def<strong>in</strong>ition von Klassen, aus <strong>de</strong>nen Objekte erzeugt wer<strong>de</strong>n können.<br />

Objekte können als Referenztypen behan<strong>de</strong>lt wer<strong>de</strong>n, die von Variablen angelegt<br />

und verwen<strong>de</strong>t wer<strong>de</strong>n.<br />

Beim Zuweisen von Variablen für Objekte bzw. beim Weiterreichen von Objekten als<br />

Argumente an Metho<strong>de</strong>n wer<strong>de</strong>n Referenzen auf diese Objekte festgelegt.<br />

Bsp.: Referenzen auf Objekte <strong>in</strong> <strong>de</strong>r Klasse Rechentafel bzw. Rechentafeltest<br />

System.out.pr<strong>in</strong>tln("Referenzen auf Objekte");<br />

// Referenzen auf Objekte<br />

Rechentafel rechenObj1 = new Rechentafel();<br />

rechenObj1.ersterOperand = 200;<br />

rechenObj1.zweiterOperand = 300;<br />

Rechentafel rechenObj3 = new Rechentafel(300,200);<br />

System.out.pr<strong>in</strong>tln("Operan<strong>de</strong>n von Instanz 1: " +<br />

rechenObj1.ersterOperand + ", " + rechenObj1.zweiterOperand);<br />

System.out.pr<strong>in</strong>tln("Operan<strong>de</strong>n von Instanz 2 vor Zuweisung: " +<br />

rechenObj3.ersterOperand + ", " + rechenObj3.zweiterOperand);<br />

rechenObj3 = rechenObj1;<br />

System.out.pr<strong>in</strong>tln("Operan<strong>de</strong>n von Instanz 2 nach Zuweisung: " +<br />

rechenObj3.ersterOperand + ", " + rechenObj3.zweiterOperand);<br />

// Test auf Gleichheit<br />

if (rechenObj1 == rechenObj3)<br />

{<br />

System.out.pr<strong>in</strong>tln("Die bei<strong>de</strong>n Objekte s<strong>in</strong>d tatsaechlich gleich");<br />

}<br />

Der Test mit „==“ 87 bestätigt: Es han<strong>de</strong>lt sich jetzt (nach <strong>de</strong>r Zuweisung) um das<br />

gleiche Objekt, d.h.: die gleiche Referenz.<br />

Die „null“-Referenz. In <strong>Java</strong> gibt es das spezielle Literal null. Es zeigt an, daß<br />

e<strong>in</strong>e Referenz auf ke<strong>in</strong> Objekt verweist. Der Wert ist nur für Referenzen vorgesehen<br />

und kann nicht <strong>in</strong> e<strong>in</strong>en primitiven Typ umgewan<strong>de</strong>lt wer<strong>de</strong>n. Die „null“-Referenz ist<br />

typenlos, d.h.: Sie kann je<strong>de</strong>m Objekt zugewiesen wer<strong>de</strong>n, z.B.:<br />

87 Der Operator „==“ überprüft die Gleichheit <strong>de</strong>r Referenzen.<br />

72


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Po<strong>in</strong>t p = null;<br />

Str<strong>in</strong>g s = null;<br />

Da sich h<strong>in</strong>ter „null“ ke<strong>in</strong> Objekt verbirgt, kann darüber auch ke<strong>in</strong> Objekt aufgerufen<br />

wer<strong>de</strong>n.<br />

<strong>Java</strong> verfügt über e<strong>in</strong> automatisches Speichermanagement. Deshalb braucht <strong>de</strong>r<br />

<strong>Java</strong>-Programmierer sich nicht um die Rückgabe von Speicher zu kümmern, <strong>de</strong>r von<br />

Referenzvariablen belegt ist. E<strong>in</strong> mit nie<strong>de</strong>rer Priorität im H<strong>in</strong>tergrund arbeiten<strong>de</strong>r<br />

„Garbage Collector“ sucht ständig nach Objekten, die nicht mehr referenziert wer<strong>de</strong>n,<br />

um <strong>de</strong>n belegten Speicher freizugeben.<br />

F<strong>in</strong>alizer-Metho<strong>de</strong>n. Sie wer<strong>de</strong>n aufgerufen, kurz bevor das Objekt im Papierkob<br />

lan<strong>de</strong>t und se<strong>in</strong> Speicher freigegeben wird. Zum Erstellen e<strong>in</strong>er F<strong>in</strong>alizer-Metho<strong>de</strong><br />

dient <strong>de</strong>r folgen<strong>de</strong> E<strong>in</strong>trag <strong>in</strong> <strong>de</strong>r Klassen<strong>de</strong>f<strong>in</strong>ition void f<strong>in</strong>alize(){ ... }. Im<br />

Körper dieser Metho<strong>de</strong> können alle möglichen Re<strong>in</strong>igungsprozeduren stehen, die das<br />

Objekt ausführen soll. Der Aufruf <strong>de</strong>r Metho<strong>de</strong> f<strong>in</strong>alize() bewirkt nicht unmittelbar<br />

die Ablage im Papierkorb. Nur durch das Entfernen aller Referenzen auf das Objekt<br />

wird das Objekt zum Löschen markiert.<br />

Referenztypen<br />

<strong>Java</strong> kennt zwei Arten von Typen: e<strong>in</strong>fache Typen und Referenztypen. E<strong>in</strong>fache<br />

Typen s<strong>in</strong>d: boolean, byte, short, <strong>in</strong>t, long, char, float und double.<br />

Klassen und Arrays s<strong>in</strong>d Referenztypen. E<strong>in</strong> Referenzwert enthält e<strong>in</strong>e Referenz auf<br />

die Daten e<strong>in</strong>es Objekts bzw. Arrays. Referenztypen müssen explizit mit Hilfe <strong>de</strong>s<br />

Operators new erzeugt wer<strong>de</strong>n.<br />

Aufzählungstyp<br />

Mit <strong>Java</strong> 1.5 wur<strong>de</strong> e<strong>in</strong> Aufzählungstyp <strong>in</strong> <strong>Java</strong> <strong>in</strong>tegriert. Syntaktisch ersche<strong>in</strong>t dieser<br />

Typ wie e<strong>in</strong>e Klasse, die statt <strong>de</strong>s Schlüsselworts class das Schlüsselwort enum<br />

hat.<br />

Konzeptionell s<strong>in</strong>d Aufzählungstypen Variablen und Attributen gleichgestellt.<br />

public, protected, private 88 static 89 f<strong>in</strong>al 90 enum i<strong>de</strong>ntifier { value }<br />

Im e<strong>in</strong>fachsten Anwendungsfall besteht die Def<strong>in</strong>ition e<strong>in</strong>es Aufzählungstyps aus <strong>de</strong>r<br />

durch Kommata vone<strong>in</strong>an<strong>de</strong>r getrennten vollständigen Aufzählung aller Werte.<br />

Bsp. 91 :<br />

public class PR14132<br />

{<br />

enum jahreszeiten { w<strong>in</strong>ter, fruehl<strong>in</strong>g, sommer, herbst };<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

jahreszeiten en<strong>de</strong> = jahreszeiten.w<strong>in</strong>ter;<br />

if (en<strong>de</strong> == jahreszeiten.w<strong>in</strong>ter)<br />

88 e<strong>in</strong>er aus public, protecetd, private<br />

89 optional<br />

90 optional<br />

91 pr14132<br />

73


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

System.out.pr<strong>in</strong>tln("Es ist " + en<strong>de</strong>);<br />

jahreszeiten kalt = jahreszeiten.w<strong>in</strong>ter;<br />

}<br />

}<br />

Die Verwendung von Aufzählungstypen entspricht <strong>de</strong>r von primitiven Typen. Aus<br />

diesem Grund ist ke<strong>in</strong>e Anfor<strong>de</strong>rung von Speicherplatz durch new erfor<strong>de</strong>rlich 92 .<br />

Im Gegensatz zu Ausprägungen von Primitivtypen besitzen Aufzählungstypen jedoch<br />

ke<strong>in</strong>en Vorgabewert mit <strong>de</strong>m sie standardmäßig <strong>in</strong>itialisiert wer<strong>de</strong>n. Die Verwendung<br />

e<strong>in</strong>er nicht <strong>in</strong>itialisierten Ausprägung e<strong>in</strong>es Aufzählungstypen führt zu e<strong>in</strong>em<br />

Übersetzungsfehler 93 .<br />

I<strong>de</strong>ntisch zu <strong>de</strong>n Primitivtypen besitzen Aufzählungstypen ke<strong>in</strong>e I<strong>de</strong>ntität. Zwei mit<br />

<strong>de</strong>mselben Wert belegte Aufzählungen wer<strong>de</strong>n als i<strong>de</strong>ntisch betrachtet.<br />

Aufzählungstypen lassen sich klassenartig ausbauen, z.B. 94 :<br />

public enum Tage<br />

{<br />

montag, dienstag, mittwoch, donnerstag,<br />

freitag, samstag, sonntag;<br />

public boolean istWerktag()<br />

{<br />

switch(this)<br />

{<br />

case sonntag :<br />

case samstag : return false;<br />

<strong>de</strong>fault : return true;<br />

}<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g [] args)<br />

{<br />

Tage tag = freitag;<br />

System.out.pr<strong>in</strong>tln(tag);<br />

System.out.pr<strong>in</strong>tln(tag.ord<strong>in</strong>al());<br />

System.out.pr<strong>in</strong>tln(tag.istWerktag());<br />

System.out.pr<strong>in</strong>tln(sonntag.istWerktag());<br />

}<br />

}<br />

Aufzählungsklassen enthalten <strong>in</strong> e<strong>in</strong>er Reihung alle Werte <strong>de</strong>r Aufzählung, über die<br />

mit <strong>de</strong>r neuen for-Schleife 95 iteriert wer<strong>de</strong>n kann, z.B.:<br />

public class IterTage<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g [] args)<br />

{<br />

for (Tage tag : Tage.values())<br />

System.out.pr<strong>in</strong>tln(tag.ord<strong>in</strong>al() + ": " + tag);<br />

}<br />

}<br />

Die bei<strong>de</strong>n letzten Programme zeigen die Ausgabe:<br />

92 Der Übersetzer verh<strong>in</strong><strong>de</strong>rt die Instanziierung <strong>de</strong>s Aufzählungstyps mit new und bricht beim Versuch mit <strong>de</strong>r<br />

Fehlermeldung enum types may not be <strong>in</strong>stantiated.<br />

93 variable ... might not have been <strong>in</strong>itialized<br />

94 pr14132<br />

95 vgl. 2.4.7.4<br />

74


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

1.4.1.4 Konvertieren von Objekten und Primitivtypen<br />

„Cast<strong>in</strong>g“ ist e<strong>in</strong> Mechanismus zum Konvertieren <strong>de</strong>s Werts e<strong>in</strong>es Objekts o<strong>de</strong>r e<strong>in</strong>es<br />

primitiven Typs <strong>in</strong> e<strong>in</strong>en an<strong>de</strong>ren Typ. „Cast<strong>in</strong>g“ ergibt e<strong>in</strong> neues Objekt o<strong>de</strong>r e<strong>in</strong>en<br />

neuen Wert und wirkt sich nicht auf das ursprüngliche Objekt bzw. <strong>de</strong>n<br />

ursprünglichen Wert aus.<br />

Konvertieren von Primitivtypen<br />

Ist <strong>de</strong>r angestrebte Typ „größer“ als <strong>de</strong>r zu konvertieren<strong>de</strong> Typ, ist häufig ke<strong>in</strong><br />

explizites „Cast<strong>in</strong>g“ nötig. E<strong>in</strong> „Byte“ o<strong>de</strong>r e<strong>in</strong> Zeichen kann automatisch z.B. als<br />

„<strong>in</strong>t“, e<strong>in</strong> „<strong>in</strong>t“ als „long“, e<strong>in</strong> „<strong>in</strong>t“ als „float“ behan<strong>de</strong>lt wer<strong>de</strong>n. Es gehen beim<br />

Konvertieren <strong>de</strong>s Werts ke<strong>in</strong>e Informationen verloren, weil <strong>de</strong>r größere Typ mehr<br />

Genauigkeit bietet als <strong>de</strong>r kle<strong>in</strong>ere.<br />

Ist <strong>de</strong>r angestrebte Typ „kle<strong>in</strong>er“ als <strong>de</strong>r zu konvertieren<strong>de</strong> Typ, ist „Cast<strong>in</strong>g“ nötig.<br />

Explizites Cast<strong>in</strong>g sieht so aus: (typname) wert<br />

„typname“ ist <strong>de</strong>r Name <strong>de</strong>s Typs, auf <strong>de</strong>n konvertiert wird, „wert“ ist e<strong>in</strong> Ausdruck,<br />

<strong>de</strong>r <strong>de</strong>n zu konvertieren<strong>de</strong>n Wert ergibt, z.B.: „(<strong>in</strong>t) x/y;“. Dieser Ausdruck teilt x<br />

durch y und wan<strong>de</strong>lt dann das Ergebnis <strong>in</strong> „<strong>in</strong>t“ um. Da Cast<strong>in</strong>g e<strong>in</strong>e höhere<br />

Priorität als Arithmetik hat, müssen die Klammern angegeben wer<strong>de</strong>n.<br />

Konvertieren von Objekten<br />

Mit e<strong>in</strong>er E<strong>in</strong>schränkung können auch Klassen<strong>in</strong>stanzen <strong>in</strong> Instanzen an<strong>de</strong>rer<br />

Klassen konvertiert wer<strong>de</strong>n. Die betroffenen Klassen müssen durch Vererbung<br />

mite<strong>in</strong>an<strong>de</strong>r verbun<strong>de</strong>n se<strong>in</strong>, d.h.: E<strong>in</strong> Objekt kann nur <strong>in</strong> e<strong>in</strong>e Instanz <strong>de</strong>r Sub- o<strong>de</strong>r<br />

Superklassen konvertiert wer<strong>de</strong>n, nicht aber <strong>in</strong> e<strong>in</strong>e beliebige Klasse.<br />

Durch Konvertieren e<strong>in</strong>es Objekts <strong>in</strong> e<strong>in</strong>e Instanz <strong>de</strong>r Superklasse gehen<br />

Informationen, die die Subklasse bereitgestellt hat, verloren. Spezifisches Cast<strong>in</strong>g ist<br />

erfor<strong>de</strong>rlich: (klassename) objekt.<br />

„klassename“ ist <strong>de</strong>r Name <strong>de</strong>r Klasse, <strong>in</strong> die das Objekt konvertiert wer<strong>de</strong>n soll.<br />

75


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

„objekt“ ist e<strong>in</strong>e Referenz auf das zu konvertieren<strong>de</strong> Objekt.<br />

Konvertieren von Primitivtypen <strong>in</strong> Objekte und umgekehrt<br />

Primitive Typen und Objekte s<strong>in</strong>d <strong>in</strong> <strong>Java</strong> zwei völlig verschie<strong>de</strong>ne D<strong>in</strong>ge.<br />

Konvertierung und automatisches Cast<strong>in</strong>g s<strong>in</strong>d nicht möglich. Allerd<strong>in</strong>gs enthält das<br />

Paket „java.lang“ mehrere Subklassen, die je e<strong>in</strong>em primitiven Datentyp entsprechen:<br />

Integer, Float, Boolean usw. Mit <strong>de</strong>n <strong>in</strong> diesen Klassen <strong>de</strong>f<strong>in</strong>ierten Klassenmetho<strong>de</strong>n<br />

kann mit Hilfe von new für alle Primitivtypen e<strong>in</strong>e „Art Objekt“ erstellt wer<strong>de</strong>n, z.B.:<br />

„Integer <strong>in</strong>tObjekt = new Integer(13);“. Mit <strong>in</strong>tValue() wird e<strong>in</strong><br />

primitiver „<strong>in</strong>t“-Wert aus e<strong>in</strong>em Integer-Objekt extrahiert, z.B.: <strong>in</strong>t ganzeZahl =<br />

<strong>in</strong>tObjekt.<strong>in</strong>tValue();<br />

1.4.1.5 Manipulieren von Objekten<br />

Vergleichen von Objekten<br />

Die relationalen Operatoren funktionieren <strong>in</strong> <strong>de</strong>r Regel nur mit Primitivtypen, nicht mit<br />

Objekten. Die Ausnahme zu dieser Regel bil<strong>de</strong>n die Operatoren „==“ und „!=“. Die<br />

Operatoren können für Objekte benutzt wer<strong>de</strong>n und prüfen, ob sich zwei Operan<strong>de</strong>n<br />

auf genau das gleiche Objekt beziehen.<br />

<strong>Java</strong> besitzt ke<strong>in</strong> Konzept für das Überla<strong>de</strong>n (Overload<strong>in</strong>g) von Operatoren. Zum<br />

Vergleichen von Instanzen eigen<strong>de</strong>f<strong>in</strong>ierter Klassen müssen Son<strong>de</strong>rmetho<strong>de</strong>n <strong>in</strong> die<br />

Klasse e<strong>in</strong>bezogen wer<strong>de</strong>n.<br />

Bsp.: Gleichheitstest mit Objekten <strong>de</strong>r „Str<strong>in</strong>g“-Klasse 96 .<br />

Die Str<strong>in</strong>g-Klasse <strong>de</strong>f<strong>in</strong>iert die Metho<strong>de</strong> equals(), die je<strong>de</strong>n Zeichen <strong>de</strong>r<br />

Zeichenkette prüft und „true“ ausgibt, falls die bei<strong>de</strong>n Zeichenketten <strong>de</strong>n<br />

gleichen Wert haben. Nach <strong>de</strong>m Operator „==“ s<strong>in</strong>d die bei<strong>de</strong>n „Str<strong>in</strong>g“-<br />

Objekte nicht gleich, weil sie zwar <strong>de</strong>n gleichen Inhalt haben, aber nicht die<br />

gleichen Objekte s<strong>in</strong>d.<br />

// Beson<strong>de</strong>rheiten im Zusammenhang mit Str<strong>in</strong>g-Objekten<br />

f<strong>in</strong>al class Str<strong>in</strong>gManipulationen<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

Str<strong>in</strong>g a = "test";<br />

Str<strong>in</strong>g b = "test";<br />

Str<strong>in</strong>g c = new Str<strong>in</strong>g("test");<br />

Str<strong>in</strong>g d = new Str<strong>in</strong>g("test");<br />

// Díe folgen<strong>de</strong>n Anweisungen geben wahr o<strong>de</strong>r falsch<br />

// an die Konsole aus<br />

System.out.pr<strong>in</strong>tln("a == b " + (a==b));<br />

System.out.pr<strong>in</strong>tln("c == d " + (c==d));<br />

System.out.pr<strong>in</strong>tln("a == c " + (a==c));<br />

System.out.pr<strong>in</strong>tln("a == \"test\" " + (a=="test"));<br />

System.out.pr<strong>in</strong>tln("c == \"test\" " + (c=="test"));<br />

System.out.pr<strong>in</strong>tln();<br />

System.out.pr<strong>in</strong>tln("a.equals(b) " + a.equals(b));<br />

System.out.pr<strong>in</strong>tln("c.equals(d) " + c.equals(d));<br />

System.out.pr<strong>in</strong>tln("a.equals(c) " + a.equals(c));<br />

// true<br />

// false<br />

// false<br />

// true<br />

// false<br />

// true<br />

// true<br />

// true<br />

96 pr14110<br />

76


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

System.out.pr<strong>in</strong>tln("a.equals(\"test\") " + a.equals("test")); // true<br />

System.out.pr<strong>in</strong>tln("c.equals(\"test\") " + c.equals("test")); // true<br />

}<br />

}<br />

Kopieren von Objekten<br />

Die clone()-Metho<strong>de</strong> erzeugt e<strong>in</strong> Duplikat als Kopie e<strong>in</strong>es Objekts. Damit e<strong>in</strong><br />

Objekt geklont wer<strong>de</strong>n kann, muß es die „Cloneable“-Schnittstelle unterstützen.<br />

Die Cloneable-Schnittstelle im Paket java.lang hat selbst ke<strong>in</strong>e Metho<strong>de</strong>n, sie<br />

dient lediglich als Indikator dafür, daß e<strong>in</strong> Objekt geklont wer<strong>de</strong>n kann. Das Format<br />

für die clone()-Metho<strong>de</strong> ist: protected Object clone() throws<br />

CloneNotSupportedException<br />

Bsp.: Die Klasse Rechentafel erhält e<strong>in</strong>e clone()-Metho<strong>de</strong> zum Klonen von<br />

Rechentafel-Objekten 97 .<br />

public class Rechentafel implements Cloneable<br />

{<br />

// Instanzvariable<br />

private <strong>in</strong>t ersterOperand = 0;<br />

private <strong>in</strong>t zweiterOperand = 0;<br />

private <strong>in</strong>t nummer;<br />

// I<strong>de</strong>ntifikationsnummer<br />

// Klassenvariable<br />

static <strong>in</strong>t anzRechenobjekte = 0;<br />

// Konstruktoren<br />

Rechentafel()<br />

{<br />

this(0,1);<br />

}<br />

Rechentafel(<strong>in</strong>t ersterOperand, <strong>in</strong>t zweiterOperand)<br />

{<br />

super();<br />

this.ersterOperand = ersterOperand;<br />

this.zweiterOperand = zweiterOperand;<br />

nummer = ++anzRechenobjekte;<br />

}<br />

// Kopierkonstruktor<br />

Rechentafel(Rechentafel rechenObjekt)<br />

{<br />

this(rechenObjekt.ersterOperand,rechenObjekt.zweiterOperand);<br />

}<br />

// Operationen<br />

public <strong>in</strong>t holersterOperand()<br />

{<br />

return ersterOperand;<br />

}<br />

public void setzersterOperand(<strong>in</strong>t ersterOperand)<br />

{<br />

this.ersterOperand = ersterOperand;<br />

}<br />

public <strong>in</strong>t holzweiterOperand()<br />

{<br />

return zweiterOperand;<br />

}<br />

public void setzweiterOperand(<strong>in</strong>t zweiterOperand)<br />

{<br />

this.zweiterOperand = zweiterOperand;<br />

}<br />

public <strong>in</strong>t addiere()<br />

97 pr14202<br />

77


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

{<br />

return ersterOperand + zweiterOperand;<br />

}<br />

public <strong>in</strong>t subtrahiere()<br />

{<br />

return ersterOperand - zweiterOperand;<br />

}<br />

public <strong>in</strong>t multipliziere()<br />

{<br />

return ersterOperand * zweiterOperand;<br />

}<br />

public <strong>in</strong>t dividiere()<br />

{<br />

// ganzzahlige Division<br />

try {<br />

/* Innerhalb <strong>de</strong>s try-Blocks wer<strong>de</strong>n diejenigen<br />

kritischen Aktionen durchgefuehrt, die<br />

Ausnahmen erzeugen koennen<br />

*/<br />

return (ersterOperand / zweiterOperand);<br />

}<br />

catch(java.lang.ArithmeticException a)<br />

{<br />

System.out.pr<strong>in</strong>tln(" Fehler: Division durch Null");<br />

System.out.pr<strong>in</strong>tln("Divisor wird auf 1 gesetzt!");<br />

zweiterOperand = 1;<br />

return (ersterOperand / zweiterOperand);<br />

}<br />

}<br />

public Str<strong>in</strong>g toStr<strong>in</strong>g()<br />

{<br />

return "Rechenobjekt# " + nummer + " Erster Operand: "<br />

+ ersterOperand + " Zweiter Operand: " + zweiterOperand;<br />

}<br />

public Object clone()<br />

{<br />

Object o = null;<br />

try {<br />

o = super.clone();<br />

}<br />

catch(CloneNotSupportedException e)<br />

{<br />

System.out.pr<strong>in</strong>tln("Objekt kann nicht geklont wer<strong>de</strong>n");<br />

}<br />

return o;<br />

}<br />

}<br />

Diese Metho<strong>de</strong> kann dann über e<strong>in</strong> <strong>in</strong> <strong>de</strong>r Klasse Rechentafeltest <strong>in</strong>stanziertes<br />

Objekt <strong>de</strong>r Klasse Rechentafel aufgerufen wer<strong>de</strong>n:<br />

import java.lang.*;<br />

class Rechentafeltest extends Object<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g argv[])<br />

{<br />

// Erzeugen Instanz <strong>de</strong>r Klasse Rechentafel<br />

Rechentafel erstesRechenobjekt = new Rechentafel(3,2);<br />

System.out.pr<strong>in</strong>tln("E<strong>in</strong>e Instanz <strong>de</strong>r Klasse " +<br />

erstesRechenobjekt.getClass().getName() +<br />

" wur<strong>de</strong> erzeugt.");<br />

System.out.pr<strong>in</strong>tln(erstesRechenobjekt.toStr<strong>in</strong>g());<br />

// Klonen e<strong>in</strong>es Rechenobjekts<br />

System.out.pr<strong>in</strong>tln("Klonen <strong>de</strong>s Objekts");<br />

78


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Rechentafel zweitesRechenobjekt = (Rechentafel)<br />

erstesRechenobjekt.clone();<br />

System.out.pr<strong>in</strong>tln(zweitesRechenobjekt.toStr<strong>in</strong>g());<br />

if (erstesRechenobjekt == zweitesRechenobjekt)<br />

System.out.pr<strong>in</strong>tln("Erstes Objekt == Zweites Objekt");<br />

else<br />

System.out.pr<strong>in</strong>tln("Erstes Objekt != Zweites Objekt");<br />

// Manipulation<br />

System.out.pr<strong>in</strong>tln(<br />

"Aen<strong>de</strong>rungen am Orig<strong>in</strong>al bleiben unberuecksichtigt");<br />

erstesRechenobjekt.setzersterOperand(7);<br />

erstesRechenobjekt.setzweiterOperand(5);<br />

System.out.pr<strong>in</strong>tln(erstesRechenobjekt.toStr<strong>in</strong>g());<br />

System.out.pr<strong>in</strong>tln(zweitesRechenobjekt.toStr<strong>in</strong>g());<br />

// Zuweisung<br />

System.out.pr<strong>in</strong>tln("Zuweisen <strong>de</strong>s Objekts");<br />

Rechentafel drittesRechenobjekt;<br />

drittesRechenobjekt = erstesRechenobjekt;<br />

System.out.pr<strong>in</strong>tln(drittesRechenobjekt.toStr<strong>in</strong>g());<br />

if (erstesRechenobjekt == drittesRechenobjekt)<br />

System.out.pr<strong>in</strong>tln("Erstes Objekt == Drittes Objekt");<br />

else<br />

System.out.pr<strong>in</strong>tln("Erstes Objekt != Drittes Objekt");<br />

// Manipulieren<br />

System.out.pr<strong>in</strong>tln("Aen<strong>de</strong>rungen beziehen sich auch auf die Kopie");<br />

erstesRechenobjekt.setzersterOperand(2);<br />

erstesRechenobjekt.setzweiterOperand(3);<br />

System.out.pr<strong>in</strong>tln(erstesRechenobjekt.toStr<strong>in</strong>g());<br />

System.out.pr<strong>in</strong>tln(drittesRechenobjekt.toStr<strong>in</strong>g());<br />

// Kopierkonstruktor<br />

System.out.pr<strong>in</strong>tln("Arbeiten mit e<strong>in</strong>em Kopierkonstruktor");<br />

Rechentafel viertesRechenobjekt = new Rechentafel(erstesRechenobjekt);<br />

System.out.pr<strong>in</strong>tln(viertesRechenobjekt.toStr<strong>in</strong>g());<br />

}<br />

}<br />

Der Test zeigt: Das geklonte Objekt enthält die gleichen Werte.<br />

Bestimmen <strong>de</strong>r Klasse e<strong>in</strong>es Objekts<br />

Die Klasse Class stellt über die Metho<strong>de</strong> getName() <strong>de</strong>n Namen e<strong>in</strong>er Klasse zur<br />

Verfügung: public Str<strong>in</strong>g getName()<br />

Bsp.: Der folgen<strong>de</strong> Aufruf<br />

Rechentafel e<strong>in</strong>Rechenobjekt = new<br />

Rechentafel(3,2);<br />

System.out.pr<strong>in</strong>t("E<strong>in</strong>e Instanz <strong>de</strong>r Klasse ");<br />

e<strong>in</strong>Rechenobjekt.zeigeKlasse();<br />

betimmt <strong>de</strong>n Namen <strong>de</strong>r Klasse von „e<strong>in</strong>Rechenobjekt“. Die Metho<strong>de</strong> „zeigeKlasse“<br />

enthält die Metho<strong>de</strong> getName() <strong>de</strong>r Klasse Class. Das Objekt, <strong>de</strong>ssen<br />

Klassenzugehörigkeit bestimmt wer<strong>de</strong>n soll ruft die Metho<strong>de</strong> getClass() <strong>de</strong>r Klasse<br />

Object auf. Das Ergebnis dieser Metho<strong>de</strong> ist e<strong>in</strong> Class-Objekt, das die Metho<strong>de</strong><br />

getName() kennt. „getName()“ gibt die Zeichenkette aus.<br />

void zeigeKlasse()<br />

{<br />

System.out.pr<strong>in</strong>t(this.getClass().getName());<br />

}<br />

79


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

E<strong>in</strong> an<strong>de</strong>ren Test bietet <strong>de</strong>r Operator „<strong>in</strong>stanceof“. Er hat zwei Operan<strong>de</strong>n: E<strong>in</strong><br />

Objekt l<strong>in</strong>ks und <strong>de</strong>n Namen <strong>de</strong>r Klasse rechts. Der Ausdruck gibt true o<strong>de</strong>r false<br />

aus, je nach <strong>de</strong>m, ob das Objekt e<strong>in</strong>e Instanz <strong>de</strong>r benannten Klasse ist, z.B.:<br />

if (!(an<strong>de</strong>resObj <strong>in</strong>stanceof Rechentafel)) return false;<br />

<strong>de</strong>r Operator <strong>in</strong>stanceof kann auch als Schnittstelle benutzt wer<strong>de</strong>n. Falls e<strong>in</strong><br />

Objekt e<strong>in</strong>e Schnittstelle implementiert, gibt <strong>in</strong>stanceof mit <strong>de</strong>m<br />

Schnittstellennamen auf <strong>de</strong>r rechten Seite true aus.<br />

1.4.1.6 Innere (lokale) und anonyme Klassen<br />

Bisher gab es nur Klassen, die entwe<strong>de</strong>r <strong>in</strong> Paketen organisiert waren o<strong>de</strong>r sich <strong>in</strong><br />

e<strong>in</strong>em Verzeichnis befan<strong>de</strong>n. Diese Form von Klassen heißen „Top-Level“-Klassen.<br />

Klassen können aber auch <strong>in</strong> an<strong>de</strong>re Klassen aufgenommen wer<strong>de</strong>n. Das nennt sich<br />

dann „<strong>in</strong>nere Klassen“.<br />

class Aussen<br />

{<br />

...<br />

class Innen<br />

{<br />

...<br />

}<br />

...<br />

}<br />

Lokale Klassen<br />

Seit JDK 1.1 kann man <strong>in</strong>nerhalb e<strong>in</strong>er bestehen<strong>de</strong>n Klasse z.B. X e<strong>in</strong>e neue Klasse<br />

z.B. Y <strong>de</strong>f<strong>in</strong>ieren 98 . Diese Klasse ist nur <strong>in</strong>nerhalb von X sichtbar. Objekte von Y<br />

können damit nur aus X erzeugt wer<strong>de</strong>n. Y kann aber auf alle Instanzmerkmale von X<br />

zugreifen. Bei <strong>de</strong>r Instanzierung wird (neben e<strong>in</strong>em impliziten this-Zeiger) e<strong>in</strong><br />

weiterer Verweis auf die erzeugen<strong>de</strong> Instanz <strong>de</strong>r umschließen<strong>de</strong>n Klasse übergeben,<br />

<strong>de</strong>r es ermöglicht, auf sie zuzugreifen. Für <strong>in</strong>nere Klassen wird e<strong>in</strong>e eigenständige<br />

Datei mit <strong>de</strong>r Namenskonvention Umgeben<strong>de</strong>Klasse$<strong>in</strong>nereKllasse erzeugt.<br />

Die Anwendung lokaler Klassen für die Ereignisbehandlung besteht dar<strong>in</strong>, mit ihrer<br />

Hilfe die benötigten EventListener zu implementieren. Dazu wird das GUI-Objekt, das<br />

e<strong>in</strong>en Event-Handler benötigt, e<strong>in</strong>e lokale Klasse <strong>de</strong>f<strong>in</strong>iert und aus e<strong>in</strong>er passen<strong>de</strong>n<br />

Adapter-Klasse abgeleitet. Es braucht nicht mehr das gesamte Interface<br />

implementiert zu wer<strong>de</strong>n, ( <strong>de</strong>nn die Metho<strong>de</strong>nrümpfe wer<strong>de</strong>n ja aus <strong>de</strong>r<br />

Adapterklasse geerbt,) son<strong>de</strong>rn lediglich die tatsächlich benötigetn Metho<strong>de</strong>n.<br />

Anonyme Klassen<br />

E<strong>in</strong>e Variante <strong>de</strong>r lokalen Klassen s<strong>in</strong>d anonyme Klassen. Sie wer<strong>de</strong>n ebenfalls lokal<br />

zu e<strong>in</strong>er an<strong>de</strong>ren Klasse erzeugt, kommen aber ohne Klassennamen aus.<br />

98 Im JDK wird das Konzept als Inner Classes bezeichnet<br />

80


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Klassen<strong>de</strong>f<strong>in</strong>ition: new BasisKlasse(parameter) { /* <strong>in</strong>ner class<br />

methods and data */ }<br />

Damit e<strong>in</strong>e anonyme Klasse überhaupt irgen<strong>de</strong><strong>in</strong>er s<strong>in</strong>nvollen Aufgabe zugeführt<br />

wer<strong>de</strong>n kann, muß sie aus e<strong>in</strong>er an<strong>de</strong>ren Klasse abgeleitet se<strong>in</strong> o<strong>de</strong>r e<strong>in</strong><br />

bestehen<strong>de</strong>s Interface implementieren.<br />

Bsp. 99 :<br />

public class Anonymous<br />

{<br />

private TestClass test()<br />

{<br />

return new TestClass()<br />

{<br />

public void hello()<br />

{ System.out.pr<strong>in</strong>tln("overrid<strong>de</strong>n test!"); } //hello()<br />

}; //class TestClass<br />

} //test()<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

TestClass myRV;<br />

myRV = (new Anonymous()).test();<br />

myRV.hello();<br />

System.out.pr<strong>in</strong>tln( myRV <strong>in</strong>stanceof TestClass);<br />

System.out.pr<strong>in</strong>tln( myRV.getClass().getName() );<br />

} //end ma<strong>in</strong>()<br />

} //class Anonymous<br />

class TestClass<br />

{<br />

public void hello()<br />

{<br />

System.out.pr<strong>in</strong>tln("orig<strong>in</strong>al");<br />

} //hello()<br />

} //class TestClass<br />

Adapterklassen<br />

E<strong>in</strong>e Adapterklasse implementiert e<strong>in</strong> vorgegebenes Interface mit leeren<br />

Metho<strong>de</strong>nrümpfen. Adapterklassen können verwen<strong>de</strong>t wer<strong>de</strong>n, wenn aus e<strong>in</strong>em<br />

Interface lediglich e<strong>in</strong> Teil <strong>de</strong>r Metho<strong>de</strong>n benötigt wird, <strong>de</strong>r Rest aber un<strong>in</strong>teressant<br />

ist. In diesem Fall leitet man e<strong>in</strong>e neue Klasse aus <strong>de</strong>r Adapterklasse ab, anstatt das<br />

zugehörige Interface zu implementieren und überlagert die benötigten Metho<strong>de</strong>n. Alle<br />

übrigen Metho<strong>de</strong>n <strong>de</strong>s Interfaces wer<strong>de</strong>n von <strong>de</strong>r Basisklasse zur Verfügung gestellt.<br />

Zu je<strong>de</strong>m <strong>de</strong>r Low-Level-Ereignisempfänger stellt java.awt.event e<strong>in</strong>e passen<strong>de</strong><br />

Adapterklasse zur Verfügung.<br />

99 pr14160<br />

81


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

1.4.1.7 Schnittstellen und Pakete<br />

1.4.1.7.1 Schnittstellen<br />

Def<strong>in</strong>ition<br />

E<strong>in</strong>e Schnittstelle ist <strong>in</strong> <strong>Java</strong> e<strong>in</strong>e Sammlung von Metho<strong>de</strong>nnamen ohne konkrete<br />

Def<strong>in</strong>ition. Klassen haben die Möglichkeit zur Def<strong>in</strong>ition e<strong>in</strong>es Objekts, Schnittstellen<br />

können lediglich e<strong>in</strong> paar abstrakte Metho<strong>de</strong>n und Konstanten (f<strong>in</strong>ale Daten)<br />

<strong>de</strong>f<strong>in</strong>ieren 100 .<br />

Schnittstellen bil<strong>de</strong>n <strong>in</strong> <strong>Java</strong> <strong>de</strong>n Ersatz für Mehrfachvererbung. E<strong>in</strong>e Klasse kann<br />

<strong>de</strong>mnach nur e<strong>in</strong>e Superklasse, jedoch dafür mehrere Schnittstellen haben.<br />

E<strong>in</strong> Schnittstelle („Interface“) ist e<strong>in</strong>e beson<strong>de</strong>re Form <strong>de</strong>r Klasse, die ausschließlich<br />

abstrakte Metho<strong>de</strong>n und Konstanten enthält. Anstelle von class dient zur Def<strong>in</strong>ition<br />

e<strong>in</strong>es „Interface“ das Schlüsselwort „<strong>in</strong>terface“.<br />

"Interfaces" wer<strong>de</strong>n formal wie Klassen <strong>de</strong>f<strong>in</strong>iert, z.B.:<br />

public <strong>in</strong>terface me<strong>in</strong>eSchnittstelle<br />

{<br />

// Abstrakte Metho<strong>de</strong>n<br />

}<br />

bzw.<br />

public <strong>in</strong>terface me<strong>in</strong>eSpezialschnittstelle extends me<strong>in</strong>eSchnittstelle<br />

{<br />

// Abstrakte Metho<strong>de</strong>n<br />

}<br />

"Interfaces" können benutzt wer<strong>de</strong>n, <strong>in</strong><strong>de</strong>m sie <strong>in</strong> e<strong>in</strong>er Klasse implementiert wer<strong>de</strong>n,<br />

z.B.:<br />

public class Me<strong>in</strong>eKlasse extends Object implements me<strong>in</strong>eSchnittstelle<br />

{<br />

/* Normale Klassen<strong>de</strong>f<strong>in</strong>ition + Metho<strong>de</strong>n aus me<strong>in</strong>eSchnittstelle */<br />

}<br />

E<strong>in</strong>e Klasse kann mehrere Schnittstellen implementieren, z.B.<br />

public class Me<strong>in</strong>eKlasse extends Object<br />

implements me<strong>in</strong>eSchnittstelle1, me<strong>in</strong>eSchnittstelle2<br />

{<br />

/* Normale Klassen<strong>de</strong>f<strong>in</strong>ition + Metho<strong>de</strong>n aus me<strong>in</strong>erSchnittstelle1<br />

und me<strong>in</strong>erSchnittstelle2 */<br />

}<br />

Verwendung<br />

Bei <strong>de</strong>r Vererbung von Klassen spricht man von Ableitung, bei Interfaces nennt man<br />

es Implementierung. Durch Implementieren e<strong>in</strong>er Schnittstelle verpflichtet sich die<br />

Klasse, alle Metho<strong>de</strong>n, die im Interface <strong>de</strong>f<strong>in</strong>iert s<strong>in</strong>d, zu implementieren. Die<br />

100 Angaben zur Implementierung s<strong>in</strong>d nicht möglich<br />

82


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Implementierung e<strong>in</strong>es Interface wird durch das Schlüsselwort „implements“ bei <strong>de</strong>r<br />

Klassen<strong>de</strong>f<strong>in</strong>ition angezeigt.<br />

1. Bsp.: Natürliche Ordnungsbeziehungen s<strong>in</strong>d <strong>in</strong> <strong>Java</strong> über das Interface<br />

Comparable e<strong>in</strong>gefangen:<br />

/**************** Comparable.java *****************************<br />

/** Das Interface <strong>de</strong>klariert e<strong>in</strong>e Metho<strong>de</strong> anhand <strong>de</strong>r sich das<br />

* das aufgerufene Objekt mit <strong>de</strong>m uebergebenen vergleicht.<br />

* Fuer je<strong>de</strong>n vom Objekt abgeleiteten Datentyp muss e<strong>in</strong>e solche<br />

* Vergleichsklasse implementiert wer<strong>de</strong>n.<br />

* Die Metho<strong>de</strong> erzeugt e<strong>in</strong>e Fehlermeldung, wenn "a" e<strong>in</strong> Objekt<br />

* e<strong>in</strong>er an<strong>de</strong>ren Klasse als dieses Objekt ist.<br />

*<br />

* <strong>in</strong>t compareTo(Comparable a)<br />

* liefert 0, wenn this == 0<br />

* liefert < 0, wenn this < a<br />

* liefert > 0, wenn this > a<br />

*<br />

*/<br />

public <strong>in</strong>terface Comparable<br />

{<br />

public <strong>in</strong>t compareTo(Comparable a);<br />

}<br />

Zahlreiche von <strong>Java</strong> bereitgestellte Referenztypen (Klassen) haben dieses Interface<br />

implementiert. Deshalb sortiert die nachfolgen<strong>de</strong>n Sortierrout<strong>in</strong>e (Sort) Str<strong>in</strong>g- und<br />

Zahlen-Objekte, wie <strong>de</strong>r Aufrufe aus SortTest beweisen.<br />

public class Sort<br />

{<br />

public void sort(Comparable x[])<br />

{<br />

bubbleSort(x);<br />

}<br />

public void bubbleSort(Comparable x[])<br />

{<br />

for (<strong>in</strong>t i = 0; i < x.length; i++)<br />

{<br />

for (<strong>in</strong>t j = i + 1; j < x.length; j++)<br />

{<br />

if (x[i].compareTo(x[j]) > 0)<br />

{<br />

Comparable temp = x[i];<br />

x[i] = x[j];<br />

x[j] = temp;<br />

}<br />

}<br />

}<br />

}<br />

}<br />

public class SortTest<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

Str<strong>in</strong>g sa[] = {<br />

"Juergen","Christian","Bernd","Werner","Uwe",<br />

"Erich","Kurt","Karl","Emil","Ernst"<br />

};<br />

// Ausgabe <strong>de</strong>s noch nicht sortierten x<br />

System.out.pr<strong>in</strong>tln("Vor <strong>de</strong>m Sortieren:");<br />

for (<strong>in</strong>t i = 0; i < sa.length; i++)<br />

83


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

{<br />

System.out.pr<strong>in</strong>tln("sa["+i+"] = " + sa[i]);<br />

}<br />

// Aufruf <strong>de</strong>r Sortierout<strong>in</strong>e<br />

Sort s = new Sort();<br />

s.sort(sa);<br />

// Gib das sortierte Feld x aus<br />

System.out.pr<strong>in</strong>tln();<br />

System.out.pr<strong>in</strong>tln("Nach <strong>de</strong>m Sortieren:");<br />

for (<strong>in</strong>t i = 0; i < sa.length; i++)<br />

{<br />

System.out.pr<strong>in</strong>tln("sa["+i+"] = " + sa[i]);<br />

}<br />

System.out.pr<strong>in</strong>tln();<br />

<strong>in</strong>t za[] = { 50, 70, 80, 10, 20, 30, 1, 2, 3, 99, 12, 11, 13};<br />

Integer o[] = new Integer[za.length];<br />

for (<strong>in</strong>t i = 0; i < za.length; i++)<br />

{<br />

o[i] = new Integer(za[i]);<br />

}<br />

// Ausgabe <strong>de</strong>s noch nicht sortierten za<br />

System.out.pr<strong>in</strong>tln("Vor <strong>de</strong>m Sortieren:");<br />

for (<strong>in</strong>t i = 0; i < za.length; i++)<br />

{<br />

System.out.pr<strong>in</strong>tln("za["+i+"] = " + za[i]);<br />

}<br />

// Aufruf <strong>de</strong>r Sortierout<strong>in</strong>e<br />

Sort zs = new Sort();<br />

zs.sort(o);<br />

// Gib das sortierte Feld x aus<br />

for (<strong>in</strong>t i = 0; i < za.length; i++)<br />

{<br />

za[i] = ((Integer) o[i]).<strong>in</strong>tValue();;<br />

}<br />

System.out.pr<strong>in</strong>tln();<br />

System.out.pr<strong>in</strong>tln("Nach <strong>de</strong>m Sortieren:");<br />

for (<strong>in</strong>t i = 0; i < za.length; i++)<br />

{<br />

System.out.pr<strong>in</strong>tln("za["+i+"] = " + za[i]);<br />

}<br />

}<br />

}<br />

2. Bsp. 101 : Erweiterte Form <strong>de</strong>r for-Schleife for {Type i<strong>de</strong>ntifier : expr) {<br />

... } <strong>in</strong> <strong>Java</strong> 1. 5<br />

1.4.1.7.2 Pakete<br />

Def<strong>in</strong>ition: package packageName; // Am Anfang <strong>de</strong>r Quelldatei<br />

Pakete s<strong>in</strong>d <strong>in</strong> <strong>Java</strong> Zusammenfassungen von Klassen und Schnittstellen. Sie<br />

entsprechen <strong>in</strong> <strong>Java</strong> <strong>de</strong>n Bibliotheken an<strong>de</strong>rer Programmiersprachen. Pakete<br />

ermöglichen, daß modulare Klassengruppen nur verfügbar s<strong>in</strong>d, wenn sie gebraucht<br />

wer<strong>de</strong>n. Potentielle Konflikte zwischen Klassenamen <strong>in</strong> unterschiedlichen<br />

Klassengruppen können dadurch vermie<strong>de</strong>n wer<strong>de</strong>n.<br />

Für Metho<strong>de</strong>n und Variablen <strong>in</strong>nerhalb von Paketen besteht e<strong>in</strong>e<br />

Zugriffsschutzschablone. Je<strong>de</strong>s Paket ist gegenüber an<strong>de</strong>ren, nicht zum Paket<br />

101 vgl. 2.4.7.4<br />

84


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

zählen<strong>de</strong>n Klassen abgeschirmt. Klassen, Metho<strong>de</strong>n o<strong>de</strong>r Variablen s<strong>in</strong>d nur sichtbar<br />

für Klassen im gleichen Paket. Klassen, die ohne „package“ Anweisung <strong>de</strong>f<strong>in</strong>iert s<strong>in</strong>d,<br />

wer<strong>de</strong>n vom Compiler <strong>in</strong> e<strong>in</strong> „Standardpaket“ gestellt. Voraussetzung dafür ist: Die<br />

„.java“- und „.class“-Dateien dieses Pakets bef<strong>in</strong><strong>de</strong>n sich im aktuellen Verzeichnis<br />

(o<strong>de</strong>r <strong>in</strong> e<strong>in</strong>en darunter liegen<strong>de</strong>n Klassenverzeichnis).<br />

Die <strong>Java</strong>-Klassenbibliothek 102 von <strong>Java</strong> 1.0 enthält folgen<strong>de</strong> Pakete:<br />

- java.lang: Klassen, die unmittelbar zur Sprache gehören. Das Paket umfaßt u.a.<br />

die Klassen Object, Str<strong>in</strong>g, System, außer<strong>de</strong>m die Son<strong>de</strong>rklassen für die<br />

Primitivtypen (Integer, Character, Float, etc.)<br />

Object<br />

Class<br />

Str<strong>in</strong>g<br />

Str<strong>in</strong>gBuffer<br />

Thread<br />

ThreadGroup<br />

Throwable<br />

System<br />

Runtime<br />

Process<br />

Math<br />

Number<br />

Character<br />

Aus dieser Klasse leiten sich alle weiteren Klassen ab. Ohne explizite Angabe <strong>de</strong>r Klasse,<br />

die e<strong>in</strong>e neue Klasse erweitern soll, erweitert die neue Klasse die Object-Klasse. Die<br />

Klasse Object ist die Basis-Klasse je<strong>de</strong>r an<strong>de</strong>ren Klasse <strong>in</strong> <strong>Java</strong>. Sie <strong>de</strong>f<strong>in</strong>iert Metho<strong>de</strong>n,<br />

die von allen Klasse <strong>in</strong> <strong>Java</strong> unterstützt wer<strong>de</strong>n.<br />

Für je<strong>de</strong> <strong>in</strong> <strong>Java</strong> <strong>de</strong>f<strong>in</strong>ierte Klasse gibt es e<strong>in</strong>e Instanz von Class, die diese Klasse<br />

beschreibt<br />

Enthält Metho<strong>de</strong>n zur Manipulation von <strong>Java</strong>-Zeichenketten<br />

Dient zum Erstellen von <strong>Java</strong>-Zeichenketten<br />

Stellt e<strong>in</strong>en Ausführungs-Thread <strong>in</strong> e<strong>in</strong>em <strong>Java</strong>-Programm dar. Je<strong>de</strong>s Programm kann<br />

mehrere Threads laufen lassen<br />

Ermöglicht die Verknüpfung von Threads untere<strong>in</strong>an<strong>de</strong>r. E<strong>in</strong>ige Thread-Operationen<br />

können nur von Threads aus <strong>de</strong>r gleichen ThreadGroup ausgeführt wer<strong>de</strong>n.<br />

Ist die Basisklasse für Ausnahmen. Je<strong>de</strong>s Objekt, das mit <strong>de</strong>r "catch"-Anweisung<br />

gefangen o<strong>de</strong>r mit <strong>de</strong>r "throw"-Anweisung verworfen wird, muß e<strong>in</strong>e Subklasse von<br />

Throwable se<strong>in</strong>.<br />

Stellt spezielle Utilities auf Systemebene zur Verfügung<br />

Enthält e<strong>in</strong>e Vielzahl gleicher Funktionen wie System, behan<strong>de</strong>lt aber auch das Laufen<br />

externer Programme<br />

Stellt e<strong>in</strong> externes Programm dar, das von e<strong>in</strong>em Runtime-Objekt gestartet wur<strong>de</strong>.<br />

Stellt e<strong>in</strong>e Reihe mathematischer Funktionen zur verfügung<br />

Ist die Basisklasse für Double,Float, Integer und Long (Objeckt-Wrapper)<br />

Ist e<strong>in</strong> Objekt-Wrapper für <strong>de</strong>n datentyp char und enthält e<strong>in</strong>e Reihe nützlicher<br />

zeichenorientierter Operationen<br />

Ist e<strong>in</strong> Objekt-Wrapper für <strong>de</strong>n Datentyp boolean<br />

Boolean<br />

ClassLoa<strong>de</strong>r Ermöglicht <strong>de</strong>r Laufzeit-Umgebung von <strong>Java</strong> neue Klassen h<strong>in</strong>zuzufügen<br />

SecurityManager Legt die Sicherheits-Restriktionen <strong>de</strong>r aktuellen Laufzeitumgebung fest. Viele <strong>Java</strong>-<br />

Klassen benutzen <strong>de</strong>n Security-Manager zur Sicherstellung, daß e<strong>in</strong>e Operation auch<br />

tatsächlich genehmigt ist.<br />

Compiler Ermöglicht, falls vorhan<strong>de</strong>n, <strong>de</strong>n Zugriff auf <strong>de</strong>n "just-<strong>in</strong>-time" Compiler<br />

Abb.: Klassen <strong>de</strong>s java.lang-Pakets<br />

Zusätzlich enthält das java.lang-Paket noch zwei Schnittstellen:<br />

Cloneable<br />

Runnable<br />

Muß von e<strong>in</strong>em an<strong>de</strong>ren Objekt implementiert wer<strong>de</strong>n, das dann geklont o<strong>de</strong>r kopiert<br />

wer<strong>de</strong>n kann<br />

Wird zusammen mit <strong>de</strong>r Thread-Klasse benutzt, um die aufgerufene Metho<strong>de</strong> zu<br />

<strong>de</strong>f<strong>in</strong>ieren, wenn e<strong>in</strong> Thread gestartet wird.<br />

Abb.: Schnittstellen im java.lang-Paket<br />

- java.util<br />

102 Die Klassenbibliothek <strong>de</strong>s JDK bef<strong>in</strong><strong>de</strong>t sich <strong>in</strong> e<strong>in</strong>em Paket mit <strong>de</strong>m namen „java“.<br />

85


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

- java.io: Klassen zum Lesen und Schreiben von Datenströmen und zum<br />

Handhaben von Dateien.<br />

- java.net: Klassen zur Netzunterstützung, z.B. socket und URL (e<strong>in</strong>e Klasse<br />

zum Darstellen von Referenzen auf Dokumente im World Wi<strong>de</strong> Web).<br />

- java.awt (Abstract W<strong>in</strong>dow<strong>in</strong>g Toolkit): Klassen zum Implementieren e<strong>in</strong>er<br />

grafischen Benutzeroberfläche. Das Paket enthält auch e<strong>in</strong>e Klasse für Grafik<br />

(java.awt.Graphics) und Bildverarbeitung (java.awt.Image).<br />

- java.applet: Klassen zum Implementieren von Applets, z.B. die Klasse<br />

Applet.<br />

Die <strong>Java</strong> Version 1.1 hat die Klassenbibliothek umfassend erweitert 103 :<br />

Paket<br />

Be<strong>de</strong>utung<br />

<strong>Java</strong>.applet<br />

Applets<br />

<strong>Java</strong>.awt<br />

Abstract W<strong>in</strong>dow Toolkit<br />

<strong>Java</strong>.awt.datatranfer<br />

ClipBoard-Funktionalität (Copy / Paste)<br />

<strong>Java</strong>.awt.event<br />

AWT Event-handl<strong>in</strong>g<br />

<strong>Java</strong>.awt.image<br />

Bildanzeige<br />

<strong>Java</strong>.beans<br />

<strong>Java</strong> Beans<br />

<strong>Java</strong>.io<br />

E<strong>in</strong>- und Ausgabe, Streams<br />

<strong>Java</strong>.lang<br />

Elementare Sprachunterstützung<br />

<strong>Java</strong>.lang.reflect<br />

Introspektion, besserer Zugriff auf Klassen durch<br />

Debugger und Inspektoren<br />

<strong>Java</strong>.math<br />

<strong>Java</strong>.net<br />

Netzzugriffe<br />

<strong>Java</strong>.rmi<br />

Remote Method Invocation, Zugriff auf Objekte <strong>in</strong><br />

an<strong>de</strong>ren virtuellen Masch<strong>in</strong>en<br />

<strong>Java</strong>.rmi.dgc<br />

RMI Distributed Garbage Collection<br />

<strong>Java</strong>.rmi.registry Verwaltet Datenbank, die RMI-Verb<strong>in</strong>dungen<br />

koord<strong>in</strong>iert<br />

<strong>Java</strong>.rmi.server<br />

RMI-Server<br />

<strong>Java</strong>.security<br />

Sicherheit durch digitale Signaturen, Schlüssel<br />

<strong>Java</strong>.security.aci<br />

Access Control Lists<br />

<strong>Java</strong>.security.<strong>in</strong>terfaces<br />

Digital Signature Algorithm (DAS-Klassen)<br />

<strong>Java</strong>.sql<br />

Datenbankzugriff (JDBC)<br />

<strong>Java</strong>.util<br />

Diverse Utilities, Datenstrukturen<br />

<strong>Java</strong>.util.zip<br />

JAR-Files, Kompression, Prüfsummen<br />

Abb.: Klassenbibliothek <strong>de</strong>r <strong>Java</strong>-Version 1.1<br />

Verwendung<br />

Je<strong>de</strong> Klasse ist Bestandteil e<strong>in</strong>es Pakets. Der vollständige Name e<strong>in</strong>er Klasse<br />

besteht aus <strong>de</strong>m Namen <strong>de</strong>s Pakets, danach kommt <strong>de</strong>r e<strong>in</strong> Punkt, gefolgt von <strong>de</strong>m<br />

eigentlichen Namen <strong>de</strong>r Klasse.<br />

Zur Verwendung e<strong>in</strong>er Klasse muß angegeben wer<strong>de</strong>n, <strong>in</strong> welchem Paket sie liegt.<br />

Hier gibt es zwei unterschiedliche Möglichkeiten:<br />

- Die Klasse wird über ihren vollen (qualifizieren) Namen angesprochen, z.B.<br />

java.util.Date d = new java.util.Date();<br />

- Am Anfang <strong>de</strong>s Klassenprogramms wer<strong>de</strong>n die gewünschten Klassen mit Hilfe <strong>de</strong>r import-<br />

Anweisung e<strong>in</strong>gebun<strong>de</strong>n, z.B.:<br />

import java util.*;<br />

103 Zusätzlich 15 weitere Packages, etwa 500 Klassen und Schnittstellen<br />

86


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

......<br />

Date d = new Date();<br />

Die import-Anweisung gibt es <strong>in</strong> unterschiedlichen Ausprägungen:<br />

- Mit "import paket.Klasse" wird genau e<strong>in</strong>e Klasse importiert, alle an<strong>de</strong>ren Klassen <strong>de</strong>s<br />

Pakets bleiben verborgen<br />

- Mit "import paket.*" 104 können alle Klassen <strong>de</strong>s angegebenen Pakets auf e<strong>in</strong>mal importiert<br />

wer<strong>de</strong>n.<br />

Standardmäßig haben <strong>Java</strong>-Klassen Zugang zu <strong>de</strong>n <strong>in</strong> java.lang bef<strong>in</strong>dlichen<br />

Klassen. Klassen aus an<strong>de</strong>ren Paketen müssen explizit über <strong>de</strong>n Paketnamen<br />

e<strong>in</strong>bezogen o<strong>de</strong>r <strong>in</strong> die Quelldatei importiert wer<strong>de</strong>n.<br />

Statische Imports<br />

Mit <strong>de</strong>r Überarbeitung zur Sprachversion 1.5 wur<strong>de</strong>n durch die statischen Importe<br />

e<strong>in</strong>e abkürzen<strong>de</strong> Schreibweise für importierte statische Metho<strong>de</strong>n etabliert:<br />

Durch die zusätzliche Angabe <strong>de</strong>s Schlüsselworts static vor <strong>de</strong>r vollqualifizierte<br />

Klasseni<strong>de</strong>ntifikation stehen als Resultate alle als statisch <strong>de</strong>klarierten Metho<strong>de</strong>n<br />

zum direkten Aufruf zur Verfügung.<br />

Bsp.: Über statische Importe kann die Funktion max() aus Math importiert wer<strong>de</strong>n,<br />

so dass <strong>de</strong>r Klassenname entfallen kann<br />

import static java.lang.Math.max;<br />

class StatischesImport<br />

{<br />

static <strong>in</strong>t max3(<strong>in</strong>t a, <strong>in</strong>t b, <strong>in</strong>t c)<br />

{<br />

return max(a, max(b,c));<br />

}<br />

}<br />

104 type import on <strong>de</strong>mand, d.h.: Die Klasse wird erst dann <strong>in</strong> <strong>de</strong>m angegebenen Paket gesucht, wenn das<br />

Programm sie wirklich benötigt.<br />

87


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

1.4.1.8 Polymorphismus und B<strong>in</strong><strong>de</strong>n<br />

Polymorphismus<br />

Def<strong>in</strong>ition<br />

Polymorphismus ist die Eigenschaft von Objekten, mehreren Klassen (und nicht nur<br />

genau e<strong>in</strong>er Klasse) anzugehören, d.h. je nach Zusammenhang <strong>in</strong> unterschiedlicher<br />

Gestalt (polymorph) aufzutreten. <strong>Java</strong> ist polymorph.<br />

Bsp. 105 : Das folgen<strong>de</strong> Programm Poly ist e<strong>in</strong>e Anwendung zu <strong>de</strong>r bereits<br />

bekannten Klassenhierarchie für geometrische Objekte. Es behan<strong>de</strong>lt<br />

Ersche<strong>in</strong>ungsformen <strong>de</strong>s Polymorphismus.<br />

import java.lang.*;<br />

public class Poly extends Object<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

Kreis k = new Kreis(0.0,1.0,2.0);<br />

Rechteck r = new Rechteck(0.0,1.0,2.0,2.0);<br />

// GeomObjekt geom = new GeomObjekt();<br />

/* unzulaessig, da GeomObjekt abstrakt ist */<br />

Object o = new Object();<br />

// Folgen<strong>de</strong> Zuweisungen s<strong>in</strong>d zulaessig<br />

GeomObjekt geom1 = k;<br />

GeomObjekt geom2 = new Kreis(1.0,1.0,1.0);<br />

GeomObjekt geom3 = r;<br />

GeomObjekt geom4 = new Rechteck(1.0,1.0,1.0,4.0);<br />

o = k;<br />

// Aufruf <strong>de</strong>r Metho<strong>de</strong>n toStr<strong>in</strong>g() und umfang()<br />

// ueber geom1<br />

System.out.pr<strong>in</strong>tln(geom1.toStr<strong>in</strong>g());<br />

System.out.pr<strong>in</strong>tln("Umfang: " + geom1.umfang());<br />

/* geom1 ist e<strong>in</strong> GeomObjekt. GeomObjekt enthaelt e<strong>in</strong>e<br />

(abstrakte) Def<strong>in</strong>ition von toStr<strong>in</strong>g() und von umfang().<br />

Zur Laufzeit ist <strong>de</strong>r Wert von geom1 e<strong>in</strong>e Referenz auf<br />

e<strong>in</strong> Objekt <strong>de</strong>r Klasse Kreis, daher wer<strong>de</strong>n die im Kreis<br />

<strong>de</strong>f<strong>in</strong>ierten (allgeme<strong>in</strong>er: die <strong>in</strong> Kreis <strong>de</strong>f<strong>in</strong>ierten<br />

bzw. ererbten) Metho<strong>de</strong>n toStr<strong>in</strong>g() und umfang() ausgefuehrt.<br />

*/<br />

// Aufruf <strong>de</strong>r Metho<strong>de</strong>n toStr<strong>in</strong>g() und umfang() ueber o<br />

// System.out.pr<strong>in</strong>tln(o.toStr<strong>in</strong>g());<br />

/* o ist e<strong>in</strong> Object. Object enthaelt e<strong>in</strong>e Def<strong>in</strong>ition von<br />

toStr<strong>in</strong>g(), daher akzeptiert <strong>de</strong>r <strong>Java</strong>-Compiler<br />

(zur Uebersetzungszeit) o.toStr<strong>in</strong>g().<br />

*/<br />

// System.out.pr<strong>in</strong>tln("Umfang: " + o.umfang());<br />

/* o ist e<strong>in</strong> Object. Object enthaelt o<strong>de</strong>r erbt ke<strong>in</strong>e<br />

Def<strong>in</strong>ition von umfang(), daher akzeptiert <strong>de</strong>r <strong>Java</strong>-<br />

Compiler (zur Uebersetzungszeit) o.umfang() nicht.<br />

*/<br />

// System.out.pr<strong>in</strong>tln("Umfang: " + (Rechteck) o.umfang());<br />

/* (Rechteck) o.umfang() akzeptiert <strong>de</strong>r <strong>Java</strong>-Compiler<br />

(zur Uebesersetzungszeit) aus <strong>de</strong>m gleichen Grund wie<br />

zuvor nicht.<br />

Die Schreibweise (Rechteck) o.umfang() be<strong>de</strong>utet auch:<br />

Der Rueckgabewert von o.umfang() soll als Rechteck-<br />

105 Vgl. pr14181<br />

88


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Referenz <strong>in</strong>terpretiert wer<strong>de</strong>n.<br />

*/<br />

System.out.pr<strong>in</strong>tln("Umfang: " + ((Rechteck) o).umfang());<br />

/* o ist e<strong>in</strong> Object, (Rechteck o) be<strong>de</strong>utet: o soll als<br />

Rechteck <strong>in</strong>terprtiert wer<strong>de</strong>n. Fuer Rechteck-Objekte<br />

ist umfang() erkaert, <strong>de</strong>r <strong>Java</strong>-Compiler akzeptiert<br />

(zur Uebersetzungszeit) ((Rechteck o).umfang().<br />

Zur Laufzeit wird die <strong>in</strong> Recteck <strong>de</strong>f<strong>in</strong>ierte<br />

(allgeme<strong>in</strong>er: die <strong>in</strong> Recheck <strong>de</strong>f<strong>in</strong>ierte bzw. ererbte)<br />

Metho<strong>de</strong> umfang() ausgefuehrt.<br />

*/<br />

}<br />

}<br />

B<strong>in</strong><strong>de</strong>n<br />

Das Schema e<strong>in</strong>er Botschaft wird aus "Empfänger Metho<strong>de</strong> Argument" gebil<strong>de</strong>t.<br />

Darüber soll e<strong>in</strong>e Nachricht die physikalische Adresse e<strong>in</strong>es Objekts im Speicher<br />

f<strong>in</strong><strong>de</strong>n. Trotz Polymorphismus und Vererbung ist <strong>de</strong>r Metho<strong>de</strong>nname (oft. Selektor<br />

genannt) nur e<strong>in</strong>e Verknüpfung zum Programmco<strong>de</strong> e<strong>in</strong>er Metho<strong>de</strong>. Vor <strong>de</strong>r<br />

Ausführung e<strong>in</strong>er Metho<strong>de</strong> muß daher e<strong>in</strong>e genaue Zuordnung zwischen <strong>de</strong>m<br />

Selektor und <strong>de</strong>r physikalischen Adresse <strong>de</strong>s tatsächlichen Programmco<strong>de</strong>s erfolgen<br />

(B<strong>in</strong><strong>de</strong>n o<strong>de</strong>r L<strong>in</strong>ken).<br />

In <strong>de</strong>r objektorientierten Programmierung unterschei<strong>de</strong>t man: frühes und spätes<br />

B<strong>in</strong><strong>de</strong>n:<br />

Frühes B<strong>in</strong><strong>de</strong>n: E<strong>in</strong> Compiler ordnet schon zum Zeitpunkt <strong>de</strong>r Übersetzung <strong>de</strong>s Programms die<br />

tatsächliche, physikalische Adresse <strong>de</strong>r Metho<strong>de</strong> <strong>de</strong>m Metho<strong>de</strong>naufruf zu.<br />

Spätes B<strong>in</strong><strong>de</strong>n: Hier wir erst zur Laufzeit <strong>de</strong>s Programms die tatsächliche Verknüpfung zwischen<br />

Selektor und Co<strong>de</strong> hergestellt. Die richtige Verb<strong>in</strong>dung übernimmt das Laufzeitprogramm <strong>de</strong>r<br />

Programmiersprache.<br />

<strong>Java</strong> unterstützt das Konzept <strong>de</strong>s „Late B<strong>in</strong>d<strong>in</strong>g“.<br />

89


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

1.4.2 Klassen <strong>de</strong>s Pakets java.lang<br />

1.4.2.1 Die Klasse Object<br />

Die Object-Klasse ist die Basisklasse für je<strong>de</strong> an<strong>de</strong>re Klasse <strong>in</strong> <strong>Java</strong>. Sie <strong>de</strong>f<strong>in</strong>iert<br />

Metho<strong>de</strong>n, die von allen Klassen <strong>in</strong> <strong>Java</strong> unterstützt wer<strong>de</strong>n.<br />

Test auf Gleichheit von Objekten: public boolean equals(Object obj)<br />

Darüber kann ermittelt wer<strong>de</strong>n, ob zwei Objekte gleich s<strong>in</strong>d. Subklassen<br />

überschreiben diese Metho<strong>de</strong> zur Durchführung e<strong>in</strong>es <strong>in</strong>haltlichen Vergleichs. In<br />

je<strong>de</strong>r Klasse ist die Metho<strong>de</strong> auch gut aufgehoben, <strong>de</strong>nn nur e<strong>in</strong> Objekt weiß, wann<br />

es mit e<strong>in</strong>em an<strong>de</strong>ren gleich ist. Ob zwei Referenzen auf das gleiche Objekt zeigen,<br />

läßt sich durch <strong>de</strong>n Vergleichsoperator == feststellen.<br />

Zeichenkettendarstellung von Objekten: public Str<strong>in</strong>g toStr<strong>in</strong>g()<br />

Darüber kann e<strong>in</strong> Objekt an e<strong>in</strong>en Ausgabestrom übergeben wer<strong>de</strong>n.<br />

Berechnung von Hashco<strong>de</strong>: public <strong>in</strong>t hashCo<strong>de</strong>() berechnet <strong>de</strong>n<br />

numerischen Wert, <strong>de</strong>r als In<strong>de</strong>x zur Speicherung e<strong>in</strong>es Objekts <strong>in</strong> e<strong>in</strong>er Hashtabelle<br />

verwen<strong>de</strong>t wer<strong>de</strong>n kann.<br />

Hashso<strong>de</strong>s wer<strong>de</strong>n zur Speicherung von Elementen <strong>in</strong> Hashtabellen. Das s<strong>in</strong>d<br />

Datenstrukturen, die e<strong>in</strong>e effizienten Zugriff auf ihre Elemente erlauben.<br />

Klonen von Objekten: protected Object clone() throws<br />

CloneNotSupportedException, OutOfMemoryError erzeugt e<strong>in</strong> exaktes<br />

Duplikat e<strong>in</strong>es Objekts. E<strong>in</strong> Objekt kann nur geklont wer<strong>de</strong>n, falls es die Cloneable-<br />

Schnittstelle unterstützt.<br />

Fragen nach <strong>de</strong>m Namen <strong>de</strong>r Objektklasse: public f<strong>in</strong>al Class getClass()<br />

Benachrichtungsmechanismus. Hierfür gibt es die Metho<strong>de</strong>n: public f<strong>in</strong>al void<br />

notify() bzw. public f<strong>in</strong>al void notifyAll() und public f<strong>in</strong>al void<br />

wait() throws InterruptedException bzw. public f<strong>in</strong>al void<br />

wait(long timeout) throws InterruptedException. Threads 106 können<br />

mite<strong>in</strong>an<strong>de</strong>r kommunizieren und gegenseitig auf sich warten. Object <strong>de</strong>f<strong>in</strong>iert fünf<br />

Versionen von wait() und notify() zur Synchronisation von Threads.<br />

Aufräumen: protected void f<strong>in</strong>alize() throws Throwable. E<strong>in</strong>e spezielle<br />

Metho<strong>de</strong> f<strong>in</strong>alize() wird immer dann aufgerufen, wenn <strong>de</strong>r Garbage Collector<br />

das Objekt entfernen möchte. Objekte sollten diese Funktion überschreiben, wenn<br />

sie bspw. noch Dateien schliessen müssen.<br />

Objekti<strong>de</strong>ntifikation: public Str<strong>in</strong>g toStr<strong>in</strong>g(). Je<strong>de</strong>s Objekt sollte sich durch<br />

die Metho<strong>de</strong> toStr<strong>in</strong>g() mit e<strong>in</strong>er Zeichenkette i<strong>de</strong>ntifizieren. Die Zeichenkette gibt<br />

die Inhalt <strong>de</strong>r Attribute an. Neue Objekte sollten diese Metho<strong>de</strong> überschreiben. Ist<br />

dies nicht <strong>de</strong>r Fall, gelangt das zugehörige Programm zur Standardimplementierung<br />

<strong>in</strong> Object.<br />

106 vgl. 1.4.2.4<br />

90


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

1.4.2.2 Die Class-Klasse<br />

Diese Klasse stellt Metho<strong>de</strong>n zur Abfrage von Eigenschaften von Klassen zur<br />

Verfügung und erlaubt es, Klassen dynamisch zu la<strong>de</strong>n und Instanzen dynamisch zu<br />

erzeugen. In <strong>Java</strong> hat je<strong>de</strong> Klasse e<strong>in</strong>e entsprechen<strong>de</strong> Instanz von Class. In <strong>Java</strong><br />

liegen Klassen selbst als Objekt vor. Neben normalen Klassen wer<strong>de</strong>n auch<br />

Interfaces über Class-Objekte abgelegt. Für e<strong>in</strong> bestimmtes Objekt kann man auf<br />

mehreren Wegen das zugehörige Class-Objekt <strong>in</strong> Erfahrung br<strong>in</strong>gen. Für e<strong>in</strong><br />

bestimmtes Objekt kann man auf mehreren Wegen das zugehörige Class-Objekt <strong>in</strong><br />

Erfahrung br<strong>in</strong>gen:<br />

- Ist e<strong>in</strong> Exemplar <strong>de</strong>r Klasse verfügbar, dann ruft man die Metho<strong>de</strong> getClass() von<br />

java.lang.Object auf. Man erhält die zugehörige Klasse.<br />

- Ist die Klasse bekannt, besteht aber zusätzlich Interesse an <strong>de</strong>n Vorfahren, dann kann man das mit<br />

getSuperClass() herausf<strong>in</strong><strong>de</strong>n<br />

- Ist die Klasse nicht zur Compilezeit aber zur Laufzeit bekannt, dann hilft die Klassenmetho<strong>de</strong><br />

forName() weiter.<br />

E<strong>in</strong> Klassenobjekt kann e<strong>in</strong>e Schnittstelle, e<strong>in</strong>e Klasse, e<strong>in</strong>en primitiven Datentyp<br />

o<strong>de</strong>r auch e<strong>in</strong> Array beschreiben.<br />

Class<br />

public static Class forName(Str<strong>in</strong>g className) throws ClassNotFoundException<br />

// liefert das Klassen-Objekt mit <strong>de</strong>m vollqualifizierten Namen <strong>de</strong>r Klasse o<strong>de</strong>r <strong>de</strong>r Schnittstelle<br />

// className. Das Metaobjekt wird, falls es nicht von <strong>de</strong>r VM e<strong>in</strong>gebun<strong>de</strong>n ist, gesucht und<br />

// gela<strong>de</strong>n. Der Rückgabewert ist nicht null, falls es nicht gefun<strong>de</strong>n wer<strong>de</strong>n konnte, son<strong>de</strong>rn e<strong>in</strong>e<br />

// ClassNotFoundException.<br />

public Str<strong>in</strong>g getName()<br />

// liefert <strong>de</strong>n voll qualifizierten Namen <strong>de</strong>r Klasse, <strong>de</strong>r Schittstelle, <strong>de</strong>s Arrays o<strong>de</strong>r <strong>de</strong>s<br />

// primitiven Datentyps <strong>de</strong>s Class-Objekts<br />

public Class getSuperclass()<br />

// liefert die Superklasse <strong>de</strong>s Klassen-Objekts.<br />

public boolean isInterface()<br />

// liefert true, falls das Class-Objekt e<strong>in</strong> Interface beschreibt<br />

public boolean isArray()<br />

// liefert true, falls das Class-Objekt e<strong>in</strong>en Array beschreibt<br />

public boolean isPrimitive()<br />

// testet, ob das Interface e<strong>in</strong>en primitiven Datentyp beschreibt<br />

public <strong>in</strong>t getModifiers()<br />

// liefert die Modifizierer für e<strong>in</strong>e Klasse o<strong>de</strong>r e<strong>in</strong> Interface<br />

public Field[] getFields() throws SecurityException<br />

// liefert e<strong>in</strong> Feld mit Field-Objekten 107 .<br />

public Method[] getMethods() throws SecurityException<br />

// gibt e<strong>in</strong> Array von Method-Objekten 108 zurück, die alle öffentlichen Metho<strong>de</strong>n <strong>de</strong>r Klasse/<br />

// Schnittstelle beschreiben<br />

...<br />

Abb.: Die Klasse Class<br />

107 Objekten <strong>de</strong>r Klasse java.lang.reflect.Field<br />

108 Objekte <strong>de</strong>r Klasse java.lang.reflect.Method<br />

91


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Class besitzt Metho<strong>de</strong>n, die hauptsächlich zur Beschreibung <strong>de</strong>r Schnittstellen e<strong>in</strong>er<br />

Klasse und zur E<strong>in</strong>ordnung <strong>in</strong> die Klassenhierarchie dienen. Diese abstrakte und<br />

dynamische Dokumentation von Klassen wird als Reflection bezeichnet 109 .<br />

Das folgen<strong>de</strong> Programmstück 110 testet Objekte systematisch durch:<br />

import java.util.*;<br />

class CheckClassTyp<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

Class observer = Observer.class;<br />

Class observable = Observable.class;<br />

Class array = (new <strong>in</strong>t[2][3][4]).getClass();<br />

Class primitive = Integer.TYPE;<br />

checkClassType(observer);<br />

checkClassType(observable);<br />

checkClassType(array);<br />

checkClassType(primitive);<br />

}<br />

static void checkClassType(Class c)<br />

{<br />

Str<strong>in</strong>g name = c.getName();<br />

if (c.isArray())<br />

System.out.pr<strong>in</strong>tln(name + " ist e<strong>in</strong> Array.");<br />

else if (c.isPrimitive())<br />

System.out.pr<strong>in</strong>tln(name + " ist e<strong>in</strong> primitiver Datentyp.");<br />

else if (c.isInterface())<br />

System.out.pr<strong>in</strong>tln(name + " ist e<strong>in</strong> Schnittstelle.");<br />

else<br />

System.out.pr<strong>in</strong>tln(name + " ist e<strong>in</strong>e Klasse.");<br />

}<br />

}<br />

Liegt <strong>de</strong>r Name <strong>de</strong>r Klasse als Class-Objekt vor, kann man zur Laufzeit <strong>de</strong>n voll<br />

qualifizierten Namen über die Metho<strong>de</strong> getName() ausgeben.<br />

import java.util.Date;<br />

class BspName<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g [] args)<br />

{<br />

Date d = new Date();<br />

Class c = d.getClass();<br />

Str<strong>in</strong>g s = c.getName();<br />

System.out.pr<strong>in</strong>tln(s);<br />

System.out.pr<strong>in</strong>tln((new <strong>in</strong>t[2][3][4]).getClass().getName());<br />

}<br />

}<br />

Arrays wer<strong>de</strong>n über getName() mit führen<strong>de</strong>n „[“ kodiert. Je<strong>de</strong> Klammer beschreibt<br />

die Tiefe <strong>de</strong>s Arrays. Nach <strong>de</strong>r Klammer folgt <strong>in</strong> kodierter Form, welchen Typ das<br />

Array speichert. So liefert System.out.pr<strong>in</strong>tln((new <strong>in</strong>t [2] [3] [4] ).<br />

getClass().getName());<strong>de</strong>n Str<strong>in</strong>g „[[[I“. Nimmt das Array Objekte auf, wird dies<br />

mit e<strong>in</strong>em „LKlassenname;“ kodiert. So ergibt (new<br />

Object[3]).getClass().getName() <strong>de</strong>n Str<strong>in</strong>g „[Ljava.lang.Object;“.<br />

109 Die Klasse java.lang.Class ist Basis für Metho<strong>de</strong>n, die Objekte vom Typ Constructor, Method, Field usw.<br />

zurückliefern. Die zugehörigen Klassen s<strong>in</strong>d im Package java.lang.reflect <strong>de</strong>f<strong>in</strong>iert.<br />

110 vgl. pr14220<br />

92


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

1.4.2.3 Die Klasse System<br />

Die Klasse System besteht nur aus Klassenvariablen und –metho<strong>de</strong>n und kann nicht<br />

<strong>in</strong>stanziiert wer<strong>de</strong>n. Sie stellt die Verb<strong>in</strong>dung zum Betriebssystem her und erlaubt<br />

bspw. <strong>de</strong>n Zugriff auf die Standar<strong>de</strong><strong>in</strong>- / Standardausgabe.<br />

System<br />

public static f<strong>in</strong>al InputStream <strong>in</strong><br />

public static f<strong>in</strong>al Pr<strong>in</strong>tStream out<br />

public static f<strong>in</strong>al Pr<strong>in</strong>tStream err<br />

public static void setIn(Inputstream <strong>in</strong>)<br />

public static void setOut(Pr<strong>in</strong>tStream out)<br />

public static void setErr(Pr<strong>in</strong>tStream err)<br />

public static Securitymanager getSecurityManager()<br />

public static void setSecurityManager(SecurityManager s)<br />

public static long currentTimeMillis()<br />

public static Properties getProperties()<br />

public static void setProperties(Properties props)<br />

public static Str<strong>in</strong>g getProperty(Str<strong>in</strong>g key)<br />

public static void exit(<strong>in</strong>t status)<br />

public static void gc()<br />

public static void load(Str<strong>in</strong>g filename)<br />

Abb.: Die Klasse System<br />

System-Properties<br />

Properties s<strong>in</strong>d Listen von Eigenschaften, die Programmen vom <strong>Java</strong>-Laufzeitsystem<br />

zur Verfügung gestellt wer<strong>de</strong>n. Je<strong>de</strong> Eigenschaft besitzt e<strong>in</strong>en Name, unter <strong>de</strong>m auf<br />

sie zugegriffen wer<strong>de</strong>n kann.<br />

Property<br />

java.version<br />

java.vendor<br />

java.vendor.url<br />

java.home<br />

java.class.version<br />

java.class.path<br />

os.name<br />

os.arch<br />

os.version<br />

file.seperator<br />

path.seperator<br />

l<strong>in</strong>e.seperator<br />

user.name<br />

user.home<br />

user.dir<br />

Be<strong>de</strong>utung<br />

<strong>Java</strong>-Versionsnummer<br />

Herstellerspez. Zeichenkette<br />

URL (Internet-L<strong>in</strong>k)<br />

Installationsverzeichnis<br />

Versionsnummer <strong>de</strong>r <strong>Java</strong>-Klassenbibliothek<br />

Aktueller Klassenpfad<br />

Name <strong>de</strong>s Betriebssystems<br />

Betriebssystem-Architektur<br />

Versionsnummer <strong>de</strong>s Betriebssystems<br />

Trennzeichen für die Bestandteile <strong>de</strong>s Pfadnamens<br />

Trennzeichen für die Laufwerksangabe e<strong>in</strong>es Pfadnamens<br />

Zeichenkette für Zeilenumschaltung<br />

Name <strong>de</strong>s angemel<strong>de</strong>ten Benutzers<br />

Home-Verzeichnis<br />

Aktuelles Arbeitsverzeichnis<br />

Für <strong>de</strong>n Zugriff auf diese Eigenschaften steht die Klasse Properties aus <strong>de</strong>m<br />

Paket java.util zur Verfügung. Sie erzeugt Property-Listen. Die Klasse<br />

Properties ist e<strong>in</strong>e Ableitung <strong>de</strong>r Klasse HashTable und stellt darüber e<strong>in</strong>e<br />

Tabelle mit Schlüssel/Wertepaaren zur Verfügung.<br />

93


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Für <strong>de</strong>n Zugriff auf e<strong>in</strong>zelne Properties reicht meistens die Klassenmetho<strong>de</strong> <strong>de</strong>r<br />

Klasse System aus:<br />

public static Str<strong>in</strong>g getProperty(Str<strong>in</strong>g key)<br />

liefert die Eigenschaft zu <strong>de</strong>m key <strong>in</strong> Form e<strong>in</strong>er Zeichenkette. Falls ke<strong>in</strong>e Eigenschaft unter diesem<br />

Namen (key) gefun<strong>de</strong>n wird, wird „null“ zurückgegeben.<br />

Die Metho<strong>de</strong><br />

public static Properties getProperties();<br />

liefert das komplette Properties-Objekt mit <strong>de</strong>n System-Properties.<br />

Bsp. 111 : Ausgabe e<strong>in</strong>er Liste aller System-Properties auf <strong>de</strong>m Bildschirm<br />

import java.lang.*;<br />

import java.util.*;<br />

class EigenschaftsTest extends Object<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g argv[])<br />

{<br />

System.out.pr<strong>in</strong>tln(new Date());<br />

// System-Eigenschaften<br />

Properties p = System.getProperties();<br />

p.list(System.out);<br />

System.out.pr<strong>in</strong>tln("Speicher-Verwendung:");<br />

Runtime rt = Runtime.getRuntime();<br />

System.out.pr<strong>in</strong>tln("Totaler Speicher = " + rt.totalMemory() +<br />

" Freier Speicher = " + rt.freeMemory());<br />

}<br />

}<br />

<strong>in</strong>, err und out<br />

out ist e<strong>in</strong>e statische Variable vom Typ Pr<strong>in</strong>tStream, die beim Starten <strong>de</strong>s System<br />

so <strong>in</strong>itialisiert wird, daß ihre Ausgabe auf die Standardausgabe geleitet wird.<br />

Analog zu out gibt es err und <strong>in</strong>. Dabei dient err zur Ausgabe von<br />

Fehlermeldungen, <strong>in</strong> ist e<strong>in</strong> Standar<strong>de</strong><strong>in</strong>gabekanal, <strong>de</strong>r zur E<strong>in</strong>gabe von <strong>de</strong>r<br />

Tastatur verwen<strong>de</strong>t wird.<br />

Mit Hilfe von<br />

public static void setIn(InputStream <strong>in</strong>);<br />

public static void setOut(Pr<strong>in</strong>tStream out);<br />

public static void setErr(Pr<strong>in</strong>tStream err);<br />

kann die Standar<strong>de</strong><strong>in</strong>- und Standardausgabe aus <strong>de</strong>m Programm heraus umgeleitet<br />

wer<strong>de</strong>n.<br />

public static void exit(<strong>in</strong>t status)<br />

Mit System.exit(status) wird das laufen<strong>de</strong> Programm been<strong>de</strong>t. „status“ dient<br />

als Fehleranzeige und wird als Exitco<strong>de</strong> an <strong>de</strong>n Aufrufer <strong>de</strong>s Programms<br />

zurückgegeben. Gemäß Konvention zeigt e<strong>in</strong> Wert größer o<strong>de</strong>r gleich 1 e<strong>in</strong>en Fehler<br />

während <strong>de</strong>r Programmausführung an, 0 signalisiert e<strong>in</strong> fehlerfreies Programmen<strong>de</strong>.<br />

111 pr14231<br />

94


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

public static void gc()<br />

führt e<strong>in</strong>en expliziten Aufruf <strong>de</strong>s Garbage Collector durch. Dieser sucht nach<br />

freiem Speicher und gibt ihn dann an das Laufzeitsystem zurück. Normalerweise ist<br />

e<strong>in</strong> aufruf dieser Metho<strong>de</strong> nicht erfor<strong>de</strong>rlich, <strong>de</strong>nn <strong>de</strong>r Garbage Collector läuft als<br />

niedrig priorisierter Thread im H<strong>in</strong>tergrund.<br />

public static long currentTimeMillis()<br />

liefert die Anzahl <strong>de</strong>r Millisekun<strong>de</strong>n, die zum Zeitpunkt <strong>de</strong>s Aufrufs seit Mitternacht<br />

<strong>de</strong>s 1.1.1970 vergangen s<strong>in</strong>d. Ob tatsächlich e<strong>in</strong>e Auflösung von e<strong>in</strong>er Millisekun<strong>de</strong><br />

erreicht wird, ist von <strong>de</strong>r <strong>Java</strong>-Implementierung abhängig. In PC-basierten Systemen<br />

orientiert sie sich meistens an <strong>de</strong>r Auflösung <strong>de</strong>s „System-Timer“.<br />

1.4.2.4 Multithread<strong>in</strong>g<br />

1.4.2.4.1. Die Klasse Thread<br />

Threads wer<strong>de</strong>n <strong>in</strong> <strong>Java</strong> durch die Klasse Thread und das Interface Runnable<br />

implementiert. In bei<strong>de</strong>n Fällen wird <strong>de</strong>r Thread-Körper (, also <strong>de</strong>r parallel<br />

auszuführen<strong>de</strong> Co<strong>de</strong>) <strong>in</strong> Form <strong>de</strong>r zu überlagern<strong>de</strong>n Metho<strong>de</strong> run bereitgestellt. Die<br />

Kommunikation kann durch Zugriff auf die Instanz- o<strong>de</strong>r Klassenvariablen o<strong>de</strong>r durch<br />

Aufruf beliebiger Metho<strong>de</strong>n, die <strong>in</strong>nerhalb von run sichtbar s<strong>in</strong>d, erfolgen. E<strong>in</strong><br />

Thread stellt e<strong>in</strong>en e<strong>in</strong>zigen Ablauf von Ausführungen <strong>in</strong> e<strong>in</strong>em <strong>Java</strong>-Programm dar.<br />

Die Klasse Thread ist Bestandteil <strong>de</strong>s Pakets java.lang und steht damit allen<br />

Anwendungen zur Verfügung. Über die Klasse Thread hat <strong>Java</strong> das<br />

(Betriebssystem-) Konzept <strong>de</strong>r Nebenläufigkeit implementiert. Mit Nebenläufigkeit<br />

bezeichnet man die Fähigkeit e<strong>in</strong>es Systems zwei o<strong>de</strong>r mehr Vorgänge gleichzeitig<br />

o<strong>de</strong>r quasi gleichzeitig ausführen zu lassen. E<strong>in</strong> Thread ist e<strong>in</strong> eigenständiges<br />

Programmfragment, das parallel zu e<strong>in</strong>em an<strong>de</strong>ren Thread laufen kann.<br />

95


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

><br />

Runnable<br />

public abstract void run()<br />

Thread<br />

public static f<strong>in</strong>al <strong>in</strong>t MIN_PRIORITY<br />

public static f<strong>in</strong>al <strong>in</strong>t NORM_PRIORITY<br />

public static f<strong>in</strong>al <strong>in</strong>t MAX_PRIORITY<br />

><br />

public Thread()<br />

public Thread(Runnable target)<br />

><br />

public static Thread currentThread()<br />

public static void yield()<br />

public static void sleep(long millis) throws InterruptedException<br />

public void start()<br />

public void run()<br />

public f<strong>in</strong>al void stop()<br />

//<strong>de</strong>precated<br />

public f<strong>in</strong>al void stop(Throwable obj)<br />

//<strong>de</strong>precated<br />

public void <strong>in</strong>terrupt()<br />

public static boolean <strong>in</strong>terrupted()<br />

public boolean isInterrupted()<br />

public void <strong>de</strong>stroy()<br />

public f<strong>in</strong>al boolean isAlive()<br />

public f<strong>in</strong>al void suspend()<br />

//<strong>de</strong>precated<br />

public f<strong>in</strong>al void resume()<br />

//<strong>de</strong>precated<br />

public f<strong>in</strong>al void setPriority(<strong>in</strong>t newPriority)<br />

public f<strong>in</strong>al <strong>in</strong>t getPriority()<br />

...<br />

public Str<strong>in</strong>g toStr<strong>in</strong>g()<br />

Abb.: Thread-Klassen<br />

Metho<strong>de</strong>n <strong>de</strong>r Klasse Thread<br />

public void run()<br />

Zum Erzeugen e<strong>in</strong>es Thread muß e<strong>in</strong>e eigene Klasse aus <strong>de</strong>r Klasse Thread<br />

abgeleitet, und die Metho<strong>de</strong> run überlagert wer<strong>de</strong>n. Mit Hilfe <strong>de</strong>s Aufrufs <strong>de</strong>r<br />

Metho<strong>de</strong> start wird <strong>de</strong>r Thread gestartet, die weitere Ausführung wird an run()<br />

übertragen. „start()“ wird nach <strong>de</strong>m Start been<strong>de</strong>t, <strong>de</strong>r Aufrufer kann parallel zum<br />

neu erzeugten Thread fortfahren.<br />

Bsp.: E<strong>in</strong>facher Thread, <strong>de</strong>r <strong>in</strong> e<strong>in</strong>er Endlosschleife e<strong>in</strong>en Thread hochzählt.<br />

class Me<strong>in</strong>Thread extends Thread<br />

{<br />

public void run()<br />

{<br />

<strong>in</strong>t i = 0;<br />

while (true)<br />

96


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

{<br />

// Endlosschleife, Abbruch durch Druecken von Strg+C<br />

System.out.pr<strong>in</strong>tln(i++);<br />

}<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

// Erzeugen e<strong>in</strong>er Instanz<br />

Me<strong>in</strong>Thread t = new Me<strong>in</strong>Thread();<br />

// Start vom erzeugten Thread<br />

t.start();<br />

// Impliziter Aufruf <strong>de</strong>r Metho<strong>de</strong> run()<br />

}<br />

}<br />

Der (quasi) parallel auszuführen<strong>de</strong> Co<strong>de</strong> wird durch die überlagerte Metho<strong>de</strong> run<br />

bereitgestellt. Die Kommunikation kann durch Zugriff auf Instanz- und<br />

Klassenvariablen o<strong>de</strong>r durch Aufruf beliebiger Metho<strong>de</strong>n erfolgen, die <strong>in</strong>nerhalb von<br />

run() sichtbar s<strong>in</strong>d.<br />

E<strong>in</strong>e <strong>Java</strong>-Applikation wird immer dann been<strong>de</strong>t, wenn <strong>de</strong>r letzte Thread been<strong>de</strong>t<br />

wur<strong>de</strong>(, <strong>de</strong>r ke<strong>in</strong> H<strong>in</strong>tergrund-Thread (Dämon) ist). Da e<strong>in</strong> e<strong>in</strong>faches Programm nur<br />

e<strong>in</strong>en e<strong>in</strong>zigen Vor<strong>de</strong>rgrund-Thread besitzt, (nämlich <strong>de</strong>n, <strong>in</strong> <strong>de</strong>m ma<strong>in</strong>() läuft 112 ,)<br />

wird es been<strong>de</strong>t, wenn ma<strong>in</strong>() been<strong>de</strong>t wird. Das Beispielprogramm erzeugt e<strong>in</strong>en<br />

zusätzlichen Vor<strong>de</strong>rgrund-Thread und kann erst dann been<strong>de</strong>t wer<strong>de</strong>n, wenn dieser<br />

Thread been<strong>de</strong>t wur<strong>de</strong>.<br />

public static void sleep(long millis) throws InterruptedException<br />

sorgt dafür, daß <strong>de</strong>r aktuelle Prozeß für die (<strong>in</strong> Millisekun<strong>de</strong>n) angegebene Zeit<br />

unterbrochen wird.<br />

public static void sleep(long millis, <strong>in</strong>t nanos) throws<br />

InterruptedException<br />

Die erzielbare Genauigkeit <strong>de</strong>r Unterbrechungs-Wartezeit wird durch Hardware-<br />

Restriktionen <strong>de</strong>r Masch<strong>in</strong>e begrenzt<br />

public f<strong>in</strong>al boolean isAlive()<br />

gibt true zurück, wenn <strong>de</strong>r aktuelle Thread gestartet, aber noch nicht been<strong>de</strong>t<br />

wur<strong>de</strong>.<br />

Been<strong>de</strong>t wird e<strong>in</strong> Thread, wenn das En<strong>de</strong> <strong>de</strong>r run-Metho<strong>de</strong> erreicht ist, o<strong>de</strong>r wenn<br />

die Metho<strong>de</strong> stop aufgerufen wur<strong>de</strong>.<br />

Bsp.: Been<strong>de</strong>n <strong>de</strong>s Thread nach 2 Sekun<strong>de</strong>n durch Aufruf von stop<br />

class Me<strong>in</strong>Thread1 extends Thread<br />

{<br />

public void run()<br />

{<br />

<strong>in</strong>t i = 0;<br />

while (true)<br />

{<br />

// Endlosschleife, Abbruch durch Druecken von Strg+C<br />

System.out.pr<strong>in</strong>tln(i++);<br />

}<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

// Erzeugen e<strong>in</strong>er Instanz<br />

112 Beim Starten e<strong>in</strong>es <strong>Java</strong>-Programms wird automatisch e<strong>in</strong> Thread für die Ausführung <strong>de</strong>s Hauptprogramms<br />

angelegt.<br />

97


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

}<br />

}<br />

Me<strong>in</strong>Thread1 t = new Me<strong>in</strong>Thread1();<br />

// Start vom erzeugten Thread<br />

t.start();<br />

// Impliziter Aufruf <strong>de</strong>r Metho<strong>de</strong> run()<br />

try {<br />

Thread.sleep(2000);<br />

}<br />

catch(InterruptedException e)<br />

{ }<br />

t.stop();<br />

Mit <strong>de</strong>m JDK 1.2 wur<strong>de</strong> stop() als <strong>de</strong>precated markiert, da es nicht vorhersehbar<br />

und auch nicht <strong>de</strong>f<strong>in</strong>iert ist, an welcher Stelle e<strong>in</strong> Thread beim Aufruf von stop()<br />

abgebrochen wird.<br />

Ersatz für stop(): Man kann e<strong>in</strong> Flag für Threadabbruch setzen und dieses zyklisch<br />

<strong>in</strong> <strong>de</strong>r Metho<strong>de</strong> run <strong>de</strong>s Thread abfragen. Falls dieses gesetzt ist, können alle<br />

Ressourcen freigegeben und <strong>de</strong>r Thread durch reguläres Been<strong>de</strong>n von run() (bpw.<br />

mit Hilfe von return) been<strong>de</strong>t wer<strong>de</strong>n. Das Flag darf allerd<strong>in</strong>gs nicht zu oft aber<br />

auch nicht zu selten abgefragt wer<strong>de</strong>n.<br />

Bsp. 113 :<br />

class Me<strong>in</strong>Thread extends Thread<br />

{<br />

boolean ok = true;<br />

public void run()<br />

{<br />

<strong>in</strong>t i = 0;<br />

while (ok)<br />

System.out.pr<strong>in</strong>tln(i++);<br />

}<br />

}<br />

public class Me<strong>in</strong>ThreadTest<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g [] args)<br />

{<br />

Me<strong>in</strong>Thread t = new Me<strong>in</strong>Thread();<br />

t.start();<br />

try {<br />

Thread.sleep(2000);<br />

}<br />

catch (InterruptedException e)<br />

{ }<br />

t.ok = false;<br />

}<br />

}<br />

Es gibt <strong>in</strong> <strong>de</strong>r Klasse Thread e<strong>in</strong>ige Metho<strong>de</strong>n, die e<strong>in</strong>en solchen Mechanismus<br />

standardmäßig unterstützen.<br />

public void <strong>in</strong>terrupt()<br />

setzt e<strong>in</strong> Flag, das e<strong>in</strong>e Unterbrechungsanfor<strong>de</strong>rung signalisiert.<br />

public boolean isInterrupted()<br />

stellt fest, ob das Abbruchflag gesetzt wur<strong>de</strong> und <strong>de</strong>r Thread been<strong>de</strong>t wur<strong>de</strong>.<br />

public static boolean <strong>in</strong>terrupted()<br />

113 pr14240<br />

98


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

stellt <strong>de</strong>n Status <strong>de</strong>s Abbruchflag beim aktuellen Thread fest und setzt zusätzlich das<br />

Abbruchflag auf se<strong>in</strong>en Initialisierungswert zurück.<br />

Bsp. 114 :<br />

public class PR14240 extends Thread<br />

{<br />

<strong>in</strong>t zaehler = 0;<br />

public void run()<br />

{<br />

while (true)<br />

{<br />

if (isInterrupted())<br />

{<br />

break;<br />

}<br />

pr<strong>in</strong>tL<strong>in</strong>e(++zaehler);<br />

}<br />

}<br />

private void pr<strong>in</strong>tL<strong>in</strong>e(<strong>in</strong>t cnt)<br />

{<br />

//Zeile ausgeben<br />

System.out.pr<strong>in</strong>t(zaehler + ": ");<br />

for (<strong>in</strong>t i = 0; i < 30; ++i)<br />

{<br />

System.out.pr<strong>in</strong>t(i == zaehler % 30 ? "* " : ". ");<br />

}<br />

System.out.pr<strong>in</strong>tln();<br />

// 100 ms. warten<br />

try {<br />

Thread.sleep(100);<br />

}<br />

catch (InterruptedException e)<br />

{<br />

<strong>in</strong>terrupt();<br />

}<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

PR14240 th = new PR14240();<br />

{<br />

//Thread starten<br />

th.start();<br />

//2 Sekun<strong>de</strong>n warten<br />

try {<br />

Thread.sleep(2000);<br />

}<br />

catch (InterruptedException e) { }<br />

//Thread unterbrechen<br />

th.<strong>in</strong>terrupt();<br />

}<br />

}<br />

}<br />

Das Programm gibt zunächst über e<strong>in</strong>en separaten Thread zunächst Textzeilen aus.<br />

Wahrsche<strong>in</strong>lich erfolgt <strong>de</strong>r Aufruf von <strong>in</strong>terrupt() während <strong>de</strong>s Aufrufs von<br />

sleep(), da die Pause nach <strong>de</strong>r Bildschirmausgabe vermulich länger dauert als die<br />

Bildschirmausgabe. Ist dies <strong>de</strong>r Fall wird sleep() mit e<strong>in</strong>er<br />

InterruptedException abgebrochen. Das Abbruchflag wird zurückgesetzt und<br />

<strong>de</strong>r Aufruf von <strong>in</strong>terrupt() damit wirkungslos. Da aber <strong>in</strong> <strong>de</strong>r catch-Klausel<br />

114 vgl. pr14240<br />

99


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

<strong>in</strong>terrupt() erneut aufgerufen wird, wird das Flag wie<strong>de</strong>r gesetzt und run() die<br />

Abbruchauffor<strong>de</strong>rung signalisiert.<br />

public f<strong>in</strong>al void jo<strong>in</strong>() throws InterruptedException<br />

Mit Hilfe dieser Metho<strong>de</strong> kann gezielt auf die Beendigung e<strong>in</strong>es Thread gewartet<br />

wer<strong>de</strong>n. So etwas ist s<strong>in</strong>nvoll, wenn zeitaufwendige Operationen vor <strong>de</strong>m Aufruf<br />

e<strong>in</strong>er Folgeoperation noch been<strong>de</strong>t wer<strong>de</strong>n müssen. Der zeitaufwendigsten<br />

Operation ist e<strong>in</strong> eigener Thread zugeordnet.<br />

public static void yield()<br />

“Freiwilliges” Angebot <strong>de</strong>s laufen<strong>de</strong>n Thread, <strong>de</strong>n Prozessor abzugeben.<br />

Unterbrechen e<strong>in</strong>es Thread: Mit Hilfe <strong>de</strong>r Metho<strong>de</strong>n suspend bzw. resume ist es<br />

möglich e<strong>in</strong>en Thread vorübergehend zu unterbrechen bzw. fortzusetzen. Durch<br />

suspend() wird die Ausführung unterbrochen, durch resume() wird <strong>de</strong>r Thread<br />

(genauer: die Metho<strong>de</strong> run) an <strong>de</strong>r Stelle fortgesetzt, an <strong>de</strong>r die Unterbrechung<br />

erfolgte. Sowohl suspend() als auch resume() s<strong>in</strong>d aber <strong>de</strong>precated und sollten<br />

nicht mehr verwen<strong>de</strong>t wer<strong>de</strong>n.<br />

Verwaltung von Threads<br />

Threads besitzen adm<strong>in</strong>istrative Eigenschaften, die sich <strong>in</strong> zwei Gruppen e<strong>in</strong>teilen<br />

lassen:<br />

- Priorität und Name<br />

- Thread-Gruppen<br />

Priorität, Name. Je<strong>de</strong>r Thread hat e<strong>in</strong>en e<strong>in</strong><strong>de</strong>utigen Namen. Ohne explizite Vorgabe<br />

erfolgt die Vergabe <strong>de</strong>s Namens automatisch <strong>in</strong> <strong>de</strong>r Form “Thread-“+n , wobei n<br />

e<strong>in</strong> <strong>in</strong>t ist. Mit public void setName(Str<strong>in</strong>g name) kann e<strong>in</strong>em Thread e<strong>in</strong><br />

Name zugewiesen wer<strong>de</strong>n. Mit public Str<strong>in</strong>g getName() kann <strong>de</strong>r Name e<strong>in</strong>es<br />

Thread abgefragt wer<strong>de</strong>n. public static Thread currentThread() liefert<br />

<strong>de</strong>n Zugriff auf <strong>de</strong>n aktuellen Thread, <strong>de</strong>r die Metho<strong>de</strong> gera<strong>de</strong> ausführt, z.B.:<br />

public class ThreadTest<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

System.out.pr<strong>in</strong>tln("THREAD: " + Thread.currentThread() );<br />

}<br />

}<br />

/* Ausgabe am Bildschirm: THREAD: Thread[ma<strong>in</strong>,5,ma<strong>in</strong>]<br />

Die Ausgabe umfasst : Name, Priorität und Gruppe<br />

*/<br />

Neben <strong>de</strong>m Namen besitzt e<strong>in</strong> Thread noch e<strong>in</strong>e Priorität. Die Priorität steuert <strong>de</strong>r<br />

Scheduler <strong>in</strong> <strong>de</strong>r Weise, daß bei Vorhan<strong>de</strong>nse<strong>in</strong> mehrerer Threads diejenigen mit<br />

höherer Priorität vor <strong>de</strong>nen mit niedrigerer Priorität ausgeführt wer<strong>de</strong>n. Beim Anlegen<br />

e<strong>in</strong>es neuen Thread bekommt dieser die Priorität <strong>de</strong>s aktuellen Thread. Mit public<br />

void setPriority(<strong>in</strong>t neuePrioritaet) kann die neue Priorität gesetzt und<br />

mit public <strong>in</strong>t getPriority() abgefragt wer<strong>de</strong>n. Als Parameter kann an<br />

setPriority() e<strong>in</strong> Wert übergeben wer<strong>de</strong>n, <strong>de</strong>r zwischen <strong>de</strong>n Konstanten<br />

Thread.MIN_PRIORITY und Thread.MAX_PRIORITY liegt. <strong>Java</strong> bietet 10<br />

100


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Prioritätsstufen zwischen 1 (Thread.MIN_PRIORITY) und 10<br />

(Thread.MAX_PRIORITY) an. Der Normalwert wird durch die Konstante<br />

Thread.NORM_PRIORITY festgelegt 115 .<br />

Thread-Gruppen. Sie verwalten Informationen für e<strong>in</strong>e ganze Gruppe und bieten<br />

Metho<strong>de</strong>n an, die auf alle Threads e<strong>in</strong>er Gruppe angewen<strong>de</strong>t wer<strong>de</strong>n können. In <strong>de</strong>r<br />

Klassenbibliothek gibt es dafür e<strong>in</strong>e eigen<strong>de</strong> ThreadGroup.<br />

ThreadGroup<br />

><br />

public ThreadGroup(Str<strong>in</strong>g name)<br />

public ThreadGroup(ThreadGroup parent, Str<strong>in</strong>g name)<br />

><br />

public f<strong>in</strong>al Str<strong>in</strong>g getName();<br />

public f<strong>in</strong>al ThreadGroup getParent()<br />

public <strong>in</strong>t activeCount()<br />

public <strong>in</strong>t activeGroupCount()<br />

public <strong>in</strong>t enumerate(Thread[] list)<br />

public enumerate(ThreadGroup[] list)<br />

public void <strong>in</strong>terrupt();<br />

public boolean isDaemon()<br />

public void setDaemon(boolean daemon)<br />

public f<strong>in</strong>al <strong>in</strong>t getMaxPriority()<br />

public f<strong>in</strong>al void setMaxPriority()<br />

Abb.: Die Klasse TheadGroup<br />

Die Klasse ThreadGroup stellt zur Verwaltung mehrerer Threads Metho<strong>de</strong>n bereit.<br />

Sie besitzt e<strong>in</strong>e Default-Priorität, kann alle Threads anhalten und kann sie als<br />

Daemons ablaufen lassen. Threads wer<strong>de</strong>n durch Konstruktoren e<strong>in</strong>er Gruppe<br />

zugeordnet. Thread-Gruppen können auch <strong>in</strong>e<strong>in</strong>an<strong>de</strong>r verschachtelt wer<strong>de</strong>n.<br />

Daemon-Threads. Es gibt Aufgaben, die zur Laufzeit <strong>de</strong>s Programms zu erledigen<br />

s<strong>in</strong>d, aber nie term<strong>in</strong>ieren, z.B. <strong>de</strong>r Garbage Collector. Da die virtuelle Masch<strong>in</strong>e erst<br />

been<strong>de</strong>t wird, wenn ke<strong>in</strong> <strong>Java</strong>-Thread mehr aktiv ist, müssen am En<strong>de</strong> <strong>de</strong>r<br />

Anwendung alle Threads angehalten wer<strong>de</strong>n. Es wäre aber lästig (und manchmal<br />

schwierig), die H<strong>in</strong>tergrund-Threads explizit zu been<strong>de</strong>n. Hierfür existiert das<br />

Konzept <strong>de</strong>r Daemon-Threads. Da sich die virtuelle Masch<strong>in</strong>e been<strong>de</strong>t, wenn nur<br />

noch Daemon-Threads laufen, brauch die Beendigung dieser Threads nicht weiter<br />

berücksichtigt wer<strong>de</strong>n. E<strong>in</strong> Thread ist e<strong>in</strong> Daemon, wenn ihm das durch<br />

public f<strong>in</strong>al void setDaemon(boolean on)<br />

mitgeteilt wur<strong>de</strong>. Die Metho<strong>de</strong><br />

public f<strong>in</strong>al boolean isDaemon()<br />

gibt Auskunft über die Art e<strong>in</strong>es Thread.<br />

115 im aktuellen JDK 1, 5, 10<br />

101


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

1.4.2.4.2 Das Interface Runnable<br />

Nicht immer ist es möglich, e<strong>in</strong>e Klasse, die als Thread laufen soll, von Thread<br />

abzuleiten. E<strong>in</strong> Thread kann <strong>de</strong>shalb auch über Implementierung <strong>de</strong>s Interface<br />

Runnable erzeugt wer<strong>de</strong>n. Runnable enthält nur e<strong>in</strong>e e<strong>in</strong>zige Deklaration nämlich<br />

die <strong>de</strong>r Metho<strong>de</strong> run:<br />

public <strong>in</strong>terface Runnable<br />

{<br />

public abstract void run();<br />

}<br />

Implementierung von Runnable: Je<strong>de</strong> Klasse, <strong>de</strong>ren Instanzen als Thread laufen,<br />

muß das Interface Runnable implementieren 116 . E<strong>in</strong>e nicht von Thread abgeleitete<br />

Instanz kann nach folgen<strong>de</strong>n Implementierungsschritten als Thread laufen:<br />

- Erzeugen e<strong>in</strong>es neuen Thread-Objekts<br />

- Übergabe <strong>de</strong>s Objekts, das parallel ausgeführt wer<strong>de</strong>n soll, an <strong>de</strong>n Konstruktor<br />

- Aufruf <strong>de</strong>r Metho<strong>de</strong> start() <strong>de</strong>s Thread-Objekts<br />

Über start() startet das Thread-Objekt die run-Meto<strong>de</strong> <strong>de</strong>s übergebenen Objekts<br />

(das sie durch die Übergabe an <strong>de</strong>n Konstruktor kennt). Da dieses Objekt das<br />

Interface Runnable implementiert, ist garantiert, daß e<strong>in</strong>e geeignete Metho<strong>de</strong> run<br />

zur Verfügung steht.<br />

Bsp. 117 : ThreadDemo<br />

Thread<br />

MAX_PRIORITY<br />

….<br />

Runnable<br />

><br />

sleep()<br />

run()<br />

start()<br />

jo<strong>in</strong>()<br />

….<br />

ErsterThread<br />

ZweiterThread<br />

run() runs run()<br />

class ErsterThread extends Thread<br />

{<br />

public void run()<br />

{<br />

for (<strong>in</strong>t i = 0; i < 10; i++)<br />

try {<br />

Thread.sleep( Math.round(1000 * Math.random()) );<br />

System.out.pr<strong>in</strong>tln(this + " " + i);<br />

}<br />

116 sogar die Klasse Thread selbst<br />

117 vgl. pr14240<br />

102


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

catch (InterruptedException e)<br />

{ System.err.pr<strong>in</strong>tln(e); }<br />

}<br />

}<br />

class ZweiterThread implements Runnable<br />

{<br />

public void run()<br />

{<br />

for (<strong>in</strong>t i = 0; i < 10; i++)<br />

try {<br />

Thread.sleep( Math.round(1000 * Math.random()) );<br />

System.out.pr<strong>in</strong>tln(Thread.currentThread().toStr<strong>in</strong>g() + " " + i);<br />

}<br />

catch (InterruptedException e)<br />

{ System.err.pr<strong>in</strong>tln(e); }<br />

}<br />

}<br />

public class ThreadDemo<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g [] args)<br />

{<br />

ErsterThread thread1 = new ErsterThread();<br />

thread1.start();<br />

Thread thread2 = new Thread(new ZweiterThread());<br />

thread2.start();<br />

try {<br />

thread1.jo<strong>in</strong>();<br />

thread2.jo<strong>in</strong>();<br />

}<br />

catch (InterruptedException e)<br />

{ System.err.pr<strong>in</strong>tln(e); }<br />

}<br />

}<br />

Abb.: Def<strong>in</strong>ition von Threads<br />

Die Klasse Thread implementiert Runnable. Dort ist die Metho<strong>de</strong> run() <strong>de</strong>klariert. Mit <strong>de</strong>r Metho<strong>de</strong><br />

start() wird e<strong>in</strong> Thread gestartet, <strong>in</strong> <strong>de</strong>m run() aufgerufen wird. „jo<strong>in</strong>()“ wartet auf die Beendigung<br />

<strong>de</strong>s Thread.<br />

ErsterThread ist Subklasse von Thread. „run()“ von Thread wird überschrieben.<br />

„sleep()“ legt e<strong>in</strong>en Thread für e<strong>in</strong>e bestimmte Zeit (<strong>in</strong> Millisekun<strong>de</strong>n) schlafen. Hier wird zufällig<br />

e<strong>in</strong>e Zeitspanne zwischen 0 und 1000 ms gewartet und dann <strong>de</strong>r Scheifenzählerwert ausgegeben.<br />

103


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

ma<strong>in</strong><br />

start thread2<br />

start thread1<br />

jo<strong>in</strong> thread2<br />

jo<strong>in</strong> thread1<br />

thread1<br />

thread2<br />

Zeit<br />

Abb.: Zusammenspiel von Threads <strong>in</strong> ThreadDemo<br />

ZweiterThread implementiert Runnable. Hier muß explizit e<strong>in</strong>e Instanz von Thread erzeugt<br />

wer<strong>de</strong>n.<br />

Die Instanz von ZweiterThread wird e<strong>in</strong>em Thread-Objekt übergeben, <strong>de</strong>r die run()-Metho<strong>de</strong><br />

betreibt.<br />

ErsterThread kann sich selber schlafen legen. ZweiterThread legt die benutzte Instanz von Thread<br />

schlafen.<br />

Bei Programmstart existiert immer automatisch e<strong>in</strong> ErsterThread, <strong>de</strong>ssen Ausführung <strong>in</strong> <strong>de</strong>r<br />

ma<strong>in</strong>()-Metho<strong>de</strong> beg<strong>in</strong>nt. Dieser ma<strong>in</strong>-Thread startet nun zwei weitere Threads und wartet (jo<strong>in</strong>())<br />

auf das En<strong>de</strong> dieser Threads.<br />

Bei je<strong>de</strong>m Ablauf ergibt sich nicht-<strong>de</strong>term<strong>in</strong>istisch e<strong>in</strong>e an<strong>de</strong>re Reihenfolge <strong>de</strong>r Ausgaben<br />

1.4.2.4.3. Prozesse und Threads <strong>in</strong> <strong>Java</strong><br />

E<strong>in</strong> <strong>Java</strong>-Programm besitzt <strong>in</strong> <strong>de</strong>r Regel e<strong>in</strong>e Klasse mit e<strong>in</strong>er statischen Metho<strong>de</strong><br />

ma<strong>in</strong>().<br />

Bei je<strong>de</strong>m Start e<strong>in</strong>es <strong>Java</strong>-Programms erzeugt das Betriebssystem e<strong>in</strong>en Prozeß<br />

und startet die virtuelle Masch<strong>in</strong>e (VM). Die VM erzeugt e<strong>in</strong>en Haupt-Thread (ma<strong>in</strong><br />

thread) auch Programmfa<strong>de</strong>n, Kontrollfluß o<strong>de</strong>r leichtgewichtiger Prozeß genannrt.<br />

Der Ma<strong>in</strong>-Thread führt daraufh<strong>in</strong> die Metho<strong>de</strong> ma<strong>in</strong>() aus.<br />

E<strong>in</strong>e Metho<strong>de</strong> im Quelltext besteht aus e<strong>in</strong>er Sequenz von Anweisungen. Beim<br />

Übersetzen <strong>de</strong>s Quelltextes, wird je<strong>de</strong> Anweisung <strong>in</strong> mehrere elementare<br />

Teilanweisungen (byte co<strong>de</strong>s) für die VM zerlegt. E<strong>in</strong> <strong>Java</strong>-Thread führt diese<br />

e<strong>in</strong>zelnen elementaren Anweisungen e<strong>in</strong>er Metho<strong>de</strong> (z.B. ma<strong>in</strong>()) und die<br />

Anweisungen <strong>de</strong>r dar<strong>in</strong> enthaltenen Metho<strong>de</strong>n squentiell aus.<br />

<strong>Java</strong>-Prozesse wer<strong>de</strong>n auf Betriebssystem-Prozesse abgebil<strong>de</strong>t, d.h.: Auch <strong>Java</strong>-<br />

Threads können auf Betriebssystem-Threads abgebil<strong>de</strong>t wer<strong>de</strong>n. Man spricht von<br />

e<strong>in</strong>er virtuellen Masch<strong>in</strong>e, die native Threads unterstützt.<br />

104


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Anwendung_1<br />

Betriebssystem<br />

Anwendung_2<br />

<strong>Java</strong>_VM_1<br />

<strong>Java</strong>_VM_2<br />

VM Prozess_1 lok. Variable<br />

Prozess_2 lok. Variable<br />

glob.<br />

Variable Prozess_3 lok. Variable<br />

Abb. Betriebssystem - VM<br />

Die Abbildung <strong>de</strong>r <strong>Java</strong>-Prozesse auf BS-Prozesse hängt von <strong>de</strong>r Implementierung<br />

<strong>de</strong>r VM ab. Es gibt Implementierungen<br />

- die das Schedul<strong>in</strong>g <strong>de</strong>r VM-Prozesse übernehmen<br />

- die das Schedul<strong>in</strong>g <strong>de</strong>m darunterliegen<strong>de</strong>n BS überlassen<br />

"schedul<strong>in</strong>g strategies". Threads benötigen für ihre Ausführung e<strong>in</strong>en Prozessor.<br />

Viele <strong>Java</strong>-Anwendungen unterstützen nur e<strong>in</strong>en Prozessor. Bei nur e<strong>in</strong>em<br />

Prozessor kann nur e<strong>in</strong> Thread aktiv se<strong>in</strong>. Der Thread wird <strong>de</strong>m Prozessor zugeteilt.<br />

Man spricht <strong>in</strong> diesem Zusammenhang von Schedul<strong>in</strong>g. Die Zuteilungsstrategie<br />

bee<strong>in</strong>flusst das Verhalten <strong>de</strong>s Systems bei mehreren Threads. Folgen<strong>de</strong> Zustän<strong>de</strong><br />

e<strong>in</strong>es Thread können unterschie<strong>de</strong>n wer<strong>de</strong>n:<br />

- Initialzustand<br />

Der Thread wur<strong>de</strong> erzeugt, führt aber die noch ke<strong>in</strong>e Metho<strong>de</strong> aus, d.h. start() wur<strong>de</strong> noch nicht<br />

aufgerufen.<br />

- Lauffähig<br />

E<strong>in</strong> Thread geht vom Initialzustand mit start() <strong>in</strong> <strong>de</strong>n Zustand "lauffähig" über.<br />

- Aktiv<br />

E<strong>in</strong> Thread wird aktiv, wenn er von <strong>de</strong>r VM aus <strong>de</strong>r Menge <strong>de</strong>r lauffähigen Threads ausgewählt wird.<br />

In diesem Zustand führt <strong>de</strong>r Thread tatsächlich Co<strong>de</strong> aus. Der Thread wird von <strong>de</strong>r VM dazu entwe<strong>de</strong>r<br />

<strong>in</strong> <strong>de</strong>n Zustand lauffähig zurückgesetzt o<strong>de</strong>r er kommt <strong>in</strong> <strong>de</strong>n Zustand "blockiert".<br />

- Blockiert<br />

E<strong>in</strong> Thread ist blockiert, wenn er auf <strong>de</strong>n exklusiven Zugriff auf e<strong>in</strong>es bei synchronized<br />

angegebenen Objekts o<strong>de</strong>r auf das Ergebnis mit wait() wartet.<br />

Ist <strong>de</strong>r Thread blockiert, dann geht er <strong>in</strong> <strong>de</strong>n Zustand "lauffähig" über, falls e<strong>in</strong> an<strong>de</strong>rer Thread mit<br />

e<strong>in</strong>em notify() o<strong>de</strong>r notifyAll() das Ergebnis mel<strong>de</strong>t und daraufh<strong>in</strong> die VM <strong>de</strong>s blockierten<br />

Threads auswählt.<br />

- Endzustand.<br />

Der Thread ist <strong>in</strong> se<strong>in</strong>em Endzustand, falls run() ausgeführt wur<strong>de</strong> o<strong>de</strong>r mit e<strong>in</strong>er Ausnahme<br />

been<strong>de</strong>t wur<strong>de</strong>.<br />

105


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Auswahl durch VM<br />

nach notify()<br />

start() lauffähig blockiert<br />

notify(), notifyAll()<br />

Initial run() En<strong>de</strong><br />

sleep() o<strong>de</strong>r<br />

Auswahl durch VM<br />

<strong>in</strong> synchronized()<br />

aktiv warten auf Zugriff<br />

En<strong>de</strong> von run() o<strong>de</strong>r unbehan<strong>de</strong>lte<br />

Ausnahme<br />

Abb.: Automatendarstellung <strong>de</strong>r Übergänge zwischen <strong>de</strong>n Zustän<strong>de</strong>n e<strong>in</strong>es Thread<br />

1.4.2.4.4 Synchronisation nebenläufiger Prozesse<br />

E<strong>in</strong>führen<strong>de</strong>s Beispiel. Mehrere Threads mit geme<strong>in</strong>samer Variablen.<br />

public class OhneSynchroTest extends Thread<br />

{<br />

static <strong>in</strong>t zaehler = 0;<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args [])<br />

{<br />

Thread t1 = new OhneSynchroTest();<br />

Thread t2 = new OhneSynchroTest();<br />

t1.start();<br />

t2.start();<br />

}<br />

static void schreibe()<br />

{<br />

System.out.pr<strong>in</strong>t(zaehler + " ");<br />

zaehler++;<br />

if (zaehler > 25) System.exit(0);<br />

}<br />

public void run()<br />

{<br />

while (true) schreibe();<br />

}<br />

}<br />

Die bei<strong>de</strong>n Threads versuchen <strong>de</strong>n Zählerstand zu <strong>in</strong>krementieren. Ziel ist, dass bei<strong>de</strong> geme<strong>in</strong>sam<br />

zaehler von 0 auf 25 hochzählen.<br />

Auszüge aus e<strong>in</strong>er möglichen Ausgabe:<br />

Abb.: Mögliche Ausgabe vom nicht synchronisierten Programm OhneSynchroTest<br />

Die Ausgabe von OhneSynchroTest ist nicht plausibel.<br />

106


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Offensichtlich ist es notwendig, bei<strong>de</strong> Threads zu koord<strong>in</strong>ieren. Sie müssen so<br />

synchronisiert wer<strong>de</strong>n, dass nur e<strong>in</strong> Thread das Prüfen <strong>de</strong>r Bed<strong>in</strong>gung und das<br />

Inkrementieren atomar, d.h. vollständig ohne Unterbrechung ausführt.<br />

H<strong>in</strong>weis. Wie immer bei fehlen<strong>de</strong>r Synchronisation muß <strong>de</strong>r im Beispiel gezeigte<br />

Fehler nicht zw<strong>in</strong>gend auftreten und kann lange Zeit völlig unbemerkt bleiben. Je<br />

nach<strong>de</strong>m, wann die Threads aktiv s<strong>in</strong>d bzw. bei e<strong>in</strong>em Prozessor sich abwechseln,<br />

ergeben sich unterschiedliche Resultate 118 .<br />

Synchronisation paralleler Prozesse <strong>in</strong> <strong>Java</strong>. Mehrere Threads können nur über<br />

geme<strong>in</strong>same Variable kommunizieren. Folgen<strong>de</strong> Konflikte können dabei auftreten:<br />

- Gleichzeitiges Lesen und Schreiben<br />

E<strong>in</strong> Thread liest und e<strong>in</strong> an<strong>de</strong>rer än<strong>de</strong>rt gera<strong>de</strong> das Objekt, ist aber noch nicht fertig.<br />

- Gleichzeitiges Schreiben<br />

Mehrere Threads versuchen das Objekt gleichzeitig zu än<strong>de</strong>rn.<br />

Rufen mehrere Threads Metho<strong>de</strong>n gleichzeitig auf, dann kann ohne Synchronisation<br />

e<strong>in</strong> Zwischenzustand e<strong>in</strong>er Metho<strong>de</strong> <strong>de</strong>n Ablauf e<strong>in</strong>er an<strong>de</strong>ren Metho<strong>de</strong><br />

bee<strong>in</strong>flussen. Zur Vermeidung von Konflikten gibt es mehrere Möglichkeiten:<br />

- Schreiben nicht zulassen<br />

E<strong>in</strong>e Klasse wird so <strong>de</strong>f<strong>in</strong>iert, dass e<strong>in</strong>e Än<strong>de</strong>rung <strong>de</strong>r Attribute nach <strong>de</strong>m Erzeugen von Objekten<br />

nicht mehr möglich ist (immutable class). Bei <strong>Java</strong> können bspw. mehrere Threads problemlos<br />

auf e<strong>in</strong> gleiches Objekt <strong>de</strong>r Klasse java.lang.Str<strong>in</strong>g zugreifen, da e<strong>in</strong> Str<strong>in</strong>g nicht mehr<br />

geän<strong>de</strong>rt wer<strong>de</strong>n kann. Je<strong>de</strong> Metho<strong>de</strong> <strong>de</strong>r Klasse produziert bei e<strong>in</strong>er Än<strong>de</strong>rung <strong>de</strong>s Werts e<strong>in</strong><br />

neues Str<strong>in</strong>g Objekt. 119<br />

- Lesen und Schreiben synchronisieren<br />

Für e<strong>in</strong>en Bereich, <strong>in</strong> <strong>de</strong>n geme<strong>in</strong>same Variable geschrieben o<strong>de</strong>r gelesen wer<strong>de</strong>n, benötigt je<strong>de</strong>r<br />

Thread e<strong>in</strong>e Sperre bevor er diesen Bereich ausführt. Danach gibt er die Sperre wie<strong>de</strong>r frei. Mit<br />

diesem gegenseitigen Ausschluß (mutually exclusive lock) lassen sich Zugriffe auf Objekte so<br />

koord<strong>in</strong>ieren, dass Konflikte vermie<strong>de</strong>n wer<strong>de</strong>n.<br />

In <strong>Java</strong> müssen alle Bereiche, <strong>in</strong> <strong>de</strong>m geme<strong>in</strong>same Variable geschrieben o<strong>de</strong>r<br />

gelesen wer<strong>de</strong>n, explizit mit <strong>de</strong>m Schlüsselwort synchronized zur Vermeidung von<br />

Konflikten versehen wer<strong>de</strong>n.<br />

synchronized(O)<br />

{<br />

S<br />

}<br />

O ist e<strong>in</strong> beliebiger Ausdruck, <strong>de</strong>r e<strong>in</strong> <strong>Java</strong>-Objekt liefert, und S e<strong>in</strong>e Menge von Anweisungen.<br />

Erreicht e<strong>in</strong> Thread die synchronized Anweisung, dann gelten folgen<strong>de</strong> Regeln:<br />

- Der Thread versucht exklusiven Zugriff auf das Objekt O zu erhalten.<br />

- Wenn ke<strong>in</strong> an<strong>de</strong>rer Thread <strong>de</strong>n exklusiven Zugriff auf das Objekt hat, gel<strong>in</strong>g dies.<br />

- Besitzt e<strong>in</strong> an<strong>de</strong>rer Thread <strong>de</strong>n exklusiven Zugriff auf das Objekt, dann wartet <strong>de</strong>r Thread, bis das<br />

Objekt O wie<strong>de</strong>r freigegeben wird. Es können mehrere Threads auf die Freigabe warten.<br />

- Erhält e<strong>in</strong> Thread <strong>de</strong>n exklusiven Zugriff auf O, dann tritt er <strong>in</strong> <strong>de</strong>n Anweisungsblock e<strong>in</strong>.<br />

- Verläßt <strong>de</strong>r Thread <strong>de</strong>n Anweisungsblock, <strong>in</strong> <strong>de</strong>m er <strong>de</strong>n exklusiven Zugriff erhalten hat, dann gibt er<br />

das Objekt wie<strong>de</strong>r frei.<br />

118 Dies macht es extrem schwierig <strong>in</strong> e<strong>in</strong>em fertigen System fehlerhafte Co<strong>de</strong>-Stellen zu f<strong>in</strong><strong>de</strong>n. Vor allem kann<br />

alle<strong>in</strong> das Suchen <strong>de</strong>s Fehlers mit Hilfe vom Debugger <strong>de</strong>n Fehler verschw<strong>in</strong><strong>de</strong>n lassen. Durch das Beobachten<br />

mit <strong>de</strong>m Debugger wird das Abwechseln von Threads bee<strong>in</strong>flusst.<br />

119 Für e<strong>in</strong>e performante Än<strong>de</strong>rung e<strong>in</strong>es Str<strong>in</strong>g steht die Klasse java.lang.Str<strong>in</strong>gBuffer zur<br />

Verfügung, diese ist aber nicht sicher für die gleichzeitige Verwendung durch mehrere Threads (thread safe).<br />

107


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Wenn e<strong>in</strong>e Ausnahme (exception) auftritt, die nicht <strong>in</strong>nerhalb e<strong>in</strong>es<br />

Anweisungsblocks abgefangen wird, dann wird <strong>de</strong>r Ausnahmeblock automatisch<br />

verlassen und dabei auch die Sperre wie<strong>de</strong>r freigegeben.<br />

Verschachteln von synchronisierten Anweisungsblöcken ist möglich, auch wenn<br />

mehrmals mit <strong>de</strong>m gleichen Objekt synchronisiert wird. Erst beim Verlassen <strong>de</strong>s<br />

äußeren Anweisungsblocks wird das Objekt freigegeben.<br />

Kritischer Bereich. In das letzte Beispiel kann zum Synchronisieren das<br />

Schlüsselwort synchronized e<strong>in</strong>gesetzt wer<strong>de</strong>n. Prüfung und Inkrementierung<br />

stellen e<strong>in</strong>en kritischen Bereich dar. Das ist e<strong>in</strong> Bereich, <strong>in</strong> <strong>de</strong>m Konflikte auftreten<br />

können.<br />

Den kritischen Bereich kann man auch <strong>in</strong> e<strong>in</strong>er eigenen Metho<strong>de</strong> zusammen fassen.<br />

public class SynchroTest extends Thread<br />

{<br />

static <strong>in</strong>t zaehler = 0;<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args [])<br />

{<br />

Thread t1 = new SynchroTest();<br />

Thread t2 = new SynchroTest();<br />

t1.start();<br />

t2.start();<br />

}<br />

static synchronized void schreibe()<br />

{<br />

}<br />

System.out.pr<strong>in</strong>t(zaehler + " ");<br />

zaehler++;<br />

if (zaehler > 25) System.exit(0);<br />

}<br />

public void run()<br />

{<br />

while (true) schreibe();<br />

}<br />

Abb.: Mögliche Ausgabe von OhneSynchroTest bzw. SynchroTest<br />

Es wird gewährleistet, dass nur e<strong>in</strong> Thread gleichzeitig <strong>in</strong> genau dieser Metho<strong>de</strong> aktiv<br />

se<strong>in</strong> kann. Es wird nicht gewährleistet, dass ke<strong>in</strong> an<strong>de</strong>rer Thread gleichzeitig <strong>in</strong> e<strong>in</strong>er<br />

an<strong>de</strong>ren Metho<strong>de</strong> aktiv ist, d. h. zaehler kann <strong>in</strong> e<strong>in</strong>er an<strong>de</strong>ren Metho<strong>de</strong> weiterh<strong>in</strong><br />

gleichzeitig geän<strong>de</strong>rt wer<strong>de</strong>n. Das be<strong>de</strong>u<strong>de</strong>t auch:<br />

"Lesen<strong>de</strong> Metho<strong>de</strong>n müssen synchronisiert wer<strong>de</strong>n".<br />

Bsp.: E<strong>in</strong>führen <strong>de</strong>r Metho<strong>de</strong> getZaehler(), die <strong>de</strong>n Zählerstand zurückliefert<br />

static long getZaehler()<br />

{<br />

return zaehler;<br />

}<br />

108


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Die Metho<strong>de</strong> getZaehler() kann aus folgen<strong>de</strong>n Grün<strong>de</strong>n e<strong>in</strong> fehlerhaftes Ergebnis haben:<br />

- getZaehler() kann ausgeführt wer<strong>de</strong>n, während irgen<strong>de</strong><strong>in</strong>e an<strong>de</strong>re auch synchronisierte<br />

Metho<strong>de</strong> (z.B. <strong>in</strong>krementieren()) aus e<strong>in</strong>er Subklasse heraus ausgeführt wird. Diese an<strong>de</strong>re<br />

Metho<strong>de</strong> könnte zaehler zwischenzeitlich auf –1 setzen und erst am En<strong>de</strong> e<strong>in</strong>en korrekten Wert<br />

zuweisen. Die Metho<strong>de</strong> getZaehler() könnte zwischenzeitlich <strong>de</strong>n uns<strong>in</strong>nigen Wert –1 liefern.<br />

- Auch wenn ke<strong>in</strong>er <strong>de</strong>n Wert zwischenzeitlich auf –1 setzt, kann getZaehler() e<strong>in</strong>en falschen Wert<br />

liefern. <strong>Java</strong> garantiert, dass alle Operationen auf primitiven Typen atomar ausgeführt wer<strong>de</strong>n,<br />

außer für double und long. Das be<strong>de</strong>utet: Die Ausführung von ++zaehler könnte <strong>in</strong> zwei<br />

Schritten ablaufen. Zwischen <strong>de</strong>n bei<strong>de</strong>n Schritten könnte e<strong>in</strong> Schrittwechsel auftreten. Der Wert<br />

von zaehler nach <strong>de</strong>m 1. Schritt ist nicht spezifiziert.<br />

Aus diesen Grün<strong>de</strong>n muß getZaehler() synchronisiert wer<strong>de</strong>n:<br />

static synchronized long getZaehler()<br />

{<br />

return zaehler;<br />

}<br />

Synchronisierte Metho<strong>de</strong>n und Vererbung. Die Synchronisation gehört <strong>in</strong> <strong>Java</strong> nur<br />

zur Implementierung und wird nicht vererbt. Daher kann auch e<strong>in</strong>e Interface-<br />

Def<strong>in</strong>ition nicht das Schlüsselwort synchronized enthalten.<br />

Synchronisation statischer Metho<strong>de</strong>n. In <strong>Java</strong> gehört zu je<strong>de</strong>r Klasse e<strong>in</strong>e, vom<br />

System erzeugte Instanz <strong>de</strong>r Klasse java.lang.Class. Diese Instanz beschreibt<br />

<strong>de</strong>n Aufbau <strong>de</strong>r Klasse und macht dies zur Laufzeit verfügbar. Der Klassenname<br />

gefogt von ".class" liefert die Instanz. Wenn e<strong>in</strong>e statische Metho<strong>de</strong> als<br />

synchronized gekennzeichnet ist, entspricht dies e<strong>in</strong>er Synchronisierung mit <strong>de</strong>r<br />

zu dieser Instanz gehören<strong>de</strong>n Instanz <strong>de</strong>r Klasse java.lang.Class 120 .<br />

Bsp.:<br />

public class Zaehler<br />

{<br />

private static long maximum = 1000;<br />

public static synchronized long getMaximum()<br />

{<br />

return maximum;<br />

}<br />

…<br />

}<br />

Die Metho<strong>de</strong> getMaximum() entspricht folgen<strong>de</strong>r Implementierung:<br />

public static long getMaximum()<br />

{<br />

synchronized(Zaehler.class)<br />

{<br />

return maximum;<br />

}<br />

}<br />

Bewachte kritische Bereiche. Bisher konnte e<strong>in</strong> Thread nur auf e<strong>in</strong>e e<strong>in</strong>zige<br />

Bed<strong>in</strong>gung warten und zwar darauf, ob e<strong>in</strong> Zugriff zur Verfügung steht o<strong>de</strong>r nicht.<br />

Manchmal möchte man komplexere zustandabhängige Bed<strong>in</strong>gungen formulieren, auf<br />

<strong>de</strong>ren Erfüllung man wartet. Das könnte man so beschreiben: await E then S<br />

endwait, wobei E e<strong>in</strong> boolscher Ausdruck (Wächter) und S e<strong>in</strong>e Menge von<br />

120 vgl. 1.4.2.2<br />

109


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Anweisungen ist. Sobald E erfüllt ist, wird S exklusiv von e<strong>in</strong>em e<strong>in</strong>zigen Thread<br />

ausgeführt.<br />

public class ClassMitGur<strong>de</strong>dSuspension<br />

{<br />

…<br />

private synchronized void awaitConditionE()<br />

{<br />

while (!E)<br />

{<br />

try {<br />

wait();<br />

}<br />

catch (InterruptedException ex) { }<br />

}<br />

}<br />

public void synchronized guar<strong>de</strong>dStatementS<br />

{<br />

awaitConditionE();<br />

S<br />

}<br />

}<br />

Die folgen<strong>de</strong>n Metho<strong>de</strong>n <strong>de</strong>r Klasse Object übernehmen "Wächter"-Funktionen:<br />

public f<strong>in</strong>al void wait()<br />

throws InterruptedException<br />

public f<strong>in</strong>al void wait(long timeout)<br />

throws InterruptedException<br />

public f<strong>in</strong>al void wait(long timeout,<strong>in</strong>t nanos) throws InterruptedException<br />

- Der aktuelle Thread wird auf wartend gesetzt und <strong>in</strong> e<strong>in</strong>en mit <strong>de</strong>m Objekt verknüpften Wartebereich<br />

gestellt<br />

- Der exklusive Zugriff, <strong>de</strong>n <strong>de</strong>r Thread hat, wird freigegeben. Das ist Voraussetzung dafür, dass<br />

an<strong>de</strong>re Threads überhaupt notify() bzw. notifyAll() aufrufen können.<br />

- Es existiert noch die Möglichkeit bei wait() die maximale Wartezeit anzugeben. Ist diese<br />

verstrichen, dann wird automatisch für diesen Thread wie bei notify() verfahren.<br />

public f<strong>in</strong>al void notify()<br />

- Wenn warten<strong>de</strong> Threads existieren, dann wird nicht<strong>de</strong>term<strong>in</strong>istisch e<strong>in</strong>er ausgewählt und dieser aus<br />

<strong>de</strong>m Wartebereich entfernt<br />

- Der ausgewählte Thread versucht Zugriff auf das Objekt zu bekommen. Der ausgewählte Thread<br />

kann <strong>de</strong>n Zugriff nur dann bekommen, wenn <strong>de</strong>r Thread <strong>de</strong>r notify() aufgerufen hat und noch im<br />

Besitz <strong>de</strong>s Zugriffs, diesen freigibt, und ke<strong>in</strong> an<strong>de</strong>rer Thread <strong>de</strong>n Zugriff erhält.<br />

- Erhält <strong>de</strong>r ausgewählte Thread später <strong>de</strong>n Zugriff, dann setzt er die Ausführung mit <strong>de</strong>m auf wait()<br />

folgen<strong>de</strong>n Co<strong>de</strong> fort.<br />

public f<strong>in</strong>al void notifyAll()<br />

- Verläuft wie notify() mit <strong>de</strong>m Unterschied, daß alle warten<strong>de</strong>n Threads aus <strong>de</strong>m Wartebereich<br />

entfernt wer<strong>de</strong>n und wie<strong>de</strong>r versuchen die Sperre zu bekommen.<br />

Nur wenn e<strong>in</strong> Thread <strong>de</strong>n exklusiven Zugriff auf e<strong>in</strong> Objekt hat, kann er <strong>de</strong>ssen<br />

wait(), notify(), o<strong>de</strong>r notifyAll() aufrufen. Ansonsten wird e<strong>in</strong>e<br />

IllegalMonitorStateException vom Laufzeitsystem erzeugt.<br />

Wenn e<strong>in</strong>e Unterbrechung während e<strong>in</strong>es wait() passiert (z.B. durch Aufruf von<br />

<strong>in</strong>terrupt() <strong>de</strong>r Klasse Thread, dann wird wie bei notify() verfahren. Die<br />

Ausführung wird aber fortgefahren mit <strong>de</strong>m Werfen e<strong>in</strong>er Ausnahme <strong>de</strong>r Klasse<br />

InterruptedException.<br />

Das Ziel ist es mit wait() auf e<strong>in</strong> Ereignis zu warten. Das Ereignis wird durch das<br />

Objekt o<strong>de</strong>r e<strong>in</strong>en Zustand <strong>de</strong>s Objekts repräsentiert. Mit notify() wird dann das<br />

E<strong>in</strong>treten <strong>de</strong>s Ereignisses mitgeteilt.<br />

Das Monitor-Konzept <strong>in</strong> <strong>Java</strong>. Zur Synchronisation nebenläufiger Prozesse hat <strong>Java</strong><br />

das Konzept <strong>de</strong>s Monitors implementiert. E<strong>in</strong> Monitor ist die Kapselung e<strong>in</strong>es<br />

kritischen Bereichs (also e<strong>in</strong>es Programmteils, <strong>de</strong>r nur von jeweils e<strong>in</strong>em Prozeß zur<br />

110


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

aktuellen Zeit durchlaufen wer<strong>de</strong>n darf) mit Hilfe e<strong>in</strong>er automatisch gesetzten Sperre.<br />

Die Sperre wird beim E<strong>in</strong>tritt <strong>in</strong> <strong>de</strong>n Monitor gesetzt und beim Verlassen wie<strong>de</strong>r<br />

zurückgenommen. Ist sie beim E<strong>in</strong>tritt <strong>in</strong> <strong>de</strong>n Monitor bereits von e<strong>in</strong>em an<strong>de</strong>ren<br />

Prozeß gesetzt, dann muß <strong>de</strong>r aktuelle Prozeß warten, bis <strong>de</strong>r Konkurent die Sperre<br />

freigegeben und <strong>de</strong>n Monitor verlassen hat.<br />

Das Monitor-Konzept wird mit Hilfe <strong>de</strong>s Schlüsselworts synchronized realisiert.<br />

Durch synchronized kann entwe<strong>de</strong>r e<strong>in</strong>e komplette Metho<strong>de</strong> o<strong>de</strong>r e<strong>in</strong> Block<br />

<strong>in</strong>nerhalb e<strong>in</strong>er Metho<strong>de</strong> geschützt wer<strong>de</strong>n. Der E<strong>in</strong>tritt <strong>in</strong> <strong>de</strong>n so <strong>de</strong>klarierten Monitor<br />

wird als Sperre <strong>de</strong>r this-Po<strong>in</strong>ter verwen<strong>de</strong>t, an<strong>de</strong>renfalls ist die Objektvariable<br />

explizit anzugeben.<br />

Neben <strong>de</strong>m Monitorkonzept stehen mit <strong>de</strong>n Metho<strong>de</strong>n wait() und notify() <strong>de</strong>r<br />

Klasse Object noch weitere Synchronisationsprimitive zur Verfügung. Zusätzlich zu<br />

<strong>de</strong>r bereits erwähnten Sperre, die e<strong>in</strong>em Objekt zugeordnet ist, besitzt e<strong>in</strong> Objekt<br />

e<strong>in</strong>e Warteliste. Dabei han<strong>de</strong>lt es sich um e<strong>in</strong>e (möglicherweise leere) Menge von<br />

Threads, die von e<strong>in</strong>em Scheduler unterbrochen wur<strong>de</strong>n und auf Ereignisse warten,<br />

um fortgesetzt wer<strong>de</strong>n zu können.<br />

wait() und notify() dürfen nur aufgerufen wer<strong>de</strong>n, wenn das Objekt bereits<br />

gesperrt ist, also nur <strong>in</strong>nerhalb e<strong>in</strong>es synchronized-Blocks. E<strong>in</strong> Aufruf von wait()<br />

nimmt die bereits gewährten Sperren (temporär) zurück und stellt <strong>de</strong>m Prozeß, <strong>de</strong>r<br />

<strong>de</strong>n Aufruf von wait() verursachte, <strong>in</strong> die Warteliste <strong>de</strong>s Objekts. Dadurch wird er<br />

unterbrochen. E<strong>in</strong> Aufruf von notify() entfernt e<strong>in</strong>en (beliebigen) Prozeß aus <strong>de</strong>r<br />

Warteliste <strong>de</strong>s Objekts, stellt die (temporär) aufgehobenen Sperren wie<strong>de</strong>r her und<br />

führt ihn <strong>de</strong>m normalen Schedul<strong>in</strong>g zu. „wait()“ und „notify()“ s<strong>in</strong>d damit für<br />

elementare Synchronisationsaufgaben geeignet, bei <strong>de</strong>nen es weniger auf die<br />

Kommunikation als auf die Steuerung zeitlicher Abläufe ankommt . Zusätzlich bietet<br />

die Klasse Object auch noch die Metho<strong>de</strong> notifyAll(). Der Unterschied zu<br />

notify() ist, dass alle warten<strong>de</strong>n Threads „aufgeweckt“ (reaktiviert wer<strong>de</strong>n, bei<br />

notify() nur e<strong>in</strong> e<strong>in</strong>ziger.<br />

Threadkommunikation<br />

Verschie<strong>de</strong>ne Threads können auf verschie<strong>de</strong>nen Wegen effizient Daten<br />

austauschen: Streams, geme<strong>in</strong>sam genutzte Objekte, statische Variablen. Die<br />

bei<strong>de</strong>n zuletzt angegebenen Alternativen basieren darauf, dass verschie<strong>de</strong>n Threads<br />

e<strong>in</strong>en geme<strong>in</strong>samen Adressraum (Heap) teilen. Somit können mehrere Thread-<br />

Objekte diesselben Objekte im Speicher ansprechen.<br />

111


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

1.4.2.5 Die Klassen Str<strong>in</strong>g und Str<strong>in</strong>gBuffer<br />

E<strong>in</strong> Str<strong>in</strong>g ist e<strong>in</strong>e Sammluung von Zeichen, die im Speicher geordnet abgelegt<br />

wer<strong>de</strong>n. Die Zeichen setzen sich aus e<strong>in</strong>em Zeichensatz zusammen, <strong>de</strong>r <strong>in</strong> <strong>Java</strong><br />

<strong>de</strong>m Unico<strong>de</strong>-Standard entspricht. In <strong>Java</strong> ist e<strong>in</strong>e Symbiose zwischen Str<strong>in</strong>g als<br />

Objekt und Str<strong>in</strong>g als e<strong>in</strong>gebautem Datentyp vorgenommen wor<strong>de</strong>n. Möglich ist e<strong>in</strong>e<br />

Zuweisung von Str<strong>in</strong>g-Literalen an Objekte und die Verknüpfung mehrerer Str<strong>in</strong>gs zu<br />

e<strong>in</strong>em.<br />

1.4.2.5.1 Die Klasse Str<strong>in</strong>g<br />

Die Klasse Str<strong>in</strong>g repräsentiert Zeichenketten, die sich nicht än<strong>de</strong>rn.<br />

Konstruktoren:<br />

Str<strong>in</strong>g()<br />

Str<strong>in</strong>g(Str<strong>in</strong>g wert)<br />

Str<strong>in</strong>g(char[] wert)<br />

Erzeugt e<strong>in</strong> leeres Str<strong>in</strong>g-Objekt<br />

Erzeugt e<strong>in</strong>en neuen Str<strong>in</strong>g durch Duplizierung e<strong>in</strong>es bereits<br />

vorhan<strong>de</strong>nen.<br />

Erzeugt e<strong>in</strong>en neuen Str<strong>in</strong>g aus e<strong>in</strong>em vorhan<strong>de</strong>nen Zeichen-<br />

Array.<br />

Zeichenextraktion<br />

char charAt(<strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

Str<strong>in</strong>g substr<strong>in</strong>g(<strong>in</strong>t<br />

anfang, <strong>in</strong>t en<strong>de</strong>)<br />

Str<strong>in</strong>g trim()<br />

Liefert das Zeichen an Position <strong>in</strong><strong>de</strong>x. Dabei hat das erste<br />

Element e<strong>in</strong>es Str<strong>in</strong>g <strong>de</strong>n Wert 0 und das letzte <strong>de</strong>n In<strong>de</strong>x<br />

„length()-1“. Falls <strong>de</strong>r Str<strong>in</strong>g kürzer als „<strong>in</strong><strong>de</strong>x + 1“ ist, wird e<strong>in</strong>e<br />

Ausnahme <strong>de</strong>s Typs „In<strong>de</strong>xOutOfBoundsException“ erzeugt<br />

Liefert <strong>de</strong>n Teilstr<strong>in</strong>g, <strong>de</strong>r an Position anfang beg<strong>in</strong>nt und an<br />

Position en<strong>de</strong> en<strong>de</strong>t. Wie bei allen Zugriffen über e<strong>in</strong>en<br />

numerischen In<strong>de</strong>x beg<strong>in</strong>nt hier das Zählen bei 0. „en<strong>de</strong>“<br />

verweist auf das erste Zeichen h<strong>in</strong>ter <strong>de</strong>m zu extrahieren<strong>de</strong>n<br />

Teilstr<strong>in</strong>g. Der Rückgabewert ist also die Zeichenkette, die von<br />

In<strong>de</strong>xposition anfang bis zur In<strong>de</strong>xpostion „en<strong>de</strong> –1“ reicht<br />

Liefert <strong>de</strong>n Str<strong>in</strong>g, <strong>de</strong>r entsteht, wenn auf bei<strong>de</strong>n Seiten <strong>de</strong>r<br />

Zeichenkette jeweils alle zusammenhängen<strong>de</strong>n Leerzeichen<br />

entfernt wer<strong>de</strong>n. Dabei wer<strong>de</strong>n alle Zeichen, die e<strong>in</strong>en Co<strong>de</strong><br />

kle<strong>in</strong>er 32 haben, als Leerzeichen angesehen. „trim“ entfernt<br />

immer Leerzeichen auf bei<strong>de</strong>n Seiten. Da die Klasse Str<strong>in</strong>g als<br />

f<strong>in</strong>al <strong>de</strong>f<strong>in</strong>iert wur<strong>de</strong>, gibt es ke<strong>in</strong>e Möglichkeit, entsprechen<strong>de</strong><br />

Metho<strong>de</strong>n nachzurüsten, z.B. für das rechtsbündige o<strong>de</strong>r<br />

l<strong>in</strong>ksbündige Entfernen von Leerzeichen.<br />

Länge <strong>de</strong>r Zeichenkette: <strong>in</strong>t length() liefert die aktuelle Länge <strong>de</strong>s Str<strong>in</strong>g-<br />

Objekts. Ist <strong>de</strong>r Rückgabewert 0, dann ist <strong>de</strong>r Str<strong>in</strong>g leer. Wird e<strong>in</strong> Wert „n“ größer 0<br />

zurückgegeben, dann enthält <strong>de</strong>r Str<strong>in</strong>g „n“ Zeichen, die an <strong>de</strong>n In<strong>de</strong>xpositionen 0 bis<br />

„n-1“ liegen.<br />

Vergleichen von Zeichenketten:<br />

boolean equals(Object<br />

e<strong>in</strong>Objekt)<br />

Liefert true, wenn das aktuelle Objekt und e<strong>in</strong>Objekt<br />

i<strong>de</strong>ntisch s<strong>in</strong>d. „equals“ testet auf <strong>in</strong>haltlliche Gleichheit und<br />

nicht darauf, ob bei<strong>de</strong> Str<strong>in</strong>gs dasselbe Objekt referenzieren.<br />

Neben „equals“ gibt es die Metho<strong>de</strong> equalsIgnoreCase, die<br />

evtl. vorhan<strong>de</strong>ne Unterschie<strong>de</strong> <strong>in</strong> <strong>de</strong>r Groß- und<br />

Kle<strong>in</strong>schreibung bei<strong>de</strong>r Zeichenketten ignoriert.<br />

112


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

boolean<br />

startsWith(Str<strong>in</strong>g s)<br />

boolean endsWith(Str<strong>in</strong>g<br />

s)<br />

<strong>in</strong>t compareTo(Str<strong>in</strong>g s)<br />

boolean regionMatches(<strong>in</strong>t<br />

toffset, Str<strong>in</strong>g other, <strong>in</strong>t<br />

ooffset, <strong>in</strong>t len)<br />

Testet, ob das Str<strong>in</strong>g-Objekt mit <strong>de</strong>r Zeichenkette s beg<strong>in</strong>nt.<br />

Ist dies <strong>de</strong>r Fall, dann gibt die Metho<strong>de</strong> „true“ zurück,<br />

an<strong>de</strong>renfalls „false“.<br />

Führt e<strong>in</strong>en lexikalischen Vergleich <strong>de</strong>r bei<strong>de</strong>n Str<strong>in</strong>gs durch.<br />

Bei e<strong>in</strong>em lexikalischen Vergleich wer<strong>de</strong>n die Zeichen<br />

paarweise von l<strong>in</strong>ks nach rechts verglichen. Tritt e<strong>in</strong><br />

Unterschied auf o<strong>de</strong>r ist e<strong>in</strong>er <strong>de</strong>r bei<strong>de</strong>n Str<strong>in</strong>gs been<strong>de</strong>t,<br />

wird das Ergebnis ermittelt. Ist das aktuelle Str<strong>in</strong>g-Objekt<br />

kle<strong>in</strong>er als s, wird e<strong>in</strong> negativer Wert zurückgegeben. Ist er<br />

größer, wird e<strong>in</strong> positiver Wert zurückgegeben. Bei Gleichheit<br />

liefert die Metho<strong>de</strong> <strong>de</strong>n Rückgabewert 0.<br />

Testet, ob 2 Str<strong>in</strong>g-Abschnitte gleich s<strong>in</strong>d.<br />

Suchen <strong>in</strong> Zeichenketten<br />

<strong>in</strong>t <strong>in</strong><strong>de</strong>xOf(Str<strong>in</strong>g s)<br />

<strong>in</strong>t In<strong>de</strong>xOf(Str<strong>in</strong>g s, <strong>in</strong>t<br />

vonIn<strong>de</strong>x)<br />

<strong>in</strong>t lastIn<strong>de</strong>xOf(Str<strong>in</strong>g s)<br />

Sucht das erste Vorkommen <strong>de</strong>r Zeichenkette s<br />

<strong>in</strong>nerhalb <strong>de</strong>s Str<strong>in</strong>g-Objekts. Wird s gefun<strong>de</strong>n, liefert<br />

die Metho<strong>de</strong> <strong>de</strong>n In<strong>de</strong>x <strong>de</strong>s ersten<br />

übere<strong>in</strong>stimmen<strong>de</strong>n Zeichens zurück, an<strong>de</strong>rnfalls wird<br />

–1 zurückgegeben.<br />

Die Metho<strong>de</strong> gibt es auch <strong>in</strong> e<strong>in</strong>er an<strong>de</strong>ren Version,<br />

die e<strong>in</strong>en Parameter vom Typ char akzeptiert. In<br />

diesem Fall sucht sie nach <strong>de</strong>m Auftreten <strong>de</strong>s ersten<br />

angegebenen Zeichens.<br />

Diese Metho<strong>de</strong> arbeitet wie die vorige, beg<strong>in</strong>nt mit <strong>de</strong>r<br />

Suche aber erst ab Position vonIn<strong>de</strong>x. Wird s<br />

beg<strong>in</strong>nend ab dieser Position gefun<strong>de</strong>n, liefert die<br />

Metho<strong>de</strong> <strong>de</strong>n In<strong>de</strong>x <strong>de</strong>s ersten übere<strong>in</strong>stimmen<strong>de</strong>n<br />

Zeichens, an<strong>de</strong>rnfalls –1.<br />

E<strong>in</strong>e Variante dieser Metho<strong>de</strong> erwartet anstelle e<strong>in</strong>es<br />

Str<strong>in</strong>g-Parameters e<strong>in</strong> Argument vom Typ char.<br />

Sucht nach <strong>de</strong>m letzten Vorkommen <strong>de</strong>s Teilstr<strong>in</strong>gs s<br />

im aktuellen Str<strong>in</strong>g-Objekt- Wird „s“ gefun<strong>de</strong>n, liefert<br />

die Metho<strong>de</strong> <strong>de</strong>n In<strong>de</strong>x <strong>de</strong>s ersten<br />

übere<strong>in</strong>stimmen<strong>de</strong>n Zeichens, an<strong>de</strong>rnfalls –1. E<strong>in</strong>e<br />

Variante dieser Metho<strong>de</strong> erwartet e<strong>in</strong> Argument <strong>de</strong>s<br />

Typs char.<br />

Ersetzen von Zeichenketten<br />

Str<strong>in</strong>g toLowerCase() Liefert <strong>de</strong>n Str<strong>in</strong>g zurück, <strong>de</strong>r entsteht, wenn alle Zeichen <strong>in</strong><br />

Kle<strong>in</strong>buchstaben umgewan<strong>de</strong>lt wer<strong>de</strong>n. Besitzt <strong>de</strong>r Str<strong>in</strong>g ke<strong>in</strong>e<br />

umwan<strong>de</strong>lbaren Zeichen, dann wird <strong>de</strong>r Orig<strong>in</strong>al-str<strong>in</strong>g<br />

zurückgegeben.<br />

Str<strong>in</strong>g toUpperCase() Liefert <strong>de</strong>n Str<strong>in</strong>g zurück, <strong>de</strong>r entsteht, wenn alle Zeichen <strong>in</strong><br />

Großbuchstaben umgewan<strong>de</strong>lt wer<strong>de</strong>n. Besitzt <strong>de</strong>r Str<strong>in</strong>g ke<strong>in</strong>e<br />

umwan<strong>de</strong>lbaren Zeichen, wird <strong>de</strong>r Orig<strong>in</strong>al-Str<strong>in</strong>g zurückgegeben.<br />

Str<strong>in</strong>g replace(char<br />

altesZeichen, char<br />

neuesZeichen)<br />

Konvertierungsfunktionen:<br />

static Str<strong>in</strong>g valueOf(boolean b)<br />

Diese Metho<strong>de</strong> führt e<strong>in</strong>e zeichenweise Konvertierung <strong>de</strong>s<br />

aktuellen Str<strong>in</strong>g-Objekts durch. Je<strong>de</strong>s Auftreten von<br />

altesZeichen wird durch neuesZeichen ersetzt. Es gibt <strong>in</strong><br />

<strong>Java</strong> ke<strong>in</strong>e Metho<strong>de</strong>, die das Ersetzen von Teilstr<strong>in</strong>gs durch<br />

an<strong>de</strong>re Teilstr<strong>in</strong>gs (von möglicherweise unterschiedlicher Länge)<br />

ermöglicht. „replace“ ist das e<strong>in</strong>zige Mittel für Ersetzungen.<br />

113


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

static Str<strong>in</strong>g valueOf(char z)<br />

static Str<strong>in</strong>g valueOf(<strong>in</strong>t i)<br />

static Str<strong>in</strong>g valueOf(long l)<br />

static Str<strong>in</strong>g valueOf(float f)<br />

static Str<strong>in</strong>g valueOf(double d)<br />

Die „valueOf“-Metho<strong>de</strong>n s<strong>in</strong>d als Klassenmetho<strong>de</strong>n implementiert und können ohne<br />

Str<strong>in</strong>g-Objekt aufgerufen wer<strong>de</strong>n. Da sie <strong>in</strong> <strong>de</strong>r Regel zur Erzeugung von Str<strong>in</strong>gs<br />

verwen<strong>de</strong>t wer<strong>de</strong>n, ist das s<strong>in</strong>nvoll. E<strong>in</strong> Aufruf von „valueOf“ wan<strong>de</strong>lt e<strong>in</strong> primitives<br />

Objekt mit Hilfe <strong>de</strong>r Metho<strong>de</strong> „toStr<strong>in</strong>g()“, die von <strong>de</strong>r zugehörigen Wrapper-<br />

Klassen bereitgestellt wird, <strong>in</strong> e<strong>in</strong>e Zeichenkette um.<br />

Formatieren mit format(). Die Klasse f<strong>in</strong>al class java.lang.Str<strong>in</strong>g implements<br />

CharSequence, Comparable, Serializable stellt mit <strong>de</strong>r statischen Funktion<br />

format() e<strong>in</strong>e Metho<strong>de</strong> bereit, Zeichenketten nach e<strong>in</strong>er Vorlage zu formatieren<br />

static Str<strong>in</strong>g format(Locale l, Str<strong>in</strong>g format, Object ... args)<br />

liefert e<strong>in</strong>en formatierten Str<strong>in</strong>g, <strong>de</strong>r aus <strong>de</strong>r gewünschten Sprache, <strong>de</strong>m Str<strong>in</strong>g und Argumenten<br />

hervorgeht.<br />

static Str<strong>in</strong>g format(Str<strong>in</strong>g format,Object ... args)<br />

liefert e<strong>in</strong>en formatierten Str<strong>in</strong>g, <strong>de</strong>r aus <strong>de</strong>m format-Str<strong>in</strong>g und <strong>de</strong>n Argumenten hervorgeht.<br />

Der Str<strong>in</strong>g format nennt sich Format-Str<strong>in</strong>g. Er enthält neben <strong>de</strong>m auszugeben<strong>de</strong>n<br />

Zeichen sogenannte Format-Spezifierer, die <strong>de</strong>m Formatierer darüber Auskunft<br />

geben, wie das Argument formatiert wer<strong>de</strong>n soll. "%s" steht für e<strong>in</strong>e unformatierte<br />

Ausgabe e<strong>in</strong>es Str<strong>in</strong>gs.<br />

Weitere Eigenschaften: Die Klasse Str<strong>in</strong>g ist f<strong>in</strong>al. Von e<strong>in</strong>er Str<strong>in</strong>g-Klasse können<br />

ke<strong>in</strong>e neuen Klassen abgeleitet wer<strong>de</strong>n.<br />

Str<strong>in</strong>g-Objekte s<strong>in</strong>d nicht dynamisch. Es wer<strong>de</strong>n durch die Klasse Str<strong>in</strong>g ke<strong>in</strong>e<br />

dynamischen Zeichenketten implementiert. Nach <strong>de</strong>r Initialisierung e<strong>in</strong>es Str<strong>in</strong>g bleibt<br />

<strong>de</strong>ssen Länge konstant, z.B.:<br />

Str<strong>in</strong>g s = "Hallo Welt";<br />

s = substr<strong>in</strong>g(0,5);<br />

Die „substr<strong>in</strong>g“-Metho<strong>de</strong> erzeugt e<strong>in</strong>e Kopie, die mit <strong>de</strong>m gewünschten Inhalt gefüllt<br />

wird. Diese gibt sie an <strong>de</strong>n Aufrufer zurück, <strong>de</strong>r das Ergebnis erneut s zuweist und<br />

damit die Orig<strong>in</strong>al<strong>in</strong>stanz für <strong>de</strong>n Garbage Collector freigibt.<br />

Bsp.: Anwendung von Metho<strong>de</strong>n <strong>de</strong>r Str<strong>in</strong>g-Klasse<br />

public class PruefeStr<strong>in</strong>g<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

Str<strong>in</strong>g str = "<strong>Java</strong> ist die beste Programmiersprache, " +<br />

"die Stu<strong>de</strong>nten <strong>de</strong>r Informatik erlernen sollten.";<br />

System.out.pr<strong>in</strong>tln("Der Str<strong>in</strong>g ist: " + str);<br />

System.out.pr<strong>in</strong>tln("Laenge <strong>de</strong>s Str<strong>in</strong>g: " + str.length());<br />

System.out.pr<strong>in</strong>tln("Das Zeichen an Position 7: " + str.charAt(7));<br />

System.out.pr<strong>in</strong>tln("Der Substr<strong>in</strong>g von 24 bis 31: " +<br />

str.substr<strong>in</strong>g(24,31));<br />

System.out.pr<strong>in</strong>tln("Der In<strong>de</strong>x zum Zeichen I: " + str.<strong>in</strong><strong>de</strong>xOf('I'));<br />

System.out.pr<strong>in</strong>tln("Der In<strong>de</strong>x zum Anfang von "<br />

+ "Substr<strong>in</strong>g \"beste\": " + str.<strong>in</strong><strong>de</strong>xOf("beste"));<br />

System.out.pr<strong>in</strong>tln("Der Str<strong>in</strong>g <strong>in</strong> Großbuchstaben: " + str.toUpperCase());<br />

}<br />

}<br />

114


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

115


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

1.4.2.5.2 Die Klasse Str<strong>in</strong>gBuffer<br />

Die Klasse Str<strong>in</strong>gBuffer dient zur Implementierung verän<strong>de</strong>rlicher Zeichenketten.<br />

Die Metho<strong>de</strong>n <strong>de</strong>r Klasse Str<strong>in</strong>gBuffer dienen hauptsächlich <strong>de</strong>m Anhängen und<br />

E<strong>in</strong>fügen von Texten. Dabei wird <strong>de</strong>r benötigte Speicherplatz automatisch <strong>de</strong>r Größe<br />

angepaßt.<br />

Konstruktoren:<br />

Str<strong>in</strong>gBuffer()<br />

Erzeugt e<strong>in</strong>en leeren Str<strong>in</strong>gBuffer<br />

Str<strong>in</strong>gBuffer(Str<strong>in</strong>g s) Erzeugt e<strong>in</strong> neues Str<strong>in</strong>gBuffer-Objekt, das e<strong>in</strong>e Kopie <strong>de</strong>r<br />

Zeichenkette s ist.<br />

E<strong>in</strong>fügen von Elementen<br />

Str<strong>in</strong>gBuffer append(Str<strong>in</strong>g s) Hängt Str<strong>in</strong>g s an das En<strong>de</strong> <strong>de</strong>s Str<strong>in</strong>gBuffer-Objekts.<br />

Zurückgegeben wird das verlängerte Str<strong>in</strong>gBuffer-<br />

Objekt. Zusätzlich gibt es die Metho<strong>de</strong> append <strong>in</strong><br />

Varianten für das Anhängen aller Arten von primitiven<br />

Typen. Anstelle e<strong>in</strong>es Str<strong>in</strong>g-Objekts wird hier <strong>de</strong>r<br />

entsprechen<strong>de</strong> primitive Typ übergeben, <strong>in</strong> e<strong>in</strong>en Str<strong>in</strong>g<br />

konvertiert und an das En<strong>de</strong> <strong>de</strong>s Objekts angehängt.<br />

Str<strong>in</strong>gBuffer <strong>in</strong>sert(<strong>in</strong>t offset, Str<strong>in</strong>g s) Fügt <strong>de</strong>n Str<strong>in</strong>g s an die Position offset <strong>in</strong> <strong>de</strong>n<br />

aktuellen Str<strong>in</strong>gBuffer e<strong>in</strong>. Zurückgegeben wuird das<br />

verlängerte Str<strong>in</strong>gBuffer-Objekt. Auch diese Metho<strong>de</strong><br />

gibt es für primitive Typen. Der anstelle e<strong>in</strong>es Str<strong>in</strong>g<br />

übergebene Wert wird zunächst <strong>in</strong> e<strong>in</strong>en Str<strong>in</strong>g<br />

konvertiert und dann an die gewünschte Stelle<br />

e<strong>in</strong>gefügt.<br />

Verän<strong>de</strong>rn von Elementen: „void setCharAt(<strong>in</strong>t <strong>in</strong><strong>de</strong>x, char c) throws<br />

In<strong>de</strong>xOutOfBoundsException“. Das an Position <strong>in</strong><strong>de</strong>x stehen<strong>de</strong> Zeichen wird<br />

durch c ersetzt. Falls Str<strong>in</strong>gBuffer zu kurz ist, löst die Metho<strong>de</strong> e<strong>in</strong>e Ausnahme<br />

<strong>de</strong>s Typs In<strong>de</strong>xOutOfBoundsException aus.<br />

Länge e<strong>in</strong>es Str<strong>in</strong>g-Objekts: <strong>in</strong>t length() liefert die Länge <strong>de</strong>s Objekts (Anzahl<br />

<strong>de</strong>r Zeichen, die zum Zeitpunkt <strong>de</strong>s Aufrufs <strong>in</strong> <strong>de</strong>m Str<strong>in</strong>gBuffer enthalten s<strong>in</strong>d).<br />

Konvertieren <strong>in</strong> e<strong>in</strong>en Str<strong>in</strong>g: Str<strong>in</strong>g toStr<strong>in</strong>g(). Nach<strong>de</strong>m die Konstruktion<br />

e<strong>in</strong>es Str<strong>in</strong>gBuffer-Objekts abgeschlossen ist, kann es mit Hilfe dieser Metho<strong>de</strong><br />

<strong>in</strong> e<strong>in</strong>en Str<strong>in</strong>g verwan<strong>de</strong>lt wer<strong>de</strong>n. Die Metho<strong>de</strong> legt dabei ke<strong>in</strong>e Kopie <strong>de</strong>s<br />

Str<strong>in</strong>gBuffer-Objekts an, son<strong>de</strong>rn liefert e<strong>in</strong>en Zeiger auf <strong>de</strong>n <strong>in</strong>ternen Zeichenpuffer.<br />

Erst wenn <strong>de</strong>r Str<strong>in</strong>gBuffer erneut verän<strong>de</strong>rt wer<strong>de</strong>n soll, wird tatsächlich e<strong>in</strong>e Kopie<br />

erzeugt.<br />

116


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

1.4.2.6 Die Math-Klasse<br />

Die Math-Klasse enthält nützliche numerische Konstanten und Funktionen:<br />

1. Die Funktionen „m<strong>in</strong>“ und „max“.<br />

2. Der absolute Betrag<br />

3. Zufallszahlen<br />

Die Klasse Math unterstützt die „random“-Metho<strong>de</strong>: public static double random(). Die<br />

„random“-Metho<strong>de</strong> gibt e<strong>in</strong>e Zahl zwischen 0.0 und 1.0 aus.<br />

4. Run<strong>de</strong>n<br />

Run<strong>de</strong>n e<strong>in</strong>er Zahl vom Typ float: public static <strong>in</strong>t round(float a). Gerun<strong>de</strong>t wird auf<br />

die nächste ganze Zahl (z.B. 5.4 auf 5, 5.5. auf 6)<br />

Run<strong>de</strong>n e<strong>in</strong>er Zahl vom Typ double: public static long round(double a)<br />

Abrun<strong>de</strong>n: public static double floor(double a). Math.floor run<strong>de</strong>t immer ab, z.B.<br />

ergibt Math.floor(4.99) die Zahl 4.0.<br />

Aufrun<strong>de</strong>n: public static double ceil(double a). ceil run<strong>de</strong>r immer auf, z.B.:<br />

Math.ceil(4.01) ergibt 5.0.<br />

Run<strong>de</strong>n auf die nächste ganze Zahl: public static double r<strong>in</strong>t(double a).<br />

5. Exponenten und Logarithmen<br />

x<br />

Bestimmen <strong>de</strong>s Expoentialwerts e : public static double exp(double x)<br />

Bestimmen <strong>de</strong>s natürlichen algorithmus ln(x ) : public static double log(double x)<br />

6. Trigonometrische Funktionen<br />

public static double s<strong>in</strong>(double w<strong>in</strong>kel)<br />

public static double cos(double w<strong>in</strong>kel)<br />

Der Wert von w<strong>in</strong>kel wird im Bogenmaß angegeben.<br />

7. Potenzen, Quadratwurzel<br />

y<br />

Bestimmen vom Wert <strong>de</strong>r Potenz x : public static double pow(double x, double y)<br />

Bestimmen <strong>de</strong>r Quadratwurzel von x: public static double sqrt(double x)<br />

8. Mathematische Konstanten<br />

public static f<strong>in</strong>al double E;<br />

public static f<strong>in</strong>al double PI;<br />

117


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

1.4.2.7 Object-Wrapper-Klassen<br />

Die Object-Wrapper-Klassen s<strong>in</strong>d Wrapper für primitive Typen. Zusätzlich besitzen<br />

sie Metho<strong>de</strong>n zur Umwandlung von Zeichenketten <strong>in</strong> die verschie<strong>de</strong>nen Datentypen.<br />

Boolean<br />

Character<br />

public static f<strong>in</strong>al Boolean TRUE<br />

public static f<strong>in</strong>al Boolean FALSE<br />

public static f<strong>in</strong>al char MIN_VALUE<br />

public staic f<strong>in</strong>al char MAX_VALUE<br />

public boolean booleanValue()<br />

public static Boolean valueOf(Str<strong>in</strong>g s)<br />

public static boolean getBoolean(Str<strong>in</strong>g name)<br />

public char charValue()<br />

public Character(char value)<br />

public <strong>in</strong>t hashCo<strong>de</strong>()<br />

public boolean equals(Object obj)<br />

public Str<strong>in</strong>g toStr<strong>in</strong>g()<br />

public static boolean isLowerCase(char ch)<br />

public static boolean isUpperCase(char ch)<br />

public static boolean isDigit(char ch)<br />

public static boolean isLetter(char ch)<br />

public static boolean isLetterOrDigit(char ch)<br />

public static boolean is<strong>Java</strong>Letter(char ch)<br />

public static boolean is<strong>Java</strong>LetterOrDigit(char c)<br />

public static boolean isSpace(char ch)<br />

public static boolean isTitleCase(char ch)<br />

public static char toLowerCase(char ch)<br />

public static char toUpperCase(char ch)<br />

public static <strong>in</strong>t digit(char ch, <strong>in</strong>t radix)<br />

public static <strong>in</strong>t getNumericValue(char ch)<br />

Number<br />

{ abstract }<br />

public abstract <strong>in</strong>t <strong>in</strong>tValue()<br />

public abstract long longValue()<br />

public abstract float floatValue()<br />

public abstract double doubleValue()<br />

public byte byteValue()<br />

public short shortValue()<br />

Byte Short Integer Long Float Double<br />

Abb.: Wrapper-Klassen<br />

118


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Die Klasse Character<br />

Konstruktor:<br />

Ermitteln vom „char“-Wert: public char charValue()<br />

Klassifizieren von Zeichen:<br />

Metho<strong>de</strong><br />

Beschreibung<br />

isDigit E<strong>in</strong>e numerische Zahl zwischen 0 und 9<br />

isLetter<br />

E<strong>in</strong> Buchstabe <strong>de</strong>s Alphabets<br />

isLetterOrDigit<br />

E<strong>in</strong> alphabetischer Buchstabe o<strong>de</strong>r e<strong>in</strong>e numerische Zahl<br />

isLowerCase<br />

isUpperCase<br />

E<strong>in</strong> großgeschriebener alphabetischer Buchstabe<br />

is<strong>Java</strong>Letter<br />

E<strong>in</strong> Buchstabe, ‚$‘ o<strong>de</strong>r ‚_‘<br />

is<strong>Java</strong>LetterOrDigit E<strong>in</strong> Buchstabe, e<strong>in</strong>e Zahl, $ o<strong>de</strong>r _<br />

isSpace<br />

E<strong>in</strong>e Leerstelle, e<strong>in</strong>e neue Zeile, e<strong>in</strong> Return, e<strong>in</strong> Tab o<strong>de</strong>r e<strong>in</strong><br />

Formularvorschub<br />

isTitleCase<br />

Spezielle zweibuchstabige groß- und kle<strong>in</strong>geschriebene Buchstaben<br />

Je<strong>de</strong> dieser Klassifikationsmerkmale gibt „true“ zurück, wenn das betreffen<strong>de</strong><br />

Zeichen zur Klassifikation gehört.<br />

Groß- und kle<strong>in</strong>geschriebene Variante e<strong>in</strong>es Buchstabens:<br />

public static char toUppercase(char zeichen)<br />

public static char toLowerCase(char zeichen)<br />

Konvertierungsmetho<strong>de</strong>n zur Umwandlung von Zahlen <strong>in</strong> Zeichenketten und<br />

Zeichenketten <strong>in</strong> Zahlen:<br />

public static <strong>in</strong>t digit(char zeichen, <strong>in</strong>t radix)<br />

gibt <strong>de</strong>n numerischen Wert e<strong>in</strong>es Zeichens <strong>de</strong>r angegebenen Zahlendarstellung<br />

(„radix“) 121 wie<strong>de</strong>r. Falls e<strong>in</strong> Zeichen ke<strong>in</strong>em Wert <strong>in</strong> <strong>de</strong>r angegebenen<br />

Zahlendarstellung entspricht, wird „-1“ zurückgegeben.<br />

public static char forDigit(<strong>in</strong>t digit, <strong>in</strong>t radix)<br />

Konvertiert e<strong>in</strong>en numerischen Wert <strong>in</strong> e<strong>in</strong> Zeichen, das diesen Wert <strong>in</strong> e<strong>in</strong>er<br />

bestimmten Zahlenbasis darstellt, z.B. Character.forDigit(6,8) ergibt 6 und<br />

Character.forDigit(12,16) ergibt "c“.<br />

Die Klasse Boolean<br />

Diese Klasse ist <strong>de</strong>r Objekt-Wrapper für <strong>de</strong>n primitiven Typ boolean.<br />

Konstruktoren:<br />

public Boolean(boolean wert)<br />

public Boolean(Str<strong>in</strong>g str)<br />

Beschaffung e<strong>in</strong>es booleschen Werts, <strong>de</strong>r <strong>in</strong> e<strong>in</strong>em Objekt abgelegt ist:<br />

public boolean booleanValue()<br />

Objekt-Wrapper-Varianten von true und false<br />

public f<strong>in</strong>al static Boolean FALSE;<br />

public f<strong>in</strong>al static Boolean TRUE;<br />

Die Klasse Number<br />

121 z.B. 10 für <strong>de</strong>zimal, 8 für oktal, 16 für hexa<strong>de</strong>zimal. Zahlenbasis kann Werte von 2<br />

(Character.MIN_RADIX) bir 32 (Character.MAX_RADIX) umfassen.<br />

119


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Die Objekt-Wrapper <strong>de</strong>r Typen <strong>in</strong>t, long, float und double s<strong>in</strong>d Subklassen <strong>de</strong>r<br />

abstrakten Klasse Number. Vier Metho<strong>de</strong>n s<strong>in</strong>d für die Konvertierung e<strong>in</strong>er Zahl <strong>in</strong><br />

e<strong>in</strong>en bestimmten primitiven Typ zuständig:<br />

public abstract <strong>in</strong>t <strong>in</strong>tValue();<br />

public abstract long longValue();<br />

public abstract float floatValue();<br />

public abstract double doubleValue()<br />

1. Die Klasse Integer<br />

Konstruktoren:<br />

public Integer(<strong>in</strong>t wert)<br />

public Integer(Str<strong>in</strong>g s) throws NumberFormatException<br />

Die Zahlenbasis (Radix) ist 10. Falls die Zeichenkette nicht numerische Zeichen<br />

enthält, wird die Ausnahme NumberFormatException ausgeworfen.<br />

Umwan<strong>de</strong>ln Zeichenkette <strong>in</strong> ganze Zahlen:<br />

public static <strong>in</strong>t parseInt(Str<strong>in</strong>g s) throws NumberFormatException<br />

public static <strong>in</strong>t parseInt(Str<strong>in</strong>g s, <strong>in</strong>t radix) throws NumberFormatException<br />

public static Integer valueOf(Str<strong>in</strong>g s) throws NumberFormatException<br />

public static Integer valueOf(Str<strong>in</strong>g s, <strong>in</strong>t radix) throws NumberFormatException<br />

Der Unterschied zwischen parseInt und valueOf liegt dar<strong>in</strong>: parseInt gibt e<strong>in</strong><br />

„<strong>in</strong>t“ zurück, valueOf e<strong>in</strong> Integer.<br />

Konvertieren e<strong>in</strong>er ganzen Zahl <strong>in</strong> e<strong>in</strong>e Zeichenkette:<br />

public static Str<strong>in</strong>g toStr<strong>in</strong>g(<strong>in</strong>t i)<br />

public static Str<strong>in</strong>g toStr<strong>in</strong>g(<strong>in</strong>t i, <strong>in</strong>t radix)<br />

Die bei<strong>de</strong>n Metho<strong>de</strong>n dürfen nicht mit <strong>de</strong>r Instanz-Metho<strong>de</strong> toStr<strong>in</strong>g verwechselt<br />

wer<strong>de</strong>n, die für alle Subklassen von Objekt <strong>de</strong>f<strong>in</strong>iert ist.<br />

Die Konstanten Integer.M<strong>in</strong>_VALUE und Integer.MAX_VALUE:<br />

public f<strong>in</strong>al static <strong>in</strong>t MIN_VALUE<br />

public f<strong>in</strong>al static <strong>in</strong>t MAX_VALUE<br />

2. Die Klasse Long<br />

Sie ist weitgehend mit <strong>de</strong>r Integer-Klasse i<strong>de</strong>ntisch. Allerd<strong>in</strong>gs dient sie als Wrapper<br />

für long-Werte.<br />

3. Die Klasse Float<br />

Sie enthält e<strong>in</strong>en Objekt-Wrapper für <strong>de</strong>n Datentyp float.<br />

Konstruktoren:<br />

public Float(float wert)<br />

public Float(double wert)<br />

public Float(Str<strong>in</strong>g s) throws NumberFormatException<br />

Konvertierung von bzw. zu Zeichenketten:<br />

public static Float valueOf(Str<strong>in</strong>g s) throws NumberFormatException<br />

public static Str<strong>in</strong>g toStr<strong>in</strong>g(float f)<br />

Überprüfen auf „Unendlich“ bzw. auf „ke<strong>in</strong>e Zahl“:<br />

public static boolean isInf<strong>in</strong>ite(float f)<br />

public static boolean isNaN(float f) // Instanz-Variante<br />

public boolean isNaN()<br />

// Instanz-Variante<br />

Konstanten:<br />

public f<strong>in</strong>al static float MIN_VALUE<br />

public f<strong>in</strong>al static float MAX_VALUE<br />

public f<strong>in</strong>al static float NEGATIVE_INFINITY<br />

public f<strong>in</strong>al static float NaN<br />

120


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

4. Die Klasse Double<br />

Sie hat dieselbe Funktionalität wie die Float-Klasse.<br />

1.4.3 Ausnahmen und Ausnahmenbehandlung<br />

Ausnahmen<br />

E<strong>in</strong>e Ausnahme (Exception) ist e<strong>in</strong>e unerwünschte Ersche<strong>in</strong>ung. E<strong>in</strong>e <strong>de</strong>rartige<br />

Situation (z.B. Division durch Null) ist <strong>in</strong> e<strong>in</strong>em Programm gegebenenfalls durch e<strong>in</strong>e<br />

Fehlerbehandlungsrout<strong>in</strong>e aufzufangen.<br />

Es gibt unterschiedliche Möglichkeiten zur Fehlerbehandlung:<br />

- die <strong>in</strong>dividuelle programmierte Fehlerbehandlung, z.B.:<br />

public <strong>in</strong>t dividiere()<br />

{<br />

// ganzzahlige Division<br />

if (zweiterOperand == 0)<br />

{<br />

System.out.pr<strong>in</strong>tln("Fehler: Division durch Null");<br />

System.out.pr<strong>in</strong>tln("Divisor wird auf 1 gesetzt!");<br />

zweiterOperand = 1;<br />

}<br />

return ersterOperand / zweiterOperand;<br />

}<br />

- die von <strong>Java</strong> bereitgestellten Fehlerbehandlungen<br />

Ausnahmen s<strong>in</strong>d <strong>in</strong> <strong>Java</strong> Objekte, die erzeugt wer<strong>de</strong>n, wenn e<strong>in</strong>e<br />

Ausnahmebed<strong>in</strong>gung vorliegt. Diese Objekte wer<strong>de</strong>n von <strong>de</strong>r Metho<strong>de</strong> an das<br />

aufrufen<strong>de</strong> Programm zurückgegeben und müssen behan<strong>de</strong>lt wer<strong>de</strong>n. Das<br />

Behan<strong>de</strong>ln von Ausnahmen wird als Auffangen bezeichnet. Das Auffangen wird<br />

durch Plazieren von Anweisungen, die Ausnahmen erzeugen können, <strong>in</strong> e<strong>in</strong>e trycatch<br />

Block erreicht, z.B. 122 :<br />

public <strong>in</strong>t dividiere()<br />

{<br />

// ganzzahlige Division<br />

try {<br />

/* Innerhalb <strong>de</strong>s try-Blocks wer<strong>de</strong>n diejenigen<br />

kritischen Aktionen durchgefuehrt, die<br />

Ausnahmen erzeugen koennen<br />

*/<br />

return (ersterOperand / zweiterOperand);<br />

}<br />

catch(java.lang.ArithmeticException a)<br />

{<br />

System.out.pr<strong>in</strong>tln(" Fehler: Division durch Null");<br />

System.out.pr<strong>in</strong>tln("Divisor wird auf 1 gesetzt!");<br />

zweiterOperand = 1;<br />

return (ersterOperand / zweiterOperand);<br />

}<br />

}<br />

122 pr14103<br />

121


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

In <strong>de</strong>r catch-Klausel wird die Art <strong>de</strong>r aufzufangen<strong>de</strong>n Ausnahme <strong>de</strong>f<strong>in</strong>iert. Dort ist<br />

e<strong>in</strong> formaler Parameter angegeben, <strong>de</strong>r beim Auftreten <strong>de</strong>r Ausnahme e<strong>in</strong><br />

Fehlerobjekt übernehmen soll. Fehlerobjekte s<strong>in</strong>d Instanzen <strong>de</strong>r Klasse Throwable<br />

(o<strong>de</strong>r e<strong>in</strong>e ihrer Unterklassen). Sie wer<strong>de</strong>n vom Aufrufer <strong>de</strong>r Ausnahme erzeugt und<br />

als Parameter an die catch-Klausel übergeben. Das Fehlerobjekt enthält<br />

Informationen über die Art <strong>de</strong>r aufgetretenen Ausnahme. Es kann dort e<strong>in</strong>e <strong>de</strong>r<br />

zahlreichen Standardausnahmen von <strong>Java</strong> stehen o<strong>de</strong>r auch selbst<strong>de</strong>f<strong>in</strong>ierte<br />

Ausnahmen. Das Ausnahme- / Fehlerobjekt besitzt e<strong>in</strong>ige nützliche Metho<strong>de</strong>n, z.B.<br />

public Str<strong>in</strong>g getMessage() 123 . Diese Metho<strong>de</strong> gibt Fehlermeldungen zurück.<br />

public void pr<strong>in</strong>tStackTrace(). Diese Metho<strong>de</strong> druckt e<strong>in</strong>en Auszug aus<br />

<strong>de</strong>m Laufzeit-Stack.<br />

Am En<strong>de</strong> e<strong>in</strong>es try-Blocks können beliebig viele „catch“-Klauseln stehen, so daß<br />

unterschiedliche Arten von Ausnahmen behan<strong>de</strong>lt wer<strong>de</strong>n können.<br />

Mit Hilfe <strong>de</strong>r f<strong>in</strong>ally-Klausel (letzter Bestandteil e<strong>in</strong>er try-catch-Anweisung)<br />

kann e<strong>in</strong> Programmfragment <strong>de</strong>f<strong>in</strong>iert wer<strong>de</strong>n, das immer ausgeführt wird, wenn die<br />

zugehörige try-Klausel betreten wur<strong>de</strong>.<br />

Bsp.: Rückgabe von „Resourcen“ <strong>in</strong> <strong>de</strong>r f<strong>in</strong>ally-Klausel<br />

In <strong>de</strong>m folgen<strong>de</strong>n Programm stellt die f<strong>in</strong>ally-Klausel sicher, daß <strong>de</strong>r Schalter<br />

tatsächlich nach <strong>de</strong>m En<strong>de</strong> <strong>de</strong>r Bearbeitung im try-catch-Block ausgeschaltet ist.<br />

class Schalter<br />

{<br />

boolean zustand = false;<br />

boolean lies() { return zustand; }<br />

void an() { zustand = true; }<br />

void aus() { zustand = false; }<br />

}<br />

public class AnAusSchalter<br />

{<br />

static Schalter schalter = new Schalter();<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

try<br />

{<br />

schalter.an();<br />

// hier kann sich Quellco<strong>de</strong> bef<strong>in</strong><strong>de</strong>n, <strong>de</strong>r<br />

// Ausnahmen ausloest<br />

}<br />

catch(NullPo<strong>in</strong>terException a)<br />

{<br />

System.out.pr<strong>in</strong>tln("NullPo<strong>in</strong>terException");<br />

}<br />

catch(IllegalArgumentException a)<br />

{<br />

System.out.pr<strong>in</strong>tln("IllegalArgumentException");<br />

}<br />

f<strong>in</strong>ally<br />

{<br />

schalter.aus();<br />

}<br />

}<br />

}<br />

123 Sie ist <strong>in</strong> <strong>de</strong>r Klasse Throwable <strong>de</strong>f<strong>in</strong>iert und daher allen Exception-Objekten zugänglich.<br />

122


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Globales Exception-Handl<strong>in</strong>g<br />

<strong>Java</strong> realisiert das globale Handl<strong>in</strong>g von Ausnahmen über die optionale Erweiterung<br />

e<strong>in</strong>er Metho<strong>de</strong>n<strong>de</strong>klaration mit <strong>de</strong>r „throws“-Klausel, z.B.:<br />

public class Me<strong>in</strong>eAusnahmenKlasse<br />

{<br />

public void e<strong>in</strong>eAusnahmenMetho<strong>de</strong>() throws Me<strong>in</strong>eAusnahme<br />

{<br />

.........<br />

}<br />

}<br />

Hier wird <strong>de</strong>m Compiler mitgeteilt, daß <strong>de</strong>r vorliegen<strong>de</strong> Co<strong>de</strong> e<strong>in</strong>e Ausnahme<br />

(„Me<strong>in</strong>eAusnahme“) erzeugen könnte.<br />

Falls <strong>in</strong> <strong>de</strong>r mit „throws“ gekennzeichneten Metho<strong>de</strong> e<strong>in</strong>e Ausnahmesituation<br />

auftritt, dann wird die aufrufen<strong>de</strong> Metho<strong>de</strong> nach e<strong>in</strong>er Ausnahmebehandlung<br />

durchsucht. Enthält die aufrufen<strong>de</strong> Metho<strong>de</strong> e<strong>in</strong>e Ausnahmebehandlungsmetho<strong>de</strong>,<br />

wird mit dieser die Ausnahme bearbeitet. Ist ke<strong>in</strong>e Rout<strong>in</strong>e vorhan<strong>de</strong>n, wird <strong>de</strong>ren<br />

aufrufen<strong>de</strong> Metho<strong>de</strong> durchsucht und so fort. Die Ausnahme wird von <strong>de</strong>r<br />

Hierarchieebene, wo sie aufgetreten ist, jeweils e<strong>in</strong>e Hierarchieebene weiter nach<br />

oben gereicht. Wird die Ausnahme nirgends aufgefangen, dann bricht <strong>de</strong>r <strong>Java</strong>-<br />

Interpreter normalerweise die Ausführung <strong>de</strong>s Programms ab.<br />

Das Auslösen e<strong>in</strong>er Ausnahme wird im <strong>Java</strong>-Sprachgebrauch als „throw<strong>in</strong>g“<br />

bezeichnet, das Behan<strong>de</strong>ln e<strong>in</strong>er Ausnahme wird „catch<strong>in</strong>g“ genannt.<br />

Das Grundpr<strong>in</strong>zip <strong>de</strong>s Exception-Mechanismus:<br />

- E<strong>in</strong> Laufzeitfehler o<strong>de</strong>r e<strong>in</strong>e vom Entwickler gewollte Bed<strong>in</strong>gung löst e<strong>in</strong>e Ausnahme aus.<br />

- Diese kann entwe<strong>de</strong>r vom Programmteil, <strong>in</strong> <strong>de</strong>m sie angelegt wur<strong>de</strong>, behan<strong>de</strong>lt wer<strong>de</strong>n, o<strong>de</strong>r sie kann<br />

weitergegeben wer<strong>de</strong>n.<br />

- Wird die Ausnahme weitergegeben, so hat <strong>de</strong>r Empfänger erneut die Möglichkeit sie entwe<strong>de</strong>r zu behan<strong>de</strong>ln<br />

o<strong>de</strong>r selbst weiterzugeben.<br />

- Wird die Ausnahme von ke<strong>in</strong>em Programmteil behan<strong>de</strong>lt, so führt sie zum Abbruch e<strong>in</strong>es Programms, und zur<br />

Ausgabe e<strong>in</strong>er Fehlermeldung.<br />

Auswertungen, die Ausnahmen auslösen, können entwe<strong>de</strong>r direkt <strong>in</strong> e<strong>in</strong>e try-<br />

Klausel mit optionalen catch-Anweisungen (Behandlung vor Ort) e<strong>in</strong>gefaßt o<strong>de</strong>r<br />

über die throws-Klausel an <strong>de</strong>n übergeordneten Programmblock weitergeleitet<br />

wer<strong>de</strong>n. Falls we<strong>de</strong>r die „Behandlung vor Ort“ noch e<strong>in</strong>e throws-Klausel verwen<strong>de</strong>t<br />

wird, liefert <strong>de</strong>r Compiler bereits bei <strong>de</strong>r Übersetzung e<strong>in</strong>e Fehlermeldung. E<strong>in</strong>e<br />

Ausnahme bil<strong>de</strong>t die Klasse RuntimeException e<strong>in</strong>schl. ihrer Subklassen. Für sie<br />

ist we<strong>de</strong>r die Fehlerbehandlung noch Weiterleitung mit <strong>de</strong>r throws-Klausel<br />

notwendig.<br />

Die Fehlerklassen von <strong>Java</strong><br />

Zur Behandlung von Fehlern und Ausnahmen besitzt die Klasse Throwable zwei<br />

Subklassen: die Klassen Exception und Error. In bei<strong>de</strong>n Ausnahmeklassen s<strong>in</strong>d<br />

die wichtigsten Ausnahmen und Fehler <strong>de</strong>r <strong>Java</strong>-Laufzeitbibliothek bereits enthalten.<br />

Ausnahmen <strong>de</strong>r Klasse Error und ihrer Klasse RuntimeError (sowie<br />

RuntimeException) müssen nicht extra abgefangen wer<strong>de</strong>n. „Errors“ s<strong>in</strong>d<br />

systembed<strong>in</strong>gt, „Exceptions“ dagegen programmbed<strong>in</strong>gt und müssen behan<strong>de</strong>lt<br />

123


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

wer<strong>de</strong>n. Es gibt fünf Typen von Ausnahmen, die <strong>in</strong> e<strong>in</strong>er throws-Klausel aufgelistet<br />

wer<strong>de</strong>n müssen:<br />

ClassNotFoundException<br />

IllegalAccessException<br />

InstantiationException<br />

InterruptedException<br />

NoSuchMethodException<br />

H<strong>in</strong>zu kommen verschie<strong>de</strong>ne weitere Ausnahmen aus <strong>de</strong>n <strong>Java</strong>-Paketen. So gibt es<br />

spezielle Fehlerklassen für die E<strong>in</strong>- und Ausgabe, die Netzwerkkommunikation o<strong>de</strong>r<br />

<strong>de</strong>n Zugriff auf Datenfel<strong>de</strong>r. Teilweise s<strong>in</strong>d sie <strong>de</strong>r Klasse „Error“ (o<strong>de</strong>r ihrer<br />

Subklasse RuntimeError) zugeordnet und müssen nicht extra abgefangen und<br />

dokumentiert wer<strong>de</strong>n. S<strong>in</strong>d Ausnahmen <strong>de</strong>r Klasse Exception (Ausnahme:<br />

RuntimeException) zugeordnet, dann müssen sie behan<strong>de</strong>lt wer<strong>de</strong>n (z.B. alle<br />

Ausnahmen von java.io).<br />

Die Klasse RuntimeException ist die Superklasse für die Behandlung aller<br />

Laufzeitfehler, die behan<strong>de</strong>lt wer<strong>de</strong>n können aber nicht müssen (die Entscheidung<br />

liegt beim Programmierer).<br />

UnknownError<br />

VirtualMach<strong>in</strong>eError<br />

OutOfMemoryError<br />

InternalError<br />

Error<br />

AWTError<br />

Object<br />

Throwable<br />

L<strong>in</strong>kageError<br />

ClassNotFoundException<br />

Exception<br />

RuntimeException<br />

EOFException<br />

IOException<br />

FileNotFoundException<br />

InterruptedException<br />

UTFDataFormatException<br />

Abb.: Fehlerklassenhierarchie<br />

Auslösen von Ausnahmen<br />

<strong>Java</strong> verfügt <strong>in</strong> se<strong>in</strong>en Standardbibliotheken über zahlreiche vorgefertigte<br />

Ausnahmen für fast alle Standardsitiuationen (z.B. Dateioperationen auf <strong>de</strong>r Basis<br />

<strong>de</strong>r IO-Exception). Selbst<strong>de</strong>f<strong>in</strong>ierte Ausnahmen benötigen e<strong>in</strong>e Subklasse von<br />

Exception. Das Auslösen dieser selbst<strong>de</strong>f<strong>in</strong>ierten Ausnahmen erfolgt über die throw-<br />

Anweisung: throw AusnahmeObjekt.<br />

Die Behandlung selbstausgelöseter Ausnahmen erfolgt nach <strong>de</strong>n üblichen Regeln:<br />

124


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

- Suche nach e<strong>in</strong>em Ausnahmen-Handler <strong>in</strong> <strong>de</strong>n umgeben<strong>de</strong>n Blöcken<br />

- Bei erfolgloser Suche Weitergabe <strong>de</strong>s Fehlers an <strong>de</strong>n Aufrufer.<br />

Wird die Ausnahme nicht <strong>in</strong>nerhalb <strong>de</strong>rselben Metho<strong>de</strong> behan<strong>de</strong>lt, dann ist sie mit<br />

Hilfe <strong>de</strong>r "throws"-Klausel zu <strong>de</strong>klarieren und „weiter oben“ <strong>in</strong> <strong>de</strong>r Aufrufkette zu<br />

behan<strong>de</strong>ln.<br />

Bsp.:<br />

public class AusnahmeMetho<strong>de</strong>n<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

try<br />

{<br />

throw new Exception("Hier ist me<strong>in</strong>e Ausnahme");<br />

}<br />

catch (Exception a)<br />

{<br />

System.out.pr<strong>in</strong>tln("E<strong>in</strong>gefangene Ausnahme");<br />

System.out.pr<strong>in</strong>tln("a.getMessage(): " + a.getMessage());<br />

System.out.pr<strong>in</strong>tln("a.toStr<strong>in</strong>g(): " + a.toStr<strong>in</strong>g());<br />

System.out.pr<strong>in</strong>tln("a.pr<strong>in</strong>tStackTrace(): ");<br />

a.pr<strong>in</strong>tStackTrace();<br />

}<br />

}<br />

}<br />

/* Ausgabe:<br />

E<strong>in</strong>gefangene Ausnahme<br />

a.getMessage(): Hier ist me<strong>in</strong>e Ausnahme<br />

a.toStr<strong>in</strong>g(): java.lang.Exception: Hier ist me<strong>in</strong>e Ausnahme<br />

a.pr<strong>in</strong>tStackTrace():<br />

java.lang.Exception: Hier ist me<strong>in</strong>e Ausnahme<br />

at AusnahmeMetho<strong>de</strong>n.ma<strong>in</strong>(AusnahmeMetho<strong>de</strong>n.java)<br />

*/<br />

Die throw-Anweisung besitzt Merkmale e<strong>in</strong>er Sprunganweisung. Sie unterbricht das<br />

Programm und verzweigt unmittelbar zur umgeben<strong>de</strong>n catch-Klausel. Gibt es ke<strong>in</strong>e<br />

catch-Klausel, wird <strong>de</strong>r Fehler weitergegeben. Tritt die Ausnahme <strong>in</strong>nerhalb e<strong>in</strong>er<br />

try-catch-Anweisung mit e<strong>in</strong>er f<strong>in</strong>ally-Klausel auf, wird diese noch vor <strong>de</strong>r<br />

Weitergabe ausgeführt.<br />

Auch das „Wie<strong>de</strong>rauswerfen“ e<strong>in</strong>er Ausnahme ist möglich, <strong>in</strong>sbeson<strong>de</strong>re dann, falls<br />

e<strong>in</strong>e Ausnahme e<strong>in</strong>gefangen wur<strong>de</strong>, sie aber <strong>in</strong> e<strong>in</strong>em größeren Zusammenhang<br />

behan<strong>de</strong>lt wer<strong>de</strong>n soll<br />

catch (Exception a)<br />

{<br />

System.out.pr<strong>in</strong>tln("E<strong>in</strong>e Ausnahme wur<strong>de</strong> e<strong>in</strong>gefangen");<br />

throw a;<br />

}<br />

Eigene, d.h. benutzer<strong>de</strong>f<strong>in</strong>ierte Ausnahmen können direkt von <strong>de</strong>r Klasse<br />

Exception abgeleitet wer<strong>de</strong>n, z.B. 124 :<br />

class Me<strong>in</strong>eAusnahme extends Exception<br />

{<br />

private <strong>in</strong>t i;<br />

public Me<strong>in</strong>eAusnahme() {}<br />

public Me<strong>in</strong>eAusnahme(Str<strong>in</strong>g nachricht)<br />

124 Vgl. pr14140<br />

125


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

{<br />

super(nachricht);<br />

}<br />

public Me<strong>in</strong>eAusnahme(Str<strong>in</strong>g nachricht, <strong>in</strong>t x)<br />

{<br />

super(nachricht);<br />

i = x;<br />

}<br />

public <strong>in</strong>t wert() { return i; }<br />

}<br />

public class AuswurfMe<strong>in</strong>eAusnahme<br />

{<br />

public static void f() throws Me<strong>in</strong>eAusnahme<br />

{<br />

System.out.pr<strong>in</strong>tln("Auswurf von Me<strong>in</strong>eAusnahme aus f()");<br />

throw new Me<strong>in</strong>eAusnahme();<br />

}<br />

public static void g() throws Me<strong>in</strong>eAusnahme<br />

{<br />

System.out.pr<strong>in</strong>tln("Auswurf von Me<strong>in</strong>eAusnahme aus g()");<br />

throw new Me<strong>in</strong>eAusnahme("Ursprung <strong>in</strong> g()");<br />

}<br />

public static void h() throws Me<strong>in</strong>eAusnahme<br />

{<br />

System.out.pr<strong>in</strong>tln("Auswurf von Me<strong>in</strong>eAusnahme aus h()");<br />

throw new Me<strong>in</strong>eAusnahme("Ursprung <strong>in</strong> h()",13);<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

try { f(); }<br />

catch (Me<strong>in</strong>eAusnahme a)<br />

{<br />

a.pr<strong>in</strong>tStackTrace();<br />

}<br />

try { g(); }<br />

catch (Me<strong>in</strong>eAusnahme a)<br />

{ a.pr<strong>in</strong>tStackTrace(); }<br />

try { h(); }<br />

catch (Me<strong>in</strong>eAusnahme a)<br />

{<br />

a.pr<strong>in</strong>tStackTrace();<br />

System.out.pr<strong>in</strong>tln("a.wert() = " + a.wert());<br />

}<br />

}<br />

}<br />

/* Ausgabe:<br />

Auswurf vom Me<strong>in</strong>eAusnahme aus f()<br />

Me<strong>in</strong>eAusnahme<br />

at java.lang.Throwable.(Compiled Co<strong>de</strong>)<br />

at java.lang.Exception.(Compiled Co<strong>de</strong>)<br />

at Me<strong>in</strong>eAusnahme.(AuswurfMe<strong>in</strong>eAusnahme.java:4)<br />

at AuswurMe<strong>in</strong>eAusnahme.f(Vererbung.java:21)<br />

at Auswurfme<strong>in</strong>eAusnahme.ma<strong>in</strong>(Vererbung.java:36)<br />

Auswurf von Me<strong>in</strong>eAusnahme aus g()<br />

Me<strong>in</strong>eAusnahme: Ursprung <strong>in</strong> g()<br />

at java.lang.Throwable.(Compiled Co<strong>de</strong>)<br />

at java.lang.Exception.(Compiled Co<strong>de</strong>)<br />

at Me<strong>in</strong>eAusnahme.(AuswurfMe<strong>in</strong>eAusnahme.java:7)<br />

at AuswurfMe<strong>in</strong>eAusnahme.g(AuswurfMe<strong>in</strong>eAusnahme.java:26)<br />

at AuswurfMe<strong>in</strong>eAusnahme.ma<strong>in</strong>(AuswurfMe<strong>in</strong>eAusnahme.java:43)<br />

Auswurf von Me<strong>in</strong>eAusnahme aus h()<br />

Me<strong>in</strong>eAusnahme: Ursprung <strong>in</strong> h()<br />

at java.lang.Throwable.(Compiled Co<strong>de</strong>)<br />

at java.lang.Exception.(Compiled Co<strong>de</strong>)<br />

at Me<strong>in</strong>eAusnahme.(AuswurfMe<strong>in</strong>eAusnahme.java:11)<br />

at AuswurfMe<strong>in</strong>eAusnahme.g(AuswurfMe<strong>in</strong>eAusnahme.java:31)<br />

126


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

at AuswurfMe<strong>in</strong>eAusnahme.ma<strong>in</strong>(AuswurfMe<strong>in</strong>eAusnahme.java:50)<br />

a.wert() = 13<br />

*/<br />

127


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

1.4.4 E<strong>in</strong>-, Ausgaben<br />

Quelle E i n g a b e s t r o m read() Programm<br />

Programm write() A u s g a b e s t r o m Senke<br />

Abb.: Input und Output Stream<br />

E<strong>in</strong>lesen von Daten aus e<strong>in</strong>er externen Quelle (source) o<strong>de</strong>r Ausgabe zu e<strong>in</strong>er<br />

externen Senke (s<strong>in</strong>k).<br />

Daten können aus verschie<strong>de</strong>nen Quellen stammen: Tastatur, Datei, Speicher,<br />

Internet, an<strong>de</strong>res Programm.<br />

Daten können von verschie<strong>de</strong>ner Art se<strong>in</strong>: Zeichen, Objekte, Bil<strong>de</strong>r, ....<br />

Daten können zu verschie<strong>de</strong>nen Ausgabee<strong>in</strong>heiten gehen: Bildschirm, Datei,<br />

Drucker, Speicher, Internet, an<strong>de</strong>res Programm.<br />

Daten wer<strong>de</strong>n generell sequentiell gelesen o<strong>de</strong>r geschrieben<br />

1.4.4.1 E<strong>in</strong>- und Ausgabeströme <strong>de</strong>r Klasse System<br />

Die Klasse System enthält drei Datenströme:<br />

public static InputStream 125 <strong>in</strong><br />

public static Pr<strong>in</strong>tStream 126 out<br />

public static Pr<strong>in</strong>tStream err<br />

// Standar<strong>de</strong><strong>in</strong>gabe<br />

// Standardausgabe<br />

// Standardfehlerausgabe<br />

Diese Datenströme dienen zum Lesen bzw. Schreiben <strong>in</strong> das Fenster von <strong>de</strong>m die<br />

Anwendung gestartet wur<strong>de</strong>. Die Verwendung <strong>de</strong>s Stroms System.<strong>in</strong> <strong>in</strong>nerhalb von<br />

Applets ist aber nicht s<strong>in</strong>nvoll, weil die verschie<strong>de</strong>n Browser diesen Datenstrom<br />

unterschiedlich behan<strong>de</strong>ln. Die Ströme System.out und System.err sen<strong>de</strong>t<br />

Netscape an das Fenster <strong>de</strong>r <strong>Java</strong>-Konsole, <strong>de</strong>r Appletviewer gibt sie <strong>in</strong> das Fenster<br />

weiter, aus <strong>de</strong>m <strong>de</strong>r Appletviewer gestartet wur<strong>de</strong>,<br />

E<strong>in</strong>fache Ausgaben auf das Standard-Ausgabegerät<br />

Mit Hilfe <strong>de</strong>s Kommandos „System.out.pr<strong>in</strong>tln“, das als e<strong>in</strong>ziges Argument e<strong>in</strong>e<br />

Zeichenkette erwartet, können e<strong>in</strong>fache Ausgaben auf <strong>de</strong>n Bildschirm geschrieben<br />

wer<strong>de</strong>n. Mit Hilfe <strong>de</strong>s Plus-Operators „+“ können Zeichenketten und numerische<br />

Argumente verknüpft wer<strong>de</strong>n, so daß man neben Text auch Zahlen ausgeben kann.<br />

E<strong>in</strong>fache E<strong>in</strong>gaben vom Standard-E<strong>in</strong>gabegerät<br />

125 Mit <strong>de</strong>r Klasse InputStream können Leseoperationen e<strong>in</strong>es Bytestroms verwirklicht wer<strong>de</strong>n, vgl. 7.1.1<br />

126 Die Klasse Pr<strong>in</strong>tStream bezieht sich auf die I/O von <strong>Java</strong> 1.0<br />

128


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Lei<strong>de</strong>r ist es etwas komplizierter Daten zeichenweise von <strong>de</strong>r Tastatur zu lesen. Es<br />

steht zwar e<strong>in</strong> vor<strong>de</strong>f<strong>in</strong>ierter E<strong>in</strong>gabestrom System.<strong>in</strong> zur Verfügung, <strong>de</strong>r aber nicht<br />

die e<strong>in</strong>gelesenen Zeichen primitiver Typen konvertieren kann. Statt <strong>de</strong>ssen muß e<strong>in</strong>e<br />

Instanz <strong>de</strong>r Klasse „InputStreamRea<strong>de</strong>r“ 127 und daraus e<strong>in</strong> „BufferedRea<strong>de</strong>r“<br />

erzeugt wer<strong>de</strong>n. Dieser kann zum Lesen e<strong>in</strong>er zeilenweisen E<strong>in</strong>gabe mit<br />

Umwandlung <strong>de</strong>s Ergebnisses <strong>in</strong> e<strong>in</strong>en primitiven Typ verwen<strong>de</strong>t wer<strong>de</strong>n, z.B. 128 :<br />

"E<strong>in</strong>lesen von zwei ganzen Zahlen".<br />

import java.io.*;<br />

public class E<strong>in</strong>gZweiZahlen<br />

{<br />

// E<strong>in</strong>lesen von zwei ganzen Zahlen<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[]) throws IOException<br />

{<br />

<strong>in</strong>t a, b, c;<br />

BufferedRea<strong>de</strong>r e<strong>in</strong> = new BufferedRea<strong>de</strong>r(new<br />

InputStreamRea<strong>de</strong>r(System.<strong>in</strong>));<br />

System.out.pr<strong>in</strong>t("Bitte a e<strong>in</strong>geben: ");<br />

a = Integer.parseInt(e<strong>in</strong>.readL<strong>in</strong>e());<br />

System.out.pr<strong>in</strong>t("Bitte b e<strong>in</strong>geben: ");<br />

b = Integer.parseInt(e<strong>in</strong>.readL<strong>in</strong>e());<br />

c = a + b;<br />

System.out.pr<strong>in</strong>tln("a + b = " + c);<br />

}<br />

}<br />

1.4.4.2 Datenströme<br />

Das grundlegen<strong>de</strong> Mo<strong>de</strong>ll für E<strong>in</strong>- und Ausgabe s<strong>in</strong>d Datenströme (Character-<br />

Streams), die im Paket "java.io" versammelt s<strong>in</strong>d. Das Datenstrom-Mo<strong>de</strong>ll geht<br />

davon aus, daß<br />

- Programme Zeichenfolgen aus e<strong>in</strong>em Datenstrom lesen bzw. <strong>in</strong> e<strong>in</strong>en Datenstrom schreiben<br />

- e<strong>in</strong> Datenstrom unabhängig von <strong>de</strong>n Fähigkeiten <strong>de</strong>s jeweiligen I/O-Geräts 129 organisiert wer<strong>de</strong>n<br />

muß.<br />

Streams können auch verkettet wer<strong>de</strong>n und verschachtelt wer<strong>de</strong>n. Das Schachteln<br />

erlaubt die Konstruktion von Filtern, die bei <strong>de</strong>r E<strong>in</strong>-/Ausgabe bestimmte<br />

Zusatzfunktionen übernehmen, z.B. das Puffern von Zeichen, das Zählen von<br />

Zeichen o<strong>de</strong>r die Interpretation b<strong>in</strong>ärer Dateien.<br />

Alle Klassen zur Datene<strong>in</strong>- bzw. Datenausgabe bef<strong>in</strong><strong>de</strong>n sich im Paket java.io und<br />

s<strong>in</strong>d für Zeichenströme 130 von <strong>de</strong>n abstrakten Klassen Rea<strong>de</strong>r bzw. Writer 131<br />

abgeleitet. Allgeme<strong>in</strong>e Fehlanzeige im Paket java.io ist die Ausnahme<br />

IOException, die <strong>de</strong>shalb auch von e<strong>in</strong>er Vielzahl von Metho<strong>de</strong>n ausgelöst wer<strong>de</strong>n<br />

kann und dabei sehr unterschiedliche Be<strong>de</strong>utung annehmen kann.<br />

127 Das grundlegen<strong>de</strong> Konzept für E<strong>in</strong>- und Ausgabe s<strong>in</strong>d Datenströme, die im Paket „java.io“ versammelt<br />

s<strong>in</strong>d.<br />

128 Vgl. pr14150<br />

129 Hauptspeicher, Festplatte, Drucker, Netzwerk<br />

130 Für Byte-Ströme gibt es die Klassen InputStream / OutputStream e<strong>in</strong>schl. zahlreicher Subklassen<br />

131 vgl. 7.6<br />

129


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Rea<strong>de</strong>r<br />

InputStreamRea<strong>de</strong>r BufferedRea<strong>de</strong>r FilterRea<strong>de</strong>r<br />

FileRea<strong>de</strong>r<br />

Writer<br />

OutputStreamWriter BufferedWriter FilterWriter<br />

FileWriter<br />

Abb. Subklassen von Rea<strong>de</strong>r / Writer<br />

Bsp.: Das folgen<strong>de</strong> Programm liest e<strong>in</strong>e Zeichenfolge aus e<strong>in</strong>em E<strong>in</strong>gabestrom und<br />

schreibt die Zeichenfolge <strong>in</strong> e<strong>in</strong>en Ausgabestrom 132<br />

import java.io.*;<br />

public class EchoE<strong>in</strong>Aus extends Object<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

InputStreamRea<strong>de</strong>r isr = new InputStreamRea<strong>de</strong>r(System.<strong>in</strong>);<br />

BufferedRea<strong>de</strong>r e<strong>in</strong> = new BufferedRea<strong>de</strong>r(isr);<br />

Str<strong>in</strong>g e<strong>in</strong>gabeZeile = null;<br />

// OutputStreamWriter osw = new OutputStreamWriter(System.out);<br />

Pr<strong>in</strong>tWriter aus = new Pr<strong>in</strong>tWriter(System.out);<br />

while (true)<br />

{<br />

aus.pr<strong>in</strong>t("Gib e<strong>in</strong>e Zeichenkette e<strong>in</strong>: ");<br />

aus.flush();<br />

try {<br />

e<strong>in</strong>gabeZeile = e<strong>in</strong>.readL<strong>in</strong>e();<br />

}<br />

catch (IOException e)<br />

{<br />

aus.pr<strong>in</strong>tln(e.toStr<strong>in</strong>g());<br />

System.exit(0);<br />

}<br />

if (e<strong>in</strong>gabeZeile == null)<br />

{<br />

aus.pr<strong>in</strong>tln();<br />

aus.flush();<br />

break;<br />

}<br />

aus.pr<strong>in</strong>tln(e<strong>in</strong>gabeZeile);<br />

}<br />

}<br />

}<br />

Für die E<strong>in</strong>gabe wur<strong>de</strong> herangezogen:<br />

132 vgl. pr14150<br />

130


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

- InputStream <strong>in</strong> <strong>de</strong>r Klasse System aus <strong>de</strong>m Paket java.lang<br />

- die abstrakte Klasse InputStream <strong>de</strong>s Pakets "java.io"<br />

- die Klasse InputStreamRea<strong>de</strong>r <strong>de</strong>s Pakets "java.io"<br />

- die Metho<strong>de</strong> "Str<strong>in</strong>g readL<strong>in</strong>e()" mit <strong>de</strong>r Klasse BufferedRea<strong>de</strong>r <strong>de</strong>s Pakets java.io<br />

Für die Ausgabe kann ebenso verfahren wer<strong>de</strong>n. Man zieht <strong>in</strong> diesem<br />

Zusammenhang zur Ausgabe heran:<br />

- die Klasse Pr<strong>in</strong>tStream (Subklasse <strong>de</strong>r abstrakten Klasse OutputStream)<br />

- die Klasse BufferedWriter (Subklasse von Writer). Es gibt aber ke<strong>in</strong> Gegenstück zu "Str<strong>in</strong>g<br />

readL<strong>in</strong>e()".<br />

- die Klasse Pr<strong>in</strong>tWriter (Subklasse von Writer) für pr<strong>in</strong>tln(...)<br />

1.4.4.3 E<strong>in</strong>gabe und Ausgabe mit Dateien<br />

Dateien dienen zur Ablage von Daten auf externen Speichern. Dateien wer<strong>de</strong>n zur<br />

besseren Übersicht auf externen Speichern <strong>in</strong> sog. Verzeichnissen gespeichert – <strong>de</strong>r<br />

Zugriff auf e<strong>in</strong>e Datei ist somit über e<strong>in</strong>en Verzeichnispfad und über <strong>de</strong>n Date<strong>in</strong>amen<br />

möglich. Unter <strong>de</strong>r Vielzahl verschie<strong>de</strong>ner Dateiformate s<strong>in</strong>d beson<strong>de</strong>rs<br />

hervorzuheben: Text- und B<strong>in</strong>ärdateien.<br />

Byte-weises Schreiben und Lesen von (B<strong>in</strong>är-) Dateien<br />

Zum (Byte-weisen) Schreiben und Lesen von Dateien wer<strong>de</strong>n verwen<strong>de</strong>t: Die<br />

Klassen FileOutputStream und FileInputStream.<br />

1. Bsp.: Ausgabe e<strong>in</strong>er B<strong>in</strong>ärdatei<br />

import java.io.*;<br />

public class pr14444a<br />

{<br />

public static void ma<strong>in</strong> (Str<strong>in</strong>g args[])<br />

{<br />

File f = new File("datei.b<strong>in</strong>");<br />

try {<br />

DataOutputStream raus = new DataOutputStream(<br />

new FileOutputStream(f));<br />

for (<strong>in</strong>t i=1;i


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

try {<br />

DataInputStream re<strong>in</strong> = new DataInputStream(<br />

new FileInputStream(f));<br />

for (<strong>in</strong>t i=1;i


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

{<br />

// Freigabe <strong>de</strong>r Syntax-Resourcen<br />

try {<br />

e<strong>in</strong>.close();<br />

}<br />

catch(IOException a2)<br />

{<br />

System.out.pr<strong>in</strong>tln("e<strong>in</strong>.close() nicht erfolgreich");<br />

}<br />

}<br />

}<br />

2. Bsp.: Das folgen<strong>de</strong> Programm bearbeitet die E<strong>in</strong>gabedatei.<br />

import java.io.*;<br />

public class BearbeiteE<strong>in</strong>gabeDatei extends Object<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

try {<br />

// Erzeugen e<strong>in</strong>er Instanz<br />

E<strong>in</strong>gabeDatei e<strong>in</strong> =<br />

new E<strong>in</strong>gabeDatei("BearbeiteE<strong>in</strong>gabeDatei.java");<br />

Str<strong>in</strong>g s;<br />

<strong>in</strong>t i = 1;<br />

while ((s = e<strong>in</strong>.holeZeile()) != null)<br />

System.out.pr<strong>in</strong>tln("" + i++ + ": " + s);<br />

e<strong>in</strong>.bere<strong>in</strong>igen();<br />

}<br />

catch (Exception a)<br />

{<br />

System.out.pr<strong>in</strong>tln("E<strong>in</strong>gefangen <strong>in</strong> ma<strong>in</strong>(), a.pr<strong>in</strong>tStackTrace()");<br />

a.pr<strong>in</strong>tStackTrace();<br />

}<br />

}<br />

}<br />

Das Beispiel zeigt: E<strong>in</strong>gabeströme können verschachtelt wer<strong>de</strong>n. Folgen<strong>de</strong> Klassen<br />

stehen dafür zur Verfügung (, die im Konstruktor e<strong>in</strong>en Rea<strong>de</strong>r erwarten):<br />

BufferedRea<strong>de</strong>r, L<strong>in</strong>eNumberRea<strong>de</strong>r, FilterRea<strong>de</strong>r und PushbackRea<strong>de</strong>r.<br />

E<strong>in</strong> BufferedRea<strong>de</strong>r puffert Daten <strong>in</strong> e<strong>in</strong>em 8192 Bytes großen Zwischenspeicher.<br />

Dadurch müssen weniger Zugriffe auf <strong>de</strong>n Datenträger gemacht wer<strong>de</strong>n. Zusätzlich<br />

stellt BufferedRea<strong>de</strong>r die Metho<strong>de</strong> public Str<strong>in</strong>g readL<strong>in</strong>e() zur<br />

Verfügung, die e<strong>in</strong>e komplette Textzeile e<strong>in</strong>liest und als Str<strong>in</strong>g an <strong>de</strong>n Aufrufer<br />

zurückgibt. „null“ wird zurückgegeben, wenn <strong>de</strong>r Stream am En<strong>de</strong> ist.<br />

E<strong>in</strong> L<strong>in</strong>eNumberRea<strong>de</strong>r liest die E<strong>in</strong>gabezeilen und zählt gleichzeitig die Zeilen, die<br />

gelesen wur<strong>de</strong>n. Mit public <strong>in</strong>t getL<strong>in</strong>eNumber() bzw. public void<br />

setL<strong>in</strong>eNumber(<strong>in</strong>t zeilenNummer) läßt sich auf die die Zeilennummern<br />

zugreifen.<br />

Ausgabe mit Dateien<br />

Die Ausgabe <strong>in</strong> Dateien wird unterstützt durch die Klasse FileWriter.<br />

FileWriter ist die e<strong>in</strong>zige Subklasse von OutputStreamWriter. Je<strong>de</strong>r<br />

OutputStreamWriter agiert mir e<strong>in</strong>em CharToByteConverter und konvertiert<br />

so Zeichenströme <strong>in</strong> Byteströme.<br />

import java.io.*;<br />

public class CharArrayWriterDemo<br />

{<br />

133


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

Str<strong>in</strong>g s;<br />

try<br />

{<br />

Pr<strong>in</strong>tWriter pw = new Pr<strong>in</strong>tWriter(new BufferedWriter(<br />

new FileWriter("charArrayWriterDemoPuffer.txt")));<br />

for (<strong>in</strong>t i = 1; i < 1000; i++) pw.pr<strong>in</strong>tln("Zeile " + i);<br />

pw.close();<br />

}<br />

catch (IOException e)<br />

{ System.out.pr<strong>in</strong>tln("Konnte Datei nicht erstellen"); }<br />

}<br />

}<br />

In <strong>Java</strong> gibt es drei Klassen BufferedWriter (mit standardmäßig 8192 Bytes<br />

großen Puffer), Pr<strong>in</strong>tWriter, FilterWriter, die e<strong>in</strong>en Writer im Konstruktor<br />

erlauben und ihre Ausgabe dorth<strong>in</strong> weiterleiten. Zu Beg<strong>in</strong>n <strong>de</strong>s vorliegen<strong>de</strong>n<br />

Programms 133 wird e<strong>in</strong> FileWriter erstellt. Anschließend erzeugt man e<strong>in</strong>en<br />

BufferedWriter, <strong>de</strong>r die Daten, die <strong>in</strong> die Datei geschrieben wer<strong>de</strong>n, sammelt.<br />

Dieser BufferedWriter wird zu e<strong>in</strong>em Pr<strong>in</strong>tWriter erweitert. Da e<strong>in</strong><br />

Pr<strong>in</strong>tWriter Schreibfunktionen besitzt, ist man auf write()-Metho<strong>de</strong>n nicht mehr<br />

angewiesen, son<strong>de</strong>rn kann die komfortablen pr<strong>in</strong>t()-Funktionen nutzen.<br />

Umlenken von Standar<strong>de</strong><strong>in</strong>gabe, Standardausgabe und Standardfehlerausgabe<br />

In <strong>Java</strong> 1.1 ermöglichen die folgen<strong>de</strong>n Metho<strong>de</strong>n das Umlenken:<br />

- setIn(InputStream)<br />

- setOut(Pr<strong>in</strong>tStream)<br />

- setErr(Pr<strong>in</strong>tStream)<br />

Bsp.: Demonstration <strong>de</strong>r Umlenkung von Standar<strong>de</strong><strong>in</strong>gabe und Standardausgabe <strong>in</strong><br />

<strong>Java</strong> 1.1 134<br />

import java.io.*;<br />

class Umlenken<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

try<br />

{<br />

BufferedInputStream e<strong>in</strong> =<br />

new BufferedInputStream(new FileInputStream("Umlenken.java"));<br />

// produziert "<strong>de</strong>precation"-Nachrichten, System.setOut() und<br />

// System.setErr() erfor<strong>de</strong>rn e<strong>in</strong> Pr<strong>in</strong>tStream-Objekt als Argument<br />

Pr<strong>in</strong>tStream aus = new Pr<strong>in</strong>tStream(<br />

new BufferedOutputStream(<br />

new FileOutputStream("test.aus")));<br />

System.setIn(e<strong>in</strong>); System.setOut(aus); System.setErr(aus);<br />

BufferedRea<strong>de</strong>r br =<br />

new BufferedRea<strong>de</strong>r(new InputStreamRea<strong>de</strong>r(System.<strong>in</strong>));<br />

Str<strong>in</strong>g s;<br />

while ((s = br.readL<strong>in</strong>e()) != null)<br />

System.out.pr<strong>in</strong>tln(s);<br />

aus.close();<br />

133 vgl. pr14330<br />

134 vgl. pr14150<br />

134


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

}<br />

catch(IOException a)<br />

{<br />

a.pr<strong>in</strong>tStackTrace();<br />

}<br />

}<br />

}<br />

Random Access Files 135<br />

wer<strong>de</strong>n über die Klasse Random Access File realisiert. Beim Konstruktor<br />

RandomAccessFile(Str<strong>in</strong>g,Zugriffsmodus) wird neben <strong>de</strong>m Date<strong>in</strong>amen<br />

auch die Zugriffsart angegegeben ("r" nur für Lesezugriffe, "rw" zum Lesen und<br />

Schreiben). Mit <strong>de</strong>r Metho<strong>de</strong> seek(long) kann <strong>de</strong>r Dateizeiger (FilePo<strong>in</strong>ter) gesetzt<br />

wer<strong>de</strong>n, getFilePo<strong>in</strong>ter() liefert die aktuelle Position <strong>de</strong>s FilePo<strong>in</strong>ter.<br />

Weiterh<strong>in</strong> stehen zur Verfügung: length(), close(), readL<strong>in</strong>e(), read(), readByte(),<br />

readShort(), readInt(), readLong(), readFloat(), readDouble(), readChar(),<br />

readBoolean() und die analogen write-Metho<strong>de</strong>n.<br />

Bsp.: E<strong>in</strong>- und Ausgabe von und nach Dateien mit wahlfreiem Zugriff 136<br />

1. Erzeugen <strong>de</strong>r Datei rafrw (Datei mit wahlfreiem Zugriff). Auf rafrw wer<strong>de</strong>n ganze Zahlen<br />

geschrieben.<br />

2. Erzeugen <strong>de</strong>r Datei rafr (Datei mit wahlfreiem Zugriff), von <strong>de</strong>r ganze Zahlen gelesen wer<strong>de</strong>n.<br />

import java.io.*;<br />

public class RandomAccessTest<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

throws java.io.IOException<br />

{<br />

BufferedRea<strong>de</strong>r br = new BufferedRea<strong>de</strong>r<br />

(new InputStreamRea<strong>de</strong>r(System.<strong>in</strong>));<br />

<strong>in</strong>t <strong>in</strong>tLength = 4; // Laenge <strong>de</strong>r Darstellung von ganzen Zahlen<br />

// updat<strong>in</strong>g part<br />

try<br />

{<br />

RandomAccessFile rafrw = new RandomAccessFile("testfile", "rw");<br />

System.out.pr<strong>in</strong>t("\nAen<strong>de</strong>rn e<strong>in</strong>es Datensatzes <strong>de</strong>r Datei\n");<br />

// Datensatznummer erfragen und Dateizeiger setzen und anzeigen<br />

System.out.pr<strong>in</strong>t("Datei geoeffnet. ");<br />

System.out.pr<strong>in</strong>t("Dateizeiger="+rafrw.getFilePo<strong>in</strong>ter()<br />

+" DateiLaenge="+rafrw.length()+" Byte(s)\n");<br />

System.out.pr<strong>in</strong>t("\n? Welchen Datensatz aen<strong>de</strong>rn = ");<br />

long pos = <strong>in</strong>tLength * (new Integer(br.readL<strong>in</strong>e()).<strong>in</strong>tValue());<br />

rafrw.seek(pos);<br />

System.out.pr<strong>in</strong>t( "Dateizeiger="+rafrw.getFilePo<strong>in</strong>ter()+"\n");<br />

// Falls vorhan<strong>de</strong>n, Datensatz anzeigen<br />

if (pos < rafrw.length())<br />

{<br />

// Datensatz anzeigen, Dateizeiger wie<strong>de</strong>rherstellen<br />

System.out.pr<strong>in</strong>t("Datensatz = "+rafrw.readInt()+"\n");<br />

rafrw.seek(pos);<br />

}<br />

else<br />

System.out.pr<strong>in</strong>t("Aha, Datensatz soll angefuegt wer<strong>de</strong>n.\n");<br />

// Neuen Datensatz e<strong>in</strong>lesen<br />

System.out.pr<strong>in</strong>t("\n? Neuer Datensatz = ");<br />

135 vgl. 7.4<br />

136 vgl. pr14444<br />

135


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

}<br />

<strong>in</strong>t val = new Integer(br.readL<strong>in</strong>e()).<strong>in</strong>tValue();<br />

// Neuen Datensatz <strong>in</strong> die Datei schreiben, Dateizeiger anzeigen<br />

rafrw.writeInt(val);<br />

System.out.pr<strong>in</strong>t( "Nach <strong>de</strong>m Schreiben: Dateizeiger="<br />

+rafrw.getFilePo<strong>in</strong>ter()+"\n");<br />

// test outpout part<br />

RandomAccessFile rafr = new RandomAccessFile("testfile", "r");<br />

System.out.pr<strong>in</strong>t("\nVoila, die geaen<strong>de</strong>rte Datei\n");<br />

// Wie<strong>de</strong>rgabe <strong>de</strong>r Datensaetze aus <strong>de</strong>r Datei<br />

// nach <strong>de</strong>r Veraen<strong>de</strong>rung<br />

<strong>in</strong>t i;<br />

try<br />

{<br />

while (true)<br />

{<br />

i = rafr.readInt();<br />

System.out.pr<strong>in</strong>t(i+" ");<br />

}<br />

} catch (EOFException e) {}<br />

rafrw.close();<br />

rafr.close();<br />

} catch (IOException e)<br />

{<br />

System.err.pr<strong>in</strong>tln("RandomAccessTest: " + e);<br />

}<br />

System.out.pr<strong>in</strong>t("\n");<br />

}<br />

136


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

1.4.4.4 Verwalten von Dateien und Verzeichnissen durch die Klasse File<br />

Informationen über e<strong>in</strong>e Datei erhält man über e<strong>in</strong> File-Objekt von class File<br />

java.io.File implements Serializable, Comparable. E<strong>in</strong> File-Objekt<br />

repräsentiert e<strong>in</strong>e Datei o<strong>de</strong>r e<strong>in</strong> Verzeichnis auf <strong>de</strong>m Dateisystem. Der Verweis auf<br />

Datei bzw. Verzeichnis wird durch e<strong>in</strong>en Pfadnamen spezifiziert. Pfadnamen 137<br />

können absolut o<strong>de</strong>r relativ zum aktuellen Verzeichnis angegeben wer<strong>de</strong>n.<br />

Dateieigenschaften und Dateiattribute können über zahlreiche Metho<strong>de</strong>n <strong>de</strong>r Klasse<br />

File 138 gesetzt bzw. wer<strong>de</strong>n.<br />

Bsp.:<br />

import java.io.*;<br />

// import java.nio.charset.*;<br />

public class FileInfo<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

File f = new File("./FileInfo.java");<br />

System.out.pr<strong>in</strong>tln("The length of " + f.getName() +<br />

" is " + f.length() + " bytes.");<br />

try {<br />

System.out.pr<strong>in</strong>tln(f.getName() + " is <strong>in</strong> the " +<br />

f.getCanonicalPath() + " directory.");<br />

}<br />

catch (IOException e) { System.out.pr<strong>in</strong>tln("exception");}<br />

}<br />

}<br />

Weitere wichtige Metho<strong>de</strong>n <strong>de</strong>r Klasse File 139 s<strong>in</strong>d: exit(), list(), <strong>de</strong>lete(),<br />

length(), mkdir(), renameTo(File ...), isDirectory(), isFile().<br />

1.4.4.5 Filtern mit <strong>de</strong>n Klasse FilterRea<strong>de</strong>r und FilterWriter<br />

Die abstrakten Filterklassen von java.io erlauben beim Lesen und Schreiben<br />

zusätzliche Operationen auszuführen.<br />

Bsp.: Filtern von Textdateien mit <strong>de</strong>r Extension .java aus <strong>de</strong>m aktuellen Verzeichnis<br />

/*<br />

* Auflisten <strong>de</strong>s Inhalts vom aktuellen Verzeichnis und Filtern<br />

*/<br />

import java.io.*;<br />

class DateiFilter implements FilenameFilter<br />

{<br />

public boolean accept(File f,Str<strong>in</strong>g s)<br />

{<br />

if (s.toLowerCase().endsWith(".java"))<br />

return true;<br />

return false;<br />

}<br />

}<br />

137 Pfadnamen s<strong>in</strong>d plattformabhängig:<br />

Bei W<strong>in</strong>dows-Rechnern trennt e<strong>in</strong> BackSlash die Pfa<strong>de</strong><br />

Bei Unix-Rechnern trennt e<strong>in</strong> Slash die Pfa<strong>de</strong>.<br />

138 vgl. 7.3<br />

139 vgl. 7.3<br />

137


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

public class Verzeichnistest<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

Str<strong>in</strong>g nameVerz = System.getProperty("user.dir");<br />

File userdir = new File(nameVerz);<br />

// System.out.pr<strong>in</strong>tln(System.getProperty("user.dir"));<br />

Str<strong>in</strong>g e<strong>in</strong>traege [] = userdir.list();<br />

System.out.pr<strong>in</strong>tln("Alle E<strong>in</strong>traege aus <strong>de</strong>m Verzeichnis " + nameVerz);<br />

for (<strong>in</strong>t i = 0;i < e<strong>in</strong>traege.length; i++)<br />

System.out.pr<strong>in</strong>tln(e<strong>in</strong>traege[i]);<br />

System.out.pr<strong>in</strong>tln("Alle E<strong>in</strong>traege aus <strong>de</strong>m Verzeichnis mit Endung .java "<br />

+ nameVerz);<br />

e<strong>in</strong>traege = userdir.list(new DateiFilter());<br />

// Arbeitet wie die Metho<strong>de</strong> list(), nur filtert e<strong>in</strong> spezielles FileName-<br />

// Filter-Objekt bestimmte Namen heraus<br />

for (<strong>in</strong>t i = 0;i < e<strong>in</strong>traege.length; i++)<br />

System.out.pr<strong>in</strong>tln(e<strong>in</strong>traege[i]);<br />

}<br />

}<br />

E<strong>in</strong>gabefilter<br />

Die Klasse FilterRea<strong>de</strong>r erhält <strong>de</strong>n eigentlichen Rea<strong>de</strong>r über <strong>de</strong>n Konstruktor und<br />

speichert ihn als Membervariable.<br />

Bsp. 140 : Die Klasse CaseFilter implementiert e<strong>in</strong>e erweiterte Funktionalität für<br />

read(). Beim E<strong>in</strong>lesen wer<strong>de</strong>n Kle<strong>in</strong>buchstaben <strong>in</strong> Großbuchstaben umgewan<strong>de</strong>lt.<br />

import java.io.*;<br />

public class CaseFilter extends FilterRea<strong>de</strong>r<br />

{<br />

public CaseFilter(Rea<strong>de</strong>r f)<br />

{<br />

super(f);<br />

}<br />

public <strong>in</strong>t read() throws IOException<br />

{<br />

<strong>in</strong>t zeichen = super.read();<br />

if (Character.isLowerCase((char) zeichen))<br />

return Character.toUpperCase((char) zeichen);<br />

else return zeichen;<br />

}<br />

}<br />

In <strong>de</strong>r Klasse DekoStream wird nun das read() e<strong>in</strong>es CaseFilter-Objekts<br />

verwen<strong>de</strong>t. Direkt beim E<strong>in</strong>lesen wird <strong>de</strong>r Text formatiert.<br />

import java.io.*;<br />

public class DekoStream<br />

{<br />

public DekoStream()<br />

{<br />

Str<strong>in</strong>g s = readNormal("DekoStream.java");<br />

System.out.pr<strong>in</strong>tln(s);<br />

s = readFilter("DekoStream.java");<br />

System.out.pr<strong>in</strong>tln(s);<br />

}<br />

140 vgl. pr14341<br />

138


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

private Str<strong>in</strong>g readNormal(Str<strong>in</strong>g fl)<br />

{<br />

Str<strong>in</strong>gBuffer s = new Str<strong>in</strong>gBuffer();<br />

try {<br />

FileRea<strong>de</strong>r fread = new FileRea<strong>de</strong>r(fl);<br />

<strong>in</strong>t zeichen;<br />

while ((zeichen = fread.read()) != -1)<br />

s.append((char) zeichen);<br />

fread.close();<br />

}<br />

catch(IOException e) { System.err.pr<strong>in</strong>tln(e); }<br />

return s.toStr<strong>in</strong>g();<br />

}<br />

private Str<strong>in</strong>g readFilter(Str<strong>in</strong>g fl)<br />

{<br />

Str<strong>in</strong>gBuffer s = new Str<strong>in</strong>gBuffer();<br />

try {<br />

FileRea<strong>de</strong>r fread = new FileRea<strong>de</strong>r(fl);<br />

CaseFilter ff = new CaseFilter(fread);<br />

/* Die CaseFilter-Klasse implementiert e<strong>in</strong>e erweiterte<br />

Funktionalitaet fuer read(). Beim E<strong>in</strong>lesen wer<strong>de</strong>n<br />

Kle<strong>in</strong>buchstaben <strong>in</strong> Grossbuchstaben umgewan<strong>de</strong>lt */<br />

<strong>in</strong>t zeichen;<br />

while ((zeichen = ff.read()) != -1)<br />

s.append((char) zeichen);<br />

ff.close();<br />

}<br />

catch(IOException e) { System.err.pr<strong>in</strong>tln(e); }<br />

return s.toStr<strong>in</strong>g();<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g [] args)<br />

{<br />

new DekoStream();<br />

}<br />

}<br />

Ab JDK 1.1 gibt es e<strong>in</strong>en vor<strong>de</strong>f<strong>in</strong>ierten E<strong>in</strong>gabefilter PushBackRea<strong>de</strong>r, <strong>de</strong>r aus<br />

FilterRea<strong>de</strong>r abgeleitet ist. E<strong>in</strong> PushBackRea<strong>de</strong>r erweitert die Klasse<br />

FilterRea<strong>de</strong>r um e<strong>in</strong>en e<strong>in</strong> Byte großen PushBack-Puffer. Dieser erlaubt e<strong>in</strong>er<br />

Anwendung, das zuletzt gelesenen Zeichen wie<strong>de</strong>r <strong>in</strong> <strong>de</strong>n E<strong>in</strong>gabestrom zurück zu<br />

schieben. Der nächste Lesezugriff liest dann nicht das folgen<strong>de</strong> Zeichen im<br />

E<strong>in</strong>gabestrom, son<strong>de</strong>rn das gera<strong>de</strong> zurückgelesene Zeichen.<br />

Ausgabefilter<br />

Die Architektur <strong>de</strong>r Writer-Klassen macht es möglich eigene Filter zu konstruieren.<br />

Das kann bspw. durch Überlagern <strong>de</strong>r Klasse Writer 141 geschehen. Der offizielle<br />

Weg besteht allerd<strong>in</strong>gs <strong>in</strong> <strong>de</strong>r Überlagerung <strong>de</strong>r abstrakten Klasse FilterWriter.<br />

FilterWriter besitzt e<strong>in</strong> <strong>in</strong>ternes Writer-Objekt out, das bei <strong>de</strong>r Initialisierung an<br />

<strong>de</strong>n Konstruktor übergeben wird. Zusätzlich überlagert es drei <strong>de</strong>r vier write-<br />

Metho<strong>de</strong>n, um die Ausgabe auf out umzuleiten.<br />

Damit e<strong>in</strong> eigener FilterWriter erzeugt wer<strong>de</strong>n kann, muß beachtet wer<strong>de</strong>n:<br />

- die eigene Klasse leitet sich von FilterWriter ab<br />

- <strong>de</strong>r Konstruktor bekommt als Parameter e<strong>in</strong> Writer-Objekt und ruft mit super(out) <strong>de</strong>n Konstruktor<br />

<strong>de</strong>r Superklasse auf. Das ist die Klasse FilterWriter.<br />

141 wie es bspw. bei Pr<strong>in</strong>tWriter und BufferedWriter realisier wur<strong>de</strong>.<br />

139


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

- Überlagern <strong>de</strong>r drei write() Metho<strong>de</strong>n, Ausführung <strong>de</strong>r Filterfunktionen, Weitergabe <strong>de</strong>r wahren<br />

Daten an <strong>de</strong>n Writer<br />

Bsp. 142 : Konvertieren aller Zeichen <strong>in</strong> Großbuchstaben <strong>in</strong>nerhalb e<strong>in</strong>es Streams.<br />

import java.io.*;<br />

class UpCaseWriter extends FilterWriter<br />

{<br />

public UpCaseWriter(Writer out)<br />

{<br />

super(out);<br />

}<br />

public void write(<strong>in</strong>t c) throws IOException<br />

{<br />

super.write(Character.toUpperCase((char) c));<br />

}<br />

public void write(char cbuff[], <strong>in</strong>t off, <strong>in</strong>t len)<br />

throws IOException<br />

{<br />

for (<strong>in</strong>t i = 0; i < len; i++)<br />

write(cbuff[off + i]);<br />

}<br />

public void write(Str<strong>in</strong>g str, <strong>in</strong>t off, <strong>in</strong>t len)<br />

throws IOException<br />

{<br />

write(str.toCharArray(),off,len);<br />

}<br />

}<br />

Test:<br />

import java.io.*;<br />

public class PR14441<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

Pr<strong>in</strong>tWriter f;<br />

Str<strong>in</strong>g s = " und diese Zeichenkette auch";<br />

try {<br />

f = new Pr<strong>in</strong>tWriter(<br />

new UpCaseWriter(new FileWriter("upcase.txt")));<br />

f.pr<strong>in</strong>tln("Diese Zeile wird gross geschrieben");<br />

/* Die drei write() Metho<strong>de</strong>n wer<strong>de</strong>n <strong>in</strong> UpCaseWriter<br />

do überlagert, dass zunächst Ausgabezeichen <strong>in</strong><br />

Grossschrift konvertiert und anschliessend die<br />

passen<strong>de</strong> Superklassen-Metho<strong>de</strong> aufgerufen wird, um<br />

die Datei an <strong>de</strong>n <strong>in</strong>ternen Writer out zu uebergeben<br />

*/<br />

// Test write(<strong>in</strong>t);<br />

f.pr<strong>in</strong>tln();<br />

// Test write(Str<strong>in</strong>g)<br />

f.write(s); f.pr<strong>in</strong>tln();<br />

// Test von write(Str<strong>in</strong>g,<strong>in</strong>t,<strong>in</strong>t)<br />

f.write(s,0,23); f.pr<strong>in</strong>tln();<br />

// Test von write(char[],<strong>in</strong>t,<strong>in</strong>t)<br />

f.write(s.toCharArray(),0,10);<br />

f.pr<strong>in</strong>tln();<br />

f.close();<br />

}<br />

catch(IOException e)<br />

{<br />

System.out.pr<strong>in</strong>tln("Fehler beim Erstellen <strong>de</strong>r Datei");<br />

}<br />

142 vgl. pr14341<br />

140


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

}<br />

}<br />

1.4.4.6 Der Dateiauswahl-Dialog<br />

E<strong>in</strong>e spezielle Klasse FileDialog (class java.awt.FileDialog extends<br />

Dialog) zeigt e<strong>in</strong>en vom Betriebssystem unabhängigen Dateiauswahl-Dialog. Er<br />

kann für das Speichern und Öffnen konfiguriert se<strong>in</strong>. Er lassen sich die Pfa<strong>de</strong> und e<strong>in</strong><br />

FilenameFilter setzen. Erst nach <strong>de</strong>m Schließen und Been<strong>de</strong>n mit <strong>de</strong>m OK-<br />

Button stehen ausgewählte Dateien zur Verfügung:<br />

Bsp.:<br />

import java.awt.*;<br />

public class DateiAuswahl extends Frame<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args [])<br />

{<br />

Frame f = new Frame();<br />

// e<strong>in</strong> Objekt von Frame wird <strong>de</strong>m FileDialog mit auf <strong>de</strong>n<br />

// Weg gegeben<br />

FileDialog d = new FileDialog(f,"oeffne was",FileDialog.LOAD);<br />

d.setFile("*.java");<br />

d.show();<br />

Str<strong>in</strong>g datei = d.getDirectory() + d.getFile();<br />

System.out.pr<strong>in</strong>tln(datei);<br />

}<br />

}<br />

141


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

2. Hauptbestandteile <strong>de</strong>r Sprache<br />

2.1 Token<br />

Token be<strong>de</strong>uten <strong>in</strong> <strong>Java</strong> dasselbe wie Worte und Zeichensetzung für die<br />

menschliche Sprache. Wenn <strong>Java</strong> <strong>de</strong>n Quellco<strong>de</strong> mit <strong>de</strong>m <strong>Java</strong>-Compiler (javac)<br />

kompiliert, f<strong>in</strong><strong>de</strong>t e<strong>in</strong>e Zerlegung <strong>de</strong>s Quellco<strong>de</strong>s <strong>in</strong> kle<strong>in</strong>e Bestandteile, Symbole<br />

o<strong>de</strong>r Token genannt, statt. Token müssen sich <strong>in</strong> s<strong>in</strong>nvolle E<strong>in</strong>heiten und gültige<br />

Arten e<strong>in</strong>ordnen lassen. Das übernimmt <strong>de</strong>r sog. Parser <strong>de</strong>s javac, <strong>de</strong>r Quelltext <strong>in</strong><br />

Byteco<strong>de</strong> übersetzt und zusätzlich noch Leerzeichen und Kommentare (aus <strong>de</strong>m<br />

Quelltext) entfernt.<br />

Aus Token setzen sich die Ausdrucksformen von <strong>Java</strong> zusammen. Es gibt <strong>in</strong> <strong>Java</strong><br />

fünf Arten von Token: Bezeichner o<strong>de</strong>r I<strong>de</strong>ntifizierer, Schlüsselwörter, Literale,<br />

Operatoren, Trennzeichen.<br />

Token Beschreibung Bsp.<br />

Schlüsselworte Alle Wörter, die e<strong>in</strong> wesentlicher Teil <strong>de</strong>r Public, class, static, void, Str<strong>in</strong>g,<br />

<strong>Java</strong>-Sprach<strong>de</strong>f<strong>in</strong>ition s<strong>in</strong>d<br />

else, if, this, etc.<br />

Bezeichner, I<strong>de</strong>ntifiziere Namen für Klassen,Objekte, Variablen,<br />

Konstanten, Metho<strong>de</strong>n, etc.; Namen s<strong>in</strong>d<br />

zusammengesetzt aus Unico<strong>de</strong>-Zeichen.<br />

An erster Stelle e<strong>in</strong>es Bezeichners darf<br />

ke<strong>in</strong>e Zahl stehen.<br />

Literal<br />

Mit e<strong>in</strong>em Literal können Variablen und “Hallo <strong>Java</strong>“, 13, false, true<br />

Konstanten bestimmte Werte<br />

zugewiesen wer<strong>de</strong>n. Die können<br />

sämtliche <strong>in</strong> <strong>de</strong>r <strong>Java</strong>-Sprach<strong>de</strong>f<strong>in</strong>ition<br />

erlaubte Arten von Werten (numerische<br />

Werte, boolesche Werte, Zeichen,<br />

Zeichenketten) se<strong>in</strong><br />

Trennzeichen<br />

Symbol zum Anzeigen für Trennungen ( ) { } [ ] ; . ,<br />

und Zusammenfassungen von Co<strong>de</strong><br />

Operatoren<br />

Zeichen bzw. Zeichenkomb<strong>in</strong>ationen zur + - * / , >>>


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Bei <strong>de</strong>r Kompilierung wird <strong>de</strong>r Quellco<strong>de</strong> automatisch <strong>in</strong> e<strong>in</strong>e Folge von Unico<strong>de</strong>-<br />

Zeichen transformiert. E<strong>in</strong>e ASCII-Kodierung wird e<strong>in</strong>fach mit Voranstellen <strong>de</strong>r<br />

Zeichenfolge „\u00“ und folgen<strong>de</strong>r Hexa<strong>de</strong>zimalzahl <strong>in</strong> das passen<strong>de</strong> Unico<strong>de</strong>-<br />

Zeichen übersetzt. Dies <strong>de</strong>f<strong>in</strong>iert <strong>in</strong> <strong>Java</strong> e<strong>in</strong>e Escape-Sequenz, mit <strong>de</strong>r alle Unico<strong>de</strong>-<br />

Zeichen verschlüsselt wer<strong>de</strong>n können. Durch die „Escape-Sequenz-Darstellung“ <strong>de</strong>s<br />

Quelltextes <strong>in</strong> <strong>Java</strong> ist die Verwendung von Umlauten und an<strong>de</strong>ren Son<strong>de</strong>rzeichen <strong>in</strong><br />

Bezeichnern möglich.<br />

Zwei Bezeichner gelten <strong>in</strong> <strong>Java</strong> als i<strong>de</strong>ntisch, wenn ihre Unico<strong>de</strong>-Darstellung<br />

übere<strong>in</strong>stimmend ist. Deshalb muß auch <strong>in</strong> <strong>Java</strong> unbed<strong>in</strong>gt zwischen Groß- und<br />

Kle<strong>in</strong>schreibung unterschie<strong>de</strong>n wer<strong>de</strong>n.<br />

2.1.1 Schlüsselworte<br />

Es gibt bestimmte Buchstabenkomb<strong>in</strong>ationen, die <strong>in</strong> <strong>Java</strong> e<strong>in</strong>e beson<strong>de</strong>re Be<strong>de</strong>utung<br />

haben. Diese Buchstabenfolgen wer<strong>de</strong>n <strong>in</strong> <strong>Java</strong> Schlüsselworte genannt<br />

abstract boolean Break byte case cast<br />

catch char Class const cont<strong>in</strong>ue <strong>de</strong>fault<br />

do double Else extends false f<strong>in</strong>al<br />

f<strong>in</strong>ally float For future generic goto<br />

if implements Import <strong>in</strong>ner <strong>in</strong>stanceof <strong>in</strong>t<br />

<strong>in</strong>terface long Native new null operator<br />

outer package Private protected public rest<br />

return short Static super switch synchronized<br />

this throw Throws transient true try<br />

var void Volatile while<br />

Abb.: Reservierte <strong>Java</strong> Schlüsselworte<br />

2.1.2 Bezeichner und Namenskonventionen<br />

Bezeichner bzw. I<strong>de</strong>ntifizierer (i<strong>de</strong>ntifier) s<strong>in</strong>d Worte, die vom Programmierer<br />

gewählt, zu Token wer<strong>de</strong>n und die Variablen, Konstanten, Klassen, Objekte,<br />

Beschriftungen und Metho<strong>de</strong>n darstellen. Bezeichner dürfen nicht mit <strong>Java</strong>-<br />

Schlüsselworten i<strong>de</strong>ntisch se<strong>in</strong>.<br />

Es gibt <strong>in</strong> <strong>Java</strong> e<strong>in</strong>ige Regeln zur Namensvergabe, die unbed<strong>in</strong>gt e<strong>in</strong>gehalten wer<strong>de</strong>n<br />

müssen:<br />

- Bezeichner bestehen aus Unico<strong>de</strong>-Buchstaben und Zahlen <strong>in</strong> unbeschränkter Länge.<br />

- Das erste Zeichen e<strong>in</strong>es Bezeichners muß e<strong>in</strong> Buchstabe, <strong>de</strong>r Unterstrich „_“ o<strong>de</strong>r das Zeichen „$“ se<strong>in</strong>.<br />

Alle folgen<strong>de</strong>n Zeichen s<strong>in</strong>d entwe<strong>de</strong>r Buchstaben o<strong>de</strong>r Zahlen.<br />

- Zwei Token gelten nur dann als <strong>de</strong>rselbe Bezeichner, wenn sie dieselbe Länge haben, und je<strong>de</strong>s Zeichen im<br />

ersten Token genau mit <strong>de</strong>m korrespondieren<strong>de</strong>n Zeichen <strong>de</strong>s zweiten Token übere<strong>in</strong>stimmt, d.h. <strong>de</strong>n<br />

vollkommen i<strong>de</strong>ntischen Unico<strong>de</strong>-Wert hat. <strong>Java</strong> unterschei<strong>de</strong>t Groß- und Kle<strong>in</strong>schreibung.<br />

Es hat sich e<strong>in</strong>gebürgert, e<strong>in</strong>ige Namenskonventionen <strong>in</strong> <strong>Java</strong> e<strong>in</strong>zuhalten:<br />

- Man sollte möglichst „sprechen<strong>de</strong>“ Bezeichner wählen (Ausnahme s<strong>in</strong>d Schleifen, wo für Zählvariablen<br />

meistens nur e<strong>in</strong> Buchstabe (z.B. i, j) verwen<strong>de</strong>t wird.<br />

- Konstanten (Elemente mit <strong>de</strong>m „Modifizierer“ „f<strong>in</strong>al“) sollten vollständig groß geschrieben wer<strong>de</strong>n.<br />

144


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

- I<strong>de</strong>ntifizierer von Klassen sollten mit e<strong>in</strong>em Großbuchstaben beg<strong>in</strong>nen und anschließend kle<strong>in</strong> geschrieben<br />

wer<strong>de</strong>n. Wenn sich e<strong>in</strong> Bezeichner aus mehreren Wörtern zusammensetzt, dürfen diese nicht getrennt<br />

wer<strong>de</strong>n. Die jeweiligen Anfangsbuchstaben <strong>in</strong>nerhalb <strong>de</strong>s gesamten Bezeichners wer<strong>de</strong>n jedoch groß<br />

geschrieben.<br />

- Die I<strong>de</strong>ntifizierer von Variablen, Metho<strong>de</strong>n und Elementen beg<strong>in</strong>nen mit Kle<strong>in</strong>buchstaben und wer<strong>de</strong>n auch<br />

anschließend kle<strong>in</strong> geschrieben. Wenn e<strong>in</strong> Bezeichner sich aus mehreren Wörtern zusammensetzt, dürfen<br />

diese nicht getrennt wer<strong>de</strong>n. Die jeweiligen Anfangsbuchstaben <strong>de</strong>r folgen<strong>de</strong>n Wörter können jedoch<br />

<strong>in</strong>nerhalb <strong>de</strong>s Gesamtbezeichners groß geschrieben wer<strong>de</strong>n.<br />

2.1.3 Literale<br />

Literale s<strong>in</strong>d Token, die für zu speichern<strong>de</strong> Werte <strong>de</strong>r Datentypen byte, short, <strong>in</strong>t,<br />

long, float, double, boolean und char stehen. Literale wer<strong>de</strong>n auch zur<br />

Darstellung von Werten benutzt, die <strong>in</strong> Zeichenketten gespeichert wer<strong>de</strong>n.<br />

Es gibt: Boolesche Literale, Zeichenliterale, Zeichenkettenliterale, Gleitpunktliterale,<br />

ganzzahlige Literale.<br />

2.1.3.1 Ganzzahlige Literale<br />

Die Datentypen „<strong>in</strong>t“ und „long“ können <strong>de</strong>zimal, hexa<strong>de</strong>zimal und oktal<br />

beschrieben wer<strong>de</strong>n. Die Vore<strong>in</strong>stellung ist <strong>de</strong>zimal und gilt immer dann, wenn Werte<br />

ohne weitere Än<strong>de</strong>rung dargestellt wer<strong>de</strong>n. Hexa<strong>de</strong>zimale Darstellung beg<strong>in</strong>nt immer<br />

mit <strong>de</strong>r Sequenz 0x o<strong>de</strong>r 0X 145 . Oktale Darstellungen beg<strong>in</strong>nen mit e<strong>in</strong>er führen<strong>de</strong>n<br />

Null. Negativen Ganzzahlen wird e<strong>in</strong> M<strong>in</strong>uszeichen vorangestellt. Ganzzahlige<br />

Literale haben gemäß Vore<strong>in</strong>stellung <strong>de</strong>n Typ „<strong>in</strong>t“. Durch Anhängen vom „l“ bzw.<br />

„L“ kann man jedoch explizit <strong>de</strong>n Typ „long“ wählen.<br />

2.1.3.2 Gleitpunktliterale<br />

Gleitpunktliterale bestehen aus mehreren Teilen. Sie ersche<strong>in</strong>en <strong>in</strong> folgen<strong>de</strong>r<br />

Reihenfolge:<br />

Teil Nötig? Beispiele<br />

Ganzzahliger Teil Nicht, wenn <strong>de</strong>r gebrochene Teil 0, 1, 2, ... , 9, 131140<br />

vorhan<strong>de</strong>n ist<br />

Dezimalpunkt<br />

Nicht, wenn e<strong>in</strong> Exponent vorhan<strong>de</strong>n<br />

ist. Muß vorliegen, wenn es e<strong>in</strong>en<br />

Dezimalpunkt gibt<br />

Gebrochener Teil Darf nicht vorhan<strong>de</strong>n se<strong>in</strong>, wenn es 0, 1, 1311, 41421, 9944, 718281828<br />

ke<strong>in</strong>en Dezimalpunkt gibt. Muß<br />

vorhan<strong>de</strong>n se<strong>in</strong>, wenn es ke<strong>in</strong>en<br />

ganzzahligen Teil gibt<br />

Exponent Nur, wenn es ke<strong>in</strong>en Dezimalpunkt gibt E23, E-19, E6, e+307<br />

Typensuffix<br />

Ne<strong>in</strong>. Bei Abwesenheit e<strong>in</strong>es<br />

Typensuffix wird angenommen, daß es<br />

f, F, d, D<br />

145 Die Zahlen 0 bis 15 wer<strong>de</strong>n durch die Buchstaben „A“ bis „F“ bzw. „a“ bis „f“ dargestellt (Groß- o<strong>de</strong>r<br />

Kle<strong>in</strong>schreibung ist hier nicht relevant.<br />

145


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

sich um e<strong>in</strong>e Zahl doppelter<br />

Genauigkeit han<strong>de</strong>lt.<br />

Bsp.: Typensuffix für Ganzzahlen bzw. Gleitpunktzahlen<br />

class Literale<br />

{<br />

char c = 0xffff; // max. hex. Wert fuer char<br />

byte b = 0x7f;<br />

// max. hex. Wert fuer byte<br />

short s = 0x7fff; // max. hex. Wert fuer short<br />

<strong>in</strong>t i1 = 0x2f;<br />

// hexa<strong>de</strong>zimal <strong>in</strong> Kle<strong>in</strong>buchstaben<br />

<strong>in</strong>t i2 = 0X2F;<br />

// hexa<strong>de</strong>zimal <strong>in</strong> Grossbuchstaben<br />

// hexa<strong>de</strong>imale und oktale Werte mit long<br />

long n1 = 200L;<br />

// "long"-Suffix<br />

long n2 = 200l;<br />

// "long"-suffix<br />

long n3 = 200;<br />

// float- und double-Literale<br />

float f1 = 1;<br />

float f2 = 1F;<br />

// "float"-Suffix<br />

float f3 = 1f;<br />

// "float"-Suffix<br />

float f4 = 1e-45f; // wissenschaftl. Schreibweise<br />

float f5 = 1e+9f; // "float"-Suffix<br />

double d1 = 1d;<br />

// "double"-Suffix<br />

double d2 = 1D;<br />

// "double"-Suffix;<br />

double d3 = 47e47d; // wissenschaftl. Schreibweise<br />

}<br />

2.1.3.3 Boolesche Literale<br />

Es gibt zwei Boolesche Literale: true und false. Es gibt ke<strong>in</strong>en Nullwert und ke<strong>in</strong><br />

numerisches Äquivalent.<br />

2.1.3.4 Zeichenliterale<br />

Sie wer<strong>de</strong>n <strong>in</strong> e<strong>in</strong>zelne, hochgestellte Anführungszeichen gesetzt. Als e<strong>in</strong>zelne<br />

Zeichen gelten alle druckbaren Zeichen mit Ausnahme <strong>de</strong>s B<strong>in</strong><strong>de</strong>strichs „-“ und <strong>de</strong>s<br />

Backslash „\“. Bei Escape-Zeichenliteralen folgen <strong>de</strong>n Anführungszeichen <strong>de</strong>r<br />

Backslash und <strong>de</strong>m Backslash e<strong>in</strong>e <strong>de</strong>r folgen<strong>de</strong>n Zeichen:<br />

- b, t, n, f, r,


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Escape-Literal Unico<strong>de</strong>-Steuersequenz Oktal-Sequenz Be<strong>de</strong>utung<br />

‘\b‘ \u0008 \010 Backspace<br />

‘\t‘ \u0009 \011 Tab<br />

‘\n‘ \u000a \012 Neue Zeile<br />

‘\f‘ \u000c \014 Formularvorschub<br />

‘\r‘ \u000d \015 Return<br />

‘\“ \u0022 \042 Doppeltes Anführungszeichen<br />

‘\‘ \u0027 \047 E<strong>in</strong>faches Anführungszeichen<br />

‘\\‘ \u005c \134 Backslash<br />

Abb.: Escape-Literale<br />

2.1.3.5 Zeichenkettenliterale<br />

Zeichenkettenliterale s<strong>in</strong>d aus mehreren Zeichenliteralen zusammengesetzte Ketten<br />

(Str<strong>in</strong>gs). Bei Zeichenkettenliteralen wer<strong>de</strong>n null o<strong>de</strong>r mehr Zeichen <strong>in</strong> Anführungszeichen<br />

(“)dargestellt. <strong>Java</strong> erzeugt Zeichenketten als Instanz <strong>de</strong>r Klasse Str<strong>in</strong>g.<br />

Damit stehen alle Metho<strong>de</strong>n <strong>de</strong>r Klasse Str<strong>in</strong>g zur Manipulation e<strong>in</strong>er Zeichenkette<br />

zur Verfügung.<br />

Zeichenkettenliterale stehen zwischen zwei Anführungszeichen (“) und können<br />

Steuerzeichen wie Tabulatoren, Zeilenvorschübe, nichtdruckbare Unico<strong>de</strong>-Zeichen<br />

und druckbare Unico<strong>de</strong>-Spezialzeichen enthalten. Es kann sich auch um Escape-<br />

Sequenzen han<strong>de</strong>ln. Bei<strong>de</strong> Anführungszeichen müssen <strong>in</strong> <strong>de</strong>rselben Zeile <strong>de</strong>s<br />

Quellco<strong>de</strong>s stehen.<br />

Je<strong>de</strong>s Str<strong>in</strong>g-Literal ist e<strong>in</strong>e Referenz auf e<strong>in</strong> Objekt <strong>de</strong>r Klasse Str<strong>in</strong>g. Falls <strong>de</strong>r<br />

Compiler beim Übergeben <strong>de</strong>s Quelltextes e<strong>in</strong> str<strong>in</strong>g-Literal f<strong>in</strong><strong>de</strong>t, erzeut er e<strong>in</strong><br />

neues Str<strong>in</strong>g-Objekt und verwen<strong>de</strong>t es anstelle <strong>de</strong>s Literals.<br />

In <strong>Java</strong> ist <strong>de</strong>r Operator + auch auf Str<strong>in</strong>gs <strong>de</strong>f<strong>in</strong>iert. Auf zwei Str<strong>in</strong>g-Objekte angewen<strong>de</strong>t,<br />

liefert er die Verkettung bei<strong>de</strong>r Objekte.<br />

2.1.4 Trennzeichen<br />

Trennnzeichen s<strong>in</strong>d Token, die aus e<strong>in</strong>em e<strong>in</strong>zigen Zeichen bestehen und an<strong>de</strong>re<br />

Token trennen. <strong>Java</strong> kennt 9 Trennzeichen:<br />

( Wird sowohl zum Öffnen e<strong>in</strong>er Parameterliste für e<strong>in</strong>e Metho<strong>de</strong> als auch zur Festlegung e<strong>in</strong>es<br />

Vorrangs für Operationen <strong>in</strong> e<strong>in</strong>em Ausdruck benutzt.<br />

) Wird sowohl zum Schließen e<strong>in</strong>er Parameterliste für e<strong>in</strong>e Metho<strong>de</strong> als auch zur Festlegung<br />

e<strong>in</strong>es Vorrangs für Operationen <strong>in</strong> e<strong>in</strong>em Ausdruck benutzt<br />

{ Wird zu Beg<strong>in</strong>n e<strong>in</strong>es Blocks mit Anweisungen o<strong>de</strong>r e<strong>in</strong>er Initialisierungsliste gesetzt<br />

} Wird an das En<strong>de</strong> e<strong>in</strong>es Blocks mit Anweisungen o<strong>de</strong>r e<strong>in</strong>er Initialisierungsliste gesetzt<br />

[ Steht vor e<strong>in</strong>em Ausdruck, <strong>de</strong>r als In<strong>de</strong>x für e<strong>in</strong> Datenfeld (Array) steht<br />

] Folgt e<strong>in</strong>em Ausdruck, <strong>de</strong>r als In<strong>de</strong>x für e<strong>in</strong> Datenfeld dient<br />

; Dient sowohl zum Been<strong>de</strong>n e<strong>in</strong>er Ausdrucksanweisung als auch zum Trennen <strong>de</strong>r Teile e<strong>in</strong>er for-<br />

Anweisung.<br />

, wird <strong>in</strong> vielen Zusammenhängen als Begrenzer verwen<strong>de</strong>t<br />

. Wird sowohl als Dezimalpunkt als auch zum Trennen solcher D<strong>in</strong>ge wie Paketnamen von<br />

Klassennamen o<strong>de</strong>r Variablennamen benutzt.<br />

Abb.: Die <strong>Java</strong>-Trennzeichen<br />

147


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

2.1.5 Operatoren<br />

Operatoren geben an, welche Operation mit e<strong>in</strong>em o<strong>de</strong>r mehreren gegebenen<br />

Operan<strong>de</strong>n durchgeführt wer<strong>de</strong>n soll. Es gibt <strong>in</strong> <strong>Java</strong> 37 Zeichensequenzen, die<br />

Token darstellen, welche als Operatoren benutzt wer<strong>de</strong>n.<br />

2.1.5.1 Arithmetische Operatoren<br />

Arithmetische Operatoren benutzen nur zwei Operan<strong>de</strong>n. Diese s<strong>in</strong>d entwe<strong>de</strong>r<br />

ganzzahlige Werte o<strong>de</strong>r Gleitpunktzahlen. Als Rückgabe e<strong>in</strong>er arithmetischen<br />

Operation erhält man e<strong>in</strong>en neuen Wert, <strong>de</strong>ssen Datentyp sich folgen<strong>de</strong>rmaßen<br />

ergibt:<br />

- Zwei ganzzahlige Datentypen (byte, short, <strong>in</strong>t o<strong>de</strong>r long) als Operan<strong>de</strong>n ergeben immer e<strong>in</strong>en<br />

ganzzahligen Datentyp als Ergebnis. Dabei kann als Datentyp <strong>in</strong>t o<strong>de</strong>r long entstehen, byte und<br />

short s<strong>in</strong>d nicht möglich. Der Datentyp long entsteht nur, wenn e<strong>in</strong>er <strong>de</strong>r bei<strong>de</strong>n Operan<strong>de</strong>n<br />

bereits vom Datentyp long war o<strong>de</strong>r das Ergebnis von <strong>de</strong>r Größe her nur als long dargestellt<br />

wer<strong>de</strong>n kann.<br />

- Zwei Gleitpunktzahlentypen als Operan<strong>de</strong>n ergeben immer e<strong>in</strong>en Gleitpunktzahlentyp als Ergebnis.<br />

Die Anzahl <strong>de</strong>r Stellen <strong>de</strong>s Ergebnisses ist immer das Maximum <strong>de</strong>r Stellenanzahl <strong>de</strong>r bei<strong>de</strong>n<br />

Operan<strong>de</strong>n.<br />

- Falls die Operan<strong>de</strong>n e<strong>in</strong> ganzzahliger Typ und e<strong>in</strong>e Gleitpunktzahlentyp s<strong>in</strong>d, dann ist das Ergebnis<br />

immer e<strong>in</strong> Gleitpunktzahlentyp.<br />

Operator Be<strong>de</strong>utung Beispiel<br />

+ Additionsoperator 13 + 11<br />

- Subtraktionsoperator 13 - 11<br />

* Multiplikationsoperator 13 * 11<br />

/ Divisionsoperator 13 / 11<br />

% Modulo-Operator 13 % 11<br />

Abb.: Die arithmetischen <strong>Java</strong>-Operatoren<br />

2.1.5.1.1 E<strong>in</strong>stellige arithmetische Operatoren<br />

Es gibt zwei e<strong>in</strong>stellige (d.h. mit nur e<strong>in</strong>em Operan<strong>de</strong>n) arithmetische Operatoren <strong>in</strong><br />

<strong>Java</strong>:<br />

- E<strong>in</strong>stellige arithmetische Negierung: -<br />

- Das Gegenteil <strong>de</strong>r arithmetischen Negierung: +<br />

2.1.5.1.2 Arithmetische Zuweisungsoperatoren<br />

Neben <strong>de</strong>m direkten Zuweisungsoperator gibt es die arithmetischen<br />

Zuweisungsoperatoren. Diese s<strong>in</strong>d eigentlich nur e<strong>in</strong>e Abkürzung für arithmetische<br />

Operationen mit ganzen Zahlen und Gleitpunktzahlen. Das Ergebnis e<strong>in</strong>er<br />

Zuweisung über e<strong>in</strong>en arithmetischen Zuweisungsoperator steht immer auf <strong>de</strong>r l<strong>in</strong>ken<br />

Seite.<br />

Operator Be<strong>de</strong>utung<br />

+= Additions- und Zuweisungsoperator<br />

-= Subtraktions- und Zuweisungsoperator<br />

*= Multiplikations- und Zuweisungsoperator<br />

148


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

/= Divisions- und Zuweisungsoperator<br />

%= Modulo- und Zuweisungsoperator<br />

= Direkter Zuweisungsoperator<br />

Abb.: Die arithmetischen Zuweisungsoperatoren<br />

2.1.5.2 Inkrement- / Dekrement-Operatoren<br />

Inkrement- und Dekrement-Operatoren s<strong>in</strong>d e<strong>in</strong>stellige Operatoren und wer<strong>de</strong>n nur <strong>in</strong><br />

Verb<strong>in</strong>dung mit e<strong>in</strong>em ganzzahligen Wert o<strong>de</strong>r e<strong>in</strong>er Gleitpunktzahl benutzt.<br />

Der Inkrement-Operator (++) erhöht <strong>de</strong>n Wert <strong>de</strong>s Operan<strong>de</strong>n um 1. Steht <strong>de</strong>r<br />

Operator vor <strong>de</strong>m Operan<strong>de</strong>n, erfolgt die Erhöhung <strong>de</strong>s Werts, bevor <strong>de</strong>r Wert <strong>de</strong>m<br />

Operan<strong>de</strong>n zugewiesen wird. Wenn er h<strong>in</strong>ter <strong>de</strong>m Operan<strong>de</strong>n steht, erfolgt die<br />

Erhöhung, nach<strong>de</strong>m <strong>de</strong>r Wert bereits zugewiesen wur<strong>de</strong>.<br />

Der Dekrement-Operator (--) erniedrigt <strong>de</strong>n Wert <strong>de</strong>s Operan<strong>de</strong>n um 1. Steht <strong>de</strong>r<br />

Operator vor <strong>de</strong>m Operan<strong>de</strong>n, erfolgt die Erniedrigung <strong>de</strong>s Werts, bevor <strong>de</strong>r Wert<br />

<strong>de</strong>m Operan<strong>de</strong>n zugewiesen wird. Wenn er h<strong>in</strong>ter <strong>de</strong>m Operan<strong>de</strong>n steht, erfolgt die<br />

Erniedrigung, nach<strong>de</strong>m <strong>de</strong>r Wert bereits zugewiesen wur<strong>de</strong>.<br />

2.1.5.3 Bitweise arithmetische Operatoren<br />

Bitweise Arithmetik wird im wesentlichen zum Setzen und Testen e<strong>in</strong>zelner Bits und<br />

Komb<strong>in</strong>ationen e<strong>in</strong>zelner Bits <strong>in</strong>nerhalb e<strong>in</strong>er Variablen benutzt.<br />

Operator Beschreibung<br />

&<br />

Bitweiser AND-Operator<br />

| Bitweiser OR-Operator<br />

^<br />

Bitweiser XOR-Operator<br />

~ Bitweiser Komplement-Operator<br />

Abb.: Bitoperatoren <strong>in</strong> <strong>Java</strong><br />

2.1.5.4 Bitweise Verschiebungsoperatoren<br />

Bitweise Verschiebungsoperatoren verschieben die Bits <strong>in</strong> e<strong>in</strong>er ganzen Zahl.<br />

Operator Beschreibung<br />

> Operator für bitweise Verschiebung nach rechts<br />

>>> Operator für b<strong>in</strong>äre Verschiebung nach rechts mit Füllnullen<br />

Abb.: Die bitweisen Verschiebungsoperatoren<br />

Bsp.: Bitweises Manipulieren 147<br />

import java.util.*;<br />

public class BitManipulation<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

147 pr21502<br />

149


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

{<br />

Random rand = new Random();<br />

<strong>in</strong>t i = rand.nextInt();<br />

<strong>in</strong>t j = rand.nextInt();<br />

pB<strong>in</strong>Int("-1",-1);<br />

pB<strong>in</strong>Int("+1",+1);<br />

<strong>in</strong>t maxpos = 2147483647;<br />

pB<strong>in</strong>Int("maxpos", maxpos);<br />

<strong>in</strong>t maxneg = -2147483648;<br />

pB<strong>in</strong>Int("maxneg",maxneg);<br />

pB<strong>in</strong>Int("i",i);<br />

pB<strong>in</strong>Int("~i",~i);<br />

pB<strong>in</strong>Int("-i",-i);<br />

pB<strong>in</strong>Int("j",j);<br />

pB<strong>in</strong>Int("i & j",i & j);<br />

pB<strong>in</strong>Int("i | j",i | j);<br />

pB<strong>in</strong>Int("i ^ j",i ^ j);<br />

pB<strong>in</strong>Int("i > 5",i >> 5);<br />

pB<strong>in</strong>Int("(~i) >> 5",(~i) >> 5);<br />

pB<strong>in</strong>Int("i >>> 5",i >>> 5);<br />

pB<strong>in</strong>Int("(~i) >>> 5", (~i) >>> 5);<br />

long l = rand.nextLong();<br />

long m = rand.nextLong();<br />

pB<strong>in</strong>Long("-1L",-1L);<br />

pB<strong>in</strong>Long("+1L",+1L);<br />

long ll = 9223372036854775807L;<br />

pB<strong>in</strong>Long("maxpos", ll);<br />

long lln = -9223372036854775807L;<br />

pB<strong>in</strong>Long("maxneg",lln);<br />

pB<strong>in</strong>Long("l",l);<br />

pB<strong>in</strong>Long("~l",~l);<br />

pB<strong>in</strong>Long("-l",-l);<br />

pB<strong>in</strong>Long("m",m);<br />

pB<strong>in</strong>Long("l & m",l & m);<br />

pB<strong>in</strong>Long("l | m",l | m);<br />

pB<strong>in</strong>Long("l ^ m",l ^ m);<br />

pB<strong>in</strong>Long("l > 5",l >> 5);<br />

pB<strong>in</strong>Long("(~l) >> 5",(~l) >> 5);<br />

pB<strong>in</strong>Long("l >>> 5",l >>> 5);<br />

pB<strong>in</strong>Long("(~l) >>> 5", (~l) >>> 5);<br />

}<br />

static void pB<strong>in</strong>Int(Str<strong>in</strong>g s, <strong>in</strong>t i)<br />

{<br />

System.out.pr<strong>in</strong>tln(s + ", <strong>in</strong>t: " + i + ", b<strong>in</strong>aer: ");<br />

System.out.pr<strong>in</strong>t(" ");<br />

for (<strong>in</strong>t j = 31; j >= 0; j--)<br />

if (((1 = 0; j--)<br />

if (((1L


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Die Ausgabe zu dieser Anwendung zeigt für <strong>de</strong>n Bereich „<strong>in</strong>t“ jeweils die <strong>in</strong>terne<br />

Zahlendarstellung:<br />

-1, <strong>in</strong>t: -1, b<strong>in</strong>aer:<br />

11111111111111111111111111111111<br />

+1, <strong>in</strong>t: 1, b<strong>in</strong>aer:<br />

00000000000000000000000000000001<br />

maxpos, <strong>in</strong>t: 2147483647, b<strong>in</strong>aer:<br />

01111111111111111111111111111111<br />

maxneg, <strong>in</strong>t: -2147483648, b<strong>in</strong>aer:<br />

10000000000000000000000000000000<br />

i, <strong>in</strong>t: -1465644750, b<strong>in</strong>aer:<br />

10101000101001000000100100110010<br />

~i, <strong>in</strong>t: 1465644749, b<strong>in</strong>aer:<br />

01010111010110111111011011001101<br />

-i, <strong>in</strong>t: 1465644750, b<strong>in</strong>aer:<br />

01010111010110111111011011001110<br />

j, <strong>in</strong>t: -1273964081, b<strong>in</strong>aer:<br />

10110100000100001101100111001111<br />

i & j, <strong>in</strong>t: -1610610430, b<strong>in</strong>aer:<br />

10100000000000000000100100000010<br />

i | j, <strong>in</strong>t: -1128998401, b<strong>in</strong>aer:<br />

10111100101101001101100111111111<br />

i ^ j, <strong>in</strong>t: 481612029, b<strong>in</strong>aer:<br />

00011100101101001101000011111101<br />

i > 5, <strong>in</strong>t: -45801399, b<strong>in</strong>aer:<br />

11111101010001010010000001001001<br />

(~i) >> 5, <strong>in</strong>t: 45801398, b<strong>in</strong>aer:<br />

00000010101110101101111110110110<br />

i >>> 5, <strong>in</strong>t: 88416329, b<strong>in</strong>aer:<br />

00000101010001010010000001001001<br />

(~i) >>> 5, <strong>in</strong>t: 45801398, b<strong>in</strong>aer:<br />

00000010101110101101111110110110<br />

2.1.5.5 Bitweise Zuweisungsoperatoren<br />

Bitweise Zuweisungsoperatoren verwen<strong>de</strong>n e<strong>in</strong>en Wert, führen e<strong>in</strong>e entsprechen<strong>de</strong><br />

bitweise Operation mit <strong>de</strong>m zweiten Operan<strong>de</strong>n durch und legen das Ergebnis als<br />

Inhalt <strong>de</strong>s ersten Operan<strong>de</strong>n ab.<br />

Operator Beschreibung<br />

&=<br />

Bitweiser AND-Zuweisungsoperator<br />

|= Bitweiser OR-Zuweisungsoperator<br />

^= Bitweiser XOR-Zuweisungsoperator<br />

= Zuweisungsoperator für die bitweise Verschiebung nach rechts<br />

>>>= Zuweisungsoperator für die bitweise Verschiebung nach rechts mit Füllnullen<br />

Abb.: Die bitweisen Zuweisungsoperatoren<br />

151


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

2.1.5.6 Vergleichsoperatoren<br />

Vergleichsoperatoren haben zwei Operan<strong>de</strong>n und vergleichen diese (zwei<br />

Ganzzahlen o<strong>de</strong>r zwei Gleitpunktzahlen) 1 . Als Rückgabewert <strong>de</strong>r Operation entsteht<br />

e<strong>in</strong> boolescher Wert (true o<strong>de</strong>r false).<br />

Operator Be<strong>de</strong>utung<br />

== Gleichheitsoperator<br />

!= Ungleichheitsoperator<br />

< Kle<strong>in</strong>er-als-Operator<br />

> Größer-als-Operator<br />

= Größer-als-o<strong>de</strong>r-gleich-Operator<br />

Abb.: Die <strong>Java</strong>-Vergleichsoperatoren<br />

Die relationalen Operatoren „==“ und „!=“ arbeiten mit allen Objekten. Ihre<br />

Anwendung zeigt häufig e<strong>in</strong> „verwirren<strong>de</strong>s Ergebnis“, z.B. 149 :<br />

public class GleichheitsTest<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

Integer i1 = new Integer(13);<br />

Integer i2 = new Integer(13);<br />

System.out.pr<strong>in</strong>tln(i1 == i2); // false<br />

System.out.pr<strong>in</strong>tln(i1 != i2); // true<br />

}<br />

}<br />

Der Inhalt <strong>de</strong>r Objekte ist zwar gleich, die Referenzen auf die Objekte s<strong>in</strong>d nicht<br />

gleich. Falls <strong>de</strong>r aktuelle Infalt auf Gleichheit überprüft wer<strong>de</strong>n soll, steht die Metho<strong>de</strong><br />

equals() 150 bereit, z.B.:<br />

public class EqualsMetho<strong>de</strong><br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

Integer i1 = new Integer(13);<br />

Integer i2 = new Integer(13);<br />

System.out.pr<strong>in</strong>tln(i1.equals(i2));<br />

}<br />

}<br />

// true<br />

2.1.5.7 Logische Vergleichsoperatoren<br />

Die logischen Vergleichsoperatoren wer<strong>de</strong>n nur auf boolesche Operatoren<br />

angewandt und erzeugen nur boolesche Ergebnisse.<br />

Operator Beschreibung<br />

&&<br />

Logischer AND-Operator<br />

|| Logischer OR-Operator<br />

148 Die Werte zweier Variablen vom Typ char können ebenfalls mit <strong>de</strong>n Vergleichsoperatoren verglichen<br />

wer<strong>de</strong>n. Es wer<strong>de</strong>n die Variablen vom Typ char bei <strong>de</strong>r Verwendung e<strong>in</strong>es Vergleichsoperators wie ganze 16-<br />

Bit-Zahlen entsprechend ihrer Unico<strong>de</strong>-Codierung behan<strong>de</strong>lt.<br />

149 Vgl. pr21500<br />

150 Viele <strong>in</strong> <strong>de</strong>r <strong>Java</strong>-Bibliorhek bereitgestellte Klassen implementieren die Metho<strong>de</strong> equals()<br />

152


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

! Logischer NOT-Operator<br />

Abb.: Logische Vergleichsoperatoren<br />

2.1.6 Kommentare, e<strong>in</strong>gebettete Dokumentation<br />

Es gibt drei Arten von Kommentaren<br />

- E<strong>in</strong>zeilige Kommentare beg<strong>in</strong>nen mit // und en<strong>de</strong>n mit <strong>de</strong>m En<strong>de</strong> <strong>de</strong>r Zeile<br />

- Mehrzeilige Kommentare (Blockkommentare) beg<strong>in</strong>nen mit /* und en<strong>de</strong>n mit */. Sie können sich über<br />

mehrere Zeilen erstrecken.. Blockkommentare dürfen nicht geschachtelt se<strong>in</strong>.<br />

- Dokumentationskommentare beg<strong>in</strong>nen mit /** und en<strong>de</strong>n mit */. Sie können sich ebenfalls über<br />

mehrere Zeilen erstrecken und s<strong>in</strong>d spezielle Kommentare, die für das javadoc-System benutzt<br />

wer<strong>de</strong>n. <strong>Java</strong>doc dient zum Erzeugen von API-Dokumentationen aus an<strong>de</strong>rem <strong>Java</strong>-Quellco<strong>de</strong>.<br />

Das JDK verfügt über das Dokumentations-Tool javadoc, das auf <strong>de</strong>r Basis von<br />

speziellen Kommando-Tags <strong>in</strong>nerhalb e<strong>in</strong>er <strong>Java</strong>-Quelltextdatei e<strong>in</strong>e HTML-Datei als<br />

API-Dokumentation <strong>de</strong>r angegebenen Datei 151 o<strong>de</strong>r <strong>de</strong>s Pakets erzeugt.<br />

Tag<br />

@see [Klassenname]<br />

@see<br />

[Klassenname]#[Metho<strong>de</strong>nname]<br />

@param [Parametername]<br />

[Beschreibung]<br />

@version [Text]<br />

@author [Name]<br />

@return [Beschreibung]<br />

@exception [Klassenname]<br />

@<strong>de</strong>precated [Ausdruck]<br />

@s<strong>in</strong>ce [Text]<br />

Beschreibung<br />

Dieses tag erzeugt e<strong>in</strong>en Verweis <strong>in</strong> <strong>de</strong>r HTML-Datei zu <strong>de</strong>r<br />

Klasse, die von Klassenname spezifiziert wird. Dabei kann <strong>de</strong>r<br />

Klassenname auch e<strong>in</strong>e vollständige Pfadangabe se<strong>in</strong><br />

Dieses Tag erzeugt e<strong>in</strong>en Verweis („See also“-L<strong>in</strong>k) <strong>in</strong> <strong>de</strong>r HTML-<br />

Datei zu <strong>de</strong>r Metho<strong>de</strong> (<strong>in</strong>nerhalb <strong>de</strong>r mit Klassenname<br />

spezifizierten Klasse), die von Metho<strong>de</strong>nname spezifiziert wird<br />

Dieses Tag dient zur Dokumentation <strong>de</strong>r Parameter.<br />

Parametername ist selbsterklärend, und die Beschreibung<br />

spezifizeirt <strong>de</strong>n Parameter. Geht die Beschreibung über e<strong>in</strong>e Zeile<br />

h<strong>in</strong>aus, so wird dies <strong>in</strong> <strong>de</strong>r nächsten Zeile fortgeführt.<br />

Mit diesem Tag kann die Version <strong>de</strong>s Programms spezifiziert<br />

wer<strong>de</strong>n.<br />

Diese Tag fügt <strong>de</strong>n Namen <strong>de</strong>s Autors <strong>in</strong> die HTML-Datei e<strong>in</strong><br />

Mit diesem Tag kann <strong>de</strong>r wert beschrieben wer<strong>de</strong>n, <strong>de</strong>r von e<strong>in</strong>er<br />

Metho<strong>de</strong> zurückgegeben wird<br />

Dieses Tag erzeugt e<strong>in</strong>en L<strong>in</strong>k auf Ausnahmen, die von <strong>de</strong>r<br />

Klasse, die durch Klassenname spezifiziert ist, erzeugt wer<strong>de</strong>n.<br />

Neu seit Version 1.1. Das Tag markiert e<strong>in</strong>e Klasse, e<strong>in</strong> Interface,<br />

e<strong>in</strong> Feld o<strong>de</strong>r e<strong>in</strong>e Metho<strong>de</strong> als nicht verwendbar <strong>in</strong> weiteren<br />

Anwendungen´. Bereits existieren<strong>de</strong>r Co<strong>de</strong> wird trotz dieser<br />

kennzeichnung weiter kompiliert und laufen, aber <strong>de</strong>r Compiler<br />

wird e<strong>in</strong>e Warnung generieren,<br />

Spezifiziert, wann das Release erstellt wur<strong>de</strong>.<br />

Abb.: Die javadoc-Tags<br />

Die Syntax von javadoc:<br />

javadoc [Optionen] [Date<strong>in</strong>ame]<br />

javadoc [Klassenname] [Paketname]<br />

Falls e<strong>in</strong> Paketname angegeben wur<strong>de</strong>, dann dokumentiert javadoc alle <strong>Java</strong>-<br />

Quelldateien <strong>in</strong>nerhalb <strong>de</strong>s aktuellen Verzeichnisses und anschließend das<br />

151 vgl. 1.3.2.2, 3. Aufgabe<br />

153


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

dazugehörige Paketverzeichnis 152 relat<strong>in</strong> zum CLASSPATH. Für je<strong>de</strong> Klasse wird e<strong>in</strong><br />

eigenes HTML-Dokument erzeugt und für die Klassen <strong>in</strong>nerhalb e<strong>in</strong>es Pakets e<strong>in</strong><br />

HTML-In<strong>de</strong>x generiert.<br />

Option<br />

-version<br />

-classpath<br />

[Verzeichnis]<br />

Beschreibung<br />

Diese Option veranlaßt javadoc, die version <strong>de</strong>s JDK anzuzeigen.<br />

Diese Angabe korrespondiert wie bei <strong>de</strong>n an<strong>de</strong>ren JDK-Komponenten mit <strong>de</strong>r<br />

Umgebungsvariablen CLASSPATH und teilt <strong>in</strong> diesem Fall javadoc mit, <strong>in</strong><br />

welchen Verzeichnissen nach <strong>de</strong>n Quelltext-Dateien zu suchen ist. Die<br />

Verzeichnisse wer<strong>de</strong>n <strong>in</strong> <strong>de</strong>r Unix-Syntax durch Doppelpunkte getrennt,<br />

W<strong>in</strong>dows benutzt das Semikolon. Die angegebenen Pfa<strong>de</strong> wer<strong>de</strong>n <strong>in</strong> <strong>de</strong>r<br />

Reihenfolge ihres Auftretens durchsucht.<br />

-v<br />

-verbose I<strong>de</strong>ntisch mit <strong>de</strong>r Option –y<br />

-d [Verzeichnis] Mit dieser Option wird javadoc gezeigt, <strong>in</strong> welchem Verzeichnis sich das HTML-<br />

Dokument zur Dokumentation nach <strong>de</strong>r Generierung bef<strong>in</strong><strong>de</strong>n soll.<br />

Normalerweise wird das aktuelle Verzeichnis verwen<strong>de</strong>t.<br />

-public<br />

-protected<br />

-private<br />

-package<br />

-Jflag<br />

-notree<br />

-doencod<strong>in</strong>g [Name]<br />

-sourcepath [Pfad]<br />

Abb.: Die javadoc-Optionen<br />

Zeigt nur die öffentlichen elemente an<br />

Zeigt die geschützten und die öffentlichen Klassen und Metho<strong>de</strong>n an<br />

Zeigt alle Klassen und Metho<strong>de</strong>n an<br />

Zeigt die Packages, die geschützten und öffentlichen Klassen und Metho<strong>de</strong>n an<br />

Gibt e<strong>in</strong> Flag direkt an das Runtime-System weiter<br />

Wenn diese Option gesetzt ist, wird ke<strong>in</strong>e Klassenhierarchie generiert.<br />

Gibt <strong>de</strong>n Suchpfad für zu dokumentieren<strong>de</strong> Quelldateien an. Bee<strong>in</strong>flußt das<br />

la<strong>de</strong>n von Klassendateien nicht. Wird die Option nicht verwen<strong>de</strong>t, dokumentiert<br />

javadoc <strong>de</strong>faultmäßig nur die im aktuellen verzeichnis vorhan<strong>de</strong>nen Klassen<br />

und Packages im Tree-Bereich <strong>de</strong>r Dokumentation. Sollen Klassen und<br />

Packages <strong>in</strong> an<strong>de</strong>ren verzeichnissen mit dokumentiert wer<strong>de</strong>n, müssen diese<br />

hier angegeben wer<strong>de</strong>n.<br />

152 als Paketname zu verstehen, nicht als physikalisches Verzeichnis<br />

154


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

2.2 Typen<br />

E<strong>in</strong> Typ gibt <strong>in</strong> e<strong>in</strong>er Computersprache an, wie etwas (z.B. e<strong>in</strong>e Variable) im Speicher<br />

<strong>de</strong>s Rechners dargestellt wird. In <strong>Java</strong> gibt es vier verschie<strong>de</strong>ne Arten von Typen:<br />

„primitive Datentypen“, Datenfel<strong>de</strong>r (Arrays) , Klassen und Schnittstellen<br />

2.2.1 Primitive Datentypen<br />

<strong>Java</strong> besitzt 8 primitive Datentypen.<br />

Bezeichnung Typ Beschreibung<br />

byte 8 Bits Kle<strong>in</strong>ster Wertebereich. Mit Vorzeichen. Wird zum Darstellen von<br />

Ganzzahlwerten (ganzzahliges Zweierkomplement) von (-2 hoch 7 = -<br />

128) bis (+2 hoch 7 = 128) verwen<strong>de</strong>t.<br />

short 16 Bits Wertebereich mit Vorzeichen. Wird zum Darstellen von<br />

Ganzzahlwerten (ganzzahliges Zweierkomplement) von (-2 hoch 15 =<br />

-32167) bis (+2 hoch 15 - 1 = 32.167) verwen<strong>de</strong>t.<br />

<strong>in</strong>t 32 Bits Standardwertebereich. Wird zum Darstellen von Ganzzahlwerten<br />

(ganzzahliges Zweierkomplement) von (-2 hoch 31 = -2.147.483.648)<br />

bis (+2 hoch 31 = 2.147.483.647) verwen<strong>de</strong>t.<br />

long 64 Bits Größter Wertebereich. Wird zum Darstellen von Ganzzahlwerten<br />

(ganzzahliges Zweierkomplement) von (-2 hoch 63) bis (+2 hoch 63)<br />

verwen<strong>de</strong>t.<br />

float 32 Bits Kürzester Wertebereich mit Vorzeichen zur Darstellung von<br />

Gleitpunktzahlenwerten. Dies entspricht Gleitpunktzahlen mit<br />

e<strong>in</strong>facher Genauigkeit, die <strong>de</strong>n IEEE-754-1985-Standard benutzen.<br />

Es existiert e<strong>in</strong> Literal zur Darstellung von plus/m<strong>in</strong>us unendlich sowie<br />

<strong>de</strong>r Wert NaN (Not a Number) zur Darstellung von nicht <strong>de</strong>f<strong>in</strong>ierten<br />

Ergebnissen<br />

double 64 Bits Größter Wertebereich mit Vorzeichen zur Darstellung von<br />

Gleitpunktzahlenwerten. Der Wertebereich liegt zwischen +/-10 hoch<br />

317. Auch diese Gleitpunktzahlen benutzen <strong>de</strong>n IEEE-754-1985-<br />

Standard. Es existiert wie beim Typ float e<strong>in</strong> Literal zur Darstellung<br />

von plus/m<strong>in</strong>us unendlich sowie <strong>de</strong>r Wert NaN (Not a Number) zur<br />

Darstellung von nicht <strong>de</strong>f<strong>in</strong>ierten Ergebnissen<br />

char 16 Bits Darstellung e<strong>in</strong>es Zeichens <strong>de</strong>s Unico<strong>de</strong>-Zeichensatzes. Zur<br />

Darstellung von alphanumerischen Zeichen wird dieselbe Kodierung<br />

wie beim ASCII-Zeichensatz verwen<strong>de</strong>t, aber das höchste Bit ist auf 0<br />

gesetzt. Der Datentyp ist als e<strong>in</strong>ziger primitiver <strong>Java</strong>-Datentyp<br />

vorzeichenlos. Der Maximalwert ist \uFFFF.<br />

E<strong>in</strong>er char-Variablen kann, da sie 2 Bytes lang ist, e<strong>in</strong>e Ganzzahl<br />

zwischen 0 und 65535 ohne Konvertierung zugewiesen wer<strong>de</strong>n. Die<br />

Umkehrung – also die Zuweisung von char-Zeichen an an<strong>de</strong>re<br />

Datentypen – funktioniert nur für <strong>in</strong>t problemlos. Für an<strong>de</strong>re<br />

Datentypen ist ke<strong>in</strong>e direkte Zuweisung möglich.<br />

boolean 1 Bit Der boolschesche Datentyp umfaßt die Werte: true o<strong>de</strong>r false<br />

(Defaultwert). Logische Vergleiche s<strong>in</strong>d <strong>in</strong> <strong>Java</strong> vom Typ boolean.<br />

Auch boolesche Variablen s<strong>in</strong>d <strong>de</strong>m Datentyp boolean zugeordnet.<br />

Werte vom Typ boolean s<strong>in</strong>d zu allen an<strong>de</strong>ren Datentypen<br />

<strong>in</strong>kompatibel und lassen sich nicht durch Cast<strong>in</strong>g <strong>in</strong> an<strong>de</strong>re Typen<br />

überführen.<br />

Abb.: Primitive Datentypen <strong>de</strong>r <strong>Java</strong>-Sprache<br />

155


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

2.2.2 Operationen mit primitiven Datentypen<br />

2.2.2.1 Operationen mit booleschen Variablen<br />

Operation Name Be<strong>de</strong>utung<br />

= Zuweisung E<strong>in</strong>er booleschen Variable wird <strong>de</strong>r Wert true o<strong>de</strong>r false<br />

zugewiesen<br />

== Gleichheit Durchführung e<strong>in</strong>es Vergleichs <strong>in</strong>nerhalb e<strong>in</strong>er Kontrollstruktur.<br />

Rückgabe ist true, wenn bei<strong>de</strong> boolesche<br />

Operan<strong>de</strong>n <strong>de</strong>nselben Wert (true o<strong>de</strong>r false) haben.<br />

An<strong>de</strong>renfalls wird false zurückgegeben.<br />

!= Ungleichheit Durchführung e<strong>in</strong>es Vergleichs <strong>in</strong>nerhalb e<strong>in</strong>er Kontrollstruktur.<br />

Rückgabe ist true, wenn bei<strong>de</strong> boolesche<br />

Operan<strong>de</strong>n unterschiedliche Werte (true o<strong>de</strong>r false)<br />

haben. An<strong>de</strong>renfalls wird false zurückgeben.<br />

! Logisches NOT Falls <strong>de</strong>r Operand false ist, wird true zurückgegeben<br />

& AND Rückgabewert ist true, falls bei<strong>de</strong> Operan<strong>de</strong>n true s<strong>in</strong>d<br />

| OR Rückgabewert ist false, falls bei<strong>de</strong> Operan<strong>de</strong>n false s<strong>in</strong>d.<br />

^ XOR Rückgabewert ist true, falls genau e<strong>in</strong> Operand true ist<br />

(exklusives O<strong>de</strong>r)<br />

&& Logisches AND Rückgabe von true nur dann, wenn die bei<strong>de</strong>n Operan<strong>de</strong>n<br />

true s<strong>in</strong>d.<br />

|| Logisches OR Rückgabe von false nur dann, wenn bei<strong>de</strong> Operan<strong>de</strong>n<br />

false s<strong>in</strong>d.<br />

?: if-then-else Diese Operation benötigt e<strong>in</strong>en booleschen Ausdruck vor <strong>de</strong>m<br />

Fragezeichen. Falls er true ist, wird <strong>de</strong>r Wert vor <strong>de</strong>m<br />

Doppelpunkt zurückgegeben, ansonsten <strong>de</strong>r Wert h<strong>in</strong>ter <strong>de</strong>m<br />

Doppelpunkt.<br />

Abb.: Operationen mit booleschen Variablen<br />

2.2.2.2 Operationen mit Zeichenvariablen<br />

Zeichenvariablen können Operan<strong>de</strong>n <strong>in</strong> je<strong>de</strong>r ganzzahligen Operation se<strong>in</strong> und<br />

wer<strong>de</strong>n wie ganze 16-Bit-Zahlen ohne Vorzeichen behan<strong>de</strong>lt.<br />

Operation Name Be<strong>de</strong>utung<br />

= E<strong>in</strong>er Zeichenvariablen wird e<strong>in</strong> Wert zugewiesen<br />

== Durchführung e<strong>in</strong>es Vergleichs <strong>in</strong>nerhalb e<strong>in</strong>er Kontrollstruktur.<br />

Rückgabe ist true, wenn bei<strong>de</strong> Operan<strong>de</strong>n<br />

<strong>de</strong>nselben Wert (Unico<strong>de</strong>-Werte stimmen übere<strong>in</strong>) haben.<br />

An<strong>de</strong>renfalls wird false zurückgegeben.<br />

!= Ungleichheit Durchführung e<strong>in</strong>es Vergleichs <strong>in</strong>nerhalb e<strong>in</strong>er Kontrollstruktur.<br />

Rückgabe ist true, wenn bei<strong>de</strong> boolesche<br />

Operan<strong>de</strong>n unterschiedliche Werte ( Bezogen auf die<br />

Unico<strong>de</strong>-Darstellung) haben. An<strong>de</strong>renfalls wird false<br />

zurückgeben<br />

= Relational Operatoren zum Vergleich <strong>in</strong>nerhalb e<strong>in</strong>er Kontrollstruktur.<br />

+,- Vorzeichen Vorzeichenoperatoren bei e<strong>in</strong>em Operan<strong>de</strong>n<br />

+,-,*,/ B<strong>in</strong>äre Arithmetik Die Zeichenvariablen gehen <strong>in</strong> die Berechnung mit ihren<br />

Unico<strong>de</strong>-Werten e<strong>in</strong>.<br />

+=,-=,*=,/= Zuweisung Additions-, Subtraktions-, Multiplikations-, Divisions-<br />

Zuweisungen<br />

++,-- B<strong>in</strong>äre Arithmetik Inkrement- und Dekrement-Operatoren für <strong>de</strong>n Unico<strong>de</strong>-Wert<br />

von Zeichenvariablen<br />

156


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

,>>> Verschiebung Bitweise Verschiebeoperatoren: Operatoren für bitweises<br />

Verschieben nach l<strong>in</strong>ks, für bitweises Verschieben nach<br />

rechts und für das bitweise Verschieben nach rechts mit<br />

Füllnullen.<br />

=, Verschiebung und Bitweise Verschiebungs- und Zuweisungsoperatoren (nach<br />

>>>= Zuweisung<br />

l<strong>in</strong>ks, nach rechts und nach rechts mit Füllnullen<br />

~ Bitweises NOT E<strong>in</strong>stellige bitweise Komplementbildung. Wenn e<strong>in</strong> Zeichen<br />

komplementiert wird, dann wer<strong>de</strong>n alle se<strong>in</strong>e Bits <strong>in</strong>vertiert.<br />

& Bitweises AND Falls AND mit 2 Zeichenvariablen benutzt wird und das<br />

Ergebnis <strong>in</strong> e<strong>in</strong>em dritten Zeichen abgelegt wird, dann hat das<br />

resultieren<strong>de</strong> Zeichen nur für die Bits <strong>de</strong>n E<strong>in</strong>trag 1, wenn alle<br />

bei<strong>de</strong>n Operan<strong>de</strong>n an <strong>de</strong>r gleichen Stelle Bits mit <strong>de</strong>m Wert 1<br />

hatten<br />

| Bitweises OR Falls OR mit 2 Zeichenvariablen benutzt wird und das<br />

Ergebnis <strong>in</strong> e<strong>in</strong>em dritten Zeichen abgelegt wird, dann hat das<br />

resultieren<strong>de</strong> Zeichen nur für die Bits <strong>de</strong>n E<strong>in</strong>trag 1, wenn<br />

e<strong>in</strong>er <strong>de</strong>r Operan<strong>de</strong>n an dieser Position e<strong>in</strong>e 1 hatte<br />

^ Bitweises exklusives<br />

OR<br />

Falls XOR mit 2 Zeichenvariablen benutzt wird und das<br />

Resultat <strong>in</strong> e<strong>in</strong>em 3. Zeichen abgelegt wird, dann hat das<br />

resultieren<strong>de</strong> Zeichen nur für die Bits <strong>de</strong>n E<strong>in</strong>trag 1, wenn das<br />

zugehörige Bit <strong>in</strong> genau e<strong>in</strong>em <strong>de</strong>r bei<strong>de</strong>n Operan<strong>de</strong>n gesetzt<br />

ist.<br />

&=,|=,^= Bitweise Zuweisung Bitweise AND-, OR-, exklusive OR (XOR)- und<br />

Zuweisungsoperatoren<br />

Abb.: Operationen mit Zeichenvariablen<br />

2.2.2.3 Operationen mit Gleitpunktzahlen<br />

Operation Name Be<strong>de</strong>utung<br />

=,+=,-=,/= Zuweisung E<strong>in</strong>er Gleitpunktzahl wird e<strong>in</strong> Wert zugewiesen<br />

== Gleichheit Durchführung e<strong>in</strong>es Vergleichs <strong>in</strong>nerhalb e<strong>in</strong>er Kontrollstruktur.<br />

Rückgabe ist true, wenn bei<strong>de</strong> Operan<strong>de</strong>n<br />

<strong>de</strong>nselben Wert haben. An<strong>de</strong>renfalls wird false<br />

zurückgegeben<br />

!= Ungleichheit Durchführung e<strong>in</strong>es Vergleichs <strong>in</strong>nerhalb e<strong>in</strong>er Kontrollstruktur.<br />

Rückgabe ist true, wenn bei<strong>de</strong> Operan<strong>de</strong>n<br />

unterschiedliche Werte haben. An<strong>de</strong>renfalls wird false<br />

zurückgeben<br />

= Relational Operatoren zum Vergleich <strong>in</strong>nerhalb e<strong>in</strong>er Kontrollstruktur<br />

+,- Vorzeichen Vorzeichenoperatoren bei e<strong>in</strong>em Operan<strong>de</strong>n<br />

+,-,*,/ B<strong>in</strong>äre Arithmetik Additions-, Subtraktions-, Multiplikations-, Divisions-<br />

Operatoren<br />

+=,-=,*=,/= Zuweisung Additions-, Subtraktions-, Multiplikations-, Divisions-<br />

Zuweisungen<br />

++,-- B<strong>in</strong>äre Arithmetik Inkrement- und Dekrementoperatoren für <strong>de</strong>n Wert <strong>de</strong>r<br />

Variablen.<br />

Abb.: Operationen mit <strong>de</strong>n Typen float und double<br />

<strong>Java</strong> erzeugt ke<strong>in</strong>e Ausnahmen bei <strong>de</strong>r Benutzung <strong>de</strong>r Gleitpunktarithmetik. E<strong>in</strong><br />

Überlauf 153 o<strong>de</strong>r das Teilen aller möglichen Zahlen (außer „Null durch Null“) führt zur<br />

Ausgabe von positiven bzw. negativen „unendlichen“ Werten. Das Teilen von „Null<br />

153 d.h.: Größeres Ergebnis von e<strong>in</strong>er Operation als durch <strong>de</strong>n Wertebereich <strong>de</strong>s jeweiligen Typs ausgedrückt<br />

wer<strong>de</strong>n kann.<br />

157


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

durch Null“ ergibt <strong>de</strong>n Wert „NaN“ (ke<strong>in</strong>e Zahl). E<strong>in</strong> Unterlauf 154 gibt e<strong>in</strong>en speziellen<br />

Wert aus: „positiv o<strong>de</strong>r negativ Null“. Dieser Wert kann mit Vergleichsoperatoren<br />

ausgewertet wer<strong>de</strong>n (bewirkt false).<br />

2.2.2.4 Operationen mit ganzzahligen Variablen (bzw. ganzzahligen Ausdrücken)<br />

Operation Name Be<strong>de</strong>utung<br />

=,+=,-=,/= Zuweisung E<strong>in</strong>er ganzzahligen Variablen wird e<strong>in</strong> Wert zugewiesen<br />

== Gleichheit Durchführung e<strong>in</strong>es Vergleichs <strong>in</strong>nerhalb e<strong>in</strong>er Kontrollstruktur.<br />

Rückgabe ist true, wenn bei<strong>de</strong> Operan<strong>de</strong>n<br />

<strong>de</strong>nselben Wert haben. An<strong>de</strong>renfalls wird false<br />

zurückgegeben<br />

!= Ungleichheit Durchführung e<strong>in</strong>es Vergleichs <strong>in</strong>nerhalb e<strong>in</strong>er Kontrollstruktur.<br />

Rückgabe ist true, wenn bei<strong>de</strong> Operan<strong>de</strong>n<br />

unterschiedliche Werte haben. An<strong>de</strong>renfalls wird false<br />

zurückgeben<br />

= Relational Weitere Operatoren zum Vergleich <strong>in</strong>nerhalb e<strong>in</strong>er<br />

Kontrollstruktur<br />

+,- Vorzeichen Vorzeichenoperatoren bei e<strong>in</strong>em Operan<strong>de</strong>n<br />

+,-,*,/ B<strong>in</strong>äre Arithmetik Additions-, Subtraktions-, Multiplikations-, Divisions-<br />

Operatoren<br />

+=,-=,*=,/= Zuweisung Additions-, Subtraktions-, Multiplikations-, Divisions-<br />

Zuweisungen<br />

++,-- B<strong>in</strong>äre Arithmetik Additions-, Subtraktions-, Multiplikations-, Divisions-<br />

Operatoren<br />

,>>> Bitweise Verschiebeoperatoren: Operatoren für bitweises<br />

Verschieben nach l<strong>in</strong>ks, für bitweises Verschieben nach<br />

rechts und für das bitweise Verschieben nach rechts mit<br />

Füllnullen<br />

=,<br />

>>>=<br />

Bitweise Verschiebungs- und Zuweisungsoperatoren (nach<br />

l<strong>in</strong>ks, nach rechts und nach rechts mit Füllnullen<br />

~ Bitweises NOT E<strong>in</strong>stellige bitweise Operation<br />

& Bitweises AND Falls AND mit 2 Ganzzahlen benutzt wird und das Ergebnis <strong>in</strong><br />

e<strong>in</strong>em dritten Ganzzahl abgelegt wird, dann hat die<br />

resultieren<strong>de</strong> Ganzzahl nur für die Bits <strong>de</strong>n E<strong>in</strong>trag 1, wenn<br />

alle bei<strong>de</strong>n Operan<strong>de</strong>n an <strong>de</strong>r gleichen Stelle Bits mit <strong>de</strong>m<br />

Wert 1 hatten<br />

| Bitweises OR Falls OR mit 2 Ganzzahlen benutzt wird und das Ergebnis <strong>in</strong><br />

e<strong>in</strong>er dritten Ganzzahl abgelegt wird, dann hat die<br />

resultieren<strong>de</strong> Ganzzahl nur für die Bits <strong>de</strong>n E<strong>in</strong>trag 1, wenn<br />

e<strong>in</strong>er <strong>de</strong>r Operan<strong>de</strong>n an dieser Position e<strong>in</strong>e 1 hatte<br />

^ Biweises exklusives<br />

OR<br />

Falls XOR mit 2 Ganzzahlen benutzt wird und das Resultat <strong>in</strong><br />

e<strong>in</strong>er 3. Ganzzahl abgelegt wird, dann hat die resultieren<strong>de</strong><br />

Ganzzahl nur für die Bits <strong>de</strong>n E<strong>in</strong>trag 1, wenn das zugehörige<br />

Bit <strong>in</strong> genau e<strong>in</strong>em <strong>de</strong>r bei<strong>de</strong>n Operan<strong>de</strong>n gesetzt ist.<br />

&=,|=,^= Bitweise Zuweisung Inkrement- und Dekrementoperatoren für <strong>de</strong>n Wert <strong>de</strong>r<br />

Variablen<br />

Abb.: Operationen mit ganzzahligen Variablen<br />

Ganzzahlige Variable wer<strong>de</strong>n <strong>in</strong> <strong>Java</strong> als „Zweierkomplement“-Zahlen mit Vorzeichen<br />

verwen<strong>de</strong>t.<br />

154 d.h.: kle<strong>in</strong>eres Ergebnis (- außer Null -) von e<strong>in</strong>er Operation als durch <strong>de</strong>n Wertebereich <strong>de</strong>s jeweiligen Typs<br />

ausgedrückt wer<strong>de</strong>n kann.<br />

158


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

2.2.3 Datenfel<strong>de</strong>r (Arrays)<br />

E<strong>in</strong> Datenfeld (Array) ist e<strong>in</strong>e Ansammlung von Objekten e<strong>in</strong>es bestimmten Typs 1 ,<br />

die über e<strong>in</strong>en laufen<strong>de</strong>n In<strong>de</strong>x adressierbar s<strong>in</strong>d. Gegenüber „normalen Objekten“<br />

haben Arrays zwei wesentliche E<strong>in</strong>schränkungen:<br />

1. Arrays haben ke<strong>in</strong>e Konstruktoren. Statt <strong>de</strong>ssen wird <strong>de</strong>r Operator new mit spezieller Syntax<br />

aufgerufen.<br />

2. Es können ke<strong>in</strong>e Subklassen e<strong>in</strong>es Datenfelds <strong>de</strong>f<strong>in</strong>iert wer<strong>de</strong>n.<br />

Datenfel<strong>de</strong>r gehören zu <strong>de</strong>n Referenzvariablen 1 . Arrays können je<strong>de</strong>n Wertetyp<br />

(primitiver Typ o<strong>de</strong>r Objekt) enthalten, jedoch können <strong>in</strong> e<strong>in</strong>em Array ke<strong>in</strong>e<br />

unterschiedlichen Typen gespeichert wer<strong>de</strong>n.<br />

Arrays <strong>in</strong> <strong>Java</strong> s<strong>in</strong>d Objekte und unterschei<strong>de</strong>n sich von Datenfel<strong>de</strong>rn <strong>in</strong> an<strong>de</strong>ren<br />

Programmiersprachen durch folgen<strong>de</strong> Merkmale:<br />

- Array-Variable s<strong>in</strong>d Referenzen<br />

- Arrays besitzen Metho<strong>de</strong>n und Instanz-Variable<br />

- Arrays wer<strong>de</strong>n zur Laufzeit erzeugt<br />

2.2.3.1 Deklarieren, Erstellen von Array-Objekten<br />

In <strong>Java</strong> umfaßt das Erstellen e<strong>in</strong>es Datenfelds drei Schritte:<br />

1. Deklaration e<strong>in</strong>er Variablen zur Aufnahme e<strong>in</strong>es Datenfelds.<br />

Array-Variable zeigen <strong>de</strong>n Objekttyp an, <strong>de</strong>n das Datenfeld aufnimmt und <strong>de</strong>n namen<br />

<strong>de</strong>s Arrays, gefolgt von leeren eckigen Klammern „[]“, z.B: <strong>in</strong>t x[]. Alternativ<br />

dazu kann e<strong>in</strong>e Array-Variable auch so (Klammern nach <strong>de</strong>m Typ) festgelegt se<strong>in</strong>,<br />

z.B. <strong>in</strong>t[] x.<br />

2. Erstellen e<strong>in</strong>es neuen Array-Objekts und Zuweisen e<strong>in</strong>er Array-Variablen.<br />

Das kann erfolgen<br />

- mit new, z.B. Str<strong>in</strong>g[] namen = new Str<strong>in</strong>g[10];<br />

Hier wird e<strong>in</strong> neues Datenfeld von Str<strong>in</strong>gs mit 10 Elementen erstellt. Beim Erstellen<br />

e<strong>in</strong>es Array-Objekts mit new ist anzugeben, wieviele Elemente das Array<br />

aufnehmen soll. Alle Elemente <strong>de</strong>s Array wer<strong>de</strong>n automatisch <strong>in</strong>itialisiert (0 für<br />

numerische Datenfel<strong>de</strong>r, false für boolesche, ‘\0‘ für Zeichen-Arrays und „null“<br />

für alles an<strong>de</strong>re.<br />

Bsp.: Erzeugen von Datenfel<strong>de</strong>rn mit „new“<br />

a) Aufnahme von Werten primitiver Typen<br />

import java.util.*;<br />

public class FeldPrim<br />

{<br />

static Random rand = new Random();<br />

static <strong>in</strong>t zRand(<strong>in</strong>t mod)<br />

{<br />

return Math.abs(rand.nextInt()) % mod;<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

155 Es kann sich dabei um primitive Variablentypen (byte, char, short, <strong>in</strong>t, long, float, double, boolean), aber<br />

auch um an<strong>de</strong>re Datenfel<strong>de</strong>r (verschachtelte Arrays) o<strong>de</strong>r Objekte han<strong>de</strong>ln.<br />

156 Es gibt drei Arten von Referenzvariablen: Klassen, Schnittstellen und Datenfel<strong>de</strong>r<br />

159


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

{<br />

<strong>in</strong>t[] a;<br />

// Die Groesse <strong>de</strong>s Array wird zur Laufzeit<br />

// zufaellig bestimmt<br />

a = new <strong>in</strong>t[zRand(20)];<br />

ausgabe("Laenge von a = " + a.length);<br />

for (<strong>in</strong>t i = 0; i < a.length; i++)<br />

ausgabe("a[" + i + "] = " + a[i]);<br />

}<br />

static void ausgabe(Str<strong>in</strong>g s)<br />

{<br />

System.out.pr<strong>in</strong>tln(s);<br />

}<br />

}<br />

b) Aufnahme von Objekten<br />

E<strong>in</strong> Datenfeld, <strong>de</strong>ssen Komponenten ke<strong>in</strong>e primitiven Typen aufnehmen<br />

sollen, muß immer mit „new“ gefüllt wer<strong>de</strong>n.<br />

import java.util.*;<br />

public class FeldObj<br />

{<br />

static Random rand = new Random();<br />

static <strong>in</strong>t zRand(<strong>in</strong>t mod)<br />

{<br />

return Math.abs(rand.nextInt()) % mod;<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

Integer[] a;<br />

// Die Groesse <strong>de</strong>s Array wird zur Laufzeit<br />

// zufaellig bestimmt<br />

a = new Integer[zRand(20)];<br />

ausgabe("Laenge von a = " + a.length);<br />

for (<strong>in</strong>t i = 0; i < a.length; i++)<br />

{<br />

a[i] = new Integer(zRand(500));<br />

ausgabe("a[" + i + "] = " + a[i]);<br />

}<br />

}<br />

static void ausgabe(Str<strong>in</strong>g s)<br />

{<br />

System.out.pr<strong>in</strong>tln(s);<br />

}<br />

}<br />

c) Variable Argumente<br />

Das zweite Format <strong>in</strong> <strong>de</strong>r Feld<strong>in</strong>itialisierung bestimmt e<strong>in</strong>e bequeme<br />

syntaktische Form für <strong>de</strong>n „Aufruf von Metho<strong>de</strong>n“, die <strong>de</strong>n gleichen Effekt<br />

besitzt wie die „Argumentenliste <strong>in</strong> C“. Über e<strong>in</strong>e solche Argumentenliste<br />

kann e<strong>in</strong>e unbekannte Menge von Argumenten e<strong>in</strong>es unbekannten Typs<br />

behan<strong>de</strong>lt wer<strong>de</strong>n. Da alle Klassen von <strong>de</strong>r Klasse Object abstammen,<br />

kann e<strong>in</strong>e Metho<strong>de</strong> mit e<strong>in</strong>em Datenfeld als Argument verwen<strong>de</strong>t wer<strong>de</strong>n,<br />

das aus Objekten vom Typ Object besteht, z.B.<br />

class A<br />

{<br />

<strong>in</strong>t i;<br />

}<br />

public class VarArgs<br />

{<br />

static void f(Object[] x)<br />

160


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

{<br />

for (<strong>in</strong>t i = 0; i < x.length; i++)<br />

System.out.pr<strong>in</strong>tln(x[i]);<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

f(new Object[] {<br />

new Integer(40), new VarArgs(),<br />

new Float(3.14), new Double(11.11)<br />

});<br />

f(new Object[] { "e<strong>in</strong>s", "zwei", "drei" });<br />

f(new Object[] { new A(), new A(), new A() });<br />

}<br />

}<br />

- durch direktes Initialisieren <strong>de</strong>s Array-Inhalts, z.B.: Str<strong>in</strong>g[] namen =<br />

{"juergen", "bernd", "liesel", "dieter", "hans", "vera",<br />

"christian", "theo", "emil", "karl"};<br />

Alle Elemente <strong>de</strong>r <strong>in</strong> <strong>de</strong>r geschweiften Klammer stehen<strong>de</strong>n Elemente müssen vom<br />

gleichen Typ se<strong>in</strong><br />

3. Speichern von Elementen im Array<br />

Zur Speicherung e<strong>in</strong>es Werts <strong>in</strong> e<strong>in</strong>em Array, wird <strong>de</strong>r Array-Ausdruck „subscript“<br />

benutzt, z.B.: x[subscript] . „subscript“ ist die Stelle (Position) für <strong>de</strong>n Zugriff<br />

auf e<strong>in</strong>e Datenfeld-Element. Das Array-Subskript muß vom Typ <strong>in</strong>t se<strong>in</strong>. Wegen <strong>de</strong>r<br />

vom Compiler vorgenommenen automatischen Typenkonvertierungen s<strong>in</strong>d auch<br />

short, byte, char zulässig. In<strong>de</strong>xausdrücke wer<strong>de</strong>n vom Laufzeitsystem auf<br />

E<strong>in</strong>haltung <strong>de</strong>r Array-Grenzen überprüft.<br />

2.2.3.2 Zugriff auf Datenfeld-Elemente, Än<strong>de</strong>rn von Datenfeld-Elementen<br />

Datenfeld-Subskripte beg<strong>in</strong>nen mit 0. Alle Array-Subskripte wer<strong>de</strong>n beim Kompilieren<br />

geprüft, ob sie sich <strong>in</strong>nerhalb <strong>de</strong>r Grenzem <strong>de</strong>s Array bef<strong>in</strong><strong>de</strong>n (Größer als 0, kle<strong>in</strong>er<br />

als die Länge <strong>de</strong>s Datenfelds).<br />

Str<strong>in</strong>g ort[] = new Str<strong>in</strong>g[10];<br />

ort[10] = "D<strong>in</strong>kesbuehl";<br />

ist falsch (Fehler beim Kompilieren). Das <strong>in</strong> „ort“ gespeicherte Array hat nur 10 ab 0<br />

numerierte Elemente.<br />

Wird das Array-Subskript zur Laufzeit berechnet und resultiert daraus e<strong>in</strong> Wert<br />

außerhalb <strong>de</strong>r Array-Grenzen, dann erzeugt <strong>de</strong>r <strong>Java</strong>-Interpreter e<strong>in</strong>en Fehler 157 . Die<br />

Länge e<strong>in</strong>es Datenfelds kann mit <strong>de</strong>r Instanzvariablen length getestet wer<strong>de</strong>n, z.B.:<br />

<strong>in</strong>t laenge = ort.length;.<br />

Zum Zuweisen e<strong>in</strong>es Werts wird h<strong>in</strong>ter <strong>de</strong>m Zugriffsausdruck die<br />

Zuweisungsanweisung gestellt. Falls e<strong>in</strong>em Array-Element e<strong>in</strong> Wert zugewiesen<br />

wird, dann wird auf das betreffen<strong>de</strong> Objekt e<strong>in</strong>e Referenz erzeugt. Datenfel<strong>de</strong>r mit<br />

primitiven Typen (z.B. <strong>in</strong>t, float) kopieren die Werte von e<strong>in</strong>em Element <strong>in</strong> e<strong>in</strong><br />

an<strong>de</strong>res.<br />

157 Er weist auf e<strong>in</strong>e „Ausnahme“ h<strong>in</strong>.<br />

161


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

2.2.3.3 Anwendungen mit e<strong>in</strong>dimensionaler Datenfel<strong>de</strong>rn<br />

1. Sammeln<br />

Datenfel<strong>de</strong>r s<strong>in</strong>d die wohl nützlichsten Objekte <strong>in</strong> <strong>Java</strong>, mit <strong>de</strong>nen Objekte <strong>in</strong> leicht<br />

zugänglichen Listen gesammelt wer<strong>de</strong>n können.<br />

2. Suchen<br />

a) sequentielle Suche<br />

Aufgabenstellung: Gegeben ist e<strong>in</strong>e ganze Zahl (Schlüssel schl) und e<strong>in</strong> Feld (Array) mit<br />

ganzzahligen Werten, die <strong>in</strong> diesem Array beliebig verteilt s<strong>in</strong>d. Such die In<strong>de</strong>xposition, für die gilt<br />

„a[i] == schl“. Ist die Suche ergebnislos, dann gib „Nicht Gefun<strong>de</strong>n“ zurück.<br />

b) b<strong>in</strong>äre Suche<br />

Aufgabenstellung: Gegeben ist e<strong>in</strong>e ganze Zahl (Schlüssel schl) und e<strong>in</strong> Array a mit ganzzahligen<br />

Werten, die <strong>in</strong> diesem Feld <strong>in</strong> aufsteigen<strong>de</strong>r Folge vorliegen. Suche die In<strong>de</strong>xposition, für die gilt:<br />

a[i] == schl“. Ist die Suche ergebnislos, dann gib „Nicht Gefun<strong>de</strong>n“ zurück.<br />

Lösungsverfahren: Es wird geprüft, ob <strong>de</strong>r gesuchte Schlüssel sich <strong>in</strong> <strong>de</strong>r Mitte <strong>de</strong>s Array bef<strong>in</strong><strong>de</strong>t.<br />

Falls dies nicht <strong>de</strong>r Fall ist, wird „schl < a[mitte]“ im l<strong>in</strong>ken Teil (von <strong>de</strong>r Mitte aus gesehen) und<br />

für „schl > a[mitte]“ im rechten Teil <strong>de</strong>s Array gesucht. Diese Strategie kann fortgesetzt wer<strong>de</strong>n,<br />

bis die Suche erfolgreich war o<strong>de</strong>r <strong>de</strong>r Schlüssel nicht gefun<strong>de</strong>n wur<strong>de</strong>.<br />

3. Sortieren 158<br />

Aufgabenstellung: Schreibe e<strong>in</strong> Programm, das die <strong>in</strong> e<strong>in</strong>em Datenfeld („array“) gespeicherten ganzen<br />

Zahlen nach e<strong>in</strong>em e<strong>in</strong>fachen Sortierverfahren („Sortieren durch Austauschen, Bubble Sort“) <strong>in</strong><br />

aufsteigen<strong>de</strong> Sortierreihenfolge br<strong>in</strong>gt.<br />

Lösungsverfahren (Algorithmus): Man kann jeweils <strong>de</strong>n ersten Schlüssel gegen alle weiteren<br />

Schlüssel im Arbeitsspeicherfeld vergleichen und so an <strong>de</strong>r ersten Position <strong>de</strong>n kle<strong>in</strong>sten Schlüssel<br />

nach (N-1) Vergleichen ermitteln. Danach vergleicht man jeweils <strong>de</strong>n Schlüssel aus <strong>de</strong>r zweiten<br />

Position mit allen nachfolgen<strong>de</strong>n Schlüsslwerten. Nach (N-2) Vergleichen steht <strong>de</strong>r zweitkle<strong>in</strong>ste<br />

Sachlüssel an <strong>de</strong>r zweiten Position. Bei Fortsetzung dieser Verfahrensweise ist schließlich nur noch<br />

e<strong>in</strong> e<strong>in</strong>ziger Schlüssel vorhan<strong>de</strong>n, <strong>de</strong>r dann auf <strong>de</strong>r richtigen, <strong>de</strong>r letzten Position steht.<br />

Bsp.:<br />

1 2 3 4<br />

37 22 18 9 9 9 9 9 9 9 9<br />

22 37 37 37 37 22 18 18 18 18 18<br />

18 18 22 22 22 37 37 37 22 22 22<br />

9 9 9 18 18 18 22 22 37 37 25<br />

25 25 25 25 25 25 25 25 25 25 37<br />

Abb.: Tabelle mit 5 Schlüsseln zur Demonstration <strong>de</strong>s Bubble-Sort<br />

Vorschlag für die Implementierung:<br />

import java.util.*;<br />

class BubbleSort<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

<strong>in</strong>t[] x;<br />

x = new <strong>in</strong>t[10];<br />

158 pr22301<br />

162


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Random zufallsZahl = new Random();<br />

// Initialisieren <strong>de</strong>s Array x<br />

for (<strong>in</strong>t i = 0; i < 10; i++)<br />

{<br />

x[i] = zufallsZahl.nextInt();<br />

}<br />

// Ausgabe <strong>de</strong>s noch nicht sortierten x<br />

System.out.pr<strong>in</strong>tln("Vor <strong>de</strong>m Sortieren:");<br />

for (<strong>in</strong>t i = 0; i < 10; i++)<br />

{<br />

System.out.pr<strong>in</strong>tln("x["+i+"] = " + x[i]);<br />

}<br />

boolean sortiert = false;<br />

// Sortiere das Feld x<br />

while (!sortiert)<br />

{<br />

sortiert = true;<br />

for (<strong>in</strong>t i=0; i < 9; i++)<br />

{<br />

if (x[i] > x[i+1])<br />

{<br />

<strong>in</strong>t temp = x[i];<br />

x[i] = x[i+1];<br />

x[i+1] = temp;<br />

sortiert = false;<br />

}<br />

}<br />

}<br />

// Gib das sortierte Feld x aus<br />

System.out.pr<strong>in</strong>tln();<br />

System.out.pr<strong>in</strong>tln("Nach <strong>de</strong>m Sortieren:");<br />

for (<strong>in</strong>t i = 0; i < 10; i++) {<br />

System.out.pr<strong>in</strong>tln("x["+i+"] = " + x[i]);<br />

}<br />

}<br />

}<br />

163


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

2.2.3.4 Mehrdimensionale Datenfel<strong>de</strong>r<br />

<strong>Java</strong> <strong>de</strong>f<strong>in</strong>iert mehrdimensionale Arrays durch Arrays von Arrays.<br />

Zweidimensionale Datenfel<strong>de</strong>r<br />

E<strong>in</strong> zweidimensionale Feld entspricht e<strong>in</strong>er zweidimensionalen Wertetabelle, z.B.:<br />

S0 S1 S2 S3<br />

Z0 1 2 3 4<br />

Z1 1 2 3 4<br />

Z2 1 2 3 4<br />

Z3 1 2 3 4<br />

Z4 1 2 3 4<br />

Das vorliegen<strong>de</strong> Datenfeld umfaßt 5 Zeilen und 4 Spalten. E<strong>in</strong> Datenfeld „M“ mit 4<br />

Zeilen und 5 Spalten ist folgen<strong>de</strong>rmaßen aufgebaut.<br />

M[0][0] M[0][1] M[0][2] M[0][3] M[0][4]<br />

M[1][0] M[1][1] M[1][2] M[1][3] M[1][4]<br />

M[2][0] M[2][1] M[2][2] M[2][3] M[2][4]<br />

M[3][0] M[3][1] M[3][2] M[3][3] M[3][4]<br />

Zweidimensionale Datenfel<strong>de</strong>r wer<strong>de</strong>n, wie das folgen<strong>de</strong> Beispiel zeigt, auf gleiche<br />

Weise erstellt und <strong>in</strong>itialisiert wie e<strong>in</strong>dimensionale Fel<strong>de</strong>r.<br />

class E<strong>in</strong>heitsmatrix<br />

{<br />

publich static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

double[][] EM;<br />

EM = new double[4][4];<br />

for (<strong>in</strong>t zeile = 0; zeile < 4; zeile++)<br />

{<br />

for (<strong>in</strong>t spalte = 0; spalte < 4; spalte++)<br />

{<br />

if (zeile != spalte)<br />

{<br />

EM[zeile][spalte] = 0.0;<br />

}<br />

else {<br />

EM[zeile][spalte] = 1.0;<br />

}<br />

}<br />

}<br />

}<br />

}<br />

Da mehrdimensionale Arrays als geschachtelte Arrays gespeichert wer<strong>de</strong>n, ist es<br />

möglich, „nicht-rechteckige“ Arrays zu erzeugen, z.B.:<br />

public class Ke<strong>in</strong>RechteckFeld<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

<strong>in</strong>t a[][] = { {0},<br />

164


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

{1,2},<br />

{3,4,5},<br />

{6,7,8,9}<br />

};<br />

for (<strong>in</strong>t i = 0; i < a.length; i++)<br />

{<br />

for (<strong>in</strong>t j = 0; j < a[i].length; j++)<br />

{<br />

System.out.pr<strong>in</strong>t(a[i][j] + " ");<br />

}<br />

System.out.pr<strong>in</strong>tln();<br />

}<br />

}<br />

}<br />

Beispiele<br />

1. Das 8-Damen-Problem 159<br />

Aufgabenstellung: Acht Damen sollen so auf e<strong>in</strong>em Schachbrett positioniert wer<strong>de</strong>n, daß sie sich nicht<br />

schlagen können, d.h.: Zwei Damen stehen nie <strong>in</strong> <strong>de</strong>rselben Zeile o<strong>de</strong>r Spalte o<strong>de</strong>r Diagonale.<br />

Algorithmus zur Lösung: Zu Beg<strong>in</strong>n wird e<strong>in</strong> zweidimensionales Feld mit 0 gefüllt.<br />

0 0 0 0 0 0 0 0<br />

0 0 0 0 0 0 0 0<br />

0 0 0 0 0 0 0 0<br />

0 0 0 0 0 0 0 0<br />

0 0 0 0 0 0 0 0<br />

0 0 0 0 0 0 0 0<br />

0 0 0 0 0 0 0 0<br />

0 0 0 0 0 0 0 0<br />

Danach wird zufällig im zweidimensionalen Feld e<strong>in</strong>e „Dame“ gesetzt. Das Setzen <strong>de</strong>r Dame wird<br />

durch e<strong>in</strong>e 9 markiert.<br />

0 0 0 0 0 0 0 0<br />

0 0 0 0 0 0 0 0<br />

0 0 0 0 0 0 0 0<br />

0 0 9 0 0 0 0 0<br />

0 0 0 0 0 0 0 0<br />

0 0 0 0 0 0 0 0<br />

0 0 0 0 0 0 0 0<br />

0 0 0 0 0 0 0 0<br />

Durch das Positionieren <strong>de</strong>r Dame s<strong>in</strong>d Positionierungen von weiteren Damen <strong>in</strong> <strong>de</strong>rselben Zeile und<br />

Spalte wie die soeben positionierte Dame, aber auch <strong>in</strong> <strong>de</strong>n zugehörigen Diagonalen nicht erlaubt.<br />

Die nicht mehr zulässigen Positionen wer<strong>de</strong>n mit 1 markiert.<br />

0 0 1 0 0 1 0 0<br />

1 0 1 0 1 0 0 0<br />

0 1 1 1 0 0 0 0<br />

1 1 9 1 1 1 1 1<br />

0 1 1 1 0 0 0 0<br />

1 0 1 0 1 0 0 0<br />

0 0 1 0 0 1 0 0<br />

0 0 1 0 0 0 1 0<br />

159 vgl. pr22305<br />

165


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Damen dürfen jetzt nur noch auf die mit 0 markierten Komponenten <strong>de</strong>s zweidimensionalen Felds<br />

gebracht wer<strong>de</strong>n. Das geschieht solange bis alle Komponenten <strong>de</strong>s zweidimensionalen Felds mit 1<br />

o<strong>de</strong>r 9 gefüllt s<strong>in</strong>d. Wur<strong>de</strong>n beim Füllen <strong>de</strong>s Felds <strong>in</strong>sgesamt 8 Damen positioniert, dann wur<strong>de</strong> e<strong>in</strong>e<br />

Lösung erreicht, die so aussehen könnte:<br />

1 1 1 1 1 1 9 1<br />

1 9 1 1 1 1 1 1<br />

1 1 1 1 1 9 1 1<br />

1 1 9 1 1 1 1 1<br />

9 1 1 1 1 1 1 1<br />

1 1 1 9 1 1 1 1<br />

1 1 1 1 1 1 1 9<br />

1 1 1 1 9 1 1 1<br />

Vorschlag zur Implementierung:<br />

import java.util.*;<br />

public class Damen<br />

{<br />

// Konstanten<br />

f<strong>in</strong>al <strong>in</strong>t N = 8;<br />

// Variable<br />

boolean fuellen;<br />

boolean ausgeben;<br />

Random rand = new Random();<br />

<strong>in</strong>t zeile, spalte;<br />

<strong>in</strong>t[][] brett;<br />

<strong>in</strong>t aktZahl = 0;<br />

<strong>in</strong>t[] positionen = new <strong>in</strong>t[N];<br />

// Metho<strong>de</strong>n<br />

<strong>in</strong>t zRand(<strong>in</strong>t mod)<br />

{<br />

return Math.abs(rand.nextInt() % mod);<br />

}<br />

public void <strong>in</strong>itialisiere()<br />

{<br />

fuellen = false;<br />

ausgeben = false;<br />

aktZahl = 0;<br />

brett = new <strong>in</strong>t[N][N];<br />

for (<strong>in</strong>t i = 0; i < N; i++)<br />

for (<strong>in</strong>t j = 0; j < N; j++)<br />

{<br />

brett[i][j] = 0;<br />

}<br />

}<br />

public void nimmPosition()<br />

{<br />

do {<br />

zeile = zRand(N);<br />

// System.out.pr<strong>in</strong>tln(zeile);<br />

spalte = zRand(N);<br />

// System.out.pr<strong>in</strong>tln(spalte);<br />

} while (brett[zeile][spalte] != 0);<br />

brett[zeile][spalte] = 9;<br />

aktZahl++;<br />

}<br />

public void bewertePosition()<br />

{<br />

for (<strong>in</strong>t i = 0; i < 8; i++)<br />

{<br />

if (i != spalte) brett[zeile][i] = 1;<br />

}<br />

166


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

for (<strong>in</strong>t i = 0; i < 8; i++)<br />

{<br />

if (i != zeile) brett[i][spalte] = 1;<br />

}<br />

<strong>in</strong>t lDzeile = zeile;<br />

<strong>in</strong>t lDspalte = spalte;<br />

// Beruecksichtigung <strong>de</strong>r nach l<strong>in</strong>ks laufen<strong>de</strong>n Diagonale<br />

while ((lDzeile > 0) && (lDspalte > 0))<br />

{<br />

lDzeile--;<br />

lDspalte--;<br />

}<br />

do {<br />

if ((lDzeile != zeile) && (lDspalte != spalte))<br />

{<br />

brett[lDzeile][lDspalte] = 1;<br />

}<br />

lDzeile++;<br />

lDspalte++;<br />

} while ((lDzeile < 8) && (lDspalte < 8));<br />

// ausgabe();<br />

<strong>in</strong>t rDzeile = zeile;<br />

<strong>in</strong>t rDspalte = spalte;<br />

// Beruecksichtiung <strong>de</strong>r nach rechts laufen<strong>de</strong>n Diagonale<br />

while ((rDzeile > 0) && (rDspalte < 7))<br />

{<br />

rDzeile--;<br />

rDspalte++;<br />

}<br />

do {<br />

if ((rDzeile != zeile) && (rDspalte != spalte))<br />

{<br />

brett[rDzeile][rDspalte] = 1;<br />

}<br />

rDzeile++;<br />

rDspalte--;<br />

} while ((rDzeile < 8) && (rDspalte >= 0));<br />

}<br />

public void pruefe()<br />

{<br />

<strong>in</strong>t nullen = 0;<br />

<strong>in</strong>t anzDamen = 0;<br />

for (<strong>in</strong>t i = 0; i < 8; i++)<br />

for (<strong>in</strong>t j = 0; j < 8; j++)<br />

{<br />

if (brett[i][j] == 0) nullen++;<br />

if (brett[i][j] == 9) anzDamen++;<br />

}<br />

if (nullen == 0) fuellen = true;<br />

if (anzDamen == 8) ausgeben = true;<br />

}<br />

public void ausgabe()<br />

{<br />

System.out.pr<strong>in</strong>tln();<br />

for (<strong>in</strong>t i = 0; i < 8; i++)<br />

{<br />

for (<strong>in</strong>t j = 0; j < 8; j++)<br />

{<br />

System.out.pr<strong>in</strong>t(brett[i][j] + " ");<br />

}<br />

System.out.pr<strong>in</strong>tln();<br />

}<br />

System.out.pr<strong>in</strong>tln();<br />

<strong>in</strong>t k = 0;<br />

for (<strong>in</strong>t i = 0; i < brett.length; i++)<br />

for (<strong>in</strong>t j = 0; j < brett[i].length; j++)<br />

167


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

{<br />

if (brett[i][j] == 9) positionen[k++] = j;<br />

}<br />

for (k = 0; k < N; k++)<br />

System.out.pr<strong>in</strong>t(positionen[k]+" ");<br />

System.out.pr<strong>in</strong>tln();<br />

}<br />

public void erzeugeDamen()<br />

{<br />

do<br />

{<br />

<strong>in</strong>itialisiere();<br />

do {<br />

nimmPosition();<br />

// ausgabe();<br />

bewertePosition();<br />

// ausgabe();<br />

if (aktZahl > 4) pruefe();<br />

} while (!fuellen);<br />

} while (!ausgeben);<br />

ausgabe();<br />

}<br />

}<br />

public class Damentest<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

Damen d = new Damen();<br />

d.erzeugeDamen();<br />

}<br />

}<br />

2. Pascal’sches Dreieck<br />

1<br />

1 1<br />

1 2 1<br />

1 3 3 1<br />

1 4 6 4 1<br />

1 5 10 10 5 1<br />

1 6 15 20 15 6 1<br />

Das Pascal'sche Dreieck wird mit Hilfe e<strong>in</strong>es nicht rechteckigen Arrays abgebil<strong>de</strong>t.<br />

Für die ersten Elemente kann geschrieben wer<strong>de</strong>n:<br />

<strong>in</strong>t element[][] = {<br />

{1},<br />

{1,1},<br />

{1,2,1}<br />

};<br />

In <strong>de</strong>r folgen<strong>de</strong>n Implementierung wird zu je<strong>de</strong>r Ebene dynamisch e<strong>in</strong> Feld mit <strong>de</strong>r<br />

passen<strong>de</strong>n Länge h<strong>in</strong>zugefügt.<br />

Bsp: 160<br />

public class Pascal<br />

160 vgl. pr22331, Pascal.java<br />

168


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

<strong>in</strong>t dreieck[][] = new <strong>in</strong>t[7][];<br />

for (<strong>in</strong>t i = 0; i < dreieck.length; i++)<br />

{<br />

dreieck[i] = new <strong>in</strong>t[i+1];<br />

for (<strong>in</strong>t j = 0; j


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

{<br />

a3[i][j] = new <strong>in</strong>t[zRand(5)];<br />

}<br />

}<br />

for (<strong>in</strong>t i = 0; i < a3.length; i++)<br />

for (<strong>in</strong>t j = 0; j < a3[i].length; j++)<br />

for (<strong>in</strong>t k = 0; k < a3[i][j].length;k++)<br />

ausgabe("a3[" + i + "][" + j + "][" + k + "] = "<br />

+ a3[i][j][k]);<br />

// Mehrdimensionales Feld mit nicht primitiven Objekten<br />

Integer[][] a4 = {<br />

{ new Integer(1), new Integer(2) },<br />

{ new Integer(3), new Integer(4) },<br />

{ new Integer(5), new Integer(6) },<br />

};<br />

for (<strong>in</strong>t i = 0; i < a4.length; i++)<br />

for (<strong>in</strong>t j = 0; j < a4[i].length; j++)<br />

ausgabe("a4[" + i + "][" + j + "] = " + a4[i][j]);<br />

// Array mit nicht primitiven Objekten <strong>de</strong>r stueckweise<br />

// aufgebaut wird<br />

Integer[][] a5;<br />

a5 = new Integer[3][];<br />

for (<strong>in</strong>t i = 0; i < a5.length; i++)<br />

{<br />

a5[i] = new Integer[3];<br />

for (<strong>in</strong>t j = 0; j < a5[i].length; j++)<br />

a5[i][j] = new Integer(i * j);<br />

}<br />

for (<strong>in</strong>t i = 0; i < a5.length; i++)<br />

for (<strong>in</strong>t j = 0; j < a5[i].length; j++)<br />

ausgabe("a5[" + i + "][" + j + "] = " + a5[i][j]);<br />

}<br />

static void ausgabe(Str<strong>in</strong>g s)<br />

{<br />

System.out.pr<strong>in</strong>tln(s);<br />

}<br />

}<br />

2.2.3.5 Die Klasse Arrays<br />

Seit <strong>de</strong>m JDK1.2 gibt es die Klasse Arrays im Paket java.util mit nützlichen<br />

Metho<strong>de</strong>n zum Zugriff auf Arrays:<br />

public static void fill(<strong>in</strong>t[] a, <strong>in</strong>t wert)<br />

public static <strong>in</strong>t b<strong>in</strong>arySearch(<strong>in</strong>t[] a, <strong>in</strong>t schl)<br />

public static void sort(<strong>in</strong>t[] a)<br />

public static boolean equals(<strong>in</strong>t[] a1, <strong>in</strong>t [] a2)<br />

Diese Metho<strong>de</strong>n stehen auch <strong>in</strong> vergleichbaren Versionen für an<strong>de</strong>re primitive<br />

Datentypen zur Verfügung.<br />

170


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

2.3 Ausdrücke<br />

E<strong>in</strong> Ausdruck ist das Ergebnis e<strong>in</strong>er Verknüpfung von Operan<strong>de</strong>n und Operatoren<br />

nach <strong>de</strong>n syntaktischen Regeln <strong>de</strong>r Sprache. Ausdrücke wer<strong>de</strong>n üblicherweise zur<br />

Durchführung von Operationen (Manipulationen) an Variablen o<strong>de</strong>r Werten<br />

verwen<strong>de</strong>t.<br />

Ausdrücke gehören zu <strong>de</strong>n kle<strong>in</strong>sten ausführbaren E<strong>in</strong>heiten e<strong>in</strong>es Programms. Sie<br />

dienen zur Verzuweisung an Variable, zur Durchführung numerischer berechnungen<br />

und zur Formulierung logischer Bed<strong>in</strong>gungen.<br />

E<strong>in</strong> Ausdruck besteht immer aus m<strong>in</strong><strong>de</strong>stens e<strong>in</strong>em Operan<strong>de</strong>n, auf <strong>de</strong>m <strong>de</strong>r<br />

Operator angewandt wird. Nach <strong>de</strong>m Typ <strong>de</strong>r Operan<strong>de</strong>n unterschei<strong>de</strong>t man<br />

numerische, relationale, logische, bitweise Operatoren. Je<strong>de</strong>r Ausdruck hat e<strong>in</strong>en<br />

Rückgabewert, <strong>de</strong>r durch die Anwendung <strong>de</strong>s Operators auf die Operan<strong>de</strong>n entsteht.<br />

Der Typ <strong>de</strong>s Rückgabewerts bestimmt sich aus <strong>de</strong>n Typen <strong>de</strong>r Operan<strong>de</strong>n und <strong>de</strong>r<br />

Art <strong>de</strong>s verwen<strong>de</strong>ten Operators.<br />

2.3.1 Arithmetische Ausdrücke<br />

Je<strong>de</strong> Programmiersprache hat e<strong>in</strong>en Mechanismus für arithmetische Berechnungen.<br />

In <strong>Java</strong> wer<strong>de</strong>n solche Berechnungen <strong>in</strong> arithmetischen Ausdrücken durchgeführt.<br />

2.3.2 Bewertung von Ausdrücken<br />

Bei <strong>de</strong>r Bewertung von Ausdrücken spielen Operatorassoziativität, Operatorvorrang<br />

und Bewertungsreihenfolge e<strong>in</strong>e Rolle.<br />

Operatorassoziativität<br />

Alle arithmetischen Operatoren assoziieren von l<strong>in</strong>ks nach rechts, d.h.: Falls <strong>de</strong>rselbe<br />

Operator <strong>in</strong> e<strong>in</strong>em Ausdruck mehr als e<strong>in</strong>mal vorkommt, dann wird <strong>de</strong>r am weitesten<br />

l<strong>in</strong>ks stehen<strong>de</strong> zuerst bewertet, gefolgt von <strong>de</strong>m rechts daneben stehen<strong>de</strong>n, usw. Die<br />

Assoziativitätsregel bestimmt, wie Komb<strong>in</strong>ationen <strong>de</strong>s gleichen Operators bewertet<br />

wer<strong>de</strong>n können.<br />

Priorität<br />

<strong>Java</strong> hält sich, wie die grundlegen<strong>de</strong> Arithmetik, strikt an die Reglen <strong>de</strong>r<br />

Vorrangigkeit. Die multiplikativen Operatoren (*, / und %) haben Vorrang vor <strong>de</strong>n<br />

additiven Operatoren (+ und -). Immer wenn die Bewertungsreihenfolge von<br />

Operatoren <strong>in</strong> e<strong>in</strong>em Ausdruck geän<strong>de</strong>rt wer<strong>de</strong>n soll, müssen Klammern benutzt<br />

wer<strong>de</strong>n. Je<strong>de</strong>r Ausdruck <strong>in</strong> Klammern wird zuerst bewertet. Der Vorrang <strong>de</strong>r<br />

e<strong>in</strong>stelligen arithmetischen Operatoren steht über allen an<strong>de</strong>ren arithmetischen<br />

Operatoren.<br />

171


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Bewertungsreihenfolge<br />

Die Vorrangregeln helfen bei <strong>de</strong>r Bewertung, welche Operatoren <strong>in</strong> e<strong>in</strong>em Ausdruck<br />

zuerst benutzt wer<strong>de</strong>n und welche Operan<strong>de</strong>n zu welchen Operatoren gehören. Die<br />

Regeln für die Bewertungsreihenfolge helfen festzulegen, wann welche Operan<strong>de</strong>n<br />

bewertet wer<strong>de</strong>n. Die drei folgen<strong>de</strong>n Regeln bestimmen, wie e<strong>in</strong> Ausdruck bewertet<br />

wird:<br />

- Bei allen b<strong>in</strong>ären Operatoren wird <strong>de</strong>r l<strong>in</strong>ke Operand vor <strong>de</strong>m rechten bewertet.<br />

- Zuerst wer<strong>de</strong>n die Operan<strong>de</strong>n, danach die Operatoren bewertet.<br />

- Falls mehrere Parameter, die durch Kommata vone<strong>in</strong>an<strong>de</strong>r getrennt s<strong>in</strong>d, durch e<strong>in</strong>en Metho<strong>de</strong>naufruf zur<br />

Verfügung gestellt wer<strong>de</strong>n, wer<strong>de</strong>n diese Parameter von l<strong>in</strong>ks nach rechts bewertet.<br />

2.3.3 Typkonvertierungen<br />

<strong>Java</strong> ist e<strong>in</strong>e typisierte Sprache. Es f<strong>in</strong><strong>de</strong>n gründliche Typüberprüfungen statt, und es<br />

gelten strikte Beschränkungen für die Konvertierung von Werten e<strong>in</strong>es Typs zu<br />

e<strong>in</strong>em an<strong>de</strong>ren. Unter „Cast<strong>in</strong>g“ versteht man die Umwandlung von e<strong>in</strong>em Datentyp<br />

<strong>in</strong> e<strong>in</strong>en an<strong>de</strong>ren.<br />

<strong>Java</strong> unterstützt explizite Konvertierungen und „ad hoc“-Konvertierungen.<br />

Ad-hoc-Konvertierungen. Hier gibt es folgen<strong>de</strong> Regeln für numerische Datentypen:<br />

- Bei Operationen mit ausschl. ganzzahligen Operan<strong>de</strong>n wird, falls e<strong>in</strong>er <strong>de</strong>r bei<strong>de</strong>n Operan<strong>de</strong>n <strong>de</strong>n<br />

Datentyp long hat, <strong>de</strong>r an<strong>de</strong>re ebenfalls zu long konvertiert; ansonsten wer<strong>de</strong>n bei<strong>de</strong> Operan<strong>de</strong>n<br />

zu <strong>in</strong>t konvertiert. Das Ergebnis ist dann ebenfalls vom Typ <strong>in</strong>t. Ist allerd<strong>in</strong>gs <strong>de</strong>r ausgegebene<br />

Wert zu groß, um im Wertebereich von <strong>in</strong>t dargestellt zu wer<strong>de</strong>n, wird <strong>de</strong>r Typ long verwen<strong>de</strong>t.<br />

- Bei Operationen mit wenigstens e<strong>in</strong>em Gleitpunkt-Operan<strong>de</strong>n wird, wenn e<strong>in</strong>er <strong>de</strong>r Operan<strong>de</strong>n <strong>de</strong>n<br />

Datentyp double hat, <strong>de</strong>r an<strong>de</strong>re ebenfalls zu double konvertiert. Das Ergebnis ist dann ebenfalls<br />

vom Datentyp double, an<strong>de</strong>renfalls wer<strong>de</strong>n bei<strong>de</strong> Operan<strong>de</strong>n zum Datentyp float konvertiert.<br />

Das Ergebnis ist ebenfalls vom Typ float.<br />

Explizite Konvertierungen. Sie s<strong>in</strong>d immer nötig, wenn e<strong>in</strong>e Umwandlung <strong>in</strong> e<strong>in</strong>en<br />

an<strong>de</strong>ren Datentyp gewünscht wird und diese nicht „ad hoc“ e<strong>in</strong>tritt. „Cast<strong>in</strong>g“ muß<br />

dann angewen<strong>de</strong>t wer<strong>de</strong>n. Der zugehörige (Festlegungs-)Operator besteht aus<br />

e<strong>in</strong>em Typnamen <strong>in</strong> run<strong>de</strong>n Klammern. Er ist e<strong>in</strong> e<strong>in</strong>stelliger Operator mit hoher<br />

Priorität und steht vor se<strong>in</strong>en Operan<strong>de</strong>n. Er hat immer die folgen<strong>de</strong> Form:<br />

„(Datentyp) Wert“. Der (Festlegungs-)Operator bestimmt <strong>de</strong>n Wert se<strong>in</strong>es<br />

Operan<strong>de</strong>n, <strong>de</strong>r auf <strong>de</strong>n <strong>in</strong> Klammern bezeichneten Typ festgelegt wird. Es gibt<br />

folgen<strong>de</strong> Cast<strong>in</strong>g-Operatoren:<br />

Operator Beispiel Erläuterung<br />

(byte)<br />

(byte) (x/y) Wan<strong>de</strong>lt das Errgebnis von x/y <strong>in</strong> e<strong>in</strong>en Wert vom Datentyp byte<br />

um<br />

(short) (short) x Wan<strong>de</strong>lt x <strong>in</strong> e<strong>in</strong>en Wert vom Datentyp short um<br />

(<strong>in</strong>t) (<strong>in</strong>t) (x/y) Wan<strong>de</strong>lt das Ergebnis von x/y <strong>in</strong> e<strong>in</strong>en Wert vom Datentyp <strong>in</strong>t<br />

(long) (long) x Wan<strong>de</strong>lt x <strong>in</strong> e<strong>in</strong>en Wert vom Datentyp long um<br />

(float) (float) x Wan<strong>de</strong>lt x <strong>in</strong> e<strong>in</strong>en Wert vom Datentyp float um<br />

(double) (double) x Wan<strong>de</strong>lt x <strong>in</strong> e<strong>in</strong>en Wert vom Datentyp double um<br />

(char) (char) x Wan<strong>de</strong>lt x <strong>in</strong> e<strong>in</strong>en Wert vom Datentyp char um<br />

(boolean) (boolean) 0 Wan<strong>de</strong>lt 0 <strong>in</strong> e<strong>in</strong>en booleschen Datentyp um<br />

Abb.: Cast<strong>in</strong>g-Operatoren<br />

172


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Cast<strong>in</strong>g hat e<strong>in</strong>e höhere Priorität als Arithmetik. Deshalb müssen arithmetische<br />

Operationen <strong>in</strong> Verb<strong>in</strong>dung mit Cast<strong>in</strong>g <strong>in</strong> Klammern gesetzt wer<strong>de</strong>n<br />

Nicht alle Konvertierungen s<strong>in</strong>d möglich. Variable e<strong>in</strong>es arithmetischen Typs können<br />

auf je<strong>de</strong>n an<strong>de</strong>ren arithmetischen Typ festgelegt wer<strong>de</strong>n. Boolesche Werte können<br />

nicht auf irgen<strong>de</strong><strong>in</strong>en an<strong>de</strong>ren Wert festgelegt wer<strong>de</strong>n. Die Umkehrung funktioniert<br />

mit E<strong>in</strong>schränkungen: 0 und 1 lassen sich <strong>in</strong> boolesche Werte konvertieren.<br />

Bsp. 162 : Konvertieren primitiver Typen<br />

public class KonvPrim<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

char z = 'a';<br />

System.out.pr<strong>in</strong>tln("char z = '" + z + '\'');<br />

System.out.pr<strong>in</strong>tln("Unico<strong>de</strong> von z: " + (<strong>in</strong>t) z);<br />

<strong>in</strong>t i = 17;<br />

System.out.pr<strong>in</strong>tln("<strong>in</strong>t i = " + i);<br />

long l = 4L;<br />

System.out.pr<strong>in</strong>tln("long l = " + l);<br />

double d = 17.3;<br />

System.out.pr<strong>in</strong>tln("double d = " + d);<br />

float f = 4.5F;<br />

System.out.pr<strong>in</strong>tln("float f = " + f);<br />

double sd;<br />

float sf;<br />

long sl;<br />

<strong>in</strong>t si;<br />

sd = i + l + d + f;<br />

System.out.pr<strong>in</strong>tln("sd = i + l + d + f = " + sd);<br />

// sf = i + l + d + f;<br />

/* Inkompatibler Typ! */<br />

// System.out.pr<strong>in</strong>tln("sf = i + l + d + f = " + sf);<br />

sf = (float) (i + l + d + f);<br />

// sl = i + l + d + f;<br />

/* Inkompatibler Typ! */<br />

// System.out.pr<strong>in</strong>tln("sl = i + l + d + f = " + sl);<br />

sl = i + l + (long) d + (long) f;<br />

System.out.pr<strong>in</strong>tln("sl = l + i + (long) d + (long) f = " + sl);<br />

sl = (long) (i + l + d + f);<br />

System.out.pr<strong>in</strong>tln("sl = (long) (i + l + d + f) = " + sl);<br />

// si = i + l + d + f;<br />

/* Inkompatibler Typ! */<br />

// System.out.pr<strong>in</strong>tln("si = i + l + d + f = " + si);<br />

si = i + (<strong>in</strong>t) l + (<strong>in</strong>t) d + (<strong>in</strong>t) f;<br />

System.out.pr<strong>in</strong>tln("sl = l + (<strong>in</strong>t) i + (<strong>in</strong>t) d + (<strong>in</strong>t) f = " + sl);<br />

si = (<strong>in</strong>t) (i + l + d + f);<br />

System.out.pr<strong>in</strong>tln("sl = (<strong>in</strong>t) (i + l + d + f) = " + si);<br />

}<br />

}<br />

Konvertieren von Objekten. Mit E<strong>in</strong>schränkungen lassen sich Klassen<strong>in</strong>stanzen <strong>in</strong><br />

Instanzen an<strong>de</strong>rer Klassen konvertieren. Die Klassen müssen allerd<strong>in</strong>gs durch<br />

Vererbung mite<strong>in</strong>an<strong>de</strong>r verbun<strong>de</strong>n se<strong>in</strong>. Allgeme<strong>in</strong> gilt: E<strong>in</strong> Objekt e<strong>in</strong>er Klasse kann<br />

auf se<strong>in</strong>e Superklasse festgelegt wer<strong>de</strong>n. Spezifische Informationen <strong>de</strong>r Subklasse<br />

gehen dabei verloren. Das Konvertieren erfolgt immer nach folgen<strong>de</strong>r Form:<br />

(Klassenname) Objekt.<br />

162 vgl. pr23301<br />

173


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Bsp. 163 : Konvertieren Datentyp Object<br />

import java.applet.Applet;<br />

import java.awt.*;<br />

import java.util.Vector;<br />

public class MittleresDrittel extends Applet<br />

{<br />

<strong>in</strong>t appletHoehe;<br />

<strong>in</strong>t appletBreite;<br />

Vector endpunkt = new Vector();<br />

public void <strong>in</strong>it()<br />

{<br />

Dimension d = getSize();<br />

appletHoehe = d.height - 1;<br />

appletBreite = d.width - 1;<br />

// Anhaengen an Liste<br />

endpunkt.addElement(new Float(0.0f));<br />

endpunkt.addElement(new Float(1.0f));<br />

}<br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

float x1, x2;<br />

Float tempFloat;<br />

g.setColor(Color.yellow);<br />

g.fillRect(0,0,appletBreite,appletHoehe);<br />

g.setColor(Color.black);<br />

for (<strong>in</strong>t i = 0; i < appletHoehe; i+=5)<br />

{<br />

// Zeichne die L<strong>in</strong>ien<br />

for (<strong>in</strong>t j = 0; j < endpunkt.size(); j+=2)<br />

{<br />

tempFloat = (Float) endpunkt.elementAt(j);<br />

x1 = tempFloat.floatValue();<br />

tempFloat = (Float) endpunkt.elementAt(j+1);<br />

x2 = tempFloat.floatValue();<br />

g.drawL<strong>in</strong>e(Math.round(x1 * appletBreite),i,<br />

Math.round(x2 * appletBreite),i);<br />

}<br />

// Entferne das mittlere Drittel aus <strong>de</strong>n L<strong>in</strong>ien<br />

schnei<strong>de</strong>ausSegment();<br />

tempFloat = (Float) endpunkt.elementAt(0);<br />

x1 = tempFloat.floatValue();<br />

System.out.pr<strong>in</strong>tln(x1);<br />

tempFloat = (Float) endpunkt.elementAt(1);<br />

x2 = tempFloat.floatValue();<br />

System.out.pr<strong>in</strong>tln(x2);<br />

if (Math.round(x1*appletBreite) ==<br />

Math.round(x2*appletBreite)) break;<br />

}<br />

}<br />

public void schnei<strong>de</strong>ausSegment()<br />

{<br />

<strong>in</strong>t <strong>in</strong><strong>de</strong>x = 0;<br />

float luecke;<br />

Float tempFloat1, tempFloat2;<br />

<strong>in</strong>t stop = endpunkt.size();<br />

for (<strong>in</strong>t i = 0; i < stop; i += 2)<br />

{<br />

schnei<strong>de</strong>ausMittleresDrittel(<strong>in</strong><strong>de</strong>x,<strong>in</strong><strong>de</strong>x+1);<br />

<strong>in</strong><strong>de</strong>x += 4;<br />

}<br />

}<br />

163 pr23302<br />

174


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

public void schnei<strong>de</strong>ausMittleresDrittel(<strong>in</strong>t l<strong>in</strong>ks, <strong>in</strong>t rechts)<br />

{<br />

float luecke;<br />

float x1, x2;<br />

Float tempFloat1, tempFloat2;<br />

// Zugriff an <strong>de</strong>r angegebenen Position<br />

tempFloat1 = (Float) endpunkt.elementAt(l<strong>in</strong>ks);<br />

tempFloat2 = (Float) endpunkt.elementAt(rechts);<br />

luecke = tempFloat2.floatValue() - tempFloat1.floatValue();<br />

x1 = tempFloat1.floatValue() + luecke/3.0f;<br />

x2 = tempFloat2.floatValue() - luecke/3.0f;<br />

// E<strong>in</strong>fuegen an <strong>de</strong>r angegebenen Stelle <strong>in</strong> <strong>de</strong>r Liste<br />

endpunkt.<strong>in</strong>sertElementAt(new Float(x2),rechts);<br />

endpunkt.<strong>in</strong>sertElementAt(new Float(x1),rechts);<br />

}<br />

}<br />

2.3.4 Vergleichsoperatoren<br />

<strong>Java</strong> hat e<strong>in</strong>e Menge von Operatoren für das Vergleichen von zwei o<strong>de</strong>r mehr<br />

Größen. Diese Operatoren lassen sich aufteilen <strong>in</strong><br />

- relationale Operatoren. Sie s<strong>in</strong>d zum Ordnen von Größen bestimmt, ob etwa e<strong>in</strong> Wert größer o<strong>de</strong>r<br />

kle<strong>in</strong>er als e<strong>in</strong> an<strong>de</strong>rer ist.<br />

- Gleichheitsoperatoren. Sie sagen nur aus, ob zwei Werte gleich s<strong>in</strong>d<br />

2.3.5 Logische Ausdrücke<br />

Logische Operationen können auf zwei verschie<strong>de</strong>ne Arten ausgeführt wer<strong>de</strong>n:<br />

- Short-turn-Operatoren (logisches AND && und logisches OR ||). Sie operieren nur mit booleschen<br />

Variablen.<br />

- Bitweise Operatoren. Sie operieren mit je<strong>de</strong>m Bit zweier ganzzahliger Operan<strong>de</strong>n.<br />

175


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

2.4 Anweisungen<br />

2.4.1 Blöcke und Anweisungen<br />

Metho<strong>de</strong>n und statische Initialisatoren wer<strong>de</strong>n <strong>in</strong> <strong>Java</strong> durch Anweisungsblöcke<br />

<strong>de</strong>f<strong>in</strong>iert. E<strong>in</strong> Anweisungsblock besteht <strong>in</strong> <strong>de</strong>r Regel aus e<strong>in</strong>er Reihe von<br />

Anweisungen, die <strong>in</strong> geschweiften Klammern stehen. Das be<strong>de</strong>utet: Es können <strong>in</strong><br />

diesem Block lokale Variablen <strong>de</strong>klariert wer<strong>de</strong>n, die außerhalb <strong>de</strong>s Blocks nicht<br />

verfügbar s<strong>in</strong>d, und <strong>de</strong>ren Existenz erlischt, wenn <strong>de</strong>r Block ausgeführt wur<strong>de</strong>.<br />

2.4.2 Leere Anweisungen<br />

In <strong>Java</strong> können leere Anweisungen erstellt wer<strong>de</strong>n. Für <strong>de</strong>n Compiler sieht e<strong>in</strong>e leere<br />

Anweisung nur wie e<strong>in</strong> zusätzliches Semikolon aus.<br />

2.4.3 Benannte Anweisungen<br />

Je<strong>de</strong> Anweisung darf <strong>in</strong> <strong>Java</strong> e<strong>in</strong>e „Benennung“ haben. Die „Benennung“ hat die<br />

gleichen Eigenschaften wie je<strong>de</strong>r an<strong>de</strong>re Bezeichner. Die Reichweite <strong>de</strong>r<br />

„Benennung“ erstreckt sich über <strong>de</strong>n ganzen Block. Der Benennung folgt e<strong>in</strong><br />

Doppelpunkt.<br />

„Benennungen“ wer<strong>de</strong>n nur von <strong>de</strong>n Sprunganweisungen break und cont<strong>in</strong>ue<br />

benutzt.<br />

2.4.4 Deklarationen<br />

E<strong>in</strong>e Deklarationsanweisung <strong>de</strong>f<strong>in</strong>iert e<strong>in</strong>e Variable, egal ob Klasse, Schnittstelle,<br />

Datenfeld, Objekt o<strong>de</strong>r primitiver Typ. Das Format e<strong>in</strong>er solchen Anweisung hängt<br />

davon ab, welcher <strong>de</strong>r 5 verschie<strong>de</strong>nen Formen <strong>de</strong>klariert wird.<br />

Typname variablenName;<br />

bzw.<br />

Typname variablenName = <strong>in</strong>itialerWert;<br />

2.4.5 Ausdrucksanweisungen<br />

In <strong>Java</strong> gibt es sieben verschie<strong>de</strong>ne Arten von Ausdrucksanweisungen:<br />

Ausdrucksanweisung<br />

Zuordnung<br />

Prä-Inkrement<br />

Prae-Dekrement<br />

Beispiel<br />

x=13;<br />

++wert;<br />

--wert;<br />

176


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Post-Inkrement<br />

Post-Dekrement<br />

Metho<strong>de</strong>naufruf<br />

Zuweisungsausdruck<br />

wert++;<br />

wert--;<br />

System.out.pr<strong>in</strong>tln("Aller Anfang ist schwer!");<br />

byte x = new byte;<br />

Abb.: Die sieben Ausdrucksanweisungen <strong>in</strong> <strong>Java</strong><br />

Bsp.: Demonstrationsprogramm zur Wirkungsweise <strong>de</strong>r Inkrement- und Dekrement-<br />

Operatoren 164<br />

// Demonstration <strong>de</strong>r Operatoren ++ und --<br />

public class AutoInkr<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

<strong>in</strong>t i = 1;<br />

ausgabe("i: " + i);<br />

ausgabe("++i: " + ++i); // Pre-Inkrement<br />

ausgabe("i++: " + i++); // Post-Inkrement<br />

ausgabe("i: " + i);<br />

ausgabe("--i: " + --i); // Pre-Dekrement<br />

ausgabe("i--: " + i--); // Post-Inkrement<br />

ausgabe("i: " + i);<br />

}<br />

static void ausgabe(Str<strong>in</strong>g s)<br />

{<br />

System.out.pr<strong>in</strong>tln(s);<br />

}<br />

}<br />

/* Ausgabe<br />

i: 1<br />

++i: 2<br />

i++: 2<br />

i: 3<br />

--i: 2<br />

i--: 2<br />

i: 1<br />

*/<br />

2.4.6 Auswahlanweisungen<br />

2.4.6.1 if-Anweisungen<br />

E<strong>in</strong>e if-Anweisung testet e<strong>in</strong>e boolesche Variable o<strong>de</strong>r e<strong>in</strong>en Ausdruck zur<br />

Feststellung, ob e<strong>in</strong>e Anweisung o<strong>de</strong>r e<strong>in</strong> Anweisungsblock ausgeführt wer<strong>de</strong>n soll.<br />

Hat die boolesche Variable <strong>de</strong>n Wert true, wird <strong>de</strong>r Block ausgeführt. Falls nicht,<br />

spr<strong>in</strong>gt die Programmkontrolle zur nächsten Anweísung h<strong>in</strong>ter <strong>de</strong>m Block.<br />

if (boolscher_Ausdruck)<br />

anweisung 165<br />

2.4.6.2 if-else-Anweisungen<br />

164 vgl. pr24500<br />

165 „anweisung“ be<strong>de</strong>utet: E<strong>in</strong>e e<strong>in</strong>fache Anweisung, die nur von e<strong>in</strong>em Semikolon abgeschlossen ist bzw.<br />

e<strong>in</strong>e zusammengesetzte Folge von anweisungen, die von { ... } umschlossen ist.<br />

177


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Ergänzt if um e<strong>in</strong>en else-Teil. Dieser else-Teil reicht die Kontrolle an e<strong>in</strong>e<br />

Anweisung o<strong>de</strong>r Block weiter, wenn <strong>de</strong>r boolesche Wert im if-Teil <strong>de</strong>r Anweisung<br />

false war.<br />

if (boolscher_Ausdruck)<br />

anweisung<br />

else<br />

anweisung<br />

Bsp.: Demonstrationsprogramm zur if-Anweisung 166<br />

import java.lang.*;<br />

public class IfDemo extends Object<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

<strong>in</strong>t i;<br />

<strong>in</strong>t abs;<br />

// if - else<br />

i = 13;<br />

System.out.pr<strong>in</strong>tln("i = " + i);<br />

if (i >= 0)<br />

{<br />

abs = i;<br />

}<br />

else {<br />

abs = -i;<br />

}<br />

System.out.pr<strong>in</strong>tln("abs = " + abs);<br />

i = -13;<br />

System.out.pr<strong>in</strong>tln("i = " + i);<br />

if (i >= 0)<br />

{<br />

abs = i;<br />

}<br />

else {<br />

abs = -i;<br />

}<br />

System.out.pr<strong>in</strong>tln("abs = " + abs);<br />

// if<br />

i = 13;<br />

abs = i;<br />

System.out.pr<strong>in</strong>tln("i = " + i);<br />

if (abs < 0)<br />

{<br />

abs = -abs;<br />

}<br />

System.out.pr<strong>in</strong>tln("abs = " + abs);<br />

i = -13;<br />

abs = i;<br />

System.out.pr<strong>in</strong>tln("i = " + i);<br />

if (abs < 0)<br />

{<br />

abs = -abs;<br />

}<br />

System.out.pr<strong>in</strong>tln("abs = " + abs);<br />

// Konditionaloperator<br />

i = -13;<br />

System.out.pr<strong>in</strong>tln("i = " + i);<br />

abs = ( i < 0 ? -i : i);<br />

System.out.pr<strong>in</strong>tln("abs = " + abs);<br />

166 Vgl. pr24601<br />

178


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

i = 13;<br />

System.out.pr<strong>in</strong>tln("i = " + i);<br />

abs = ( i < 0 ? -i : i);<br />

System.out.pr<strong>in</strong>tln("abs = " + abs);<br />

}<br />

}<br />

2.4.6.3 switch-Anweisungen<br />

E<strong>in</strong>e switch-Anweisung ermöglicht die Weitergabe <strong>de</strong>s Kontrollflusses an e<strong>in</strong>e von<br />

vielen Anweisungen <strong>in</strong> ihrem Block mit Unteranweisungen (ist abhängig vom Wert<br />

<strong>de</strong>s Ausdrucks <strong>in</strong> <strong>de</strong>r switch-Anweisung).<br />

switch (<strong>in</strong>tegraler_Selektor)<br />

{<br />

case <strong>in</strong>tegraler_Wert1: anweisung; break;<br />

case <strong>in</strong>tegraler_Wert2: anweisung; break;<br />

case <strong>in</strong>tegraler_Wert3: anweisung; break;<br />

.....<br />

<strong>de</strong>fault: anweisung;<br />

}<br />

E<strong>in</strong> <strong>in</strong>tegraler_Selektor ist e<strong>in</strong> Ausdruck zur Produktion e<strong>in</strong>es <strong>in</strong>tegralen Werts 167 .<br />

„switch“ vergleicht das Ergebnis e<strong>in</strong>er Auswertung vom <strong>in</strong>tegralen Selektor mit je<strong>de</strong>m<br />

<strong>in</strong>tegralen Wert. Falls e<strong>in</strong> passen<strong>de</strong>r Wert gefun<strong>de</strong>n wird, wird die zugehörige<br />

Anweisung (e<strong>in</strong>fach o<strong>de</strong>r zusammengesetzt) ausgeführt. An<strong>de</strong>renfalls kommt es zur<br />

Ausführung <strong>de</strong>r bei „<strong>de</strong>fault“ angegebenen Anweisung. Die „break“-Anweisung ist<br />

optional. Fehlt sie, dann wer<strong>de</strong>n die folgen<strong>de</strong>n Anweisungen ausgeführt, bis e<strong>in</strong><br />

„break“ auftritt.<br />

Bsp.: Demonstrationsprogramm zur switch-Anweisung 168<br />

import java.lang.*;<br />

public class SwitchDemo extends Object<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

<strong>in</strong>t <strong>de</strong>zimal = 10;<br />

System.out.pr<strong>in</strong>t("<strong>de</strong>zimal = " + <strong>de</strong>zimal + " hex: ");<br />

switch (<strong>de</strong>zimal)<br />

{<br />

case 0:<br />

case 1:<br />

case 2:<br />

case 3:<br />

case 4:<br />

case 5:<br />

case 6:<br />

case 7:<br />

case 8:<br />

case 9:<br />

System.out.pr<strong>in</strong>t("" + <strong>de</strong>zimal);<br />

break;<br />

case 10:<br />

167 Integrale Werte s<strong>in</strong>d bspw. vom Typ „<strong>in</strong>t“, „char“. Nicht<strong>in</strong>tegrale Typen. Z.B. float, Str<strong>in</strong>g, müssen über e<strong>in</strong>e<br />

Serie von „if“-Anweisungen „switch“ simulieren<br />

168 vgl. pr24601<br />

179


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

System.out.pr<strong>in</strong>t("A");<br />

break;<br />

case 11:<br />

System.out.pr<strong>in</strong>t("B");<br />

break;<br />

case 12:<br />

System.out.pr<strong>in</strong>t("C");<br />

break;<br />

case 13:<br />

System.out.pr<strong>in</strong>t("D");<br />

break;<br />

case 14:<br />

System.out.pr<strong>in</strong>t("E");<br />

break;<br />

case 15:<br />

System.out.pr<strong>in</strong>t("F");<br />

break;<br />

<strong>de</strong>fault:<br />

System.out.pr<strong>in</strong>t("Ke<strong>in</strong>e gueltige Hex-Ziffer");<br />

break;<br />

}<br />

System.out.pr<strong>in</strong>tln();<br />

}<br />

}<br />

2.4.7 Iterationsanweisungen<br />

2.4.7.1 while-Anweisung<br />

Die while-Anweisung testet e<strong>in</strong>e boolesche Variable o<strong>de</strong>r e<strong>in</strong>en Ausdruck. Ist er<br />

true, wird die Unteranweisung o<strong>de</strong>r <strong>de</strong>r Block solange ausgeführt, bis sich <strong>de</strong>r Wert<br />

false e<strong>in</strong>stellt. Ist die Variable o<strong>de</strong>r <strong>de</strong>r Ausdruck false, wird die Kontrolle an die<br />

nächste Anweisung nach <strong>de</strong>r Unteranweisung o<strong>de</strong>r nach <strong>de</strong>m Block <strong>de</strong>r while-<br />

Anweisung weitergegeben.<br />

while (boolescher_Ausdruck)<br />

anweisung<br />

Bsp. 169 :<br />

public class WhileDemo extends Object<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

double a = 2.0;<br />

<strong>in</strong>t n = 10;<br />

System.out.pr<strong>in</strong>tln("a = " + a);<br />

System.out.pr<strong>in</strong>tln("n = " + n);<br />

<strong>in</strong>t absn = (n < 0) ? -n : n;<br />

double aHochn = 1.0;<br />

<strong>in</strong>t i = 1;<br />

while (i


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

{<br />

aHochn = 1.0 / aHochn;<br />

}<br />

System.out.pr<strong>in</strong>tln("aHochn = " + aHochn);<br />

}<br />

}<br />

2.4.7.2 do-Anweisung<br />

Die do-Anweisung testet e<strong>in</strong>e boolesche Variable o<strong>de</strong>r e<strong>in</strong>en Ausdruck. Solange<br />

dieser <strong>de</strong>n Wert true hat, wird die Unteranweisung o<strong>de</strong>r <strong>de</strong>r Block ausgeführt. Erst<br />

wenn die boolesche Variable o<strong>de</strong>r <strong>de</strong>r Ausdruck <strong>de</strong>n Wert false hat, wird die<br />

Wie<strong>de</strong>rholung e<strong>in</strong>gestellt und die Schleife verlassen. Der Co<strong>de</strong>-Block <strong>in</strong>nerhalb <strong>de</strong>r<br />

do-Anweisung wird auf je<strong>de</strong>n Fall m<strong>in</strong><strong>de</strong>stens e<strong>in</strong>mal ausgeführt.<br />

do<br />

anweisung<br />

while (boolescher_Ausdruck)<br />

Bsp. 170 :<br />

public class DoDemo extends Object<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

double a = 2.0;<br />

<strong>in</strong>t n = 10;<br />

System.out.pr<strong>in</strong>tln("a = " + a);<br />

System.out.pr<strong>in</strong>tln("n = " + n);<br />

<strong>in</strong>t absn = (n < 0) ? -n : n;<br />

double aHochn = 1.0;<br />

<strong>in</strong>t i = 1;<br />

do<br />

{<br />

if (n == 0)<br />

{<br />

break;<br />

}<br />

aHochn *= a;<br />

i++;<br />

} while (i


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

2.4.7.3 for-Anweisung<br />

Sie besteht aus<br />

- for, gefolgt von optionalem Leerraum, gefolgt von e<strong>in</strong>er öffnen<strong>de</strong>n Klammer.<br />

- Initialisierungsteil. Er enthält e<strong>in</strong>e durch Kommata getrente Reihe von<br />

Deklarations- und Zuweisungsanweisungen, die durch e<strong>in</strong> Semikolon been<strong>de</strong>t<br />

wird. Die Deklarationen haben nur Gültigkeit für <strong>de</strong>n Bereich <strong>de</strong>r for-Anweisung<br />

und ihrer Unteranweisungen. Die Zuweisungen wer<strong>de</strong>n nur e<strong>in</strong>mal vor <strong>de</strong>r esrten<br />

Wie<strong>de</strong>rholung <strong>de</strong>r Unteranweisung o<strong>de</strong>r <strong>de</strong>s Blocks gemacht.<br />

- Testteil. Enthält e<strong>in</strong>e boolesche Variable o<strong>de</strong>r e<strong>in</strong>en Ausdruck, <strong>de</strong>r e<strong>in</strong>mal je<br />

Schleife neu bewertet wird. Falls <strong>de</strong>r Ausdruck false wird, geht die Kontrolle zur<br />

nächsten Anweisung nach <strong>de</strong>r for-Anweisung und ihrer Unteranweisung o<strong>de</strong>r<br />

ihrem Block weiter. Die Zuweisungen wer<strong>de</strong>n nur e<strong>in</strong>mal vor <strong>de</strong>r ersten<br />

Wie<strong>de</strong>rholung <strong>de</strong>r Unteranweisung o<strong>de</strong>r <strong>de</strong>s Blocks gemacht.<br />

- Inkrementteil. Enthält e<strong>in</strong>e durch Kommata getrennte Reihe von Ausdrücken, die<br />

e<strong>in</strong>mal je Durchlauf <strong>de</strong>r Schleife bewertet wer<strong>de</strong>n. Dieser Teil wird gewöhnlich<br />

dazu verwen<strong>de</strong>t, e<strong>in</strong>en In<strong>de</strong>x, <strong>de</strong>r im Testteil überprüft wird, zu <strong>in</strong>krementieren.<br />

Am En<strong>de</strong> <strong>de</strong>s Teils steht e<strong>in</strong> Semikolon.<br />

Je<strong>de</strong>r <strong>de</strong>r drei Ausdrücke kann entfallen.<br />

for (<strong>in</strong>itialisierung; boolescher_Ausdruck; naechster_Schritt)<br />

anweisung<br />

Bsp.: public class ForDemo extends Object<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

double a = 2.0;<br />

<strong>in</strong>t n = 10;<br />

System.out.pr<strong>in</strong>tln("a = " + a);<br />

System.out.pr<strong>in</strong>tln("n = " + n);<br />

<strong>in</strong>t absn = (n < 0) ? -n : n;<br />

double aHochn = 1.0;<br />

for (<strong>in</strong>t i = 1; i


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

2.4.7.4 Die erweiterte Form <strong>de</strong>r for-Schleife <strong>in</strong> <strong>Java</strong> 1.5<br />

for (Type i<strong>de</strong>ntifier : expr) { body }<br />

Dieser Konstrukt wird so gelesen: "Für je<strong>de</strong>s i<strong>de</strong>ntifier <strong>de</strong>s Typs Type <strong>in</strong> expr<br />

führe aus".<br />

Das erweiterte for möchte l<strong>in</strong>ks vom Doppelpunkt e<strong>in</strong> Feld (Array) o<strong>de</strong>r Objekt, das<br />

vom Typ Iterable ist. Das Interface Iterable schreibt nur die Existenz e<strong>in</strong>er<br />

Funktion iterator() vor, die e<strong>in</strong>en java.lang.SimpleIterator liefert. Der<br />

konkrete SimpleIterator muß nur die Metho<strong>de</strong>n hasNext() und next()<br />

implementieren, um das nächste Element <strong>in</strong> <strong>de</strong>r Aufzählung zu beschaffen und das<br />

En<strong>de</strong> anzuzeigen. E<strong>in</strong> SimpleIterator ist e<strong>in</strong> Iterator 171 ohne remove().<br />

Bsp.:<br />

import java.util.ArrayList;<br />

import java.util.Iterator;<br />

public class ForLoopTest<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

double[] array = {2.5, 5.2, 7.9, 4.3, 2.0, 4.1, 7.3, 0.1, 2.6};<br />

// E<strong>in</strong>fache Iteration vorwaerts durch die Schleife<br />

for(double d: array) { System.out.pr<strong>in</strong>tln(d); }<br />

System.out.pr<strong>in</strong>tln("---------------------");<br />

// Das folgen<strong>de</strong> arbeitet mit allem, das das Interface Iterable<br />

// implementiert, z.B mit Collections wie ArrayList<br />

ArrayList list = new ArrayList();<br />

list.add(7); list.add(15); list.add(-67);<br />

for(Integer number : list) { System.out.pr<strong>in</strong>tln(number); }<br />

System.out.pr<strong>in</strong>tln("---------------------");<br />

// Es unterstuetzt auch Autobox<strong>in</strong>g<br />

for(<strong>in</strong>t item: list) { System.out.pr<strong>in</strong>tln(item); }<br />

System.out.pr<strong>in</strong>tln("---------------------");<br />

}<br />

}<br />

Test:<br />

Abb.:<br />

171 vgl. 6.2.1<br />

183


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

2.4.8 Sprung-Anweisungen<br />

2.4.8.1 break-Anweisung<br />

Unteranweisungsblöcke von Schleifen und switch-Anweisungen können durch<br />

Verwendung e<strong>in</strong>er break-Anweisung verlassen wer<strong>de</strong>n. E<strong>in</strong>e unbezeichnete break-<br />

Anweisung spr<strong>in</strong>gt zur nächsten Zeile nach <strong>de</strong>r aktuellen (<strong>in</strong>nersten) Wie<strong>de</strong>rholungsund<br />

switch-Anweisung. Mit e<strong>in</strong>er bezeichneten break-Anweisung am Anfang e<strong>in</strong>er<br />

Schleife kann an e<strong>in</strong>e Anweisung mit dieser Bezeichnung <strong>in</strong> <strong>de</strong>r <strong>de</strong>rzeitigen Metho<strong>de</strong><br />

gesprungen wer<strong>de</strong>n. Am Anfangsteil <strong>de</strong>r Schleife muß e<strong>in</strong> Label (e<strong>in</strong>e Bezeichnung)<br />

mit e<strong>in</strong>em Doppelpunkt stehen, z.B.: „label1:“.<br />

2.4.8.2 cont<strong>in</strong>ue-Anweisung<br />

Der aktuelle Schleifendurchlauf wird unterbrochen. Es wird zum Anfang <strong>de</strong>r Schleife<br />

zurückgekehrt, falls h<strong>in</strong>ter cont<strong>in</strong>ue ke<strong>in</strong> „Bezeichner“ steht. An<strong>de</strong>renfalls wird zu<br />

e<strong>in</strong>er äußeren Schleife zurückgekehrt, die e<strong>in</strong>e Markierung (label) gleichen<br />

Namens enthält.<br />

E<strong>in</strong>e cont<strong>in</strong>ue-Anweisung darf nur <strong>in</strong> e<strong>in</strong>em Unterweisungsblock e<strong>in</strong>er<br />

Iterationsanweisung stehen (while, do o<strong>de</strong>r for).<br />

Bsp. 172 : „break“ und „cont<strong>in</strong>ue“ <strong>in</strong>nerhalb von „for“- bzw. „while“-Schleifen<br />

public class BreakundCont<strong>in</strong>ue<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

for (<strong>in</strong>t i = 0; i < 100; i++)<br />

{<br />

if (i == 13) break; // raus aus <strong>de</strong>r Schleife<br />

if (i % 9 != 0) cont<strong>in</strong>ue; // naechster Schleifendurchlauf<br />

System.out.pr<strong>in</strong>tln(i);<br />

}<br />

<strong>in</strong>t i = 0;<br />

// e<strong>in</strong>e "unendliche Schleife<br />

while (true)<br />

{<br />

i++;<br />

<strong>in</strong>t j = i * 27;<br />

if (j == 1269) break; // raus aus <strong>de</strong>r Schleife<br />

if (i % 10 != 0) cont<strong>in</strong>ue; // zurueck an <strong>de</strong>n Anfang <strong>de</strong>r Schleife<br />

System.out.pr<strong>in</strong>tln(i);<br />

}<br />

}<br />

}<br />

172 pr24800<br />

184


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

2.4.8.3 Marken (labels)<br />

E<strong>in</strong>e Markierung bzw. e<strong>in</strong> „label“ bewirkt <strong>in</strong> Verb<strong>in</strong>dung mit <strong>de</strong>r „break“- bzw.<br />

„cont<strong>in</strong>ue“-Anweisung e<strong>in</strong>e Unterbrechnug <strong>de</strong>s Schleifendurchgangs und e<strong>in</strong>en<br />

Sprung zu <strong>de</strong>r <strong>de</strong>m „label“ folgen<strong>de</strong>n Anweisung. S<strong>in</strong>nvollerweise steht dann e<strong>in</strong><br />

„label“ am Anfang <strong>de</strong>r Schleifen, z.B.:<br />

label1:<br />

äußere_Iteration<br />

{<br />

<strong>in</strong>nere_Iteration<br />

{<br />

// ...<br />

break;<br />

// ...<br />

cont<strong>in</strong>ue;<br />

// ...<br />

}<br />

}<br />

cont<strong>in</strong>ue label1;<br />

// ...<br />

break label1;<br />

// 1. Fall<br />

// 2. Fall<br />

// 3. Fall<br />

// 4. Fall<br />

Im 1. Fall bricht „break“ aus <strong>de</strong>r <strong>in</strong>neren Iteration heraus und führt <strong>in</strong> die äußere<br />

Iteration. Im 2. Fall verzweigt „cont<strong>in</strong>ue“ auf <strong>de</strong>n Anfang <strong>de</strong>r <strong>in</strong>neren Iteration. Im 3.<br />

Fall führt „cont<strong>in</strong>ue label1“ aus <strong>de</strong>r <strong>in</strong>neren und äußeren Iteration heraus auf<br />

„label1“. Die Iteration wird mit <strong>de</strong>m Anfang <strong>de</strong>r äußeren Iteration fortgesetzt. Im 4.<br />

Fall bricht „break label1“ aus <strong>de</strong>r <strong>in</strong>neren Iteration heraus auf label1. Es kommt<br />

aber nicht zu e<strong>in</strong>em erneuten E<strong>in</strong>tritt <strong>in</strong> die Iterationen, son<strong>de</strong>rn zu e<strong>in</strong>em Ausbruch<br />

aus bei<strong>de</strong>n Iterationen.<br />

Bsp. 173 : Marken im Zusammenhang mit „for“- bzw. „while“-Schleifen<br />

1. Marken im Zusammenhang mit „for“<br />

public class MarkiertesFor<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

<strong>in</strong>t i = 0;<br />

aussen:<br />

// hier soll ke<strong>in</strong>e Anweisung stehen<br />

for (; true; )<br />

// Endlos-Schleife<br />

{<br />

<strong>in</strong>nen:<br />

// hier soll ke<strong>in</strong>e Anweisung stehen<br />

for (; i < 10; i++)<br />

{<br />

ausgabe("i = " + i);<br />

if (i == 2)<br />

{<br />

ausgabe("cont<strong>in</strong>ue");<br />

cont<strong>in</strong>ue;<br />

}<br />

if (i == 3)<br />

{<br />

ausgabe("break");<br />

i++;<br />

break;<br />

}<br />

if (i == 7)<br />

{<br />

ausgabe("cont<strong>in</strong>ue aussen");<br />

173 pr24800<br />

185


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

i++;<br />

cont<strong>in</strong>ue aussen;<br />

}<br />

if (i == 8)<br />

{<br />

ausgabe("break aussen");<br />

break aussen;<br />

}<br />

for (<strong>in</strong>t k = 0; k < 5; k++)<br />

{<br />

if (k == 3)<br />

{<br />

ausgabe("cont<strong>in</strong>ue <strong>in</strong>nen");<br />

cont<strong>in</strong>ue <strong>in</strong>nen;<br />

}<br />

}<br />

}<br />

}<br />

}<br />

static void ausgabe(Str<strong>in</strong>g s)<br />

{<br />

System.out.pr<strong>in</strong>tln(s);<br />

}<br />

}<br />

/* Ausgabe<br />

i = 0<br />

cont<strong>in</strong>ue <strong>in</strong>nen<br />

i = 1<br />

cont<strong>in</strong>ue <strong>in</strong>nen<br />

i = 2<br />

cont<strong>in</strong>ue<br />

i = 3<br />

break<br />

i = 4<br />

cont<strong>in</strong>ue <strong>in</strong>nen<br />

i = 5<br />

cont<strong>in</strong>ue <strong>in</strong>nen<br />

i = 6<br />

cont<strong>in</strong>ue <strong>in</strong>nen<br />

i = 7<br />

cont<strong>in</strong>ue <strong>in</strong>nen<br />

i = 8<br />

break aussen<br />

*/<br />

2. Marken im Zusammenhang mit „while“<br />

public class MarkiertesWhile<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

<strong>in</strong>t i = 0;<br />

aussen:<br />

while (true)<br />

{<br />

ausgabe("Aeussere While-Schleife");<br />

while (true)<br />

{<br />

i++;<br />

ausgabe("i = " + i);<br />

if (i == 1)<br />

{<br />

ausgabe("cont<strong>in</strong>ue");<br />

cont<strong>in</strong>ue;<br />

}<br />

if (i == 3)<br />

{<br />

ausgabe("cont<strong>in</strong>ue aussen");<br />

186


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

cont<strong>in</strong>ue aussen;<br />

}<br />

if (i == 5)<br />

{<br />

ausgabe("break");<br />

break;<br />

}<br />

if (i == 7)<br />

{<br />

ausgabe("break aussen");<br />

break aussen;<br />

}<br />

}<br />

}<br />

}<br />

static void ausgabe(Str<strong>in</strong>g s)<br />

{<br />

System.out.pr<strong>in</strong>tln(s);<br />

}<br />

}<br />

/* Ausgabe<br />

Aeussere While-Schleife<br />

i = 1<br />

cont<strong>in</strong>ue<br />

i = 2<br />

i = 3<br />

cont<strong>in</strong>ue aussen<br />

Aeussere While-Schleife<br />

i = 4<br />

i = 5<br />

break<br />

Aeussere While-Schleife<br />

i = 6<br />

i = 7<br />

break aussen<br />

*/<br />

2.4.8.4 return-Anweisung<br />

Alle Funktionen haben e<strong>in</strong>en e<strong>in</strong><strong>de</strong>utigen Typ, <strong>de</strong>r gleichzeitig Typ <strong>de</strong>s<br />

Rückgabewerts ist. Mögliche Typen für die Rückgabe s<strong>in</strong>d primitive Typen,<br />

Datenfel<strong>de</strong>r (Arrays), Klassen, Schnittstellen. Es gibt e<strong>in</strong>en speziellen Typ für e<strong>in</strong>e<br />

Funktion ohne Rückgabewert: „void“.<br />

Der Rückgabewert ist <strong>de</strong>r Wert e<strong>in</strong>er return-Anweisung.<br />

2.4.8.5 throw-Anweisung<br />

E<strong>in</strong>e throw-Anweisung erzeugt e<strong>in</strong>e Laufzeit-Ausnahme.<br />

2.4.9 Synchronisationsanweisungen<br />

Sie wird für <strong>de</strong>n Umgang mit „Multithread<strong>in</strong>g“ benutzt.<br />

187


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

2.4.10 Schutzanweisungen<br />

<strong>Java</strong> kennt drei Schutzanweisungen: try, catch, f<strong>in</strong>ally. Sie wer<strong>de</strong>n zur<br />

Handhabung von Ausnahmen <strong>in</strong> e<strong>in</strong>er Metho<strong>de</strong> benutzt, die e<strong>in</strong>e Ausnahmesituation<br />

hervorrufen kann.<br />

2.4.11 Unerreichbare Anweisungen<br />

Es ist das Schreiben e<strong>in</strong>er Metho<strong>de</strong> mit Co<strong>de</strong>zeilen möglich, die nie erreicht wer<strong>de</strong>n<br />

können, z.B. Zeilen zwischen e<strong>in</strong>er bed<strong>in</strong>gungslosen return-Anweisung und <strong>de</strong>r<br />

nächsten Bezeichnung o<strong>de</strong>r <strong>de</strong>m En<strong>de</strong> e<strong>in</strong>es Blocks. Solche Anweisungen erzeugen<br />

e<strong>in</strong>en Fehler beim Kompilieren.<br />

2.5 Klassen<br />

2.5.1 Deklaration<br />

Klassen <strong>de</strong>f<strong>in</strong>ieren Zustand und Verhalten von Objekten. Je<strong>de</strong>s <strong>Java</strong>-Programm<br />

besteht aus e<strong>in</strong>er Sammlung von Klassen. Alle Klassen <strong>in</strong> <strong>Java</strong> haben e<strong>in</strong>e<br />

geme<strong>in</strong>same Oberklasse, die Klasse Object. Auch <strong>Java</strong> selbst (als<br />

Entwicklungsplattform) ist aus Klassen aufgebaut, die mit <strong>de</strong>m JDK frei verfügbar<br />

s<strong>in</strong>d. Das eigentliche RUNTIME-Modul besteht aus <strong>de</strong>r Datei <strong>Java</strong> Core Classes<br />

(classes.zip), die normalerweise nicht entpackt wird und im Unterverzeichnis<br />

\lib <strong>de</strong>s JDK vohan<strong>de</strong>n ist. Die Datei enthält <strong>de</strong>n vollständigen kompilierten Co<strong>de</strong><br />

von <strong>Java</strong>.<br />

Je<strong>de</strong> Klasse besteht formal aus zwei Teilen: <strong>de</strong>r Deklaration und <strong>de</strong>m Body (Körper).<br />

Generell haben Klassen<strong>de</strong>klarationen folgen<strong>de</strong>s Format:<br />

Modifizierer class NeueKlasse extends NameSuperklasse<br />

implements NameSchnittstelle<br />

Es gibt vier Eigenschaften e<strong>in</strong>er Klasse, die <strong>in</strong> e<strong>in</strong>er Deklaration <strong>de</strong>f<strong>in</strong>iert wer<strong>de</strong>n<br />

können: Modifizierer, Klassenname, Superklasse, Schnittstellen<br />

Modifizierer<br />

Sie stehen am Beg<strong>in</strong>n <strong>de</strong>r Klassen<strong>de</strong>klaration und legen fest, wie die Klasse<br />

während <strong>de</strong>r weiteren Entwicklung gehandhabt wer<strong>de</strong>n kann. Klassen haben e<strong>in</strong>en<br />

vore<strong>in</strong>gestellten, „freundlichen“ Defaultstatus. Er wird immer dann verwen<strong>de</strong>t, wenn<br />

ke<strong>in</strong> Modifizierer am Anfang e<strong>in</strong>er Klassen<strong>de</strong>f<strong>in</strong>ition steht. „Freundlich“ be<strong>de</strong>utet: Die<br />

Klasse darf erweitert und von an<strong>de</strong>ren Klassen benutzt wer<strong>de</strong>n, aber nur von<br />

Objekten <strong>in</strong>nerhalb <strong>de</strong>sselben Pakets. Die Grun<strong>de</strong><strong>in</strong>stellung bezieht sich also auf die<br />

Sichtbarkeit von an<strong>de</strong>ren Klassen und <strong>de</strong>ren Objekten. Falls davon abgewichen<br />

wer<strong>de</strong>n soll, ist e<strong>in</strong>er <strong>de</strong>r folgen<strong>de</strong>n Modifizierer zu verwen<strong>de</strong>n: public , f<strong>in</strong>al,<br />

abstract.<br />

188


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Öffentliche Klassen – <strong>de</strong>r Modifizierer public. E<strong>in</strong>e Klasse wird als öffentlich<br />

<strong>de</strong>klariert, wenn man <strong>de</strong>n Modifizierer public vor die Klassen<strong>de</strong>klaration setzt. Alle<br />

Objekte dürfen auf public-Klassen zugreifen, d.h.: Sie können von allen Objekten<br />

benutzt und erweitert wer<strong>de</strong>n, ganz egal zu welchem Paket sie gehören.. Die<br />

Deklaration e<strong>in</strong>er öffentlichen Klasse muß immer i<strong>de</strong>ntisch se<strong>in</strong> mit <strong>de</strong>m Namen,<br />

unter <strong>de</strong>m die Quelle dieser Datei gespeichert ist.<br />

F<strong>in</strong>ale Klassen – <strong>de</strong>r Modifizierer f<strong>in</strong>al. F<strong>in</strong>ale Klassen dürfen ke<strong>in</strong>e Subklassen<br />

haben. Der Modifizierer f<strong>in</strong>al muß am Beg<strong>in</strong>n <strong>de</strong>r Klassen<strong>de</strong>klaration gesetzt se<strong>in</strong>.<br />

Abstrakte Klassen – <strong>de</strong>r Modifizierer abstract. Von e<strong>in</strong>er <strong>de</strong>rartig beschriebenen<br />

Klasse wird nie e<strong>in</strong>e direkte Instanz benötigt und kann auch nie e<strong>in</strong>e Instanz gebil<strong>de</strong>t<br />

wer<strong>de</strong>n. Sie darf ke<strong>in</strong>e Implementierung e<strong>in</strong>er Metho<strong>de</strong> enthalten. In e<strong>in</strong>er abstrakten<br />

Klasse gibt es m<strong>in</strong><strong>de</strong>stens e<strong>in</strong>e nicht vollständig angegebene Metho<strong>de</strong>.<br />

2.5.2 Generische Klassen und generische Schnittstellen<br />

2.5.2.1 Generische Typen<br />

2.5.2.1.1 Typvariable<br />

Mit <strong>Java</strong> 1.5 wird das Typsystem auf generische Typen erweitert. Die I<strong>de</strong>e für<br />

generische Typen ist es e<strong>in</strong>e Klasse zu schreiben, die für verschie<strong>de</strong>ne Typen als<br />

Inhalt zu benutzen ist. Die Speicherung beliebiger Objekte kann <strong>Java</strong> bis zur Version<br />

1.4 nur <strong>in</strong> Fel<strong>de</strong>rn vom Typ Object vollziehen. Allerd<strong>in</strong>gs geht damit die typische<br />

statische Typ<strong>in</strong>formation verloren. Dynamische Typzusicherung ist <strong>in</strong> diesem Fall für<br />

weitere s<strong>in</strong>nvolle Nutzung <strong>de</strong>r Objekte unerläßich. Dynamische Typzusicherung kann<br />

aber zu Laufzeitfehlern führen:<br />

Bsp. 174 :<br />

class AlteSchachtel<br />

{<br />

Object <strong>in</strong>halt;<br />

AlteSchachtel(Object <strong>in</strong>halt)<br />

{<br />

this.<strong>in</strong>halt = <strong>in</strong>halt;<br />

}<br />

}<br />

public class AnwAlteSchachtel<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

AlteSchachtel b = new AlteSchachtel("Hallo");<br />

Str<strong>in</strong>g s = (Str<strong>in</strong>g) b.<strong>in</strong>halt;<br />

System.out.pr<strong>in</strong>tln(s.toUpperCase());<br />

System.out.pr<strong>in</strong>tln(((Str<strong>in</strong>g) s).toUpperCase());<br />

}<br />

}<br />

Wann auch immer mit <strong>in</strong>halt gearbeitet wird, e<strong>in</strong>e Typzusicherung während <strong>de</strong>r Laufzeit ist<br />

durchzuführen. Das kann zu Laufzeitfehlern führen. So übersetzt das folgen<strong>de</strong> Programm fehlerfrei<br />

gibt aber e<strong>in</strong>en Laufzeitfehler.<br />

174 pr25210<br />

189


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

public class AnwAlteSchachtelFehler<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

AlteSchachtel b = new AlteSchachtel(new Integer(42));<br />

Str<strong>in</strong>g s = (Str<strong>in</strong>g) b.<strong>in</strong>halt;<br />

System.out.pr<strong>in</strong>tln(s.toUpperCase());<br />

}<br />

}<br />

Das vorliegen<strong>de</strong> Bsp. zeigt: Sobald <strong>de</strong>r Typ Object benutzt wird gibt es e<strong>in</strong>en<br />

Verlust an Typsicherheit, sobald <strong>de</strong>r Typ Object benutzt wird.<br />

Wie kann man Klassen schreiben, die statische Typsicherheit garantieren? Im<br />

vorliegen<strong>de</strong>n Beispiel braucht man nur je<strong>de</strong>s Auftreten <strong>de</strong>s Typs Object durch e<strong>in</strong>en<br />

Variablennamen zu ersetzen. Die Variable ist e<strong>in</strong>e Typvariable, sie steht für e<strong>in</strong>en<br />

beliebigen Typ. Dem Klassennamen wird zusätzlich <strong>in</strong> <strong>de</strong>r Klassen<strong>de</strong>f<strong>in</strong>ition, <strong>in</strong><br />

spitzen Klammern e<strong>in</strong>geschlossen, h<strong>in</strong>zugefügt, dass diese Klasse e<strong>in</strong>e Typvariable<br />

benutzt.<br />

class Schachtel <br />

{<br />

elementType <strong>in</strong>halt;<br />

Schachtel(elementType <strong>in</strong>halt)<br />

{<br />

this.<strong>in</strong>halt = <strong>in</strong>halt;<br />

}<br />

}<br />

Die Typvariable elementType ist allqualifiziert. Für je<strong>de</strong>n Typ elementType kann die<br />

Klasse Schachtel verwen<strong>de</strong>t wer<strong>de</strong>n, z.B.:<br />

Schachtel zur Speicherung von Str<strong>in</strong>gs<br />

Schachtel zur Speicherung von Integer-Objekte<br />

Bsp. 175 :<br />

public class AnwSchachtel<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

Schachtel b1 = new Schachtel("Hallo");<br />

Str<strong>in</strong>g s = b1.<strong>in</strong>halt;<br />

System.out.pr<strong>in</strong>tln(s.toUpperCase());<br />

System.out.pr<strong>in</strong>tln(b1.<strong>in</strong>halt.toUpperCase());<br />

Schachtel b2 = new Schachtel(new Integer(42));<br />

System.out.pr<strong>in</strong>tln(b2.<strong>in</strong>halt.<strong>in</strong>tValue());<br />

}<br />

}<br />

175 pr25210<br />

190


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Da mit generischen Typen ke<strong>in</strong>e Typzusicherungen mehr vorzunehmen s<strong>in</strong>d, gibt es<br />

hier auch ke<strong>in</strong>e dynamischen Typfehler mehr. Laufzeitfehler, wie sie ohne die<br />

"generische Schachtel" aufgetreten s<strong>in</strong>d, wer<strong>de</strong>n jetzt bereits zur Übersetzungszeit<br />

ent<strong>de</strong>ckt.<br />

Bsp. 176 :<br />

public class AnwSchachtelFehler<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

Schachtel b = new Schachtel(new Integer(42));<br />

Str<strong>in</strong>g s = b.<strong>in</strong>halt;<br />

System.out.pr<strong>in</strong>tln(s.toUpperCase());<br />

}<br />

}<br />

AnwSchachtelFehler.java: 5 cannot f<strong>in</strong>d symbol<br />

Kovarianz gegen Kontravarianz: Das folgen<strong>de</strong> Programm führt auf e<strong>in</strong>en Fehler<br />

class Kontra<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g [] args)<br />

{<br />

Schachtel s = new Schachtel("Hallo");<br />

}<br />

}<br />

E<strong>in</strong>e Schachtel ist ke<strong>in</strong>e Schachtel. Der Grund dafür ist,<br />

dass bestimmte Laufzeitfehler vermie<strong>de</strong>n wer<strong>de</strong>n sollen. Betrachtet man e<strong>in</strong> Objekt<br />

<strong>de</strong>s Typs Schachtel über e<strong>in</strong>e Referenz <strong>de</strong>s Typs<br />

Schachtel, dann können <strong>in</strong> <strong>de</strong>m Feld <strong>in</strong>halt beliebige Objekte<br />

gespeichert wer<strong>de</strong>n. Die Referenz über <strong>de</strong>n Typ Box geht davon aus,<br />

dass <strong>in</strong> <strong>in</strong>halt nur Str<strong>in</strong>gobjekte gespeichert wer<strong>de</strong>n.<br />

Reihungen (arrays) verhalten sich <strong>in</strong> <strong>Java</strong> an<strong>de</strong>rs. Bei Reihungen ist die<br />

entsprechen<strong>de</strong> Zuweisung erlaubt. E<strong>in</strong>e Reihung von Str<strong>in</strong>gobjekten darf e<strong>in</strong>er<br />

Reihung beliebiger Objekte zugewiesen wer<strong>de</strong>n. Dann kann es bei <strong>de</strong>r Benutzung<br />

e<strong>in</strong>er Reihung von Objekten zu e<strong>in</strong>em Laufzeitfehler kommen.<br />

176 pr25210<br />

191


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

2.5.2.1.2 Vererbung<br />

Von generischen Klassen lassen sich Subklassen <strong>de</strong>f<strong>in</strong>ieren. Diese Subklassen<br />

können, müssen aber nicht selbst generische Klassen se<strong>in</strong>.<br />

Bsp. 177 : Erweiterung <strong>de</strong>r Schachtelklasse, so dass 2 Objekte gespeichert wer<strong>de</strong>n<br />

können.<br />

class Paar extends Schachtel<br />

{<br />

bt zweites;<br />

Paar(at x , bt y)<br />

{<br />

super(x); public class AnwPaar<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

Paar p =<br />

new Paar("Hallo",new Integer(40));<br />

System.out.pr<strong>in</strong>tln(p);<br />

System.out.pr<strong>in</strong>tln(p.<strong>in</strong>halt.toUpperCase());<br />

System.out.pr<strong>in</strong>tln(p.zweites.<strong>in</strong>tValue() + 2);<br />

}<br />

}<br />

Die Klasse Paar hat zwei Typvariablen. Instanzen von Paar müssen angeben von welchem Typ die<br />

bei<strong>de</strong>n zu speichern<strong>de</strong>n Objekte se<strong>in</strong> sollen.<br />

public class AnwPaar<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

Paar p =<br />

new Paar("Hallo",new Integer(40));<br />

System.out.pr<strong>in</strong>tln(p);<br />

System.out.pr<strong>in</strong>tln(p.<strong>in</strong>halt.toUpperCase());<br />

System.out.pr<strong>in</strong>tln(p.zweites.<strong>in</strong>tValue() + 2);<br />

}<br />

}<br />

Es können auch Subklassen durch Zusammenfassen mehrerer Typvariablen gebil<strong>de</strong>t<br />

wer<strong>de</strong>n, z.B. 178 :<br />

class UniPaar extends Paar<br />

{<br />

UniPaar(at x,at y)<br />

{<br />

super(x,y);<br />

}<br />

void tausch()<br />

{<br />

f<strong>in</strong>al at z = zweites;<br />

zweites = <strong>in</strong>halt;<br />

<strong>in</strong>halt = z;<br />

}<br />

}<br />

Wie man sieht, s<strong>in</strong>d Typvariablen wie bisherige Typen zu benutzen. Sie können als Typ für lokale<br />

Variable o<strong>de</strong>r Parameter genutzt wer<strong>de</strong>n.<br />

177 pr25210<br />

178 pr25210<br />

192


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

public class AnwUniPaar<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g [] args)<br />

{<br />

UniPaar p = new UniPaar("welt","hallo");<br />

System.out.pr<strong>in</strong>tln(p);<br />

p.tausch();<br />

System.out.pr<strong>in</strong>tln(p);<br />

}<br />

}<br />

Man kann auch Subklassen e<strong>in</strong>er generischen Klasse bil<strong>de</strong>n, die nicht mehr<br />

generisch ist. So lässt sich bspw. 179 die Klasse Schachtel zu e<strong>in</strong>er Klasse erweitern,<br />

<strong>in</strong> <strong>de</strong>r nur noch Str<strong>in</strong>gobjekte verpackt wer<strong>de</strong>n dürfen:<br />

class Str<strong>in</strong>gSchachtel extends Schachtel<br />

{<br />

Str<strong>in</strong>gSchachtel(Str<strong>in</strong>g x)<br />

{<br />

super(x);<br />

}<br />

}<br />

Diese Klasse kann nun vollkommen ohne spitze Klammern benutzt wer<strong>de</strong>n:<br />

public class AnwStr<strong>in</strong>gSchachtel<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args [])<br />

{<br />

Schachtel b = new Str<strong>in</strong>gSchachtel("Hallo");<br />

System.out.pr<strong>in</strong>tln(b.<strong>in</strong>halt);<br />

}<br />

}<br />

2.5.2.1.3 E<strong>in</strong>schränkung <strong>de</strong>r Typvariablen<br />

Bei <strong>de</strong>r Def<strong>in</strong>ition e<strong>in</strong>er Schablone können Typen e<strong>in</strong>geschränkt wer<strong>de</strong>n. Es kann<br />

e<strong>in</strong>geschränkt wer<strong>de</strong>n, dass e<strong>in</strong>e Typvariable nicht für alle Typen ersetzt wer<strong>de</strong>n<br />

darf, son<strong>de</strong>r nur für bestimmte Typen. Somit kann bspw. vorgeschrieben wer<strong>de</strong>n,<br />

dass <strong>de</strong>r Typ e<strong>in</strong>e konkrete Schnittstelle verwen<strong>de</strong>n muß.<br />

Bsp. 180 : Erweiterung <strong>de</strong>r Klasse Schachtel mit e<strong>in</strong>er set()-Metho<strong>de</strong>, die nur e<strong>in</strong>en<br />

neuen Wert <strong>in</strong> das entsprechen<strong>de</strong> Objekt speichert, wenn es größer ist als das<br />

bereits gespeicherte Objekt.<br />

Hierzu müssen die zu speichern<strong>de</strong>n Objekte <strong>in</strong> e<strong>in</strong>er Ordnungsrelation vergleichbar se<strong>in</strong>, was <strong>in</strong> <strong>Java</strong><br />

über die Implementierung <strong>de</strong>r Schnittstelle Comparable ausgedrückt wird.<br />

class SammleMaxAlt<br />

{<br />

private Comparable wert;<br />

SammleMaxAlt(Comparable x)<br />

{ wert = x; }<br />

void setWert(Comparable x)<br />

{ if (wert.compareTo(x) < 40) wert = x; }<br />

Comparable getWert() { return wert; }<br />

179 pr25210<br />

180 pr25210<br />

193


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

}<br />

Die Klasse SammleMaxAlt ist <strong>in</strong> <strong>de</strong>r Lage beliebige Objekte, die die Schnittstelle Comparable<br />

implementieren, zu speichern. Es besteht aber folgen<strong>de</strong>s Problem: Greift man auf das gespeicherte<br />

Objekt mit <strong>de</strong>r Metho<strong>de</strong> getWert() erneut zu, dann ist <strong>de</strong>r genaue Wert dieses Objekts nicht mehr<br />

bekannt. Evtl. ist e<strong>in</strong>e dynmische Typzusicherung notwendig, die zu Laufzeitfehlern führen kann.<br />

Die generischen Typen von <strong>Java</strong> 1.5 können dieses Problem beheben, <strong>in</strong><strong>de</strong>m man <strong>de</strong>n allgeme<strong>in</strong>en<br />

Typ Object durch e<strong>in</strong>e Typvariable ersetzt, die Subtypen <strong>de</strong>r Schnittstelle Comparable s<strong>in</strong>d. Dies<br />

wird durch e<strong>in</strong>e zusätzliche extends-Klausel für die Typvariable angegeben.<br />

class SammleMax <br />

{<br />

private elementType wert;<br />

SammleMax(elementType x) { wert = x; }<br />

void setWert(elementType x)<br />

{<br />

if (wert.compareTo(x) < 0) wert = x;<br />

}<br />

elementType getWert() { return wert; }<br />

}<br />

Für die Benutzung <strong>de</strong>r Klasse ist jetzt für je<strong>de</strong> konkrete Instanz <strong>de</strong>r konkrete Typ <strong>de</strong>s gespeicherten<br />

Objekts anzugeben. Die Metho<strong>de</strong> getWert() liefert als Rückgabetyp nicht e<strong>in</strong> allgeme<strong>in</strong>es Objekt<br />

<strong>de</strong>s Typs Comparable, son<strong>de</strong>rn exakt e<strong>in</strong> Typ <strong>de</strong>s Instanztyps.<br />

class AnwSammleMax<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g [] args)<br />

{<br />

SammleMax sm = new SammleMax("Brecht");<br />

sm.setWert("Cal<strong>de</strong>ron");<br />

sm.setWert("Shakespeare");<br />

sm.setWert("Goethe");<br />

sm.setWert("Schiller");<br />

System.out.pr<strong>in</strong>tln(sm.getWert().toUpperCase());<br />

}<br />

}<br />

Wie man <strong>in</strong> <strong>de</strong>r letzten Zeile sieht, entfällt wie<strong>de</strong>r die dynamische Typzusicherung.<br />

2.5.2.2 Generische Schnittstellen<br />

Generische Typen erlauben es, <strong>de</strong>n Typ Object <strong>in</strong> Typsignaturen zu elim<strong>in</strong>ieren.<br />

Der Typ Object ist als schlecht anzusehen, <strong>de</strong>nn er ist gleichbe<strong>de</strong>utend damit, dass<br />

ke<strong>in</strong>e Information über e<strong>in</strong>en konkreten Typ während <strong>de</strong>r Übersetzungszeit zur<br />

Verfügung steht. In herkömmlichen <strong>Java</strong> ist <strong>in</strong> APIs von Bibliotheken <strong>de</strong>r Typ Object<br />

allgegenwärtig. Sogar <strong>in</strong> <strong>de</strong>r Klasse Object selbst f<strong>in</strong><strong>de</strong>t man diesen Typ <strong>in</strong><br />

Signaturen, z.B. <strong>in</strong> <strong>de</strong>r Metho<strong>de</strong> equals(). Pr<strong>in</strong>zipiell kann <strong>de</strong>shalb e<strong>in</strong> Objekt mit<br />

Objekten je<strong>de</strong>s beliebigen Typs verglichen wer<strong>de</strong>n. Häufig will man aber nur gleiche<br />

Typen mite<strong>in</strong>an<strong>de</strong>r vergleichen. Generische Typen erlauben es, allgeme<strong>in</strong> e<strong>in</strong>e<br />

Gleichheitsmetho<strong>de</strong> zu <strong>de</strong>f<strong>in</strong>ieren, <strong>in</strong> <strong>de</strong>r nur Objekte gleichen Typs mite<strong>in</strong>an<strong>de</strong>r<br />

verglichen wer<strong>de</strong>n können.<br />

Generische Typen erweitern sich ohne Umstän<strong>de</strong> auf Schnittstellen. Speziell für<br />

Gleichheit könnte e<strong>in</strong>e <strong>de</strong>rartige Schnittstelle so aussehen:<br />

<strong>in</strong>terface EQ<br />

{ public boolean eq(otherType other); }<br />

194


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Bsp.: Äpfel mit Äpfel vergleichen<br />

Dazu <strong>de</strong>f<strong>in</strong>iert man <strong>in</strong> <strong>de</strong>r implements-Klausel, dass EQ implementiert wird.<br />

class Apfel implements EQ<br />

{<br />

Str<strong>in</strong>g typ;<br />

Apfel(Str<strong>in</strong>g typ)<br />

{<br />

this.typ=typ;<br />

}<br />

public boolean eq(Apfel other)<br />

{<br />

return this.typ.equals(other.typ);<br />

}<br />

}<br />

Jetzt können erst e<strong>in</strong>mal "Äpfel" mit "Äpfel" verglichen wer<strong>de</strong>n.<br />

class TestEq<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g [] args)<br />

{<br />

Apfel a1 = new Apfel("Gol<strong>de</strong>n Delicious");<br />

Apfel a2 = new Apfel("Mac<strong>in</strong>tosh");<br />

System.out.pr<strong>in</strong>tln(a1.eq(a2));<br />

System.out.pr<strong>in</strong>tln(a1.eq(a1));<br />

}<br />

}<br />

Der folgen<strong>de</strong> Vergleich führt zu e<strong>in</strong>em Fehler beim Übersetzen 181 :<br />

class TesteEqError<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g []args)<br />

{<br />

Apfel a = new Apfel("Gol<strong>de</strong>n Delicious");<br />

Birne b = new Birne("williams");<br />

System.out.pr<strong>in</strong>tln(a.equals(b));<br />

System.out.pr<strong>in</strong>tln(a.eq(b));<br />

}<br />

}<br />

Die Schnittstelle EQ wird nun auch für die Klasse Birne implementiert<br />

class Birne implements EQ<br />

{<br />

Str<strong>in</strong>g typ;<br />

Birne(Str<strong>in</strong>g typ)<br />

{<br />

this.typ=typ;<br />

}<br />

public boolean eq(Birne other)<br />

{<br />

return this.typ.equals(other.typ);<br />

}<br />

}<br />

Während <strong>de</strong>s statischen Typchecks wird überprüft, ob Äpfel mit Äpfeln und Birne mit Birnen<br />

verglichen wer<strong>de</strong>n. Der Versuch Äpfel mit Birnen zu vergleichen, führt zu e<strong>in</strong>em Typfehler.<br />

181 eq(Apfel) <strong>in</strong> Apfel cannot be applied to (Birne)<br />

195


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Der statische TypCheck stellt auch sicher, dass e<strong>in</strong>e generische Schnittstelle mit <strong>de</strong>r korrekten<br />

Typsignatur implementiert wird. Der Versuch e<strong>in</strong>e "Birnenklasse" zu schreiben, die e<strong>in</strong>e Gleichheit mit<br />

Äpfeln implementieren soll, dann aber die Metho<strong>de</strong> "eq" mit <strong>de</strong>m Parametertyp "Birne" zu<br />

implemntieren, führt zu e<strong>in</strong>er Fehlermeldung 182 :<br />

class BirneError implements EQ<br />

{<br />

Str<strong>in</strong>g typ;<br />

BirneError(Str<strong>in</strong>g typ)<br />

{<br />

this.typ=typ;<br />

}<br />

public boolean eq(Birne other)<br />

{<br />

return this.typ.equals(other.typ);<br />

}<br />

}<br />

2.6 Metho<strong>de</strong>n<br />

2.6.1 Die Deklaration<br />

Die Deklarationen von Metho<strong>de</strong>n haben folgen<strong>de</strong>s Aussehen 183 :<br />

Zugriffsspezifizierer Modifizierer Returnwert NameMetho<strong>de</strong>(Parameter)<br />

throws Exceptionliste<br />

Metho<strong>de</strong>nunterschrift 184 . Unter Unterschrift e<strong>in</strong>er Metho<strong>de</strong> versteht man e<strong>in</strong>e<br />

Komb<strong>in</strong>ation aus Teilen <strong>de</strong>r Def<strong>in</strong>ition: <strong>de</strong>m Namen <strong>de</strong>r Metho<strong>de</strong>, <strong>de</strong>m Rückgabetyp<br />

und <strong>de</strong>n verschie<strong>de</strong>nen Parametern.<br />

2.6.2 Die Zugriffsspezifizierung<br />

Freundliche Metho<strong>de</strong>n – <strong>de</strong>r vore<strong>in</strong>gestellte Defaultstatus.<br />

Öffentliche Metho<strong>de</strong>n – <strong>de</strong>r Zugriffsspezifizierer public.<br />

Geschützt Metho<strong>de</strong>n – <strong>de</strong>r Zugriffsspezifizierer protected.<br />

Prvate Metho<strong>de</strong>n – <strong>de</strong>r Zugriffsspezifizierer private.<br />

Privat geschützt – die Zugriffsspezifizierer private und protected <strong>in</strong> Komb<strong>in</strong>ation.<br />

Metho<strong>de</strong>n, die als private protected <strong>de</strong>klariert wur<strong>de</strong>n, s<strong>in</strong>d sowohl für e<strong>in</strong>e<br />

Klasse als auch für e<strong>in</strong>e Subklasse vefügbar, aber nicht für <strong>de</strong>n Rest <strong>de</strong>s Pakets<br />

o<strong>de</strong>r auch für Klassen außerhalb <strong>de</strong>s Pakets. Das be<strong>de</strong>utet: Subklassen e<strong>in</strong>er<br />

182 BirneFehler is not abstract and does not overri<strong>de</strong> abstract method<br />

eq(Apfel) <strong>in</strong> EQ<br />

183 Kursiv Geschriebenes ist optional<br />

184 Oft wird für <strong>de</strong>nselben Zusammenhang <strong>de</strong>r Begriff „Metho<strong>de</strong>nsignatur“ verwen<strong>de</strong>t.<br />

196


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

gegebenen Klasse können Metho<strong>de</strong>n, die private protected <strong>de</strong>klariert wur<strong>de</strong>n<br />

aufrufen. Instanzen <strong>de</strong>r Subklasse können dies aber nicht.<br />

2.6.3 Die Metho<strong>de</strong>nmodifizierer<br />

Klassenmetho<strong>de</strong>n – <strong>de</strong>r Modifizierer static.<br />

Abstrakte Metho<strong>de</strong>n – <strong>de</strong>r Modifizierer abstract<br />

F<strong>in</strong>ale Metho<strong>de</strong>n – <strong>de</strong>r Modifizierer f<strong>in</strong>al. Das Schlüsselwort f<strong>in</strong>al vor e<strong>in</strong>er<br />

Metho<strong>de</strong>n<strong>de</strong>klaration verh<strong>in</strong><strong>de</strong>rt, daß irgendwelche Subklassen die <strong>de</strong>rzeitige Klasse<br />

dieser Metho<strong>de</strong> überschreiben. Metho<strong>de</strong>n, die auf ke<strong>in</strong>em Fall geän<strong>de</strong>rt wer<strong>de</strong>n<br />

sollen, sollten <strong>de</strong>shalb immer als f<strong>in</strong>al <strong>de</strong>klariert wer<strong>de</strong>n.<br />

Native Metho<strong>de</strong>n – <strong>de</strong>r Modifizierer native. Native Metho<strong>de</strong>n s<strong>in</strong>d Metho<strong>de</strong>n, die<br />

nicht <strong>in</strong> <strong>Java</strong> geschrieben s<strong>in</strong>d, aber <strong>de</strong>nnoch <strong>in</strong>nerhalb von <strong>Java</strong> verwen<strong>de</strong>t wer<strong>de</strong>n<br />

sollen. Der Modifizierer native wird vor <strong>de</strong>r Metho<strong>de</strong> <strong>de</strong>klariert, <strong>de</strong>r Body (Körper) <strong>de</strong>r<br />

Metho<strong>de</strong> wird durch e<strong>in</strong> Semikolon ersetzt.<br />

Metho<strong>de</strong>n synchronisieren – <strong>de</strong>r Modifizierer synchronized. Wird das<br />

Schlüsselwort sychronized vor e<strong>in</strong>e Metho<strong>de</strong>n<strong>de</strong>klaration gesetzt, dann wer<strong>de</strong>n<br />

Datenverletzungen verh<strong>in</strong><strong>de</strong>rt, die entstehen können, wenn zwei Metho<strong>de</strong>n<br />

gleichzeitig versuchen auf dieselben Daten zuzugreifen.<br />

2.6.4 Rückgabewerte von Metho<strong>de</strong>n<br />

Rückgabewerte von<strong>Java</strong>-Metho<strong>de</strong>n können von je<strong>de</strong>m erlaubten Datentyp 185 se<strong>in</strong>.<br />

E<strong>in</strong>e Metho<strong>de</strong> muß immer e<strong>in</strong>en Wert zurückgeben (und zwar genau <strong>de</strong>n Datentyp,<br />

<strong>de</strong>r <strong>in</strong> <strong>de</strong>r Deklaration angegeben wur<strong>de</strong>), es sei <strong>de</strong>nn, sie wur<strong>de</strong> mit void <strong>de</strong>klariert.<br />

Die Metho<strong>de</strong> hat dann ke<strong>in</strong>en Rückgabewert.<br />

2.6.5 Metho<strong>de</strong>nname und Parameterliste<br />

Bzgl. <strong>de</strong>r Metho<strong>de</strong>nnamen gelten die gleichen Regeln wie bei allen Token.<br />

E<strong>in</strong>e Parameterliste hat folgen<strong>de</strong> Struktur: Datentyp variablenname, Datentyp<br />

variablenname, .... . Die Anzahl <strong>de</strong>r Parameter ist beliebig und kann Null se<strong>in</strong>.<br />

185 Nicht nur primitive Datentypen, son<strong>de</strong>rn auch komplexe Objekte.<br />

197


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

2.6.6 Variable Parameteranzahl<br />

Mit <strong>Java</strong> 1.5 können Metho<strong>de</strong>n mit e<strong>in</strong>er variablen Anzahl von Parametern <strong>de</strong>f<strong>in</strong>iert<br />

wer<strong>de</strong>n. Sie wer<strong>de</strong>n durch Punkte nach <strong>de</strong>m Paramtertyp <strong>in</strong> <strong>de</strong>r Signatur<br />

gekennzeichnet. Damit wird angezeigt: Es kann e<strong>in</strong>e beliebige Anzahl dieser<br />

Parameter bei e<strong>in</strong>em Metho<strong>de</strong>naufruf geben.<br />

Bsp.:<br />

public class VarParams<br />

{<br />

public static Str<strong>in</strong>g append(Str<strong>in</strong>g ... argumente)<br />

{<br />

Str<strong>in</strong>g result = "";<br />

for (Str<strong>in</strong>g a : argumente)<br />

result += a;<br />

return result;<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g [] args)<br />

{<br />

System.out.pr<strong>in</strong>tln(append("Hallo"," ","Welt"));<br />

}<br />

}<br />

2.6.7 Rekursion<br />

2.6.7.1 Rekursive Funktionen<br />

E<strong>in</strong>e Funktion ist rekursiv, falls die Ausführung <strong>de</strong>s Rumpfs <strong>de</strong>r Funktion wie<strong>de</strong>rum<br />

zum Aufruf <strong>de</strong>r Funktion führt. Man unterschei<strong>de</strong>t<br />

Direkte Rekursion (E<strong>in</strong>e Funktion ruft sich selbst im Rumpf auf)<br />

und<br />

Indirekte Rekursion (Der rekursive Aufruf befi<strong>de</strong>t sich nicht im Funktionsrumpf. So ruft bspw. e<strong>in</strong>e<br />

Funktion A e<strong>in</strong>e Funktion B auf und diese startet <strong>in</strong> ihrem Rumpf die Funktion A).<br />

Wie Wie<strong>de</strong>rholungsanweisungen neigen auch rekursive Funktionen zur Gefahr nicht<br />

abbrechbarer Berechnungen. E<strong>in</strong>e Term<strong>in</strong>ierung ist unbed<strong>in</strong>gt erfor<strong>de</strong>rlich. Das<br />

geschieht über e<strong>in</strong>e Bed<strong>in</strong>gung von <strong>de</strong>r <strong>de</strong>r rekursive Aufruf abhängt..<br />

Bei je<strong>de</strong>r rekursiven Anwendung wird e<strong>in</strong> Satz lokaler, gebun<strong>de</strong>ner Variablen kreiert.<br />

Sie haben zwar <strong>de</strong>nselben Namen wie Objekte <strong>de</strong>s vorangegengenen Aufrufs <strong>de</strong>r<br />

Prozedur, besitzen aber verschie<strong>de</strong>ne Werte. Die Namen beziehen sich immer auf<br />

die zuletzt erzeugten Variablen.<br />

198


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

2.6.7.2 Rekursion und Iteration<br />

Man kann zeigen, daß sich je<strong>de</strong>r rekursive Algorithmus <strong>in</strong> e<strong>in</strong>en iterativen<br />

umwan<strong>de</strong>ln läßt. Da die Iteration <strong>in</strong> <strong>de</strong>n meisten Fällen wesentlich effizienter ist als<br />

die die Rekursion, ist die Frage berechtigt, weshalb man überhaupt die Rekusion<br />

verwen<strong>de</strong>t. Für die Rekursion spricht:<br />

1. Es gibt bestimmte rekursiv formulierte Algorithmen, die schneller o<strong>de</strong>r wenigstens gleich<br />

schnell<br />

arbeiten als vergleichbare iterative.<br />

2. Es lassen sich viele Probleme rekursiv „sehr e<strong>in</strong>fach“ lösen.<br />

Rekursiv formulierte Algorithmen bieten sich <strong>in</strong>sbeson<strong>de</strong>re an, wenn das<br />

zugrun<strong>de</strong>liegen<strong>de</strong> Problem o<strong>de</strong>r die zu behan<strong>de</strong>ln<strong>de</strong>n Daten rekursiv <strong>de</strong>f<strong>in</strong>iert s<strong>in</strong>d.<br />

Das ist aber noch ke<strong>in</strong>e Garantie dafür, daß e<strong>in</strong> rekursiver Algorithmus auch <strong>de</strong>r<br />

beste Weg zur Lösung <strong>de</strong>s Problems ist.<br />

199


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

3. Grafische Benutzeroberflächen und Applets<br />

3.1 Ereignisse <strong>in</strong> grafischen Benutzeroberflächen<br />

Mit Hilfe e<strong>in</strong>facher Benutzerschnittstellen <strong>de</strong>s Graphical User Interface (GUI))<br />

lassen sich E<strong>in</strong>- und Ausgaben 186 bequem gestalten. Generell sollte E<strong>in</strong>-/Ausgabe <strong>in</strong><br />

<strong>Java</strong> über e<strong>in</strong> „GUI“ <strong>in</strong> Abhängigkeit von <strong>de</strong>n vom Betriebssystem registrierten<br />

Ereignisarten und Zustandsän<strong>de</strong>rungen erfolgen.<br />

3.1.1 Gestaltung von GUI mit Hilfe <strong>de</strong>r AWT-Klassen<br />

<strong>Java</strong> enthält e<strong>in</strong> e<strong>in</strong>fach zu bedienen<strong>de</strong>s System für Gestaltung grafische<br />

Benutzeroberflächen: das Abstract W<strong>in</strong>dow<strong>in</strong>g Toolkit (AWT). Die Fähigkeiten<br />

<strong>de</strong>s AWT umfassen:<br />

- Grafische Operationen zum Zeichnen von L<strong>in</strong>ien o<strong>de</strong>r Füllen von Flächen und zur Ausgabe von<br />

Text<br />

- Metho<strong>de</strong> zur Steuerung <strong>de</strong>s Programmablaufs auf <strong>de</strong>r Basis von Nachrichten für Tatstatur-,<br />

Maus- und Fensterereignisse.<br />

- Dialogelemente zur Kommunikation mit <strong>de</strong>m Anwen<strong>de</strong>r und Funktion zum Design von<br />

Dialogboxen.<br />

- Grafikfunktionen zur Darstellung von Bitmaps und Ausgabe von "Sound".<br />

Zum E<strong>in</strong>b<strong>in</strong><strong>de</strong>n <strong>de</strong>r Grafikfähigkeiten dient die Anweisung<br />

import java.awt.*;<br />

zu Beg<strong>in</strong>n <strong>de</strong>r Klassen<strong>de</strong>f<strong>in</strong>ition. Danach stehen alle Klassen aus <strong>de</strong>m Paket<br />

java.awt zur Verfügung.<br />

Zur Ausgabe von grafischen Elementen benötigt e<strong>in</strong>e Anwendung e<strong>in</strong> Fenster, das<br />

e<strong>in</strong>e Applikation (im Gegensatz zu e<strong>in</strong>em Applet) selbst erzeugen muß. Das AWT<br />

enthält verschie<strong>de</strong>ne Fensterklassen:<br />

Panel<br />

Applet<br />

Component<br />

Conta<strong>in</strong>er<br />

W<strong>in</strong>dow Dialog FileDialog<br />

Frame<br />

Component<br />

Conta<strong>in</strong>er<br />

Abstrakte Klasse mit <strong>de</strong>r Aufgabe: Repäsentation von Programmelementen, die<br />

e<strong>in</strong>e Größe und Position haben und die auf e<strong>in</strong>e Vielzahl von Ereignissen<br />

reagieren können bzw. Ereignisse sen<strong>de</strong>n können<br />

Abstrakte Klasse mit <strong>de</strong>r Aufgabe: Aufnahme von Komponenten <strong>in</strong>nerhalb an<strong>de</strong>rer<br />

Komponenten. Conta<strong>in</strong>er stellt für das H<strong>in</strong>zufügen bzw. Entfernen von<br />

Komponenten Metho<strong>de</strong>n bereit und realisiert mit Hilfe von "Layout-Manager"-<br />

186 Standar<strong>de</strong><strong>in</strong>- und Standardausgabe spielen <strong>in</strong> <strong>Java</strong> nur im Rahmen <strong>de</strong>s „Debugg<strong>in</strong>g“ e<strong>in</strong>e Rolle.<br />

201


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Klassen Positionierung und Anordnung von Komponenten.<br />

Panel<br />

Ist die konkrete Klasse mit <strong>de</strong>n Eigenschaften von Component und Conta<strong>in</strong>er. Sie<br />

erbt alle Eigenschaften von Conta<strong>in</strong>er, kann Komponenten aufnehmen und mit<br />

Hilfe <strong>de</strong>s Layoutmanagers auf <strong>de</strong>m Bildschirm anordnen.<br />

Applet Erweitert die Funktionalität <strong>de</strong>r Klasse Applet um Metho<strong>de</strong>n, die für das Ausführen<br />

von Applets von Be<strong>de</strong>utung s<strong>in</strong>d. Damit entsteht e<strong>in</strong> Programmelement, das e<strong>in</strong>e<br />

Größe und e<strong>in</strong>e Position hat, auf Ereignisse reagieren kann und <strong>in</strong> <strong>de</strong>r Lage ist,<br />

weitere Komponenten aufzunehmen.<br />

W<strong>in</strong>dow Bestimmt e<strong>in</strong> Top-Level-W<strong>in</strong>dow ohne Rahmen, Titelleiste und Menü. Sie ist für<br />

Anwendungen geeignet, die Rahmenelemente selbst zeichnen o<strong>de</strong>r volle Kontrolle<br />

über das gesamte Fenster benötigen.<br />

Frame Repräsentiert e<strong>in</strong> Top-Level-W<strong>in</strong>dow mit Rahmen, Titelleiste und optionalem<br />

Menü.<br />

Dialog Realisiert modale und nicht modale Dialoge.<br />

FileDialog Stellt e<strong>in</strong> Standard-Dateidialog <strong>de</strong>s jeweiligen Systems bereit. Dieser kann beim<br />

La<strong>de</strong>n o<strong>de</strong>r Speichern e<strong>in</strong>er datei zur E<strong>in</strong>gabe o<strong>de</strong>r zur Auswahl e<strong>in</strong>es<br />

Date<strong>in</strong>amens verwen<strong>de</strong>t wer<strong>de</strong>n.<br />

Abb. Fensterklassen-Hierarchie<br />

Alle Fensterklassen s<strong>in</strong>d von e<strong>in</strong>er geme<strong>in</strong>samen abstrakten Basisklasse abgeleitet,<br />

die die Funktionalität für Aussehen, Positionierung, Ausgabe und Laufzeitverhalten(<br />

Fokus, Mauszeiger, Sichtbarkeit) bereitstellt.<br />

Component<br />

{ abstract }<br />

public Str<strong>in</strong>g getName()<br />

public void setName(Str<strong>in</strong>g name)<br />

public Conta<strong>in</strong>er getParent()<br />

public boolean conta<strong>in</strong>s(<strong>in</strong>t x, <strong>in</strong>t y)<br />

public boolean isVisible()<br />

public void setVisible(boolean b)<br />

public boolean isEnabled()<br />

public void setEnabled(boolean b)<br />

public boolean isShow<strong>in</strong>g()<br />

public Color getForeground()<br />

public void setForeground(Color c)<br />

public Color getBackground()<br />

public void setBackground(Color c)<br />

public Font getFont()<br />

public void setFont(Font f)<br />

public Po<strong>in</strong>t getLocation()<br />

public Po<strong>in</strong>t getLocationOnScreen()<br />

public void setLocation(<strong>in</strong>t x, <strong>in</strong>t y)<br />

public void setLocation(Po<strong>in</strong>t p)<br />

public Dimension getSize()<br />

public void setSize(<strong>in</strong>t breite, <strong>in</strong>t hoehe)<br />

public void setSize(Dimension d)<br />

public Rectangle getBounds()<br />

public void setBounds(<strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t breite, <strong>in</strong>t hoehe)<br />

public void setBounds(Rectangle r)<br />

public Graphics getGraphics()<br />

public FontMetrics getFontMetrics(Font font)<br />

public void pa<strong>in</strong>tAll(Graphics g)<br />

public void repa<strong>in</strong>t()<br />

public boolean imageUpdate(Image bild, <strong>in</strong>t flags, <strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t breite, <strong>in</strong>t hoehe)<br />

public Image createImage(ImageProducer erz)<br />

public Image createImage(<strong>in</strong>t breite, <strong>in</strong>t hoehe)<br />

public boolean prepareImage(Image bild, ImageObserver obs)<br />

public boolean prepareImage(Image bild, <strong>in</strong>t breite, <strong>in</strong>t hoehe, ImageObserver obs)<br />

202


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

public <strong>in</strong>t checkImage(Image bild, ImageObserver obs)<br />

public <strong>in</strong>t checkImage(Image bild, <strong>in</strong>t breite, <strong>in</strong>t hoehe, ImageObserver obs)<br />

public void addComponentListener(ComponentListener l)<br />

public void removeComponentListener(ComponentListener l)<br />

public void addFocusListener(FocusListener l)<br />

public void removeFocusListener(FocusListener l)<br />

public void addKeyListener(KeyListener l)<br />

public void removeKeyListener(KeyListener l)<br />

public void addMouseListener(MouseListener l)<br />

public void removeMouseListener(MouseListener l)<br />

public void addMouseMotionListener(MouseMotionListener l)<br />

public void removeMouseMotionListener(MouseMotionListener l)<br />

public f<strong>in</strong>al void enableEvents(long eventsToEnable)<br />

public f<strong>in</strong>al void disableEvents(long eventsToDisable)<br />

protected void processComponentEvent(ComponentEvent e)<br />

protected void processFocusEvent(FocusEvent e)<br />

protected void processKeyEvent(KeyEvent e)<br />

protected void processMouseEvent(MouseEvent e<br />

protected void processMouseMotionEvent(MouseEvent e)<br />

public boolean gotFocus(Event evt) //<strong>de</strong>precated, -> processFocusEvent(FocusEvent e)<br />

public boolean lostFocus(Event evt, Object was)<br />

public void requestFocus()<br />

public void add(PopupMenu popup)<br />

public void remove(MenuComponent popup)<br />

public Str<strong>in</strong>g toStr<strong>in</strong>g()<br />

……<br />

…….<br />

…….<br />

…….<br />

…….<br />

Abb.: Die Klasse Component<br />

Conta<strong>in</strong>er s<strong>in</strong>d Fenster o<strong>de</strong>r Fensterbereiche, <strong>in</strong> <strong>de</strong>nen an<strong>de</strong>re Kontrollelemente<br />

platziert wer<strong>de</strong>n können.<br />

Component<br />

Conta<strong>in</strong>er<br />

{abstract}<br />

public <strong>in</strong>t countComponents()<br />

public Component getComponent(<strong>in</strong>t n)<br />

public Components[] getComponents()<br />

public Insets getInsets()<br />

public Component add(Component comp)<br />

public void remove(Component comp)<br />

public void removeAll()<br />

public LayoutManager getLayout()<br />

public void setLayout(LayoutManager mgr)<br />

public Dimension getPreferredSize()<br />

public Dimension getM<strong>in</strong>imumSize()<br />

public Dimension getMaximumSize()<br />

public void <strong>in</strong>validate()<br />

public void pa<strong>in</strong>t(Graphics g) //conta<strong>in</strong>er<br />

public void update(Graphics g) //conta<strong>in</strong>er<br />

protected void processEvent(AWTEvent e) //conta<strong>in</strong>er<br />

public void addNotify() //conta<strong>in</strong>er<br />

203


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

public void removeNotify() //conta<strong>in</strong>er<br />

Abb.: Die Klasse Conta<strong>in</strong>er<br />

Zur Anzeige e<strong>in</strong>es Fensters auf <strong>de</strong>m Bildschirm muß e<strong>in</strong>e <strong>de</strong>r Fensterklassen,<br />

W<strong>in</strong>dow, Frame, Dialog, Applet, FileDialog <strong>in</strong>stanziert wer<strong>de</strong>n. Zum Ableiten<br />

e<strong>in</strong>er eigenen Fensterklasse wird <strong>in</strong> <strong>de</strong>r Regel die Klasse Frame o<strong>de</strong>r Dialog<br />

verwen<strong>de</strong>t, die beibe aus <strong>de</strong>r Klasse W<strong>in</strong>dow abgeleitet s<strong>in</strong>d.<br />

Conta<strong>in</strong>er<br />

W<strong>in</strong>dow<br />

><br />

public void show()<br />

public boolean isShow<strong>in</strong>g()<br />

public void dispose()<br />

public void pack()<br />

public void toFront()<br />

public void toBack()<br />

public void addW<strong>in</strong>dowListener(W<strong>in</strong>dowListener l)<br />

Frame<br />

Dialog<br />

public static f<strong>in</strong>al <strong>in</strong>t DEFAULT_CURSOR<br />

> ><br />

public Frame()<br />

public Dialog(Frame eltern)<br />

public Frame(Str<strong>in</strong>g titel)<br />

public Dialog(Frame eltern, boolean modal)<br />

> ><br />

public Str<strong>in</strong>g getTitle()<br />

public boolean isModal()<br />

public void setTitle(Str<strong>in</strong>g titel) public void setModal(boolean b)<br />

public MenuBar getMenuBar()<br />

public void show()<br />

public void setmenuBar(MenuBar mb)<br />

public boolean isResizable()<br />

public boolean isResizable() public void setResizable(boolean b)<br />

public void setResizable(boolean b)<br />

public void remove(MenuComponent m)<br />

Abb.: Die Klassen Frame und Dialog<br />

Bsp. 187 : E<strong>in</strong>-, Ausgabe über Textfel<strong>de</strong>r <strong>in</strong> e<strong>in</strong>em ersten GUI<br />

E<strong>in</strong> erstes GUI soll aus e<strong>in</strong>em editierbaren und e<strong>in</strong>em nicht editierbaren Textfeld bestehen.<br />

Den bei<strong>de</strong>n Textfel<strong>de</strong>rn soll jeweils e<strong>in</strong> Label mit <strong>de</strong>r Beschriftung „E<strong>in</strong>gabestr<strong>in</strong>g:“ bzw.<br />

„Ausgabestr<strong>in</strong>g“ zugeordnet se<strong>in</strong>. Diese Komponenten sollen automatisch von l<strong>in</strong>ks nach<br />

rechts und von oben nach unten angeordnet wer<strong>de</strong>n <strong>in</strong> e<strong>in</strong>em Panel, das selbst wie<strong>de</strong>rum<br />

e<strong>in</strong>ziges Objekt <strong>in</strong> e<strong>in</strong>em Fenster (Frame) mit <strong>de</strong>m Titel „Echo“ ist.<br />

187 pr14160<br />

import java.lang.*;<br />

import java.awt.*;<br />

public class EchoMitBeno<br />

{<br />

// E<strong>in</strong>lesen und Ausgeben von Zeichenketten ueber<br />

// e<strong>in</strong>e grafische Benutzeroberflaeche<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

204


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

{<br />

TextField e<strong>in</strong>gabeTextFeld = new TextField(20);<br />

Label e<strong>in</strong>gabeTextFeldLabel = new Label("E<strong>in</strong>gabestr<strong>in</strong>g:");<br />

e<strong>in</strong>gabeTextFeld.setEditable(true);<br />

TextField ausgabeTextFeld = new TextField(20);<br />

Label ausgabeTextFeldLabel = new Label("Ausgabestr<strong>in</strong>g:");<br />

ausgabeTextFeld.setEditable(false);<br />

Panel panel = new Panel();<br />

panel.add(e<strong>in</strong>gabeTextFeldLabel);<br />

panel.add(e<strong>in</strong>gabeTextFeld);<br />

panel.add(ausgabeTextFeldLabel);<br />

panel.add(ausgabeTextFeld);<br />

Frame fenster = new Frame("Echo");<br />

fenster.add(panel);<br />

fenster.pack();<br />

fenster.setVisible(true);<br />

}<br />

}<br />

Die Implementierung <strong>de</strong>s vorliegen<strong>de</strong>n Programms zeigt das folgen<strong>de</strong><br />

Ausgabefenster:<br />

Abb.:<br />

Das vorliegen<strong>de</strong> Beispiel zeigt noch e<strong>in</strong>ige Mängel:<br />

- Zwar kann <strong>in</strong> das für die E<strong>in</strong>gabe vorgesehene Textfeld e<strong>in</strong> Str<strong>in</strong>g e<strong>in</strong>gegeben wer<strong>de</strong>n, das zweite<br />

Textfeld ist allerd<strong>in</strong>gs nicht zugänglich. Es passiert somit nicht gera<strong>de</strong> viel. Es wer<strong>de</strong>n noch ke<strong>in</strong>e<br />

Ereignisse aufgefangen und behan<strong>de</strong>lt.<br />

- Noch nicht e<strong>in</strong>mal kann das Fenster geschlossen wer<strong>de</strong>n, und die Applikation muß (nach <strong>de</strong>m<br />

Schließen <strong>de</strong>s Frame) mit "CTRL-C" explizit abgebrochen wer<strong>de</strong>n.<br />

3.1.2 Ereignisbehandlung unter grafischen Benutzeroberflächen<br />

Im Mittelpunkt <strong>de</strong>r Programmierung unter e<strong>in</strong>er GUI steht die Kommunikation<br />

zwischen System und Anwendungsprogramm. Die Anwendung wird über alle Arten<br />

von Ereignissen und Zustandsän<strong>de</strong>rungen vom System durch Versen<strong>de</strong>n von<br />

Nachrichten (z.B. über Mausklick, Tastature<strong>in</strong>gaben, Verän<strong>de</strong>rungen an Größe und<br />

Lage <strong>de</strong>r Fenster) <strong>in</strong>formiert. Die Reaktion auf die Nachrichten erfolgt <strong>in</strong> spezielllen<br />

Ereignisempfängern (EventListeners), die das zum Ereignis passen<strong>de</strong> Empfänger-<br />

Interface implementieren. Damit e<strong>in</strong> Ereignisempfänger Nachrichten e<strong>in</strong>er<br />

bestimmten Ereignisquelle erhält, muß er sich bei <strong>de</strong>r Quelle registrieren lassen, d.h.:<br />

Es muß e<strong>in</strong>e EventListener-Klasse geschrieben, <strong>in</strong>stanziert und bei <strong>de</strong>r<br />

Ereignisquelle registriert wer<strong>de</strong>n. Zum Empfang von Nachrichten muß e<strong>in</strong> Objekt<br />

e<strong>in</strong>e Reihe von Metho<strong>de</strong>n implementieren, die von <strong>de</strong>r Nachrichtenquelle, bei <strong>de</strong>r es<br />

sich registriert hat, aufgerufen wer<strong>de</strong>n können. Die Ereignisempfänger stellen diese<br />

Metho<strong>de</strong>n durch Implementierung von Interfaces bereit, die aus <strong>de</strong>r Klasse<br />

EventListener <strong>de</strong>s Pakets java.util abgeleitet s<strong>in</strong>d.<br />

205


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Ereignistypen. Im JDK 1.1 wer<strong>de</strong>n Ereignistypen durch e<strong>in</strong>e Hierarchie von<br />

Ereignisklassen repräsentiert, die alle aus <strong>de</strong>r Klasse java.util.EventObject 188<br />

abgeleitet s<strong>in</strong>d.<br />

188 Speichert das Objekt, das die Nachricht ausgelöst hat und gibt durch Aufruf von "public Object getSource()"<br />

das Objekt an.<br />

206


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

EventObject<br />

AWTEvent<br />

ComponentEvent ActionEvent AdjustmentEvent ItemEvent TextEvent<br />

FocusEvent InputEvent Conta<strong>in</strong>erEvent W<strong>in</strong>dowEvent<br />

KeyEvent<br />

MouseEvent<br />

Abb.: Spezifische Ereignisklassen<br />

Die Hierarchie <strong>de</strong>r AWT-spezifischen Ereignisklassen beg<strong>in</strong>nt mit <strong>de</strong>r Klasse<br />

AWTEvent und bef<strong>in</strong><strong>de</strong>t sich im Paket java.awt. AWTEvent ist Superklasse aller<br />

Ereignisklassen <strong>de</strong>s AWT, die sich im Paket java.awt.event bef<strong>in</strong><strong>de</strong>n. Dieses<br />

Paket ist <strong>in</strong> je<strong>de</strong> Klasse e<strong>in</strong>zubeziehen, die sich mit <strong>de</strong>m Event-Handl<strong>in</strong>g von AWT-<br />

Anwendungen beschäftigt.<br />

EventListener-Interface. Je Ereignisklasse gibt es e<strong>in</strong> EventListener-Interface. Es<br />

<strong>de</strong>f<strong>in</strong>iert e<strong>in</strong>e seperate Metho<strong>de</strong> für je<strong>de</strong> Ereignisart <strong>de</strong>r Ereignisklasse. So besitzt<br />

bspw. das Interface MouseListener die Metho<strong>de</strong>n mouseClicked,<br />

mouseEntered, mouseExited, mousePressed und mouseReleased, die beim<br />

E<strong>in</strong>treffen <strong>de</strong>s jeweiligen Ereignis aufgerufen wer<strong>de</strong>n.<br />

EventListener<br />

FocusListener ActionListener AdjustementListener ItemListener<br />

KeyListener<br />

MouseListener<br />

MouseMotionListener<br />

ComponentListener<br />

Conta<strong>in</strong>erListener<br />

W<strong>in</strong>dowListener<br />

Abb.: Hierarchie <strong>de</strong>r EventListener-Interfaces<br />

Je<strong>de</strong> <strong>de</strong>r Metho<strong>de</strong>n e<strong>in</strong>es Listener-Interface enthält als e<strong>in</strong>ziges Argument e<strong>in</strong> Objekt<br />

vom zugehörigen Ereignistyp. Alle Metho<strong>de</strong>n s<strong>in</strong>d vom Typ void.<br />

207


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

3.1.3 Anwendung lokaler Klassen für die Ereignisbehandlung<br />

Im GUI-Objekt, das e<strong>in</strong>en Event-Handler benötigt wird e<strong>in</strong>e lokale Klasse zur<br />

Implementierung <strong>de</strong>s passen<strong>de</strong>n Interface angegeben. Lokale Klassen wer<strong>de</strong>n lokal<br />

zu e<strong>in</strong>er an<strong>de</strong>ren Klasse erzeugt. Sie s<strong>in</strong>d nur <strong>in</strong>nerhalb dieser Klasse <strong>de</strong>f<strong>in</strong>iert und<br />

sichtbar. Objekte <strong>de</strong>r lokalen Klasse können nur aus <strong>de</strong>r erzeugen<strong>de</strong>n Klasse<br />

produziert wer<strong>de</strong>n. Die lokale Klasse kann aber auf alle Instanzmerkmale <strong>de</strong>r<br />

erzeugen<strong>de</strong>n Klasse zugreifen.<br />

E<strong>in</strong>e Variante lokale Klassen s<strong>in</strong>d anonyme Klassen. Sie wer<strong>de</strong>n ebenfalls lokal zu<br />

e<strong>in</strong>er an<strong>de</strong>ren Klasse erzeugt, kommen aber ohne Klassennamen aus. Dazu wer<strong>de</strong>n<br />

sie bei <strong>de</strong>r Übergabe e<strong>in</strong>es Objekts an e<strong>in</strong>e Metho<strong>de</strong> o<strong>de</strong>r als Rückgabewert e<strong>in</strong>er<br />

Metho<strong>de</strong> <strong>in</strong>nerhalb e<strong>in</strong>er e<strong>in</strong>zigen Anwendung <strong>de</strong>f<strong>in</strong>iert und <strong>in</strong>stanziert. Damit e<strong>in</strong>er<br />

anonymen Klasse überhaupt e<strong>in</strong>e s<strong>in</strong>nvolle Aufgabe zugeführt wer<strong>de</strong>n kann, muß sie<br />

aus e<strong>in</strong>er an<strong>de</strong>ren Klasse abgeleitet se<strong>in</strong> o<strong>de</strong>r e<strong>in</strong> bestehen<strong>de</strong>s Interface<br />

implementieren.<br />

Bsp.: ActionListener-Interface für die Übernahme e<strong>in</strong>es Textfeld-Inhalts <strong>in</strong> e<strong>in</strong><br />

Textfeld zur Ausgabe<br />

Falls das Textfeld für die Ausgabe <strong>de</strong>n im Textfeld für die E<strong>in</strong>gabe angegebenen Text<br />

wie<strong>de</strong>rgeben soll, muß e<strong>in</strong> ActionListener (e<strong>in</strong>e zusätzlich <strong>in</strong>nere, anonyme Klasse) mit <strong>de</strong>r<br />

Metho<strong>de</strong> public void actionPerformed (ActionEvent a) <strong>de</strong>f<strong>in</strong>iert wer<strong>de</strong>n. Diese<br />

Metho<strong>de</strong> umfaßt Aufrufe <strong>de</strong>r Metho<strong>de</strong>n getText() und setText() <strong>de</strong>r Klasse TextField.<br />

Darüber kann <strong>in</strong> das E<strong>in</strong>gabetextfeld e<strong>in</strong>gegebener Text <strong>in</strong> das Ausgabefeld ausgegeben<br />

wer<strong>de</strong>n. Da die "Metho<strong>de</strong> actionPerformed" <strong>de</strong>r anonymen <strong>in</strong>neren Klasse auf<br />

"e<strong>in</strong>gabeTextFeld" bzw. "ausgabeTextFeld" zugreift, ist außerhalb von ma<strong>in</strong>()<br />

anzugeben:<br />

private static TextField e<strong>in</strong>gabeTextFeld = new TextField(20);<br />

private static TextField ausgabeTextFeld = new TextField(20);<br />

Zum Schließen <strong>de</strong>s Fensters wird über e<strong>in</strong>e anonyme <strong>in</strong>nere Klasse e<strong>in</strong><br />

W<strong>in</strong>dowAdapter (e<strong>in</strong>e Art W<strong>in</strong>dowListener) mit <strong>de</strong>r Metho<strong>de</strong> "public void<br />

w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)" <strong>de</strong>f<strong>in</strong>iert und an <strong>de</strong>n "Frame" e<strong>in</strong>gekettet.<br />

Wird <strong>de</strong>r Frame geschlossen, dann wird diese Metho<strong>de</strong> aufgerufen, die über<br />

System.exit(0) die Applikation been<strong>de</strong>t.<br />

E<strong>in</strong>e Adapterklasse implementiert e<strong>in</strong> Interface mit mehreren Meho<strong>de</strong>n und erlaubt<br />

es somit abgeleiteten Klassen, nur noch die Metho<strong>de</strong>n zu überlagern, die tatsächlich<br />

von Interesse s<strong>in</strong>d. Passen<strong>de</strong> Adapterklasssen stellt das Paket java.awt.event bereit,<br />

z.B. FocusAdapter, KeyAdapter, MouseAdapter, MouseMotionAdapter,<br />

ComponentAdapter, Conta<strong>in</strong>erAdapter, W<strong>in</strong>dowAdapter. Die Registrierung<br />

erfolgt mit <strong>de</strong>r Metho<strong>de</strong> addW<strong>in</strong>dowListener, die <strong>in</strong> <strong>de</strong>n Klassen Dialog und<br />

Frame zur Verfügung steht.<br />

Bsp. 189 : Textfeldbearbeitung mit "ActionListener-Interface" und<br />

W<strong>in</strong>dowAdapter<br />

import java.lang.*;<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

public class EchoMitBeno extends Object<br />

{<br />

// E<strong>in</strong>lesen und Ausgeben von Zeichenketten ueber<br />

// e<strong>in</strong>e grafische Benutzeroberflaeche<br />

private static TextField e<strong>in</strong>gabeTextFeld = new TextField(20);<br />

private static TextField ausgabeTextFeld = new TextField(20);<br />

189 pr14160<br />

208


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

Frame fenster = new Frame("Echo");<br />

fenster.addW<strong>in</strong>dowListener(new W<strong>in</strong>dowAdapter()<br />

{<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{<br />

System.exit(0);<br />

}<br />

});<br />

// TextField e<strong>in</strong>gabeTextFeld = new TextField(20);<br />

Label e<strong>in</strong>gabeTextFeldLabel = new Label("E<strong>in</strong>gabestr<strong>in</strong>g:");<br />

e<strong>in</strong>gabeTextFeld.setEditable(true);<br />

e<strong>in</strong>gabeTextFeld.addActionListener(new ActionListener()<br />

{<br />

public void actionPerformed(ActionEvent ae)<br />

{<br />

Str<strong>in</strong>g s = e<strong>in</strong>gabeTextFeld.getText();<br />

ausgabeTextFeld.setText(s);<br />

}<br />

});<br />

// TextField ausgabeTextFeld = new TextField(20);<br />

Label ausgabeTextFeldLabel = new Label("Ausgabestr<strong>in</strong>g:");<br />

ausgabeTextFeld.setEditable(false);<br />

Panel panel = new Panel();<br />

panel.add(e<strong>in</strong>gabeTextFeldLabel);<br />

panel.add(e<strong>in</strong>gabeTextFeld);<br />

panel.add(ausgabeTextFeldLabel);<br />

panel.add(ausgabeTextFeld);<br />

// Frame fenster = new Frame("Echo");<br />

fenster.add(panel);<br />

fenster.pack();<br />

fenster.setVisible(true);<br />

}<br />

}<br />

Alternativ zur vorliegen<strong>de</strong>n Realisierung <strong>de</strong>r Ereignisbehandlung über e<strong>in</strong>e anonyme<br />

Klasse, kann auch e<strong>in</strong> Listener über e<strong>in</strong>e lokale Klasse implementiert wer<strong>de</strong>n.<br />

Gewöhnlich ist die Anwendung e<strong>in</strong>e Subklasse von Frame.<br />

import java.lang.*;<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

public class Vorl11b extends Frame<br />

{<br />

private static Label e<strong>in</strong>gabeTextFeldLabel = new Label("E<strong>in</strong>gabestr<strong>in</strong>g:");<br />

private static TextField e<strong>in</strong>gabeTextFeld = new TextField(20);<br />

private static Label ausgabeTextFeldLabel = new Label("Ausgabestr<strong>in</strong>g:");<br />

private static TextField ausgabeTextFeld = new TextField(20);<br />

public Vorl11b()<br />

{<br />

e<strong>in</strong>gabeTextFeld.setEditable(true);<br />

ausgabeTextFeld.setEditable(false);<br />

Panel panel = new Panel();<br />

panel.add(e<strong>in</strong>gabeTextFeldLabel);<br />

panel.add(e<strong>in</strong>gabeTextFeld);<br />

panel.add(ausgabeTextFeldLabel);<br />

panel.add(ausgabeTextFeld);<br />

e<strong>in</strong>gabeTextFeld.addActionListener(new TFL());<br />

add(panel);<br />

pack();<br />

setVisible(true);<br />

}<br />

class TFL implements ActionListener<br />

209


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

{<br />

public void actionPerformed(ActionEvent ae)<br />

{<br />

Str<strong>in</strong>g s = e<strong>in</strong>gabeTextFeld.getText();<br />

ausgabeTextFeld.setText(s);<br />

}<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

Vorl11b vorl11b = new Vorl11b();<br />

vorl11b.addW<strong>in</strong>dowListener(new W<strong>in</strong>dowAdapter()<br />

{<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{<br />

System.exit(0);<br />

}<br />

});<br />

}<br />

}<br />

Schließlich kann das Interface auch direkt implementiert wer<strong>de</strong>n.<br />

Zweckmäßigerweise ist dann die Anwendung e<strong>in</strong>e Subklasse von Frame.<br />

import java.lang.*;<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

public class Vorl11a extends Frame implements ActionListener<br />

{<br />

private static Label e<strong>in</strong>gabeTextFeldLabel = new Label("E<strong>in</strong>gabestr<strong>in</strong>g:");<br />

private static TextField e<strong>in</strong>gabeTextFeld = new TextField(20);<br />

private static Label ausgabeTextFeldLabel = new Label("Ausgabestr<strong>in</strong>g:");<br />

private static TextField ausgabeTextFeld = new TextField(20);<br />

public Vorl11a()<br />

{<br />

e<strong>in</strong>gabeTextFeld.setEditable(true);<br />

ausgabeTextFeld.setEditable(false);<br />

Panel panel = new Panel();<br />

panel.add(e<strong>in</strong>gabeTextFeldLabel); panel.add(e<strong>in</strong>gabeTextFeld);<br />

panel.add(ausgabeTextFeldLabel); panel.add(ausgabeTextFeld);<br />

e<strong>in</strong>gabeTextFeld.addActionListener(this);<br />

add(panel);<br />

pack();<br />

setVisible(true);<br />

}<br />

public void actionPerformed(ActionEvent ae)<br />

{<br />

System.out.pr<strong>in</strong>tln(ae.getActionCommand());<br />

Str<strong>in</strong>g s = e<strong>in</strong>gabeTextFeld.getText();<br />

ausgabeTextFeld.setText(s);<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

Vorl11a vorl11a = new Vorl11a();<br />

vorl11a.addW<strong>in</strong>dowListener(new W<strong>in</strong>dowAdapter()<br />

{<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{<br />

System.exit(0);<br />

}<br />

});<br />

}<br />

}<br />

210


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

211


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

3.1.4 Externe und <strong>in</strong>terne Darstellung von numerischen Werten<br />

Numerische Werte wer<strong>de</strong>n<br />

- extern (z.B. im Textfeld e<strong>in</strong>es GUI) durch Zeichenketten dargestellt<br />

- <strong>in</strong>tern als Werte von e<strong>in</strong>em <strong>de</strong>r elementaren Datentypen byte, short, <strong>in</strong>t, long,<br />

float o<strong>de</strong>r double o<strong>de</strong>r als Objekte von e<strong>in</strong>em <strong>de</strong>r Referenzdatentypen (Wrapper-<br />

Klassen) Byte, Short, Integer, Long o<strong>de</strong>r Double. Diese Klassen stellen<br />

Metho<strong>de</strong>n zur Umwandlung von numerischen Werten <strong>in</strong> Zeichenketten und<br />

umgekehrt zur Verfügung. Da nicht je<strong>de</strong> beliebige Zeichenkette als <strong>in</strong>terne<br />

Darstellung e<strong>in</strong>er Zahl <strong>in</strong>terpretiert wer<strong>de</strong>n kann, kann beim Versuch <strong>de</strong>r<br />

Umwandlung e<strong>in</strong>e NumberFormatExpression auftreten, die normalerweise mit<br />

e<strong>in</strong>er try-catch-Anweisung entsprechend behan<strong>de</strong>lt wer<strong>de</strong>n sollte.<br />

Bsp. 190 : E<strong>in</strong>lesen und Ausgeben von numersichen Werten<br />

import java.lang.*;<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

public class EchoZahl extends Object<br />

{<br />

// E<strong>in</strong>lesen und Ausgeben von Zeichenketten ueber<br />

// e<strong>in</strong>e grafische Benutzeroberflaeche<br />

private static TextField e<strong>in</strong>gabeTextFeld = new TextField(20);<br />

private static TextField ausgabeTextFeld = new TextField(20);<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

Frame fenster = new Frame("Echo");<br />

fenster.addW<strong>in</strong>dowListener(new W<strong>in</strong>dowAdapter()<br />

{<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{<br />

System.exit(0);<br />

}<br />

});<br />

// TextField e<strong>in</strong>gabeTextFeld = new TextField(20);<br />

Label e<strong>in</strong>gabeTextFeldLabel = new Label("Zahlene<strong>in</strong>gabe:");<br />

e<strong>in</strong>gabeTextFeld.setEditable(true);<br />

e<strong>in</strong>gabeTextFeld.addActionListener(new ActionListener()<br />

{<br />

public void actionPerformed(ActionEvent ae)<br />

{<br />

Double dRef = null;<br />

Str<strong>in</strong>g s = e<strong>in</strong>gabeTextFeld.getText();<br />

try {<br />

dRef = new Double(s);<br />

}<br />

catch (NumberFormatException nfe)<br />

{<br />

System.err.pr<strong>in</strong>tln(nfe.toStr<strong>in</strong>g());<br />

}<br />

double d = dRef.doubleValue();<br />

d++;<br />

Str<strong>in</strong>g s1 = Double.toStr<strong>in</strong>g(d);<br />

Str<strong>in</strong>g s2 = Str<strong>in</strong>g.valueOf(d);<br />

ausgabeTextFeld.setText(s2);<br />

}<br />

});<br />

// TextField ausgabeTextFeld = new TextField(20);<br />

Label ausgabeTextFeldLabel = new Label("Ausgabestr<strong>in</strong>g:");<br />

190 pr14160<br />

212


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

ausgabeTextFeld.setEditable(false);<br />

Panel panel= new Panel();<br />

panel.add(e<strong>in</strong>gabeTextFeldLabel);<br />

panel.add(e<strong>in</strong>gabeTextFeld);<br />

panel.add(ausgabeTextFeldLabel);<br />

panel.add(ausgabeTextFeld);<br />

// Frame fenster = new Frame("Echo");<br />

fenster.add(panel);<br />

fenster.pack();<br />

fenster.setVisible(true);<br />

}<br />

}<br />

3.1.5 Low-Level-Events<br />

Von <strong>de</strong>r Klasse ComponenEvent s<strong>in</strong>d Event-Klassen für "Low-Level"-Ereignisse<br />

abgeleitet. Sie s<strong>in</strong>d für <strong>de</strong>n Transfer von elementaren Nachrichten zuständig, die von<br />

Fenstern und Dialogelementen stammen.<br />

3.1.5.1 Component-Events<br />

Wird e<strong>in</strong>e Komponente verschoben o<strong>de</strong>r ihre Größe bzw. ihr Anzeigezustand<br />

verän<strong>de</strong>rt, dann wird e<strong>in</strong> Componen-Event generiert. Da Fenster und alle<br />

Dialogelemente aus <strong>de</strong>r Klasse Component abgeleitet s<strong>in</strong>d, gelten die hier<br />

angegebenen Ereignisse für nahezu alle GUI-Elemente.<br />

Der Empfänger für ComponentEvent muß das Interface ComponentListener<br />

implementieren und bekommt Events <strong>de</strong>s Typs ComponentEvent übergeben.<br />

ComponentEvent erweiter AWTEvent und stellt neben getID, getSource die<br />

Metho<strong>de</strong> public Component getComponent() bereit, mit <strong>de</strong>r die Komponente<br />

ermittelt wer<strong>de</strong>n kann, die die Nachricht ausgelöst hat. Die Registrierung <strong>de</strong>r<br />

Empfängerklasse erfolgt mit<br />

public void addComponentListener(ComponentListener l).<br />

Ereignismetho<strong>de</strong><br />

componentShown<br />

componentHid<strong>de</strong>n<br />

componentMoved<br />

componentResized<br />

Be<strong>de</strong>utung<br />

E<strong>in</strong>e Komponente wur<strong>de</strong> sichtbar<br />

E<strong>in</strong>e Komponente wur<strong>de</strong> unsichtbar<br />

E<strong>in</strong>e Komponente wur<strong>de</strong> verschoben<br />

Die Größe <strong>de</strong>r Komponente hat sich verän<strong>de</strong>rt<br />

Abb.: Übersicht zu <strong>de</strong>n Metho<strong>de</strong>n von ComponentListener<br />

3.1.5.2 W<strong>in</strong>dow-Events<br />

E<strong>in</strong> W<strong>in</strong>dowEvent wird generiert, falls sich am Status e<strong>in</strong>es Fensters e<strong>in</strong>e Än<strong>de</strong>rung<br />

ergeben hat, die für das Anwen<strong>de</strong>rprogramm <strong>in</strong>teressant se<strong>in</strong> könnte. E<strong>in</strong> Empfänger<br />

für W<strong>in</strong>dow-Events muß das Interface W<strong>in</strong>dowListener implementieren.<br />

W<strong>in</strong>dowEvent stellt neben getID, getSource die Metho<strong>de</strong> public W<strong>in</strong>dow<br />

getW<strong>in</strong>dow() zur Verfügung, mit <strong>de</strong>r das Fenster ermittelt wer<strong>de</strong>n kann, das die<br />

Nachricht ausgelöst hat. Die Registrierung <strong>de</strong>r Empfängerklasse erfolgt über die<br />

213


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Metho<strong>de</strong> public void addW<strong>in</strong>dowListener(W<strong>in</strong>dowListener l) , die <strong>in</strong> <strong>de</strong>n<br />

Klassen Dialog und Frame zur Verfügung steht.<br />

Ereignismetho<strong>de</strong> Be<strong>de</strong>utung<br />

w<strong>in</strong>dowActivated Das Fenster wur<strong>de</strong> aktiviert. Die Metho<strong>de</strong> wird nach <strong>de</strong>m Erstellen <strong>de</strong>s<br />

Fensters aufgerufen und wenn e<strong>in</strong> Fenster, das im H<strong>in</strong>tergrund stand,<br />

erneut <strong>in</strong> <strong>de</strong>n Vor<strong>de</strong>rgrund gelangt.<br />

w<strong>in</strong>dowClosed<br />

Das Fenster wur<strong>de</strong> geschlossen<br />

w<strong>in</strong>dowClos<strong>in</strong>g Das Fenster soll geschlossen wer<strong>de</strong>n. Diese Metho<strong>de</strong> wird aufgerufen,<br />

wenn <strong>de</strong>r Anwen<strong>de</strong>r das Fenster über die TitelLeiste, das Systemmenü<br />

o<strong>de</strong>r die Tastenkomb<strong>in</strong>ation ALT+F4 schließen will. Die Anwendung hat<br />

<strong>de</strong>n Co<strong>de</strong> bereit zu stellen, <strong>de</strong>r das Fenster schließt. Standardmäßig<br />

reagiert das Programm nicht auf diese Benutzeraktionen<br />

w<strong>in</strong>dowDeactivated Das Fenster wur<strong>de</strong> <strong>de</strong>aktiviert, also <strong>in</strong> <strong>de</strong>n H<strong>in</strong>tergrung gestellt<br />

w<strong>in</strong>dowDeiconified Das Fenster wur<strong>de</strong> wie<strong>de</strong>rhergestellt, nach<strong>de</strong>m es zuvor auf<br />

Symbolgröße verkle<strong>in</strong>ert wur<strong>de</strong><br />

w<strong>in</strong>dowIconified Das Fenster wur<strong>de</strong> auf Symbolgröße verkle<strong>in</strong>ert<br />

w<strong>in</strong>dowOpened<br />

Das Fenster wur<strong>de</strong> geöffnet.<br />

Abb.: Übersicht zu <strong>de</strong>n Metho<strong>de</strong>n von W<strong>in</strong>dowListener<br />

3.1.5.3 Mouse-Events<br />

E<strong>in</strong> Mouse-Event entsteht, wenn <strong>de</strong>r Anwen<strong>de</strong>r (<strong>in</strong>nerhalb <strong>de</strong>r Client-Area <strong>de</strong>s<br />

Fensters) e<strong>in</strong>e <strong>de</strong>r Maustasten drückt o<strong>de</strong>r losläßt. Dabei reagiert das Programm<br />

sowohl auf Klicks <strong>de</strong>r l<strong>in</strong>ken als auch <strong>de</strong>r ( - falls vorhan<strong>de</strong>n -) <strong>de</strong>r rechten Maustaste<br />

und zeigt an, welche <strong>de</strong>r Umschalttasten STRG, ALT, UMSCHALT o<strong>de</strong>r META<br />

während <strong>de</strong>s Mausklicks gedrückt waren. Es ist möglich zwischen e<strong>in</strong>fachen o<strong>de</strong>r<br />

doppelten Mausklicks zu unterschei<strong>de</strong>n. E<strong>in</strong> Empfänger für Mouse-Events erweitert<br />

die Klasse InputEvent und stellt neben getID, getSource e<strong>in</strong>e Reihe<br />

zusätzlicher Metho<strong>de</strong>n bereit. Die Registrierung <strong>de</strong>r Empfängerklasse erfolgt mit<br />

public void addMouseListener(MouseListener l), die <strong>in</strong> allen Klassen<br />

zur Verfügung steht, die aus Component abgeleitet s<strong>in</strong>d.<br />

Ereignismetho<strong>de</strong> Be<strong>de</strong>utung<br />

mousePressed Die Maustaste wur<strong>de</strong> gedrückt<br />

mouseReleased Die gedrückte Maustaste wur<strong>de</strong> losgelassen<br />

mouseClicked E<strong>in</strong>e Maustaste wur<strong>de</strong> gedrückt und wie<strong>de</strong>r losgelassen. Die Metho<strong>de</strong> wird<br />

nach mouseReleased aufgerufen.<br />

mouseEntered Der Mauszeiger wur<strong>de</strong> <strong>in</strong> <strong>de</strong>n Client-Bereich <strong>de</strong>r auslösen<strong>de</strong>n<br />

Komponente h<strong>in</strong>e<strong>in</strong>bewegt<br />

mouseExited<br />

Der Mauszeiger wur<strong>de</strong> aus <strong>de</strong>m Client-Bereich <strong>de</strong>r auslösen<strong>de</strong>n<br />

Komponente herausbewegt.<br />

Abb.: Übersicht zu <strong>de</strong>n Metho<strong>de</strong>n von MouseListener<br />

Die Ermittlung <strong>de</strong>r Position <strong>de</strong>s Mauszeigers kann über<br />

public <strong>in</strong>t getX(); // liefert die X-Koord<strong>in</strong>ate<br />

public <strong>in</strong>t getY(); // liefert die Y-Koord<strong>in</strong>ate<br />

public Po<strong>in</strong>t getPo<strong>in</strong>t(); /* liefert X- und Y-Koord<strong>in</strong>ate <strong>de</strong>s Punkts, an <strong>de</strong>m<br />

sich <strong>de</strong>r Mauszeiger beim Auftreten <strong>de</strong>s Ereignisses bef<strong>in</strong><strong>de</strong>t */<br />

ermittelt wer<strong>de</strong>n. Koord<strong>in</strong>atenwerte wer<strong>de</strong>n relativ zum Ursprung <strong>de</strong>r auslösen<strong>de</strong>n<br />

Komponente angegeben.<br />

214


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Weiterh<strong>in</strong> gibt es <strong>in</strong> MouseEvent die Metho<strong>de</strong> public boolean<br />

isPopupTrigger(). Darüber kann abgefragt wer<strong>de</strong>n, ob das Klickereignis <strong>de</strong>n<br />

Ausruf e<strong>in</strong>es Popup-Menüs anzeigen soll. Die Metho<strong>de</strong> public <strong>in</strong>t<br />

getClickCount() liefert die Anzahl <strong>de</strong>r Mausklicks.<br />

Für die Beabeitung von Mouse-Events stehen außer<strong>de</strong>m die aus InputEvent<br />

191 geerbten Metho<strong>de</strong>n<br />

public boolean isShiftDown();<br />

public boolean isControlDown();<br />

public boolean isMetaDown();<br />

public boolean isAltDown();<br />

zur Verfügung.<br />

3.1.5.4 MouseMotion-Events<br />

Sie geben Auskunft über die Bewegung <strong>de</strong>s Mauszeigers. E<strong>in</strong> Empfänger für<br />

"MouseMotionEvents" muß das Interface MouseMotionListener implementieren.<br />

Es wird mit public void addMouseMotionListener(MouseListener l)<br />

registriert. Die Metho<strong>de</strong> steht allen Objekten <strong>de</strong>r Klasse Component o<strong>de</strong>r daraus<br />

abgeleiteteter Klassen zur Verfügung. Die Metho<strong>de</strong>n von MouseMotionListener<br />

bekommen Events <strong>de</strong>s Typs MouseEvent übergeben. Damit stehen diesselben<br />

Metho<strong>de</strong>n wie bei MouseEvent zur Verfügung.<br />

Das Interface MouseMotionListener <strong>de</strong>f<strong>in</strong>iert<br />

public abstract void mouseMoved(MouseEvent e);<br />

// Aufruf bei Bewegung e<strong>in</strong>er Maus ohne Drücken <strong>de</strong>r Maustaste<br />

public abstract void mouseDragged(MouseEvent e);<br />

/* Aufruf bei Bewegung <strong>de</strong>r Maus und gedrückter rechter o<strong>de</strong>r l<strong>in</strong>ker<br />

Maustaste */<br />

3.1.5.5 Fokus-Events<br />

Der Fokus zeigt an, welches Fenster Tastature<strong>in</strong>gaben erhält. S<strong>in</strong>d mehrere Fenster<br />

gleichzeitig geöffnet, so kann immer nur e<strong>in</strong>es von ihnen <strong>de</strong>n Fokus beanspruchen.<br />

S<strong>in</strong>d auf e<strong>in</strong>em aktiven Fenster mehrere Dialogelemente aktiv, so kann ebenfalls nur<br />

e<strong>in</strong>es davon <strong>de</strong>n Fokus erhalten, <strong>de</strong>nn je<strong>de</strong>s Dialogelement wird ebenfalls durch e<strong>in</strong><br />

(meist unsichtbares) Fenster dargestellt.<br />

E<strong>in</strong> Empfänger für Fokus-Events muß das Interface FocusListener<br />

implementieren und bekommt Events <strong>de</strong>s Typs FocusEvent übergeben.<br />

FocusEvent erweitert ComponentEvent und stellt neben getID(), getSource()<br />

die Metho<strong>de</strong> public boolean isTemporary() bereit. Sie zeigt an, ob <strong>de</strong>r<br />

Fokuswechsel temporär o<strong>de</strong>r permanent ist. Die Registrierung von Focus-Events<br />

erfolgt über public void addFocusListener(FocusListener l), die allen<br />

Objekten <strong>de</strong>s Typs Component o<strong>de</strong>r daraus abgeleiteten Objekten zur Verfügung<br />

191 InputEvent ist Basisklasse von MouseEvent und KeyEvent. Sie stellt Metho<strong>de</strong>n bereit, die<br />

allgeme<strong>in</strong>e Informationen über <strong>de</strong>n Zustand <strong>de</strong>r Umschalttasten STRG, ALT, UMSCHALT o<strong>de</strong>r META zum<br />

Zeitpunkt <strong>de</strong>s Ereignisses liefern<br />

215


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

steht, Das Interface FocusListener enthält zwei unterschiedliche Metho<strong>de</strong>n:<br />

public abstract void focusGa<strong>in</strong>ed(FocusEvent e)<br />

// Aufruf, wenn die Komponente <strong>de</strong>n Fokus erhält<br />

public abstract void focusLost(FocusEvent e)<br />

// Aufruf, wenn die Komponente <strong>de</strong>n Fokus abgibt<br />

Über die Metho<strong>de</strong> public void requestFocus() kann e<strong>in</strong>e Komponente <strong>de</strong>n<br />

Fokus für sich selbst beanspruchen bzw. ihn e<strong>in</strong>er an<strong>de</strong>ren Komponenten zuweisen.<br />

3.1.5.6 Key-Events<br />

E<strong>in</strong> Empfänger für Key-Events muß das Interface KeyListener implementieren und<br />

bekommt Events <strong>de</strong>s Typs KeyEvent übergeben. KeyEvent erweitert die Klasse<br />

InputEvent, die aus ComponentEvent abgeleitet ist, und stellt neben getID,<br />

getSource e<strong>in</strong>e Reihe von Metho<strong>de</strong>n zur Erkennung und Berabeitung von<br />

Tastaturco<strong>de</strong>s zur Verfügung. Die Registrierung erfolgt mit <strong>de</strong>r Metho<strong>de</strong> public<br />

void addKeyListener (KeyListener l), die auf allen Objekten <strong>de</strong>s Typs<br />

Component o<strong>de</strong>r daraus abgeleiteter Klassen zur Verfügung steht. Das Interface<br />

KeyListener <strong>de</strong>f<strong>in</strong>iert drei unterschiedliche Metho<strong>de</strong>n:<br />

public abstract void keyTyped(KeyEvent e);<br />

public abstract void keyPressed(KeyEvent e);<br />

public abstract void keyReleased(KeyEvent e);<br />

Die Taste, die gedrückt wur<strong>de</strong>, erhält man über die folgen<strong>de</strong>n Metho<strong>de</strong>n <strong>de</strong>r Klasse<br />

KeyEvent bereitgestellt:<br />

public <strong>in</strong>t getKeyCo<strong>de</strong>()<br />

liefert virtuelle Tastenco<strong>de</strong>s, die <strong>in</strong> KeyEvent als symbolische Konstanten <strong>de</strong>f<strong>in</strong>iert wur<strong>de</strong>n.Hier wird<br />

beim Drücken <strong>de</strong>r Taste A immer <strong>de</strong>r Co<strong>de</strong> VK_A geliefert, unabhängig davon, ob UMSCHALT<br />

gedrückt wur<strong>de</strong> o<strong>de</strong>r nicht.<br />

Symbolischer Name<br />

Be<strong>de</strong>utung<br />

VK_0..VK_9 0..9<br />

VK_A..VK_Z<br />

A..Z<br />

VK_ENTER<br />

Enter<br />

VK_SPACE<br />

Leertaste<br />

VK_TAB<br />

Tabulator<br />

VK_ESCAPE<br />

Escape<br />

VK_BACK_SPACE<br />

Rückschritt<br />

VK_F1..VK_F12<br />

Die Funktionstasten F1 .. F12<br />

VK_HOME, VK_END<br />

Home, End<br />

VK_PAGE_UP, VK_PAGE_DOWN<br />

Bild hoch, Bild runter<br />

VK_DOWN, VK_UP<br />

Cursor hoch, Cursor runter<br />

VK_LEFT, VK_RIGHT<br />

Cursor l<strong>in</strong>ks, Cursor rechts<br />

VK_INSERT, VK_DELETE<br />

E<strong>in</strong>fg, Entf<br />

Abb.: Tabelle <strong>de</strong>r virtuellen Key-Co<strong>de</strong>s<br />

public char getKeyChar()<br />

liefert das Zeichen, das <strong>de</strong>r gedrückten Zeichentste entspricht, z.B. "a", wenn Taste A gedrückt wur<strong>de</strong>,<br />

und "A", wenn die Tastenkomb<strong>in</strong>ation UMSCHALT + A gedrückt wur<strong>de</strong>. Funktionstasten wer<strong>de</strong>n nicht<br />

übertragen. Der Rückgabewert ist hier KeyEvent.CHAR_UNDEFINED.<br />

216


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

getKeyCo<strong>de</strong>()<br />

keyTyped 192 Zeichentaste 193 : VK_UNDEFINED<br />

Funktionstaste 194 : -<br />

keyPressed 195 Zeichentaste: VK_...<br />

Funktionstaste: VK_...<br />

getKeyChar()<br />

Zeichentaste: Taste als char<br />

Funktionstaste: -<br />

Zeichentaste: Taste als char<br />

Funktionstaste: CHAR_UNDEFINED<br />

Rückgabeco<strong>de</strong> bei Tastatur-Ereignissen<br />

Zusätzlich stehen fogen<strong>de</strong> aus InputEvent geerbten Metho<strong>de</strong>n zur Verfügung:<br />

public boolean isShiftDown();<br />

public boolean isControlDown();<br />

public boolean isMetaDown();<br />

public boolean isAltDown();<br />

Bsp.: Das folgen<strong>de</strong> Programm 196 schreibt alle möglichen Ereignisse zu Low-Level-<br />

Events auf.<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

public class PR14166 extends Frame<br />

{<br />

PR14166()<br />

{<br />

addComponentListener(new CL());<br />

addFocusListener(new FL());<br />

addKeyListener(new KL());<br />

addMouseListener(new ML());<br />

addMouseMotionListener(new MML());<br />

}<br />

class CL implements ComponentListener<br />

{<br />

public void componentMoved(ComponentEvent e)<br />

{<br />

System.out.pr<strong>in</strong>tln(e.toStr<strong>in</strong>g());<br />

}<br />

public void componentResized(ComponentEvent e)<br />

{<br />

System.out.pr<strong>in</strong>tln(e.toStr<strong>in</strong>g());<br />

}<br />

public void componentHid<strong>de</strong>n(ComponentEvent e)<br />

{<br />

System.out.pr<strong>in</strong>tln(e.toStr<strong>in</strong>g());<br />

}<br />

public void componentShown(ComponentEvent e)<br />

{<br />

System.out.pr<strong>in</strong>tln(e.toStr<strong>in</strong>g());<br />

}<br />

}<br />

class FL implements FocusListener<br />

{<br />

public void focusGa<strong>in</strong>ed(FocusEvent e)<br />

{<br />

System.out.pr<strong>in</strong>tln(e.toStr<strong>in</strong>g());<br />

}<br />

192 zeigt das Verhalten beim Aufruf <strong>de</strong>r Listener-Metho<strong>de</strong> keyTyped<br />

193 Tasten, mit <strong>de</strong>nen Buchstaben, Ziffern o<strong>de</strong>r sonst. Unico<strong>de</strong>-Zeichen e<strong>in</strong>gegeben wer<strong>de</strong>n, wie z.B. a, A, 1, 2,<br />

% aber auch ESC, SPACE, TAB<br />

194 Dazu gehören bspw. F1, F2, Pos 1 aber auch die Umschalttasten: STRG, ALT, UMSCHALT<br />

195 zeigt das Verhalten beim Aufruf von keyPressed<br />

196 vgl. pr14160<br />

217


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

public void focusLost(FocusEvent e)<br />

{<br />

System.out.pr<strong>in</strong>tln(e.toStr<strong>in</strong>g());<br />

}<br />

}<br />

class KL implements KeyListener<br />

{<br />

public void keyPressed(KeyEvent e)<br />

{<br />

System.out.pr<strong>in</strong>tln(e.toStr<strong>in</strong>g());<br />

}<br />

public void keyReleased(KeyEvent e)<br />

{<br />

System.out.pr<strong>in</strong>tln(e.toStr<strong>in</strong>g());<br />

}<br />

public void keyTyped(KeyEvent e)<br />

{<br />

System.out.pr<strong>in</strong>tln(e.toStr<strong>in</strong>g());<br />

}<br />

}<br />

class ML implements MouseListener<br />

{<br />

public void mouseClicked(MouseEvent e)<br />

{<br />

// requestFocus();<br />

System.out.pr<strong>in</strong>tln(e.toStr<strong>in</strong>g());<br />

}<br />

public void mousePressed(MouseEvent e)<br />

{ System.out.pr<strong>in</strong>tln(e.toStr<strong>in</strong>g()); }<br />

public void mouseReleased(MouseEvent e)<br />

{ System.out.pr<strong>in</strong>tln(e.toStr<strong>in</strong>g()); }<br />

public void mouseEntered(MouseEvent e)<br />

{ System.out.pr<strong>in</strong>tln(e.toStr<strong>in</strong>g()); }<br />

public void mouseExited(MouseEvent e)<br />

{ System.out.pr<strong>in</strong>tln(e.toStr<strong>in</strong>g()); }<br />

}<br />

class MML implements MouseMotionListener<br />

{<br />

public void mouseDragged(MouseEvent e)<br />

{ System.out.pr<strong>in</strong>tln(e.toStr<strong>in</strong>g()); }<br />

public void mouseMoved(MouseEvent e)<br />

{ System.out.pr<strong>in</strong>tln(e.toStr<strong>in</strong>g()); }<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

PR14166 f = new PR14166();<br />

f.addW<strong>in</strong>dowListener(new W<strong>in</strong>dowAdapter()<br />

{<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{<br />

System.exit(0);<br />

}<br />

});<br />

f.setSize(200,200);<br />

f.setVisible(true);<br />

}<br />

}<br />

218


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

3.1.6 Der Weg e<strong>in</strong>es Ereignisses<br />

Je<strong>de</strong> Ereignisquelle besitzt e<strong>in</strong>e Reihe von Metho<strong>de</strong>n, die für das Aufbereiten und<br />

Verteilen <strong>de</strong>r Nachricht zuständig s<strong>in</strong>d. Beim Weiterreichen e<strong>in</strong>er Nachricht wird<br />

<strong>in</strong>nerhalb <strong>de</strong>r Nachrichtenquelle die Metho<strong>de</strong> processEvent aufgerufen. Diese<br />

verteilt die Nachricht anhand ihres Typs an spezialisierte Metho<strong>de</strong>n, <strong>de</strong>ren Name<br />

sich nach <strong>de</strong>m Typ <strong>de</strong>r zugehörigen Ereignisquelle richtet:<br />

protected void processComponentEvent(ComponentEvent e)<br />

protected void processFocusEvent(FocusEvent e)<br />

protected void processKeyEvent(KeyEvent e)<br />

protected void processMouseEvent(MouseEvent e)<br />

protected void processMouseMotionEvent(MouseEvent e)<br />

protected void processActionEvent(ActionEvent e)<br />

Abb.: Spezialisierte Metho<strong>de</strong>n für das Event-Handl<strong>in</strong>g<br />

"processEvent" bzw. die spezialisierten Metho<strong>de</strong>n für das Event-Handl<strong>in</strong>g wer<strong>de</strong>n<br />

nur aufgerufen, wenn <strong>de</strong>r entsprechen<strong>de</strong> Ereignistyp für diese Ereignisquelle aktiviert<br />

wur<strong>de</strong>. Dies geschieht <strong>in</strong> folgen<strong>de</strong>n Fällen:<br />

- E<strong>in</strong> passen<strong>de</strong>r Ereignisempfänger wur<strong>de</strong> über die zugehörige addEventListener-Metho<strong>de</strong><br />

registriert.<br />

- E<strong>in</strong> Ereignistyp wur<strong>de</strong> explizit durch Aufruf <strong>de</strong>r Metho<strong>de</strong> protected f<strong>in</strong>al void<br />

enableEvents(long eventsToEnable) aktiviert. Die Metho<strong>de</strong> erwartet e<strong>in</strong>e Maske, die<br />

durch e<strong>in</strong>e bitweise O<strong>de</strong>r-Verknüpfung passen<strong>de</strong>r Konstanten aus <strong>de</strong>r Klasse AWTEvent<br />

zusammengesetzt wer<strong>de</strong>n kann. Folgen<strong>de</strong> Bitmasken akzeptiert diese Metho<strong>de</strong>:<br />

ACTION_EVENT_MASK<br />

ADJUSTMENT_EVENT_MASK<br />

COMPONENT_EVENT_MASK<br />

CONTAINER_EVENT_MASK<br />

FOCUS_EVENT_MASK<br />

ITEM_EVENT_MASK<br />

KEY_EVENT_MASK<br />

MOUSE_EVENT_MASK<br />

MOUSE_MOTION_EVENT_MASK<br />

TEXT_EVENT_MASK<br />

WINDOW_EVENT_MASK<br />

Die Namen dieser Bitmasken verweisen auf die Namen <strong>de</strong>r Ereignisklassen, für<br />

<strong>de</strong>ren Objekte sie <strong>de</strong>n Ereignisfilter durchlässig schalten. Sollen mehrere aktiviert<br />

wer<strong>de</strong>n, dann ist diese Komb<strong>in</strong>ation durch Addition bzw. bitweises O<strong>de</strong>r möglich,<br />

z.B.:<br />

enableEvents(ACTION_EVENT_MASK + WINDOW_EVENT_MASK)<br />

bzw.<br />

enableEvents(ACTION_EVENT_MASK | WINDOW_EVENT_MASK)<br />

Sobald e<strong>in</strong> soches Ereignis erzeugt wird, sieht se<strong>in</strong> Weg dann folgen<strong>de</strong>rmaßen aus:<br />

1. Die Metho<strong>de</strong> processEvent <strong>de</strong>r eigenen Klasse erhält das Ereignis<br />

2. Nach <strong>de</strong>r Ereignisverarbeitung wird die Metho<strong>de</strong> <strong>de</strong>r Subklasse aufgerufen<br />

3. Diese prüft <strong>de</strong>n Ereignistyp und ruft dann typspezifische process...Event-Metho<strong>de</strong>n auf, z.B.<br />

processActionEvent, processFocusEvent.<br />

4. Diese prüfen, ob Listener registriert s<strong>in</strong>d, und rufen sie gegebenenfalls auf.<br />

5. Nach diesen Aufrufen wird die Metho<strong>de</strong> processEvent <strong>de</strong>r nächsten Superklasse aufgerufen.<br />

219


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

6. Sie verfährt wie<strong>de</strong>r so, wie unter <strong>de</strong>n Ziffern 3 bis 5 beschrieben. Das wie<strong>de</strong>rholt sich so lange, bis<br />

processEvent von ComponentEvent durchlaufen wird.<br />

Bsp.: Das folgen<strong>de</strong> Programm schreibt alle möglichen Ereignisse zu Low-Level-<br />

Events auf.<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

public class PR14168 extends Frame<br />

{<br />

PR14168()<br />

{<br />

// Bekanntmachen <strong>de</strong>r im Programm zu bearbeiten<strong>de</strong>n Ereignisse<br />

enableEvents(<br />

AWTEvent.COMPONENT_EVENT_MASK |<br />

AWTEvent.FOCUS_EVENT_MASK |<br />

AWTEvent.KEY_EVENT_MASK |<br />

AWTEvent.MOUSE_EVENT_MASK |<br />

AWTEvent.MOUSE_MOTION_EVENT_MASK |<br />

AWTEvent.CONTAINER_EVENT_MASK);<br />

}<br />

public void processEvent(AWTEvent e)<br />

{<br />

System.out.pr<strong>in</strong>tln(e.toStr<strong>in</strong>g());<br />

super.processEvent(e);<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

PR14168 f = new PR14168();<br />

f.addW<strong>in</strong>dowListener(new W<strong>in</strong>dowAdapter()<br />

{<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{<br />

System.exit(0);<br />

}<br />

});<br />

f.setSize(200,200);<br />

f.setVisible(true);<br />

}<br />

}<br />

220


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

3.2 Kommunikation Anwen<strong>de</strong>r – Programm über Dialoge bzw.<br />

Menüs<br />

3.2.1 Dialoge<br />

Der Anwen<strong>de</strong>r verkehrt im Rahmen grafischer Benutzeroberflächen über vom<br />

Programm vorgegebene Dialoge. Diese bestehen aus e<strong>in</strong>em Fenster und e<strong>in</strong>er<br />

Reihe von Dialogelementen (z.B. Textfel<strong>de</strong>r, Buttons, Listboxen) zur Darstellung und<br />

Erfassung programmspezifischer Daten. Das Design <strong>de</strong>r Dialoge wird von<br />

Layoutmangern unterstützt, die sich um Größe und Anordnung <strong>de</strong>r e<strong>in</strong>zelnen<br />

Dialogelemente kümmern.<br />

Erstellen e<strong>in</strong>es Dialogs<br />

Dafür s<strong>in</strong>d 4 Schritte nötig:<br />

- Anlegen e<strong>in</strong>es Fensters<br />

- Zuordnen e<strong>in</strong>e Layoutmanagers<br />

- E<strong>in</strong>fügen von Dialogelementen<br />

- Anzeigen <strong>de</strong>s Fensters<br />

Anlegen e<strong>in</strong>es Fensters. E<strong>in</strong> Dialogfenster kann wahlweise aus <strong>de</strong>r Klasse Frame<br />

o<strong>de</strong>r Dialog abgeleitet wer<strong>de</strong>n. "Dialog" erlaubt "modale Dialoge 197 " und<br />

verh<strong>in</strong><strong>de</strong>rt das Verän<strong>de</strong>rn <strong>de</strong>r Fenstergröße durch <strong>de</strong>n Anwen<strong>de</strong>r. Im Gegensatz zum<br />

Frame kann e<strong>in</strong> "Dialog"-Fenster ke<strong>in</strong>e Menüleiste erzeugen und <strong>de</strong>m Fenster ke<strong>in</strong><br />

Icon 198 zuordnen.<br />

Zuordnen e<strong>in</strong>es Layoutmanagers. Es wird über die Metho<strong>de</strong> "public void<br />

setLayout(LayoutManager mgr)" <strong>de</strong>r Klasse Conta<strong>in</strong>er realisiert. <strong>Java</strong> stellt fünf<br />

Layoutmanager 199 bereit: FlowLayout Bor<strong>de</strong>rLayout, GridLayout<br />

GridBagLayout und CardLayout.<br />

Neben <strong>de</strong>n Fähigkeiten e<strong>in</strong>es Layoutmanagers bestimmt <strong>in</strong> <strong>de</strong>r Regel die<br />

Reihenfolge <strong>de</strong>r Aufrufe von <strong>de</strong>r "add"-Metho<strong>de</strong> <strong>de</strong>s Fensters die tatsächliche<br />

Anordnung <strong>de</strong>r Komponenten auf <strong>de</strong>m Bildschirm.<br />

Schachteln von Layoutmanagern. Dazu wird an die Stelle, die das Sublayout erhalten<br />

soll, e<strong>in</strong>fach e<strong>in</strong> Objekt <strong>de</strong>r Klasse Panel e<strong>in</strong>gefügt, das e<strong>in</strong>en eigenen<br />

Layoutmanager erhält. Dieses Panel kann mit Dialogelementen bestückt wer<strong>de</strong>n, die<br />

entsprechend <strong>de</strong>m zugeordneten Sublayout formatiert wer<strong>de</strong>n, z.B.:<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

public class PR14171 extends Frame<br />

{<br />

public PR14171()<br />

{<br />

197 Modale Dialaloge verh<strong>in</strong><strong>de</strong>rn die Interaktion <strong>de</strong>s Anwen<strong>de</strong>rs mit an<strong>de</strong>ren Fenstern <strong>de</strong>r Anwendung bis zum<br />

Schließen <strong>de</strong>s Dialogfensters.<br />

198 Falls e<strong>in</strong> Fenster (unter W<strong>in</strong>dows) m<strong>in</strong>imiert wird, zeigt es e<strong>in</strong> Icon an. Mit Hilfe e<strong>in</strong>es Doppelklicks auf das<br />

Icon kann e<strong>in</strong>e ursprüngliche Größe <strong>de</strong>s Fensters wie<strong>de</strong>rhergestellt wer<strong>de</strong>n. Mit Hlife <strong>de</strong>r Metho<strong>de</strong> "public<br />

void setIconImage(Image bild)" kann e<strong>in</strong>em Fenster e<strong>in</strong> Icon zugeordnet wer<strong>de</strong>n, das beim<br />

M<strong>in</strong>imieren angezeigt wird.<br />

199 Vgl. 5.4.2<br />

221


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

addW<strong>in</strong>dowListener(new W<strong>in</strong>dowAdapter()<br />

{<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{<br />

setVisible(false);<br />

dispose();<br />

System.exit(0);<br />

}<br />

});<br />

// Layout festelegen und Komponenten h<strong>in</strong>zufuegen<br />

<strong>in</strong>t i = 0;<br />

Panel p1 = new Panel();<br />

p1.setLayout(new GridLayout(3,1));<br />

p1.add(new Button("Schaltflaeche " + ++i));<br />

p1.add(new Button("Schaltflaeche " + ++i));<br />

p1.add(new Button("Schaltflaeche " + ++i));<br />

Panel p2 = new Panel();<br />

p2.setLayout(new Bor<strong>de</strong>rLayout());<br />

p2.add("North",new Button("Schaltflaeche " + ++i));<br />

p2.add("South",new Button("Schaltflaeche " + ++i));<br />

p2.add("West",new Button("Schaltflaeche " + ++i));<br />

p2.add("East",new Button("Schaltflaeche " + ++i));<br />

p2.add("Center",new Button("Schaltflaeche " + ++i));<br />

// Hauptfenster<br />

setLayout(new GridLayout(1,2));<br />

add(p1);<br />

add(p2);<br />

pack();<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

PR14171 f = new PR14171();<br />

f.setVisible(true);<br />

}<br />

}<br />

Das vorliegen<strong>de</strong> Programm zeigt nach <strong>de</strong>m Aufruf das folgen<strong>de</strong> Fenster:<br />

Abb.:<br />

E<strong>in</strong>fügen von Dialogelementen. Es erfolgt über<br />

public Component add(Component komponente)<br />

public Component add(Component komponente, <strong>in</strong>t pos)<br />

public Component add (Str<strong>in</strong>g name, Component komponente)<br />

// erwartet e<strong>in</strong>en Str<strong>in</strong>g-Parameter, <strong>de</strong>r bei bestimmten Layout-Managern<br />

// (z.B. Bor<strong>de</strong>rLayout) Informationen zur Positionierung <strong>de</strong>r Elemente ("bei<br />

// Bor<strong>de</strong>rLayout: "South", "East", "West", "North", "Center") angibt.<br />

<strong>de</strong>r Klasse Conta<strong>in</strong>er. Mit "public void remove(Component komponente)"<br />

können bereits an das Fenster übergebene Komponenten gelöscht wer<strong>de</strong>n.<br />

Anzeigen e<strong>in</strong>es Dialogfensters. Es erfolgt durch e<strong>in</strong>en Aufruf von "setVisible".<br />

Zweckmäßig sollte zuvor "public void pack()" <strong>de</strong>r Klasse W<strong>in</strong>dow zur<br />

222


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Anpassung <strong>de</strong>r Fenstergröße an <strong>de</strong>n für die Darstellung <strong>de</strong>r Dialogelemente<br />

erfor<strong>de</strong>rlichen Platz ausgeführt wer<strong>de</strong>n.<br />

Popup-Fenster <strong>de</strong>r Klasse Dialog<br />

Die Klasse Dialog 200 stellt e<strong>in</strong> Popup-Fenster bereit und ermöglicht die Erzeugung<br />

"modaler" bzw. "nicht modaler" Dialoge. "Modal" be<strong>de</strong>utet: Das Dialogfeld blockiert<br />

an<strong>de</strong>re Fenster, während es angezeigt wird, z.B. 201 :<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

public class InfoDialog extends Dialog<br />

{<br />

protected Button schalter;<br />

protected Label label;<br />

public InfoDialog(Frame eltern, Str<strong>in</strong>g titel, Str<strong>in</strong>g nachricht)<br />

{<br />

super(eltern,titel,false);<br />

this.setLayout(new Bor<strong>de</strong>rLayout(15,15));<br />

label = new Label(nachricht,Label.CENTER);<br />

this.add("Center",label);<br />

schalter = new Button("Okay");<br />

schalter.addActionListener(new ActionListener()<br />

{<br />

public void actionPerformed(ActionEvent e)<br />

{<br />

dispose();<br />

setVisible(false);<br />

}<br />

});<br />

Panel p = new Panel();<br />

p.setLayout(new FlowLayout(FlowLayout.CENTER,15,15));<br />

p.add(schalter);<br />

this.add("South",p);<br />

this.pack();<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

Frame f = new Frame("InfoDialog-Test");<br />

f.setSize(300,100);<br />

f.setVisible(true);<br />

f.addW<strong>in</strong>dowListener(new W<strong>in</strong>dowAdapter()<br />

{<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{<br />

// dispose();<br />

// setVisible(false);<br />

System.exit(0);<br />

}<br />

});<br />

InfoDialog dlg = new InfoDialog(f,"Dialog-Demo",<br />

"Diese Demo wur<strong>de</strong> nach e<strong>in</strong>er Vorlage von"<br />

+ "David Flanagan geschrieben");<br />

dlg.setVisible(true);<br />

}<br />

}<br />

200 vgl. 5.3.4<br />

201 vgl. pr14170<br />

223


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Dialogfenster s<strong>in</strong>d Übergangsfenster. Sie dienen dazu, <strong>de</strong>n Benutzer über Ereignisse<br />

zu <strong>in</strong>formieren o<strong>de</strong>r E<strong>in</strong>gabe vom Benutzer anzufor<strong>de</strong>rn. Im Gegensatz zu Frames<br />

haben Dialogfel<strong>de</strong>r im allg. ke<strong>in</strong>e Titelleiste o<strong>de</strong>r Schaltfläche zum Schließen <strong>de</strong>s<br />

Fensters.<br />

E<strong>in</strong> Dialogfenster ist, wie e<strong>in</strong> Frame, e<strong>in</strong> Panel, <strong>in</strong> <strong>de</strong>m Komponenten <strong>de</strong>r<br />

Benutzeroberfläche angeordnet, gezeichnet sowie Grafikoperationen ausgeführt<br />

wer<strong>de</strong>n können.<br />

Mit <strong>de</strong>r Klasse FileDialog kann e<strong>in</strong> plattformspezifischer Dateidialog erzeugt<br />

wer<strong>de</strong>n.<br />

Dialog<br />

FileDialog<br />

public static f<strong>in</strong>al <strong>in</strong>t LOAD<br />

public static f<strong>in</strong>al <strong>in</strong>t SAVE<br />

><br />

public FileDialog(Frame eltern)<br />

// erzeugt Datei-Dialog ohne Titel zum Öffnen e<strong>in</strong>er Datei<br />

public FileDialog(Frame eltern, Str<strong>in</strong>g titel)<br />

//erzeugt Dialog zum Öffnen e<strong>in</strong>er Datei mit e<strong>in</strong>em Titel im<br />

// Fensterrahmen<br />

public FileDialog(Frame eltern, Str<strong>in</strong>g titel, <strong>in</strong>t mo<strong>de</strong>)<br />

// erzeugt Dialog mit e<strong>in</strong>em Titel im Fensterrahmen. Da <strong>de</strong>r<br />

// Dialog sowohl für Sichern und La<strong>de</strong>n ausgelegt ist, s<strong>in</strong>d<br />

// we<strong>de</strong>r LOAD o<strong>de</strong>r SAVE mit anzugeben.<br />

><br />

public Str<strong>in</strong>g getDirectory()<br />

// liefert das Verzeichnis <strong>de</strong>s Dialogs<br />

public synchronized void setDirectory(Str<strong>in</strong>g dir)<br />

// setzt das Verzeichnis, das zu Beg<strong>in</strong>n ausgegeben wer<strong>de</strong>n soll<br />

public Str<strong>in</strong>g getFile()<br />

// liefert die ausgewählte datei <strong>de</strong>s Dialogs<br />

public synchronized void setFile(Str<strong>in</strong>g file)<br />

// Datei, die zu Beg<strong>in</strong>n angezeigt wird<br />

public FilenameFilter getFilenameFilter()<br />

// liefert FilenameFilter.-Objekt zurück<br />

public synchronized void setFilenameFilter(FilenameFilter filter)<br />

// setzt <strong>de</strong>n FilenameFilter<br />

Abb.: Die Klasse FileDialog<br />

Vererbungskette. FileDialog leitet sich von Dialog ab. Dialog ist e<strong>in</strong>e Ableitung<br />

von W<strong>in</strong>dow. W<strong>in</strong>dow ist e<strong>in</strong> Conta<strong>in</strong>er und dieser wie<strong>de</strong>rum e<strong>in</strong> Component.<br />

Bsp.: La<strong>de</strong>n und Speichern von Dateien mit Unterstützung durch e<strong>in</strong> Dateidialogfeld<br />

202 .<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

public class DateiDialog extends Frame<br />

{<br />

TextField dateiName = new TextField();<br />

202 pr14160<br />

224


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

TextField verzeichnis = new TextField();<br />

Button oeffnen = new Button("Oeffnen");<br />

Button sichern = new Button("Sichern");<br />

public DateiDialog()<br />

{<br />

setTitle("Dateidialog-Test");<br />

Panel p = new Panel();<br />

p.setLayout(new FlowLayout());<br />

oeffnen.addActionListener(new OeffnenL());<br />

p.add(oeffnen);<br />

sichern.addActionListener(new SichernL());<br />

p.add(sichern);<br />

add(p,Bor<strong>de</strong>rLayout.SOUTH);<br />

verzeichnis.setEditable(false);<br />

dateiName.setEditable(false);<br />

p = new Panel();<br />

p.setLayout(new GridLayout(2,1));<br />

p.add(dateiName); p.add(verzeichnis);<br />

add(p,Bor<strong>de</strong>rLayout.NORTH);<br />

}<br />

class OeffnenL implements ActionListener<br />

{<br />

public void actionPerformed(ActionEvent e)<br />

{<br />

FileDialog d = new FileDialog(DateiDialog.this,<br />

"Welche Datei soll geoeffnet wer<strong>de</strong>n?");<br />

d.setFile("*.java");<br />

d.setDirectory("."); // Aktuelles Verzeichnis<br />

d.show();<br />

Str<strong>in</strong>g datei = "*.*";<br />

if ((datei = d.getFile()) != null)<br />

{<br />

dateiName.setText(datei);<br />

verzeichnis.setText(d.getDirectory());<br />

}<br />

else {<br />

dateiName.setText("Cancel wur<strong>de</strong> gedrueckt!");<br />

verzeichnis.setText("");<br />

}<br />

}<br />

}<br />

class SichernL implements ActionListener<br />

{<br />

public void actionPerformed(ActionEvent e)<br />

{<br />

FileDialog d = new FileDialog(DateiDialog.this,<br />

"Welche Datei soll gesichert wer<strong>de</strong>n?",<br />

FileDialog.SAVE);<br />

d.setFile("*.java");<br />

d.setDirectory("."); // Aktuelles Verzeichnis<br />

d.show();<br />

Str<strong>in</strong>g datei = "*.*";<br />

if ((datei = d.getFile()) != null)<br />

{<br />

dateiName.setText(datei);<br />

verzeichnis.setText(d.getDirectory());<br />

}<br />

else {<br />

dateiName.setText("Cancel wur<strong>de</strong> gedrueckt!");<br />

verzeichnis.setText("");<br />

}<br />

}<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

Frame f = new DateiDialog();<br />

225


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

f.addW<strong>in</strong>dowListener(<br />

new W<strong>in</strong>dowAdapter()<br />

{<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{<br />

System.exit(0);<br />

}<br />

});<br />

f.setSize(250,110); f.setVisible(true);<br />

}<br />

}<br />

Bei Applets ist es vom Browser abhängig, ob Instanzen von FileDialog e<strong>in</strong>gesetzt<br />

wer<strong>de</strong>n können. Die meisten Browser erzeugen lediglich e<strong>in</strong>en Fehler.<br />

Vor<strong>de</strong>f<strong>in</strong>ierte Dialogelemente<br />

Je<strong>de</strong>s Dialogelement wird <strong>in</strong> <strong>Java</strong> durch e<strong>in</strong>e eigene Klasse repräsentiert. Zur<br />

Aufnahme e<strong>in</strong>es Dialogelements <strong>in</strong> e<strong>in</strong>en Dialog wird e<strong>in</strong>e neue Instanz <strong>de</strong>r<br />

gewünschten Klasse angelegt und das resultieren<strong>de</strong> Element mit "add" <strong>in</strong> <strong>de</strong>n Dialog<br />

e<strong>in</strong>gefügt. Alle Dialogelemente s<strong>in</strong>d aus <strong>de</strong>r Klasse Component abgeleitet. Sie<br />

verfügen über die grundlegen<strong>de</strong>n Eigenschaften e<strong>in</strong>es Fensters, besitzen Größe und<br />

Position und s<strong>in</strong>d <strong>in</strong> <strong>de</strong>r Lage, Nachrichten zu empfangen und zu bearbeiten.<br />

Component<br />

Button TextComponent Conta<strong>in</strong>er MenuComponent Checkbox<br />

TextField TextArea Menu MenuBar MenuItem<br />

Panel<br />

W<strong>in</strong>dow<br />

Abb.: Komponenten <strong>de</strong>s AWT<br />

Applet Frame Dialog<br />

Vor<strong>de</strong>f<strong>in</strong>ierte Dialogelemente unter <strong>de</strong>n Komponenten <strong>de</strong>s AWT s<strong>in</strong>d:<br />

Labels 203 , Schaltflächen(Buttons) 204 , Kontrollkästchen und Optionsfel<strong>de</strong>r 205 ,<br />

Auswahlmenüs 206 , Listenfel<strong>de</strong>r 207 , Textbereiche und Textfel<strong>de</strong>r 208 , Schieberegler 209 .<br />

Das Dialogelement „Canvas“<br />

203 vgl. 5.2.2<br />

204 vgl. 5.2.1<br />

205 vgl. 5.2.3<br />

206 vgl. 5.2.4<br />

207 vgl. 5.2.5<br />

208 vgl. 5.2.6<br />

209 vgl. 5.2.7<br />

226


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Die meisten AWT-Komponenten bieten Möglichkeiten für die Ausführung von<br />

Zeichenoperationen. Zeichenbereiche s<strong>in</strong>d Komponenten, die nur speziell zum<br />

Zeichnen ausgerichtet s<strong>in</strong>d. Zeichenbereiche können ke<strong>in</strong>e an<strong>de</strong>ren Komponenten<br />

enthalten, akzeptieren aber Ereignisse. Außer<strong>de</strong>m können sie Animationen und<br />

Bil<strong>de</strong>r anzeigen. Zum Erstellen e<strong>in</strong>es Zeichenbereichs wird die Klasse Canvas<br />

benutzt. Das eigentliche Zeichnen erfolgt <strong>in</strong> <strong>de</strong>r Metho<strong>de</strong> pa<strong>in</strong>t(Graphics g).<br />

Diese Metho<strong>de</strong> wird immer dann aufgerufen, wenn die Zeichenfäche neu gezeichnet<br />

wer<strong>de</strong>n muß. Typische Situationen, <strong>in</strong> <strong>de</strong>nen pa<strong>in</strong>t(Graphics g) aufgerufen wird,<br />

s<strong>in</strong>d:<br />

- Der die Zeichenfläche umfassen<strong>de</strong> Rahmen (Frame) wird ertmals erstellt<br />

- Die Metho<strong>de</strong> repa<strong>in</strong>t() wird explizit aufgerufen<br />

- Die Zeichenfläche wur<strong>de</strong> teilweise o<strong>de</strong>r ganz von an<strong>de</strong>ren Fenstern über<strong>de</strong>ckt<br />

- Die Größe <strong>de</strong>r Zeichenfläche hat sich verän<strong>de</strong>rt<br />

Durch Überlagerung von public void pa<strong>in</strong>t(Graphics g) sorgt e<strong>in</strong>e Canvas-<br />

Komponente für die Darstellung auf <strong>de</strong>m Bildschirm. Der Punkt (0,0) <strong>de</strong>s<br />

übergebenen Graphics-Objekts entspricht dabei <strong>de</strong>r l<strong>in</strong>ken oberen Ecke <strong>de</strong>s<br />

Ausgabebereichs.<br />

Bsp. 210 :<br />

import java.awt.*;<br />

public class KreuzHaar extends java.applet.Applet<br />

{<br />

GridLayout g = new GridLayout(1,1);<br />

Me<strong>in</strong>Canvas can = new Me<strong>in</strong>Canvas();<br />

public void <strong>in</strong>it()<br />

{<br />

setLayout(g);<br />

add(can);<br />

}<br />

}<br />

class Me<strong>in</strong>Canvas extends java.awt.Canvas<br />

{<br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

<strong>in</strong>t x = getSize().width / 2;<br />

<strong>in</strong>t y = getSize().height / 2;<br />

g.setColor(Color.black);<br />

g.drawL<strong>in</strong>e(x-10,y,x-2,y);<br />

g.drawL<strong>in</strong>e(x+10,y,x+2,y);<br />

g.drawL<strong>in</strong>e(x,y-10,x,y-2);<br />

g.drawL<strong>in</strong>e(x,y+10,x,y+2);<br />

}<br />

}<br />

Da die Klasse Canvas aus Component abgeleitet ist, bekommt e<strong>in</strong> Canvas-Objekt<br />

alle Ereignisse zugestellt, die auch an e<strong>in</strong>e Komponente gehen. Hierzu zählen:<br />

Tastatur-, Maus-, Mausbewegungs-, Fokus- und Komponentenereignisse.<br />

210 pr52900<br />

227


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

3.2.2 Menüs<br />

Je<strong>de</strong>s Fenster / Frame kann e<strong>in</strong>e eigene Menüleiste besitzen. Je<strong>de</strong> Menüleiste kann<br />

mehrere Menüs enthalten und je<strong>de</strong>s Menü beliebige E<strong>in</strong>träge 211 .<br />

Menüs dienen zur Auswahl von Funktionen und Optionen <strong>in</strong> Anwendungen. Menüs<br />

können entwe<strong>de</strong>r <strong>in</strong> <strong>de</strong>r Form von Menubars an <strong>de</strong>r Oberseite von Fenstern o<strong>de</strong>r als<br />

Popupmenüs an e<strong>in</strong>er beliebigen Stelle auftreten.<br />

E<strong>in</strong> Menü ist entwe<strong>de</strong>r <strong>in</strong> e<strong>in</strong>er Menüleiste e<strong>in</strong>es Frame-Objekts e<strong>in</strong>gebettet (Klasse<br />

MenuBar) o<strong>de</strong>r ersche<strong>in</strong>t auf Abruf als Kontextmenü (Klasse PopupMenu). Damit<br />

s<strong>in</strong>d entwe<strong>de</strong>r Untermenüs (Klasse Menu) o<strong>de</strong>r Menüe<strong>in</strong>träge (Klasse MenuItem)<br />

enthalten.<br />

Je<strong>de</strong>s Menü besitzt e<strong>in</strong> „label“ und e<strong>in</strong> „aktionKommando“ (e<strong>in</strong> Str<strong>in</strong>g, <strong>de</strong>r das Menü<br />

beim ActionEvent kennzeichnet und <strong>de</strong>r ausgelöst wird, wenn <strong>de</strong>r E<strong>in</strong>trag<br />

angeklickt wird). Für Menüs können Shortcuts, also Tastaturkürzel, angegeben<br />

wer<strong>de</strong>n.<br />

E<strong>in</strong> MenuItem ist e<strong>in</strong> E<strong>in</strong>trag <strong>in</strong> e<strong>in</strong>em Menü, <strong>de</strong>r sich bei Selektion e<strong>in</strong>er Option<br />

än<strong>de</strong>rt (CheckboxMenuItem) o<strong>de</strong>r e<strong>in</strong> Kommando absetzt.<br />

211 vgl. 5.3.3<br />

228


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

MenuComponent<br />

{ abstract }<br />

public Str<strong>in</strong>g getName()<br />

public void setName(Str<strong>in</strong>g name)<br />

public MenuConta<strong>in</strong>er getParent()<br />

public Font getFont()<br />

public void setFont(Font f)<br />

public f<strong>in</strong>al void dispatchEvent(AWTEvent e)<br />

protected void processEvent(AWTEvent e)<br />

public Str<strong>in</strong>g toStr<strong>in</strong>g()<br />

MenuItem<br />

MenuBar<br />

> ><br />

public MenuItem()<br />

public MenuBar()<br />

public MenuItem(Str<strong>in</strong>g label) ><br />

public MenuItem(Str<strong>in</strong>g label, MenuShortcut s)<br />

public void addNotify()<br />

> public void removeNotify()<br />

public void addNotify()<br />

public Menu getHelpMenu()<br />

public Str<strong>in</strong>g getLabel() public void setHelpMenu(Menu m)<br />

public void setLabel(Str<strong>in</strong>g label) public Menu add(Menu m)<br />

public boolean isEnabled()<br />

public void remove<br />

public void setEnabled(boolean b) (MenuComponent m)<br />

public MenuShortcut getShortcut()<br />

public <strong>in</strong>t getMenuCount()<br />

public void setShortcut(MenuShortcut s) public Menu getMenu(<strong>in</strong>t i)<br />

protected f<strong>in</strong>al void enableEvents(long eventsToEnable) public Enumeration shortcuts()<br />

protected f<strong>in</strong>al void disableEvents(long eventsToEnable) public MenuItem<br />

public void setActionCommand(Str<strong>in</strong>g kommando)<br />

getShortcutMenuItem<br />

public Str<strong>in</strong>g getActionCommand() (MenuShortcut s)<br />

public void addActionListener(ActionListener l)<br />

public void removeActionListener(ActionListener l)<br />

protected void processEvent(AWTEvent e)<br />

protected processActionEvent(ActionEvent e)<br />

... ><br />

MenuConta<strong>in</strong>er<br />

CheckboxMenuItem<br />

Menu<br />

> ><br />

public CheckboxMenuItem()<br />

public Menu()<br />

public CheckboxMenuItem(Str<strong>in</strong>g label)<br />

public Menu(Str<strong>in</strong>g label)<br />

public CheckboxMenuItem(Str<strong>in</strong>g label,<br />

boolean state) ><br />

> public MenuItem add(MenuItem m)<br />

public void addItemListener(ItemListener l)<br />

public void add(Str<strong>in</strong>g label)<br />

public void addNotify()<br />

public void addNotify()<br />

public boolean getState()<br />

public MenuItem getItem(<strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

protected void processEvent(AWTEvent e)<br />

public void <strong>in</strong>sert(MenuItem mi, <strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

... public void <strong>in</strong>sert(Str<strong>in</strong>g label,<strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

...<br />

229


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

3.2.3 Popup-Menüs<br />

Popup-Menüs s<strong>in</strong>d nicht wie normale Menüs an e<strong>in</strong>e bestimmte Position gebun<strong>de</strong>n,<br />

son<strong>de</strong>rn tretem meistens dort auf, wo <strong>de</strong>r Benutzer mit <strong>de</strong>r rechten Maustaste<br />

geklickt hat. Für Popup-Menüs ist die Klasse Popup-Menu zuständig. Menüe<strong>in</strong>träge<br />

können mit <strong>de</strong>r Klasse Menu erzeugt wer<strong>de</strong>n.<br />

Menu<br />

PopupMenu<br />

><br />

public PopupMenu()<br />

// erzeugt e<strong>in</strong> Popup-Menu<br />

public PopupMenu(Str<strong>in</strong>g label)<br />

><br />

public void addNotify()<br />

public void show(Component orig<strong>in</strong>, <strong>in</strong>t x, <strong>in</strong>t y)<br />

// läßt das Popup-Menu auf <strong>de</strong>r Komponenten<br />

// orig<strong>in</strong> an <strong>de</strong>r Position x, y aufspr<strong>in</strong>gen.<br />

Abb.: Die AWT-Menüklasse PopupMenu<br />

Bsp.: 212<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

public class PopMenDemo extends Frame<br />

{<br />

PopupMenu popmen = new PopupMenu();<br />

public PopMenDemo()<br />

{<br />

addW<strong>in</strong>dowListener(new W<strong>in</strong>dowAdapter()<br />

{<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{<br />

System.exit(0);<br />

}<br />

});<br />

popmen.add(new MenuItem("E<strong>in</strong>trag 1"));<br />

popmen.addSeparator();<br />

popmen.add(new MenuItem("E<strong>in</strong>trag 2"));<br />

popmen.add(new MenuItem("E<strong>in</strong>trag 3"));<br />

popmen.add(new MenuItem("E<strong>in</strong>trag 4"));<br />

add(popmen);<br />

popmen.addActionListener(new ActionListener()<br />

{<br />

public void actionPerformed(ActionEvent e)<br />

{<br />

System.out.pr<strong>in</strong>tln(e.getActionCommand() + " wur<strong>de</strong> gedrueckt");<br />

}<br />

});<br />

addMouseListener(new MouseAdapter()<br />

212 pr53330, PopMenDemo.java<br />

230


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

{<br />

public void mouseClicked(MouseEvent m)<br />

{<br />

if (m.getModifiers() == m.BUTTON3_MASK)<br />

popmen.show(PopMenDemo.this,m.getX(),m.getY());<br />

}<br />

});<br />

setSize(300,300); setVisible(true);<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{ new PopMenDemo().show(); }<br />

}<br />

231


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

3.3 Grundlagen <strong>de</strong>r Applet-Erstellung<br />

3.3.1 HTML-Grundlagen<br />

Die E<strong>in</strong>b<strong>in</strong>dung von <strong>Java</strong>-Applets <strong>in</strong> HTML-Seiten<br />

<strong>Java</strong>-Applets wer<strong>de</strong>n <strong>in</strong>nerhalb von HTML-Seiten über Referenzen e<strong>in</strong>gebun<strong>de</strong>n.<br />

Referenzen können Überschriften, Texte, Kapitel, Unterkapitel, Absätze, Grafiken<br />

bzw. L<strong>in</strong>ks zu an<strong>de</strong>ren Seiten se<strong>in</strong>. HTML ist e<strong>in</strong>e Dokumentationsbeschreibungssprache<br />

mit <strong>de</strong>r logische Strukturen e<strong>in</strong>es Dokuments beschrieben wer<strong>de</strong>n. Über<br />

Klartext (ASCII-Text) gibt e<strong>in</strong> Dokumentenformat Empfehlungen an e<strong>in</strong>e<br />

Darstellungssoftware (Browser) zur Darstellung <strong>de</strong>r Dokumentstruktur.<br />

Außer<strong>de</strong>m wird beschrieben, welche Funktionalität wie auszuführen ist, damit sie<br />

<strong>de</strong>m geplanten Layout und <strong>de</strong>r vorgegebenen Funktionalität entspricht. Es gibt ke<strong>in</strong>e<br />

verb<strong>in</strong>dliche Darstellungsvorschrift, <strong>de</strong>shalb kann die Ausführung von HTML-Seiten<br />

<strong>in</strong> verschie<strong>de</strong>nen Browsern oft unterschiedlich anfallen.<br />

Die Basis von HTML<br />

Die Sprache HTML baut auf <strong>de</strong>r Sprache SGML auf. HTML ist die Abkürzung für<br />

Hypertext Markup Language und wur<strong>de</strong> aus <strong>de</strong>r <strong>in</strong> <strong>de</strong>r ISO-Norm 87779:1986<br />

festgeschriebenen Spache SGML (Structured Generalized Markup Language)<br />

entwickelt. E<strong>in</strong> <strong>in</strong> HTML geschiebenes Dokument kann außer Text, Grafiken und<br />

multimediale Elemente (Sound, Vi<strong>de</strong>o, usw.) enthalten (Referenz auf Grafik- o<strong>de</strong>r<br />

Multimedia-Dateien). Weiterh<strong>in</strong> können mit gewissen E<strong>in</strong>schränkungen über HTML<br />

Datenbankabfragen formuliert, Resultate optisch aufbereitet und Menüstrukturen<br />

aufgebaut wer<strong>de</strong>n.<br />

Die Normung <strong>de</strong>r HTML realisiert und kontrolliert das World Wi<strong>de</strong> Web Consortium<br />

(W3C) mit Sitz <strong>in</strong> Genf. En<strong>de</strong> 1997 kam es zu zur offiziellen Verabschiedung <strong>de</strong>s<br />

Standards.<br />

HTML-Steueranweisungen<br />

Tags. Alle HTML-Steueranweisungen wer<strong>de</strong>n <strong>in</strong> sog. Tags geschrieben, die von<br />

spitzen Klammern (< >) begrenzt s<strong>in</strong>d. Man unterschei<strong>de</strong>t zwischen E<strong>in</strong>leitungs- und<br />

Abschluß-Tags. Die Abschluß-Tags s<strong>in</strong>d be<strong>in</strong>ahe i<strong>de</strong>ntisch mit <strong>de</strong>m E<strong>in</strong>leitungs-Tag.<br />

Sie besitzen lediglich zusätzlich e<strong>in</strong>en Slash (/) nach <strong>de</strong>m „


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Das Grundgerüst e<strong>in</strong>er HTML-Seite<br />

E<strong>in</strong>e HTML-Seite wird immer <strong>in</strong> die Anweisung am Anfang und am<br />

En<strong>de</strong> e<strong>in</strong>geschlossen.<br />

Konkrete Referenzierung e<strong>in</strong>es <strong>Java</strong> Applet<br />

Die konkrete Referenzierung e<strong>in</strong>es <strong>Java</strong>-Applet wird mit <strong>de</strong>m Applet-Tag e<strong>in</strong>geleitet. Zwischen <strong>de</strong>m e<strong>in</strong>leiten<strong>de</strong>n Tag und <strong>de</strong>m abschließen<strong>de</strong>n Applet-<br />

Tag () können benötigte Parameter o<strong>de</strong>r beliebiger Text e<strong>in</strong>gegeben<br />

wer<strong>de</strong>n. Die e<strong>in</strong>fachste Form <strong>de</strong>r Applet-Referenz. Ohne irgendwelche Parameter<br />

wird e<strong>in</strong> <strong>Java</strong>-Applet e<strong>in</strong>gebun<strong>de</strong>n mit<br />

<br />

<br />

klassenelement: Applet-Klasse<br />

WIDTH = Wert: Breite <strong>de</strong>s Applet <strong>in</strong> Pixel<br />

HEIGHT = Wert: Höhe <strong>de</strong>s Applet <strong>in</strong> Pixel<br />

Das -Tag erzeugt ke<strong>in</strong>en Absatz, <strong>de</strong>shalb sollte es <strong>in</strong> e<strong>in</strong>em allgeme<strong>in</strong>en<br />

Text-Tag stehen, z.B. o<strong>de</strong>r <strong>in</strong> e<strong>in</strong>em Überschriften-Tag (, usw.).<br />

Optionale Parameter <strong>de</strong>s -Tag<br />

Parameter<br />

CODEBASE<br />

ARCHIVE<br />

OBJECT<br />

ALT<br />

NAME<br />

ALIGN<br />

VSPACE<br />

HSPACE<br />

Be<strong>de</strong>utung<br />

Hier kann e<strong>in</strong> alternatives Verzeichnis 213 für das La<strong>de</strong>n von Klassendateien<br />

angegeben wer<strong>de</strong>n. Fehlt diese Angabe, wird das Dokumentenverzeichnis<br />

genommen.<br />

Angabe <strong>de</strong>s JAR-Archivs, aus <strong>de</strong>m die Klassendateien und sonstige Resourcen<br />

<strong>de</strong>s Applet genommen wer<strong>de</strong>n<br />

Name <strong>de</strong>r Datei, die <strong>de</strong>n serialisierten Inhalt <strong>de</strong>s Applet enthält.<br />

Alternativer Text für Browser, die das Applet verstehen, aber <strong>Java</strong> nicht<br />

unterstützen<br />

E<strong>in</strong><strong>de</strong>utiger Name für das Applet. Er kann zur Unterscheidung mehrerer<br />

kommunizieren<strong>de</strong>r Applets auf e<strong>in</strong>er Web-Seite verwen<strong>de</strong>t wer<strong>de</strong>n.<br />

Vertikale Anordnung <strong>de</strong>s Applets <strong>in</strong> e<strong>in</strong>er Textzeile. Hier kann e<strong>in</strong>er <strong>de</strong>r Werte<br />

left, right, top, texttop, middle, absmiddle, basel<strong>in</strong>e, bottom, absbottom<br />

angegeben wer<strong>de</strong>n.<br />

Rand über und unter <strong>de</strong>m Applet<br />

Rand l<strong>in</strong>ks o<strong>de</strong>r rechts vom Applet<br />

Neben <strong>de</strong>n Parametern <strong>de</strong>s „Applet-Tag“ können auch Parameter an das Applet<br />

selbst übergeben wer<strong>de</strong>n. Je<strong>de</strong>r Parameter kann durch e<strong>in</strong> -Tag über zwei<br />

Attribute für Name (name) und Wert (value) festgelegt wer<strong>de</strong>n. Das -Tag<br />

steht zwischen e<strong>in</strong>em öffnen<strong>de</strong>n und schließen<strong>de</strong>n -Tag.<br />

Die Parameter wer<strong>de</strong>n beim La<strong>de</strong>n an das Applet weitergereicht. Innerhalb <strong>de</strong>s<br />

Applets können sie mit <strong>de</strong>r Metho<strong>de</strong> public Str<strong>in</strong>g getParameter(Str<strong>in</strong>g<br />

name) abgefragt wer<strong>de</strong>n.<br />

213 Pfadname, <strong>in</strong> <strong>de</strong>m sich die Klassen bef<strong>in</strong><strong>de</strong>n<br />

233


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

3.3.2 Die <strong>in</strong>terne Arbeitsweise e<strong>in</strong>es Applets<br />

E<strong>in</strong> <strong>Java</strong>-Applet besitzt im Gegensatz zu e<strong>in</strong>er <strong>Java</strong>-Anwendung ke<strong>in</strong>e ma<strong>in</strong>()-<br />

Metho<strong>de</strong>, die beim La<strong>de</strong>n gestartet wird und das Programm solange am Leben hält,<br />

bis es <strong>de</strong>r Benutzer been<strong>de</strong>t. In e<strong>in</strong>em Applet sorgen vier Metho<strong>de</strong>n dafür, daß sich<br />

das Applet <strong>in</strong> se<strong>in</strong>er Umgebung korrekt verhält.<br />

Panel<br />

Applet<br />

><br />

public Applet()<br />

><br />

public void <strong>de</strong>stroy()<br />

public AppletContext getAppletContext()<br />

public Str<strong>in</strong>g getAppletInfo()<br />

public AudioClip getAudioClip(URL url)<br />

public AudioClip getAudioClip(URL url,Str<strong>in</strong>g name)<br />

public URL getCo<strong>de</strong>Base()<br />

public URL getDocumentBase()<br />

public Image getImage(URL url)<br />

public Image getImage(URL url, Str<strong>in</strong>g name)<br />

public Locale getLocale()<br />

public Str<strong>in</strong>g getParameter(Str<strong>in</strong>g name)<br />

public Str<strong>in</strong>g[][] getParameterInfo()<br />

public void <strong>in</strong>it()<br />

public boolean isActive()<br />

public void play(URL url)<br />

public void play(URL url, Str<strong>in</strong>g name)<br />

public void resize(Dimension d)<br />

public void resize(<strong>in</strong>t width, <strong>in</strong>t height)<br />

public void setStub(AppletStub stub)<br />

public void showStatus(Str<strong>in</strong>g msg)<br />

public void start()<br />

public void stop()<br />

…..<br />

Abb. Die Klasse Applet<br />

234


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

1. Das Erstellen e<strong>in</strong>es Applets<br />

Zum Erstellen e<strong>in</strong>es Applet muß immer e<strong>in</strong>e „Subklasse“ <strong>de</strong>r Klasse Applet 214<br />

erzeugt wer<strong>de</strong>n. <strong>Java</strong> setzt voraus, daß e<strong>in</strong>e Applet-Subklasse public <strong>de</strong>klariert<br />

wur<strong>de</strong>. Erkennt <strong>Java</strong> e<strong>in</strong> Applet auf e<strong>in</strong>er Web-Seite, dann wird die Applet-<br />

Ausgangsklasse und die Hilfsklasse, die diese erste Klasse evtl. benutzt, über das<br />

Netz gela<strong>de</strong>n. <strong>Java</strong> erstellt e<strong>in</strong>e Instanz dieser Klasse, alle systembezogenenen<br />

Metho<strong>de</strong>n wer<strong>de</strong>n an diese Instanz geschickt. Mehrere Applets auf <strong>de</strong>r gleichen o<strong>de</strong>r<br />

auf unterschiedlichen Seiten verwen<strong>de</strong>n an<strong>de</strong>re Instanzen, so daß sich je<strong>de</strong>s Applet<br />

auf <strong>de</strong>m gleichen System evtl. an<strong>de</strong>rs verhält.<br />

2. Applet-Metho<strong>de</strong>n<br />

Applets können zahlreiche, unterschiedliche Aktivitäten umfassen, die verschie<strong>de</strong>nen<br />

wichtigen Ereignissen im Lebenszyklus e<strong>in</strong>es Applet entsprechen, z.B. Initialisieren,<br />

Zeichnen, Mausereignisse. Je<strong>de</strong>r Aktivität ist e<strong>in</strong>e entsprechen<strong>de</strong> Metho<strong>de</strong><br />

zugeordnet, d.h.: Falls e<strong>in</strong>e Ereignis stattf<strong>in</strong><strong>de</strong>t, ruft <strong>de</strong>r Browser (o<strong>de</strong>r e<strong>in</strong> <strong>Java</strong><br />

ähnliches Werkzeug) diese spezifische Metho<strong>de</strong> auf.<br />

Zum Reagieren auf solche Ereignisse s<strong>in</strong>d bestimmte Verhaltensweisen vorzusehen.<br />

Das geschieht durch Überschreiben <strong>de</strong>r jeweiligen Metho<strong>de</strong> <strong>in</strong> <strong>de</strong>r Applet-Subklasse.<br />

Unterschiedliche Applet-Verhalten be<strong>de</strong>utet: Jeweils an<strong>de</strong>re Metho<strong>de</strong>n müssen<br />

überschieben wer<strong>de</strong>n. Die folgen<strong>de</strong>n Metho<strong>de</strong>n bestimmen <strong>de</strong>n Lebenszyklus e<strong>in</strong>es<br />

Applet:<br />

Rückkehr zur HTML-Seite<br />

<strong>in</strong>it() start() stop() <strong>de</strong>stroy()<br />

Abb.: Metho<strong>de</strong>n im Lebenszklus e<strong>in</strong>es Applet<br />

Verlassen <strong>de</strong>r HTML-Seite<br />

Die Metho<strong>de</strong> <strong>in</strong>it() wird nach <strong>de</strong>m La<strong>de</strong>n <strong>de</strong>s Applet ausgeführt. Sie dient zur<br />

Initialisierung.<br />

Die Metho<strong>de</strong> start() wird automatisch nach Aufruf <strong>de</strong>r Metho<strong>de</strong> <strong>in</strong>it()<br />

aufgerufen bzw. dann, wenn das Applet <strong>in</strong> <strong>de</strong>n Zustand „aktiv“ versetzt wird. Applets<br />

können <strong>in</strong> zwei Zustän<strong>de</strong>n se<strong>in</strong>: aktiv und <strong>in</strong>aktiv. Nach <strong>de</strong>m La<strong>de</strong>n e<strong>in</strong>es Applets ist<br />

dieses zunächst <strong>in</strong>aktiv. Das Applet wechselt <strong>in</strong> <strong>de</strong>n Zustand aktiv, wenn es erstmalig<br />

auf <strong>de</strong>m Bildschirm ersche<strong>in</strong>t. Von dort aus wechselt es se<strong>in</strong>en Zustand zwischen<br />

aktiv und <strong>in</strong>aktiv. Wodurch dieser Zustandswechsel genau ausgelöst wird, ist<br />

abhängig vom Kontext <strong>de</strong>s Applel, d.h. <strong>in</strong> <strong>de</strong>r Regel vom verwen<strong>de</strong>tet Web-Browser.<br />

Die Metho<strong>de</strong> stop() wird aufgerufen, wenn die HTML-Seite, <strong>in</strong> <strong>de</strong>r das Applet<br />

e<strong>in</strong>gebun<strong>de</strong>n ist, verlassen wird bzw. das Applet <strong>in</strong> <strong>de</strong>n Zustand <strong>in</strong>aktiv versetzt wird.<br />

214 Alle Applets müssen java.applet.Applet <strong>in</strong> die Datei mit <strong>de</strong>r Def<strong>in</strong>ition <strong>de</strong>r Applet-Klasse importieren.<br />

java.applet.* vollzieht das E<strong>in</strong>bun<strong>de</strong>n von java.applet.Applet automatisch. Fast alle Applets (die mit grafischen<br />

Schnittstellen) benötigen auch java.awt.*<br />

235


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Die Metho<strong>de</strong> <strong>de</strong>stroy() zerstört das Applet, nach<strong>de</strong>m es gestoppt wur<strong>de</strong> und <strong>de</strong>r<br />

Kontext <strong>de</strong>s Applet sich für e<strong>in</strong>e Zerstörung entschei<strong>de</strong>t. Die Metho<strong>de</strong> <strong>de</strong>stroy()<br />

sorgt dafür, daß alle vom Applet belegte Ressourcen wie<strong>de</strong>r freigegeben wer<strong>de</strong>n.<br />

Vorhan<strong>de</strong>ne, vom Applet erzeugte Threads wer<strong>de</strong>n ebenfalls zerstört.<br />

Die pa<strong>in</strong>t()-Metho<strong>de</strong> wird <strong>in</strong> <strong>Java</strong> immer aufgerufen, wenn e<strong>in</strong> Applet gezeichnet<br />

wer<strong>de</strong>n muß, z.B. be<strong>in</strong> erstmaligen Zeichnen <strong>de</strong>s Applet, beim Verschieben <strong>de</strong>s<br />

Applet-Fenster, beim Überlagern <strong>de</strong>s Applet-Fenster durch e<strong>in</strong> an<strong>de</strong>res Fenster. Die<br />

pa<strong>in</strong>t()-Metho<strong>de</strong> hat folgen<strong>de</strong> Gestalt:<br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

....<br />

}<br />

pa<strong>in</strong>t() besitzt e<strong>in</strong> Argument: e<strong>in</strong>e Instanz <strong>de</strong>r Klasse Graphics. Dieses Objekt<br />

wird vom Browser erstellt und an pa<strong>in</strong>t() abgegeben. Es muß sichergestellt se<strong>in</strong>,<br />

daß die Graphics-Klasse 215 <strong>in</strong> <strong>de</strong>n Co<strong>de</strong> <strong>de</strong>r Applet-Subklasse importiert 216<br />

wird.<br />

Bsp.: „Aller Anfang ist schwer!. Dieser Spruch soll mit Hilfe e<strong>in</strong>es Applet gezeigt<br />

wer<strong>de</strong>n.. Die zugehörige Quellco<strong>de</strong>datei „AllerAnfangApplet.java“ umfaßt 217 :<br />

import java.applet.*;<br />

import java.awt.*;<br />

public class AllerAnfangApplet extends Applet<br />

{<br />

Font f;<br />

Str<strong>in</strong>g spruch;<br />

//<br />

public void <strong>in</strong>it()<br />

{<br />

f = new Font("Helvetica",Font.BOLD,24);<br />

this.spruch = "Aller Anfang ist schwer!";<br />

}<br />

//<br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

// Oval mit Farbe yellow<br />

g.setColor(Color.yellow);<br />

g.fillOval(10,10,330,100);<br />

// Roter Rahmen; da <strong>Java</strong> ke<strong>in</strong>e L<strong>in</strong>ienbreite kennt,<br />

// wird die L<strong>in</strong>ienbreite durrch 4 Ovale, (die sich<br />

// um Pixelbreite unterschei<strong>de</strong>n,) simuliert<br />

g.setColor(Color.red);<br />

g.drawOval(10,10,330,100);<br />

g.drawOval( 9, 9,332,102);<br />

g.drawOval( 8, 8,334,104);<br />

g.drawOval( 7, 7,336,106);<br />

g.setColor(Color.black);<br />

g.setFont(f);<br />

g.drawStr<strong>in</strong>g(this.spruch,40,70);<br />

}<br />

}<br />

Die zugehörige HTML-Datei AllerAnfangApplet.html umfaßt:<br />

215 Teil <strong>de</strong>s Pakets java.awt<br />

216 Normalerweise geschieht dies über: import java.awt.Graphics<br />

217 vgl. pr32101<br />

236


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

<br />

<br />

Hallo!<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

In e<strong>in</strong>em Browser führt das zu <strong>de</strong>r folgen<strong>de</strong>n Darstellung:<br />

<strong>Java</strong>-Applets zeichnen sich durch Überschreiben <strong>de</strong>r „pa<strong>in</strong>t“-Metho<strong>de</strong> selbst. Wie<br />

wird die „pa<strong>in</strong>t“-Metho<strong>de</strong> aufgerufen?<br />

Es gibt drei verschie<strong>de</strong>ne Metho<strong>de</strong>n zum Neuzeichnen e<strong>in</strong>es Applet:<br />

public void pa<strong>in</strong>t(Graphics g)<br />

Sie zeichnet tatsächlich die Grafik <strong>de</strong>s Applets <strong>in</strong> <strong>de</strong>n Zeichenbereich. Sie wird immer aufgerufen,<br />

wenn e<strong>in</strong> Applet neu gezeichnet 218 wer<strong>de</strong>n muß. Das <strong>in</strong> <strong>de</strong>r „pa<strong>in</strong>t“-Metho<strong>de</strong> abgebene Graphics-<br />

Objekt enthält <strong>de</strong>n Grafikstatus, d.h. die aktuellen Merkmale <strong>de</strong>r Zeichnungsoberfläche.<br />

public void repa<strong>in</strong>t()<br />

Sie kann je<strong>de</strong>rzeit aufgerufen wer<strong>de</strong>n, wann auch immer das Applet neu gezeichnet wer<strong>de</strong>n muß. Sie<br />

ist <strong>de</strong>r Auslöser, die „pa<strong>in</strong>t“-Metho<strong>de</strong> sobald wie möglich aufzurufen und das Applet neu zu<br />

zeichnen. Sollten die repa<strong>in</strong>t()-Anweisungen schneller ablaufen, als <strong>Java</strong> diese verarbeiten kann,<br />

wer<strong>de</strong>n evtl. e<strong>in</strong>ige übersprungen. In vielen Fällen ist die Verzögerung zwischen <strong>de</strong>m Aufruf von<br />

repa<strong>in</strong>t() und <strong>de</strong>r eigentlichen Aktualisierung <strong>de</strong>s Fensters vernachlässigbar.<br />

public void update(Graphics g)<br />

Sie wird von repa<strong>in</strong>t() aufgerufen. Die „update“-Metho<strong>de</strong> löscht <strong>de</strong>n vollständigen<br />

Zeichenbereich 219 und ruft anschließend die „pa<strong>in</strong>t“-Metho<strong>de</strong> auf, die dann das Applet vollständig<br />

neu zeichnet. Der Aufruf <strong>de</strong>r „pa<strong>in</strong>t“-Metho<strong>de</strong> erfolgt also nicht direkt über die „repa<strong>in</strong>t“-Metho<strong>de</strong>,<br />

son<strong>de</strong>rn <strong>in</strong>direkt über die „update“-Metho<strong>de</strong>.<br />

Applets wer<strong>de</strong>n aus Sicherheitsgrün<strong>de</strong>n gewissen E<strong>in</strong>schränkungen unterworfen:<br />

218 Dies ist immer beim ersten Aufruf <strong>de</strong>s Applets <strong>de</strong>r Fall, aber auch je<strong>de</strong>smal dann, wenn das Applet-Fenster<br />

verschoben o<strong>de</strong>r zwischenzeitlich von e<strong>in</strong>em an<strong>de</strong>ren Fenster überlagert wur<strong>de</strong>.<br />

219 Das ruft oft <strong>de</strong>n unangenehmen Flimmereffekt bei schnellen Bildsequenzen hervor.<br />

237


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

- Applets können das Dateisystem <strong>de</strong>s Bernutzers nicht lesen und nicht beschreiben, abgesehen<br />

von bestimmten Verzeichnissen (die vom Benutzer durch e<strong>in</strong>e Zugriffskontrolliste, die<br />

standardäßig leer ist, bestimmt wer<strong>de</strong>n). E<strong>in</strong>ige Browser lassen ke<strong>in</strong>erlei Schreib- und Leseaktion<br />

<strong>de</strong>s Applets auf <strong>de</strong>m Client zu.<br />

- Applets können auf <strong>de</strong>m Client ke<strong>in</strong>erlei Programme ausführen.<br />

- Applets können normalerweise nur mit <strong>de</strong>m System kommunizieren, auf <strong>de</strong>nen sie gespeichert<br />

s<strong>in</strong>d.<br />

- Applets können ke<strong>in</strong>e ke<strong>in</strong>e nativen Programme <strong>de</strong>r lokalten Plattform la<strong>de</strong>n, auch ke<strong>in</strong>e<br />

geme<strong>in</strong>same Bibliotheken (wie DLL’s).<br />

3. Metho<strong>de</strong>n zur Ereignisbehandlung <strong>in</strong> Applets<br />

E<strong>in</strong> Applet kann auch auf Ereignisse wie Mausbewegungen reagieren. Für soche<br />

Ereignisse (z.B. Drücken <strong>de</strong>r Maustaste) stellt <strong>Java</strong> Ereignisbehandlungs-Metho<strong>de</strong>n<br />

<strong>de</strong>s JDK 1.0 bzw. JDK 1.1 zur Verfügung.<br />

Bsp. 220 : E<strong>in</strong> Applet zum Zeichnen von Punkten an <strong>de</strong>n Stellen, an <strong>de</strong>nen e<strong>in</strong>e<br />

Maustaste gedrückt wur<strong>de</strong>.<br />

// Import <strong>de</strong>r Pakete<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

// Top-Level Klassen-Deklaration <strong>de</strong>s Applets<br />

public class MausDownPunktApplet extends java.applet.Applet<br />

{<br />

// Variablen-Deklarationen<br />

private <strong>in</strong>t mausX, mausY;<br />

// private boolean mausKlick = false;<br />

// Metho<strong>de</strong>n, die ueberschrieben wer<strong>de</strong>n<br />

public void <strong>in</strong>it()<br />

{<br />

setBackground(Color.yellow);<br />

addMouseListener(new MouseAdapter()<br />

{<br />

public void mouseClicked(MouseEvent e)<br />

{<br />

mausX = e.getX(); mausY = e.getY();<br />

repa<strong>in</strong>t();<br />

}<br />

});<br />

}<br />

/*<br />

public boolean mouseDown(Event e, <strong>in</strong>t x, <strong>in</strong>t y)<br />

{<br />

mausX = x; mausY = y;<br />

mausKlick = true;<br />

repa<strong>in</strong>t();<br />

return true;<br />

}<br />

*/<br />

// Ausgabemetho<strong>de</strong><br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

g.setColor(Color.blue);<br />

// if (mausKlick)<br />

// {<br />

g.fillOval(mausX,mausY,20,20);<br />

// mausKlick = false;<br />

// }<br />

220 pr32305<br />

238


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

}<br />

}<br />

Nach je<strong>de</strong>m Mausklick wird e<strong>in</strong> blauer Punkt <strong>in</strong> die Zeichenfläche gebracht.<br />

Das Bild wird neu gezeichnet. Die repa<strong>in</strong>t()-Metho<strong>de</strong> ruft vor <strong>de</strong>r<br />

Ausführung <strong>de</strong>r pa<strong>in</strong>t()-Metho<strong>de</strong> die update()-Metho<strong>de</strong> auf, die<br />

anschließend pa<strong>in</strong>t() aufruft. „update()“ leert <strong>in</strong> Orig<strong>in</strong>alform <strong>de</strong>n<br />

Anzeigebereich <strong>de</strong>s Applets. Wird update() überschrieben, z.B. durch<br />

public void update(Graphics g)<br />

{<br />

pa<strong>in</strong>t(g);<br />

}<br />

entfällt das Leeren <strong>de</strong>s Anzeigebereichs.<br />

// Import <strong>de</strong>r Pakete<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

// Top-Level Klassen-Deklaration <strong>de</strong>s Applets<br />

public class MausDownPunkteApplet extends java.applet.Applet<br />

{<br />

// Variablen-Deklarationen<br />

private <strong>in</strong>t mausX, mausY;<br />

// private boolean mausKlick = false;<br />

// Metho<strong>de</strong>n, die ueberschrieben wer<strong>de</strong>n<br />

public void <strong>in</strong>it()<br />

{<br />

setBackground(Color.yellow);<br />

addMouseListener(new MouseAdapter()<br />

{<br />

public void mouseClicked(MouseEvent e)<br />

{<br />

mausX = e.getX(); mausY = e.getY();<br />

repa<strong>in</strong>t();<br />

}<br />

});<br />

}<br />

/*<br />

public boolean mouseDown(Event e, <strong>in</strong>t x, <strong>in</strong>t y)<br />

{<br />

mausX = x; mausY = y;<br />

mausKlick = true;<br />

repa<strong>in</strong>t();<br />

return true;<br />

}<br />

*/<br />

// Ausgabemetho<strong>de</strong><br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

g.setColor(Color.blue);<br />

// if (mausKlick)<br />

// {<br />

g.fillOval(mausX,mausY,20,20);<br />

// mausKlick = false;<br />

// }<br />

}<br />

public void update(Graphics g)<br />

{<br />

pa<strong>in</strong>t(g);<br />

}<br />

}<br />

239


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

E<strong>in</strong>e überschriebene update()-Metho<strong>de</strong> kann <strong>de</strong>n Flimmereffekt erheblich senken.<br />

3.3.3 „Multithread<strong>in</strong>g“-fähige Applets<br />

Mit Threads können <strong>in</strong> <strong>Java</strong> Applets so erstellt wer<strong>de</strong>n, daß alle o<strong>de</strong>r auch e<strong>in</strong>zelnen<br />

Co<strong>de</strong>teile <strong>in</strong> ihrem eigenen Thread laufen, ohne an<strong>de</strong>re Teile <strong>de</strong>s Systems zu<br />

bee<strong>in</strong>flussen. E<strong>in</strong> Applet kann im wesentlichen über vier Schritte<br />

Multithread<strong>in</strong>g-fähig gemacht wer<strong>de</strong>n:<br />

1. Erweitern <strong>de</strong>r Unterschrift <strong>de</strong>s Applets um implements Runnable<br />

2. H<strong>in</strong>zufügen e<strong>in</strong>er Instanzvariablen, die <strong>de</strong>n Thread <strong>de</strong>s Applet enthält<br />

3. Reduktion <strong>de</strong>r start()-Metho<strong>de</strong>, so daß sie außer <strong>de</strong>m Start <strong>de</strong>s Threads ke<strong>in</strong>e weiteren Threads<br />

enthält<br />

4. H<strong>in</strong>zufügen <strong>de</strong>r run()-Metho<strong>de</strong>, die <strong>de</strong>n eigentlichen Co<strong>de</strong> enthält, <strong>de</strong>n das Applet ausführen soll.<br />

Bsp.: E<strong>in</strong> Applet zur Anzeige von Datum und Uhrzeit, je<strong>de</strong> Sekun<strong>de</strong> wird<br />

aktualisiert 221 . Nach <strong>de</strong>n bisher vorliegen<strong>de</strong>n Erkenntnissen müßte das<br />

zugehörige Applet folgen<strong>de</strong> Gestalt haben:<br />

import java.awt.Graphics;<br />

import java.awt.Font;<br />

import java.util.Date;<br />

//<br />

// Top Level Deklaration <strong>de</strong>s Applets<br />

//<br />

public class DigitalUhr extends java.applet.Applet<br />

{<br />

// Variablen-Deklaration<br />

Font e<strong>in</strong>Font = new Font("TimesRoman",Font.BOLD,24);<br />

Date datum;<br />

// Eigene Metho<strong>de</strong>n<br />

// Metho<strong>de</strong>n, die ueberschrieben wer<strong>de</strong>n<br />

public void start()<br />

{<br />

// Ausfuehrung <strong>de</strong>s Applet<br />

while (true)<br />

{<br />

datum = new Date();<br />

repa<strong>in</strong>t(); // Aufruf <strong>de</strong>r repa<strong>in</strong>t()-Metho<strong>de</strong><br />

try {Thread.sleep(1000); } // Pause von 1000 Millisekun<strong>de</strong>n<br />

catch(InterruptedException e) {}<br />

}<br />

}<br />

// Optional, aber sehr wahrsche<strong>in</strong>lich – die Ausgabemetho<strong>de</strong><br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

g.setFont(e<strong>in</strong>Font); // Setzen <strong>de</strong>s aktuellen Font<br />

g.drawStr<strong>in</strong>g(datum.toStr<strong>in</strong>g(),10,50); // Ausgabe Datum<br />

// Da pa<strong>in</strong>t() wie<strong>de</strong>rholt mit jeweils <strong>de</strong>m aktuellen Wert von<br />

// „datum“ aufgerufen wird, wird die Zeichenkette je<strong>de</strong> Sekun<strong>de</strong><br />

//zur Ausgabe <strong>de</strong>s neuen Datums aufgerufen<br />

}<br />

}<br />

In <strong>de</strong>r start()-Metho<strong>de</strong> nimmt die while-Schleife alle Systemressourcen für<br />

sich <strong>in</strong> Anspruch (e<strong>in</strong>schl. <strong>de</strong>r Anzeige am Bildschirm). Deshalb funktioniert die<br />

221 vgl. pr42001<br />

240


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

digitale Uhr nicht. Außer<strong>de</strong>m kann das Applet nicht gestoppt wer<strong>de</strong>n, da die<br />

stop()-Metho<strong>de</strong> nicht aufgerufen wer<strong>de</strong>n kann. Die Lösung <strong>de</strong>s Problems liegt<br />

im erneuten Schreiben <strong>de</strong>s Applets mit Threads. Das Applet muß dazu mit <strong>de</strong>n<br />

vorgegebenen vier Arbeitsschritten erweitert wer<strong>de</strong>n.<br />

import java.awt.Graphics;<br />

import java.awt.Font;<br />

import java.util.Date;<br />

//<br />

// Top Level Deklaration <strong>de</strong>s Applets<br />

//<br />

public class DigitalThreadUhr extends java.applet.Applet<br />

implements Runnable<br />

{<br />

// Variablen-Deklaration<br />

Font e<strong>in</strong>Font = new Font("TimesRoman",Font.BOLD,24);<br />

Date datum;<br />

Thread fa<strong>de</strong>n;<br />

// Eigene Metho<strong>de</strong>n<br />

// Metho<strong>de</strong>n, die ueberschrieben wer<strong>de</strong>n<br />

public void start()<br />

{<br />

if (fa<strong>de</strong>n == null)<br />

{<br />

fa<strong>de</strong>n = new Thread(this);<br />

fa<strong>de</strong>n.start();<br />

}<br />

}<br />

public void stop()<br />

{<br />

if (fa<strong>de</strong>n != null)<br />

{<br />

fa<strong>de</strong>n.stop();<br />

fa<strong>de</strong>n = null;<br />

}<br />

}<br />

public void run()<br />

{<br />

// Ausfuehrung <strong>de</strong>s Applet, hier f<strong>in</strong><strong>de</strong>t die Animation statt<br />

while (true)<br />

{<br />

datum = new Date();<br />

repa<strong>in</strong>t(); // Aufruf <strong>de</strong>r repa<strong>in</strong>t()-Metho<strong>de</strong><br />

try {Thread.sleep(1000); } // Pause von 1000 Millisekun<strong>de</strong>n<br />

catch(InterruptedException e) {}<br />

}<br />

}<br />

// Optional, aber sehr wahrsche<strong>in</strong>lich – die Ausgabemetho<strong>de</strong><br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

g.setFont(e<strong>in</strong>Font); // Setzen <strong>de</strong>s aktuellen Font<br />

g.drawStr<strong>in</strong>g(datum.toStr<strong>in</strong>g(),10,50); // Ausgabe Datum<br />

// Da pa<strong>in</strong>t() wie<strong>de</strong>rholt mit jeweils <strong>de</strong>m aktuellen Wert von<br />

// "datum" aufgerufen wird, wird die Zeichenkette je<strong>de</strong> Sekun<strong>de</strong><br />

// zur Ausgabe <strong>de</strong>s neuen Datums aufgerufen<br />

}<br />

}<br />

Das folgen<strong>de</strong> Gerüst umfaßt e<strong>in</strong> Muster für „multithread<strong>in</strong>g“-fähige Applets:<br />

// Name <strong>de</strong>r Klasse:<br />

// Beschreibung:<br />

241


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

// Import <strong>de</strong>r Pakete<br />

// import java.lang.*;<br />

// import java.applet.*;<br />

// import java.awt.*;<br />

// Top-Level-Klassen-Deklaration bzw. Def<strong>in</strong>ition <strong>de</strong>s Applets<br />

public Klassenname extends java.applet.Applet<br />

{<br />

// Variablen-Deklarationen bzw. Def<strong>in</strong>itionen<br />

// ...<br />

// Eigene Metho<strong>de</strong>n<br />

// ...<br />

// Metho<strong>de</strong>n, die ueberschrieben wer<strong>de</strong>n<br />

//<br />

public void <strong>in</strong>it()<br />

{<br />

// ...<br />

}<br />

public void start()<br />

{<br />

// ...<br />

}<br />

public void stop()<br />

{<br />

// ...<br />

}<br />

public void <strong>de</strong>stroy()<br />

{<br />

// ...<br />

}<br />

// Optional: die Ausgabemetho<strong>de</strong><br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

// ..<br />

}<br />

// Bei Multithread<strong>in</strong>g: Verwendung <strong>de</strong>r run-Metho<strong>de</strong><br />

public void run()<br />

{<br />

// ...<br />

}<br />

}<br />

242


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

3.3.4 Animation <strong>in</strong> Applets<br />

Animationsschritte<br />

E<strong>in</strong>e Animation umfaßt <strong>in</strong> <strong>Java</strong> zwei Schritte:<br />

1. Aufbau und Ausgabe e<strong>in</strong>es Animationsrahmens (-fenster).<br />

2. Entsprechen<strong>de</strong> häufige Wie<strong>de</strong>rholung <strong>de</strong>r Zeichnung, um <strong>de</strong>n E<strong>in</strong>druck von Bewegung zu<br />

vermitteln (Abspielen e<strong>in</strong>er Animation).<br />

Aufbau e<strong>in</strong>es Animationsrahmens<br />

Dazu gehört alles das, was die Animation vorbereitet, z.B.:<br />

- Ermitteln <strong>de</strong>r Größe <strong>de</strong>s Ausgabebereichs<br />

- Positionieren <strong>de</strong>r Animation<br />

- Erstellen o<strong>de</strong>r La<strong>de</strong>n <strong>de</strong>r e<strong>in</strong>zelnen Animationsbil<strong>de</strong>r<br />

- Aufbau von e<strong>in</strong>zelnen Animationssequenzen<br />

Abspielen e<strong>in</strong>er Animation<br />

Die pa<strong>in</strong>t()-Metho<strong>de</strong> wird von <strong>Java</strong> aufgerufen, wenn e<strong>in</strong> Applet gezeichnet<br />

wer<strong>de</strong>n muß. <strong>Java</strong> kann aber auch aufgefor<strong>de</strong>rt wer<strong>de</strong>n, e<strong>in</strong> Bild zu e<strong>in</strong>em<br />

bestimmten Zeitpunkt nachzuzeichnen. Tut man das wie<strong>de</strong>rholt und schnell genug<br />

mit <strong>de</strong>r repa<strong>in</strong>t()-Metho<strong>de</strong>, dann entsteht e<strong>in</strong>e Animation. repa<strong>in</strong>t() ist e<strong>in</strong>e<br />

Anfrage an <strong>Java</strong>, das Applet so schnell wie möglich zu zeichnen.<br />

import java.awt.*;<br />

import java.util.*;<br />

public class Pen<strong>de</strong>lAppl1 extends java.applet.Applet implements Runnable<br />

{<br />

// Instanzvariable<br />

// Position vom Zentrum <strong>de</strong>s schw<strong>in</strong>gen<strong>de</strong>n Pen<strong>de</strong>ls<br />

<strong>in</strong>t x, y;<br />

//<br />

double thetaMax = (double) 0.35;<br />

double thetaM<strong>in</strong> = (double) –0.35;<br />

// Die <strong>in</strong>itiale Position vom Pen<strong>de</strong>l<br />

double theta = (double) 0.;<br />

//<br />

double wechsel = (double) 0.01;<br />

//<br />

<strong>in</strong>t xStart = 150, yStart = 20;<br />

// Radius <strong>de</strong>s Pen<strong>de</strong>ls<br />

double r = (double) 200;<br />

// Durchmesser <strong>de</strong>s Balls<br />

<strong>in</strong>t d = 20;<br />

Thread fa<strong>de</strong>n;<br />

// Metho<strong>de</strong>n<br />

public void <strong>in</strong>it()<br />

{<br />

setBackground(Color.yellow);<br />

}<br />

public void start()<br />

{<br />

243


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

if (fa<strong>de</strong>n == null)<br />

{<br />

fa<strong>de</strong>n = new Thread(this);<br />

fa<strong>de</strong>n.start();<br />

}<br />

}<br />

public void stop()<br />

{<br />

if (fa<strong>de</strong>n != null)<br />

{<br />

fa<strong>de</strong>n.stop();<br />

fa<strong>de</strong>n = null;<br />

}<br />

}<br />

public void run()<br />

{<br />

while (true)<br />

{<br />

x = xStart + (<strong>in</strong>t)(r * Math.s<strong>in</strong>(theta));<br />

y = yStart + (<strong>in</strong>t)(r * Math.cos(theta));<br />

if ((theta >= thetaMax) | (theta


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

// Instanzvariable<br />

// Position vom Zentrum <strong>de</strong>s schw<strong>in</strong>gen<strong>de</strong>n Pen<strong>de</strong>ls<br />

<strong>in</strong>t x, y;<br />

//<br />

double thetaMax = (double) 0.35;<br />

double thetaM<strong>in</strong> = (double) –0.35;<br />

// Die <strong>in</strong>itiale Position vom Pen<strong>de</strong>l<br />

double theta = (double) 0.;<br />

//<br />

double wechsel = (double) 0.01;<br />

//<br />

<strong>in</strong>t xStart = 150, yStart = 20;<br />

// Radius <strong>de</strong>s Pen<strong>de</strong>ls<br />

double r = (double) 200;<br />

// Durchmesser <strong>de</strong>s Balls<br />

<strong>in</strong>t d = 20;<br />

Thread fa<strong>de</strong>n;<br />

<strong>in</strong>t xAlt, yAlt;<br />

// Metho<strong>de</strong>n<br />

public void <strong>in</strong>it()<br />

{<br />

setBackground(Color.white);<br />

}<br />

public void start()<br />

{<br />

if (fa<strong>de</strong>n == null)<br />

{<br />

fa<strong>de</strong>n = new Thread(this);<br />

fa<strong>de</strong>n.start();<br />

}<br />

}<br />

public void stop()<br />

{<br />

if (fa<strong>de</strong>n != null)<br />

{<br />

fa<strong>de</strong>n.stop();<br />

fa<strong>de</strong>n = null;<br />

}<br />

}<br />

public void run()<br />

{<br />

while (true)<br />

{<br />

x = xStart + (<strong>in</strong>t)(r * Math.s<strong>in</strong>(theta));<br />

y = yStart + (<strong>in</strong>t)(r * Math.cos(theta));<br />

if ((theta >= thetaMax) | (theta


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

xAlt = x;<br />

yAlt = y;<br />

}<br />

}<br />

2. Double Buffer<strong>in</strong>g<br />

Mit „double buffer<strong>in</strong>g“ wird e<strong>in</strong>e zweite Oberfläche geschaffen, <strong>in</strong> <strong>de</strong>r alles<br />

vorgezeichnet und dann auf e<strong>in</strong>mal <strong>in</strong> die Zeichnungsoberfläche <strong>de</strong>s Applet<br />

ausgegeben wird.<br />

Bsp. 223 :<br />

import java.awt.*;<br />

public class Pen<strong>de</strong>l extends java.applet.Applet implements Runnable<br />

{<br />

// Instanzvariable<br />

// Position vom Zentrum <strong>de</strong>s schw<strong>in</strong>gen<strong>de</strong>n Pen<strong>de</strong>ls<br />

<strong>in</strong>t x, y;<br />

//<br />

double thetaMax = (double) 0.35;<br />

double thetaM<strong>in</strong> = (double) –0.35;<br />

// Die <strong>in</strong>itiale Position vom Pen<strong>de</strong>l<br />

double theta = (double) 0.;<br />

//<br />

double wechsel = (double) 0.01;<br />

//<br />

<strong>in</strong>t xStart = 150, yStart = 20;<br />

// Radius <strong>de</strong>s Pen<strong>de</strong>ls<br />

double r = (double) 200;<br />

// Durchmesser <strong>de</strong>s Balls<br />

<strong>in</strong>t d = 20;<br />

Thread fa<strong>de</strong>n;<br />

<strong>in</strong>t xAlt, yAlt;<br />

Image backgroundImage;<br />

Graphics backgroundGraphics;<br />

// Metho<strong>de</strong>n<br />

public void <strong>in</strong>it()<br />

{<br />

setBackground(Color.white);<br />

backgroundImage = createImage(this.size().width,<br />

this.size().height);<br />

backgroundGraphics = backgroundImage.getGraphics();<br />

}<br />

public void start()<br />

{<br />

if (fa<strong>de</strong>n == null)<br />

{<br />

fa<strong>de</strong>n = new Thread(this);<br />

fa<strong>de</strong>n.start();<br />

}<br />

}<br />

public void stop()<br />

{<br />

if (fa<strong>de</strong>n != null)<br />

{<br />

fa<strong>de</strong>n.stop();<br />

fa<strong>de</strong>n = null;<br />

}<br />

}<br />

public void run()<br />

{<br />

223 pr32213, Pen<strong>de</strong>l.java<br />

246


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

while (true)<br />

{<br />

x = xStart + (<strong>in</strong>t)(r * Math.s<strong>in</strong>(theta));<br />

y = yStart + (<strong>in</strong>t)(r * Math.cos(theta));<br />

if ((theta >= thetaMax) | (theta


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

3.3.5 Das La<strong>de</strong>n und Anzeigen von Bil<strong>de</strong>rn<br />

Den Umgang mit Bil<strong>de</strong>rn ermöglicht die Klasse Image <strong>de</strong>s Pakets java.awt. In<br />

e<strong>in</strong>em Applet können Metho<strong>de</strong>n <strong>de</strong>r Klassen Applet und Graphics zum La<strong>de</strong>n und<br />

Anzeigen von Bil<strong>de</strong>rn herangezogen wer<strong>de</strong>n. Bil<strong>de</strong>r wer<strong>de</strong>n als seperate Dateien<br />

außerhalb <strong>de</strong>r .class-Dateien von <strong>Java</strong> gespeichert. Falls die Image-Klasse<br />

verwen<strong>de</strong>t wird, muß das Bild im Format .GIF o<strong>de</strong>r .JPG vorliegen.<br />

La<strong>de</strong>n von Bil<strong>de</strong>rn. Es erfolgt mit <strong>de</strong>r Metho<strong>de</strong> getImage()aus <strong>de</strong>r Applet-Klasse,<br />

die mit e<strong>in</strong>em o<strong>de</strong>r zwei Argumenten aufgerufen wer<strong>de</strong>n kann:<br />

- Aufruf von getImage mit e<strong>in</strong>em Argument (e<strong>in</strong> Objekt vom Typ URL 224 )<br />

- Aufruf mit zwei Argumenten (Basis URL <strong>de</strong>s Bilds (URL-Objekt)) und e<strong>in</strong> Str<strong>in</strong>g, <strong>de</strong>r <strong>de</strong>n relativen<br />

Pfad o<strong>de</strong>r <strong>de</strong>n Date<strong>in</strong>amen <strong>de</strong>s aktuellen Bilds angibt.<br />

Die Klasse Applet besitzt zwei Metho<strong>de</strong>n zum Erzeugen e<strong>in</strong>er Basis-URL ohne<br />

Angaben fester Adressen im Programm:<br />

- die Metho<strong>de</strong> getDocumentBase() gibt e<strong>in</strong> URL-Objekt zurück, das <strong>de</strong>n Ordner (das<br />

Verzeichnis) repräsentiert, die die Webseite mit <strong>de</strong>m Applet enthält.<br />

- die Metho<strong>de</strong> getCo<strong>de</strong>Base() gibt e<strong>in</strong> Verzeichnis (Ordner) zurück, das das Verzeichnis<br />

repräsentiert, <strong>in</strong> <strong>de</strong>m sich die .class-Datei <strong>de</strong>r Hauptklasse <strong>de</strong>s Applet bef<strong>in</strong><strong>de</strong>t.<br />

Ausgabe von Bil<strong>de</strong>rn. Mit <strong>de</strong>r Metho<strong>de</strong> drawImage() <strong>de</strong>r Graphics-Klasse kann e<strong>in</strong><br />

Bild, das <strong>in</strong> e<strong>in</strong> Image-Objekt gela<strong>de</strong>n wur<strong>de</strong>, angezeigt wer<strong>de</strong>n. drawImage() hat 4<br />

Argumente:<br />

- das Image-Objekt, das angezeigt wer<strong>de</strong>n soll<br />

- die x- und y-Koord<strong>in</strong>ate<br />

- das Schlüsselwort this<br />

Mit pa<strong>in</strong>t() kann das Bild zur Anzeige gebracht wer<strong>de</strong>n:<br />

public pa<strong>in</strong>t(Graphigs g)<br />

{<br />

g.drawImage(imageObjekt, xKoord, yKoord, this);<br />

}<br />

Bsp. 225 :<br />

import java.awt.*;<br />

import java.applet.*;<br />

public class ZeichneBild extends Applet<br />

{<br />

private Image bild;<br />

public void <strong>in</strong>it()<br />

{<br />

bild = getImage(getDocumentBase(),"B04240900.jpg");<br />

resize(250, 200);<br />

}<br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

<strong>in</strong>t xPos = 10;<br />

g.drawImage(bild,xPos,10,this);<br />

}<br />

}<br />

224 Adressen im World Wi<strong>de</strong> Web wer<strong>de</strong>n durch URL-Objekte repräsentiert. Die Klasse URL (Uniform<br />

Resource Locator) ist Teil <strong>de</strong>s Pakets java.net<br />

225 vgl. pr33501<br />

248


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

3.3.6 Die Ausgabe von Sound<br />

Das JDK bietet Möglichkeiten zur Ausgabe von Sound 226 an. Die Ausgabe von<br />

Sound kann über zwei Metho<strong>de</strong>n <strong>de</strong>r Klasse Applet erfolgen:<br />

public void play(URL url)<br />

public void play(URL url, Str<strong>in</strong>g name)<br />

hier kann entwe<strong>de</strong>r die URL e<strong>in</strong>er Sound-Datei o<strong>de</strong>r die Komb<strong>in</strong>ation von<br />

Verzeichnis-URL und Date<strong>in</strong>amen angegeben wer<strong>de</strong>n. Die Übergabe <strong>de</strong>r URLs<br />

geschieht über die Applet-Metho<strong>de</strong>n:<br />

public URL getCo<strong>de</strong>Base()<br />

public URL getDocumentbase()<br />

Die Metho<strong>de</strong>n liefern e<strong>in</strong>e URL <strong>de</strong>s Verzeichnisses, aus <strong>de</strong>m das Applet gestartet wur<strong>de</strong> bzw. <strong>in</strong> <strong>de</strong>m<br />

die aktuelle HTML-Seite liegt. Der nachteil dieser Vorgehensweise ist, daß die Sound-datei bei je<strong>de</strong>m<br />

Aufruf neu gela<strong>de</strong>n wer<strong>de</strong>n muß.<br />

public getAudioClip(URL url, Str<strong>in</strong>g name)<br />

Hier wird e<strong>in</strong> Objekt <strong>de</strong>r Klasse AudioClip beschafft, das dann abgespielt wer<strong>de</strong>n<br />

kann. AudioClip stellt drei Metho<strong>de</strong>n zur Verfügung:<br />

public void play()<br />

startet die zuvor gela<strong>de</strong>ne Sound-Datei und spielt sie genau e<strong>in</strong>mal ab.<br />

public void loop()<br />

startet die zuvor gela<strong>de</strong>ne Sound-Datei und spielt <strong>de</strong>n Sound <strong>in</strong> e<strong>in</strong>er Endlosschleife<br />

immer wie<strong>de</strong>r ab.<br />

public void stop()<br />

Darüber kann die zuvor mit loop() <strong>in</strong>iziierte Schleife been<strong>de</strong>t wer<strong>de</strong>n.<br />

Bsp. 227 :<br />

import java.net.*;<br />

import java.applet.*;<br />

public class PR33602<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

if (args.length >= 1)<br />

{<br />

try {<br />

URL url = new URL(args[0]);<br />

AudioClip clip = Applet.newAudioClip(url);<br />

clip.play();<br />

try {<br />

Thread.sleep(10000);<br />

}<br />

catch (InterruptedException e)<br />

{}<br />

}<br />

226 Das JDK 1.2 ermöglicht die Soundausgabe <strong>in</strong> Applets und Applikationen. Frühere Versionen gestatten<br />

Soundausgabe nur für Applets. Die Ausgabe war auf Sound beschränkt, die im AU-Format (stammt aus <strong>de</strong>r<br />

Sun-Welt und legt e<strong>in</strong> Sample im Format 8 Bit Mono, Sampl<strong>in</strong>g-Rate 8 kHz, µ -lawKompression ab)<br />

vorliegen mußte. Seit <strong>de</strong>m JDK 1.2 wer<strong>de</strong>n auch die Sample-Formate WAV und AIFF sowie die Midi-Formate<br />

Typ 0 und Typ 1 und RMF unterstützt. Zu<strong>de</strong>m gibt es e<strong>in</strong>ige Shareware- o<strong>de</strong>r Freeware-Tools, die zwischen<br />

verschie<strong>de</strong>n Formaten konvertieren können (z.B. CoolEdit o<strong>de</strong>r Gold-Wave).<br />

227 Vgl. pr33602, Aufruf z.B.: java PR33602 file:///c:/w<strong>in</strong>nt\media\r<strong>in</strong>g<strong>in</strong>.wav<br />

249


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

}<br />

}<br />

}<br />

catch (MalformedURLException e)<br />

{<br />

System.out.pr<strong>in</strong>tln(e.toStr<strong>in</strong>g());<br />

}<br />

3.3.7 Die Klasse JApplet<br />

Diese Klasse bef<strong>in</strong><strong>de</strong>t sich im Paket javax.sw<strong>in</strong>g, gehört aber logisch zu <strong>de</strong>n<br />

Applets. Sie erweitert die Applets um typische Sw<strong>in</strong>g-Eigenschaften.<br />

java.awt.Panel<br />

java.applet.Applet<br />

javax.sw<strong>in</strong>g.JApplet<br />

><br />

public JApplet()<br />

><br />

public void setJMenuBar(JMenuBar menuBar)<br />

public JMenuBar getJMenuBar()<br />

public void setLayout(LayoutManager manager)<br />

public Conta<strong>in</strong>er getContentPane()<br />

public void setContentPane(Conta<strong>in</strong>er contentPane)<br />

....<br />

Abb. Die Klasse JApplet<br />

250


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

3.3.8 Das Interface AppletContext<br />

Die Referenz vom Typ <strong>de</strong>s Interface AppletContext dient vorwiegend zur<br />

Kommunikation zwischen Applet und <strong>de</strong>m Webbrowser bzw. an<strong>de</strong>ren Applets.<br />

><br />

AppletContext<br />

public Applet getApplet(Str<strong>in</strong>g name)<br />

public Enumeration getApplets()<br />

public AudioClip getAudioClip(URL url)<br />

public Image getImage(URL url)<br />

public void showDocument(URL url)<br />

public void showDocument(URL url, Str<strong>in</strong>g target)<br />

public void showStatus(Str<strong>in</strong>g status)<br />

Abb.: Das Interface AppletContext<br />

Mit Hilfe von z.B. getAppletContext().getApplets() kann auf an<strong>de</strong>re Applets<br />

Bezug genommen wer<strong>de</strong>n. Die Metho<strong>de</strong> showDocument() lädt e<strong>in</strong>e Webseite <strong>in</strong> <strong>de</strong>n<br />

Browser, auf <strong>de</strong>m das Applet läuft.<br />

251


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

3.4 Grafische Benutzeroberfläche mit Observable-Observer<br />

Das Zusammenwirken und die Kommunikation zwischen Awendung und grafischer<br />

Benutzeroberfläche sollte folgen<strong>de</strong>rmaßen gestaltet se<strong>in</strong>:<br />

- Anwendung und grafische Benutzeroberfläche sollen möglichst unabhängig vone<strong>in</strong>an<strong>de</strong>r se<strong>in</strong>, <strong>in</strong><br />

je<strong>de</strong>m Fall aber sauber vone<strong>in</strong>an<strong>de</strong>r getrennt se<strong>in</strong> (Anwendungsklasse, GUI-Klasse).<br />

- Je<strong>de</strong>s GUI-Objekt muß se<strong>in</strong>en Anwendungsfall kennen, um <strong>de</strong>sssen Metho<strong>de</strong>n aufrufen zu<br />

können<br />

- Eigentlich müßte umgekehrt je<strong>de</strong>s Anwendungsobjekt „se<strong>in</strong> GUI-Objekt“ kennen, z.B. zur Anzeige<br />

<strong>de</strong>r Anfangswerte<br />

- Das Hauptprogramm „<strong>de</strong>generiert“ zur Erzeugung e<strong>in</strong>es Anwendungs- und GUI-Objekts.<br />

Die Klasse Observable<br />

In <strong>Java</strong> muß das Interesse an e<strong>in</strong>em Objekt durch e<strong>in</strong>e eigene Klasse ausgedrückt<br />

wer<strong>de</strong>n. Die eigene Klasse muß von <strong>de</strong>r Klasse Observable abgeleitet se<strong>in</strong>. Die<br />

Observable-Klasse erlaubt es e<strong>in</strong>em Objekt, an<strong>de</strong>re Objekte zu <strong>in</strong>formieren, wenn es<br />

e<strong>in</strong>e Än<strong>de</strong>rung erfährt.<br />

public class java.util.Observable<br />

{<br />

Observable();<br />

public void addObserver(Observer o);<br />

public void <strong>de</strong>leteObserver(Observer o);<br />

public void <strong>de</strong>leteObservers();<br />

public <strong>in</strong>t countObservers);<br />

protected void setChanged();<br />

portected void clearChanged();<br />

public boolean hasChanged();<br />

public void notifyObservers();<br />

public void notifyObservers(Object arg);<br />

}<br />

Metho<strong>de</strong>n: Die wichtigsten Metho<strong>de</strong>n beim Erzeugen e<strong>in</strong>er Subklasse von<br />

Observable s<strong>in</strong>d „setChanged“ und „notifyObservers“. Die „setChanged“-<br />

Metho<strong>de</strong> markiert, daß Observable verän<strong>de</strong>rt wur<strong>de</strong>. Beim Aufruf von<br />

„notifyObservers“ wer<strong>de</strong>n die Observer benachrichtigt.<br />

protected void setChanged()<br />

setzt e<strong>in</strong> <strong>in</strong>ternes Flag für die Modifikation (wird von „notifyObservers“ verwen<strong>de</strong>t). Es wird<br />

automatisch gelöscht, wenn „notifyObservers“ aufgerufen wird, kann aber auch manuell mit <strong>de</strong>r<br />

„clearChanged“-Metho<strong>de</strong> gelöscht wer<strong>de</strong>n.<br />

protected void clearChanged()<br />

public void notifyObservers()<br />

prüft, ob das changed-Flag gesetzt wur<strong>de</strong>. Ist dies nicht <strong>de</strong>r Fall, kann ke<strong>in</strong>e Mitteilung über e<strong>in</strong>e<br />

Än<strong>de</strong>rung versen<strong>de</strong>t wer<strong>de</strong>n.<br />

public void notifyObservers(Object arg)<br />

Das Argument kann zur Übergabe zusätzlicher Information über die Modifikation dienen. Ohne<br />

Parameter entspricht <strong>de</strong>r Aufruf e<strong>in</strong>em Aufruf mit <strong>de</strong>m Argument Null.<br />

public boolean hasChanged()<br />

stellt fest, ob e<strong>in</strong> Objekt von Interesse modifiziert wur<strong>de</strong>.<br />

public void addObserver(Observer obs)<br />

252


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Mit dieser Metho<strong>de</strong> können Observer ihr Interesse an e<strong>in</strong>em beliebigen Observable bekun<strong>de</strong>n.<br />

public void <strong>de</strong>leteObserver (Observer obs)<br />

Mit dieser Metho<strong>de</strong> kann e<strong>in</strong> Observer die Überwachung <strong>de</strong>r entsprechen<strong>de</strong>n Observables been<strong>de</strong>n<br />

public void <strong>de</strong>leteObservers()<br />

löscht die Liste aller Observer<br />

public <strong>in</strong>t countObservers()<br />

liefert die Anzahl <strong>de</strong>r für <strong>in</strong> Observable registrierten Observer<br />

Das Interface Observer<br />

Je<strong>de</strong> Klasse, die Mitteilungen über Modifikationen e<strong>in</strong>es Observable bekommen<br />

möchte, muß das Observer-Interface implementieren. Dieses Interface besteht aus<br />

e<strong>in</strong>er e<strong>in</strong>zigen Metho<strong>de</strong>:<br />

public abstract void update(Observable obs, Object arg)<br />

Sie wird bei e<strong>in</strong>er Objektmodifikation aufgerufen. „obs“ ist das Observable, das<br />

soeben geän<strong>de</strong>rt wur<strong>de</strong>. Wur<strong>de</strong> „notifyObservers“ ohne Argument aufgerufen,<br />

dann ist arg Null.<br />

Observable-Observer: Kopplung von Anwendung und GUI<br />

Die Klasse Observable 228 erlaubt e<strong>in</strong>em Objekt, Informationen an an<strong>de</strong>re Objekte<br />

zu geben, wenn es e<strong>in</strong>e Än<strong>de</strong>rung erfährt. Beim Erstellen von Benutzeroberflächen<br />

kann es verschie<strong>de</strong>ne Möglichkeiten zur Än<strong>de</strong>rung e<strong>in</strong>es Datenobjekts geben. E<strong>in</strong>e<br />

solche Än<strong>de</strong>rung kann wie<strong>de</strong>rum die Aktualisierung verschie<strong>de</strong>ner Teile e<strong>in</strong>er<br />

Anzeige zur Folge haben.<br />

Bsp.: 229<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

import java.util.*;<br />

class BoxObservable extends Observable<br />

{<br />

public void notifyObservers(Object b)<br />

{<br />

setChanged();<br />

super.notifyObservers(b);<br />

}<br />

}<br />

public class BoxObserver extends Frame<br />

{<br />

Observable notifier = new BoxObservable();<br />

public BoxObserver(<strong>in</strong>t grid)<br />

{<br />

setTitle("Demonstration Observer");<br />

setLayout(new GridLayout(grid,grid));<br />

for (<strong>in</strong>t x = 0; x < grid; x++)<br />

for (<strong>in</strong>t y = 0; y < grid; y++)<br />

add(new OCBox(x, y, notifier));<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

228 Das Konzept für diese Klasse ist e<strong>in</strong>e Leihgabe von Smalltalk und unter <strong>de</strong>m namen Mo<strong>de</strong>l-View-Controller<br />

(MVC) bekannt.<br />

229 pr34100<br />

253


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

{<br />

<strong>in</strong>t grid = 8;<br />

if (args.length > 0)<br />

grid = Integer.parseInt(args[0]);<br />

Frame f = new BoxObserver(grid);<br />

f.setSize(500,400); f.setVisible(true);<br />

f.addW<strong>in</strong>dowListener(<br />

new W<strong>in</strong>dowAdapter()<br />

{<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{ System.exit(0); }<br />

});<br />

}<br />

}<br />

class OCBox extends Canvas<br />

implements Observer<br />

{<br />

Observable notifier;<br />

<strong>in</strong>t x, y;<br />

Color fFarbe = neueFarbe();<br />

static f<strong>in</strong>al Color[] farben = {<br />

Color.black, Color.blue, Color.cyan,<br />

Color.darkGray, Color.gray, Color.green,<br />

Color.lightGray, Color.magenta, Color.orange,<br />

Color.p<strong>in</strong>k, Color.red, Color.white, Color.yellow<br />

};<br />

OCBox(<strong>in</strong>t x, <strong>in</strong>t y, Observable notifier)<br />

{<br />

this.x = x; this.y = y;<br />

notifier.addObserver(this); this.notifier = notifier;<br />

addMouseListener(new ML());<br />

}<br />

static f<strong>in</strong>al Color neueFarbe()<br />

{<br />

return farben[(<strong>in</strong>t) (Math.random() * farben.length)];<br />

}<br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

g.setColor(fFarbe); Dimension d = getSize();<br />

g.fillRect(0, 0, d.width, d.height);<br />

}<br />

class ML extends MouseAdapter<br />

{<br />

public void mousePressed(MouseEvent e)<br />

{ notifier.notifyObservers(OCBox.this); }<br />

}<br />

public void update(Observable o, Object arg)<br />

{<br />

OCBox clicked = (OCBox) arg;<br />

if (nextTo(clicked))<br />

{ fFarbe = clicked.fFarbe; repa<strong>in</strong>t(); }<br />

}<br />

private f<strong>in</strong>al boolean nextTo(OCBox b)<br />

{<br />

return Math.abs(x - b.x)


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

3.5 Sw<strong>in</strong>g<br />

Die grundlegen<strong>de</strong> GUI-Programmierung <strong>in</strong> <strong>Java</strong> wird durch die Klassenbibliothek<br />

AWT (Abstrach W<strong>in</strong>dow Toolkit) ermöglicht. Dabei wer<strong>de</strong>n <strong>in</strong> Wirklichkeit die<br />

graphischen Elemente nicht durch die AWT komplett realisiert, son<strong>de</strong>rn die<br />

Erzeugung wird im wesentlichen an die jeweilige Plattform (W<strong>in</strong>dows, Solaris, Mac)<br />

<strong>de</strong>legiert. Das im H<strong>in</strong>tergrund erzeugte und arbeiten<strong>de</strong> Element wird dann als „Peer“-<br />

Komponente bezeichnet. Die Klassenbibliothek kapselt <strong>de</strong>n Zugriff auf diese<br />

Elemente und erleichtert damit auch teilweise die Handhabung e<strong>in</strong>zelner Objekte.<br />

1996 erstellte Netscape e<strong>in</strong>e GUI-Bibliothek „Internet Foundation Classes“ mit<br />

komplett an<strong>de</strong>rem E<strong>in</strong>satz. Die Peer-Funktionalität wird auf e<strong>in</strong> M<strong>in</strong>imum reduziert:<br />

E<strong>in</strong> Fenster zu erstellen, darauf zu zeichnen und dieses wie<strong>de</strong>r zu schliessen.<br />

Dadurch wird e<strong>in</strong>e geme<strong>in</strong>same Basis für alle Elemente <strong>de</strong>r Benutzeroberfläche<br />

geschaffen, auf <strong>de</strong>r aufbauend alle GUI-Elemente wie Schaltflächen, Listen, Menüs<br />

usw. gezeichnet wer<strong>de</strong>n konnten. Die komplette Funktionalität muß dann für alle<br />

Komponenten „nachgebaut“ wer<strong>de</strong>n, mit <strong>de</strong>m Vorteil e<strong>in</strong>er e<strong>in</strong>heitlchen,<br />

plattformübergreifen<strong>de</strong>n Lösung. Diese Lösung wur<strong>de</strong> geme<strong>in</strong>sam von Netscape und<br />

Sun ausgebaut und verbessert, wodurch e<strong>in</strong>e nicht peerbasierte GUI-Bibliothek als<br />

Teil <strong>de</strong>r <strong>Java</strong> Foundation Classes (JFC) für die Benutzeroberfläche mit <strong>de</strong>m Namen<br />

Sw<strong>in</strong>g entstan<strong>de</strong>n ist.<br />

Sw<strong>in</strong>g 230 ist e<strong>in</strong>e Erweiterung <strong>de</strong>s Abstract W<strong>in</strong>dow<strong>in</strong>g Toolkit, vollständig <strong>in</strong><br />

<strong>Java</strong> 2 <strong>in</strong>tegriert und bietet e<strong>in</strong>e wesentlich verbesserte Funktionalität. Alle Elemente<br />

von Sw<strong>in</strong>g s<strong>in</strong>d Bestandteile <strong>de</strong>s Pakets javax.sw<strong>in</strong>g, die über import<br />

javax.sw<strong>in</strong>g.* <strong>in</strong> Anwendungen e<strong>in</strong>bezogen wer<strong>de</strong>n kann.<br />

Die grafische Benutzeroberfläche für <strong>Java</strong> Programme "Sw<strong>in</strong>g" umfasst die<br />

folgen<strong>de</strong>n drei wesentlichen Konzepte:<br />

- Komponenten (Schnittstellenelemente wie z.B. Buttons, Menüs, ...). Sw<strong>in</strong>g-Komponenten wer<strong>de</strong>n<br />

auf gleiche Weise verwen<strong>de</strong>t wie die Komponenten <strong>de</strong>s AWT: Erzeugen e<strong>in</strong>er Komponente über <strong>de</strong>n<br />

Konstruktor, Aufruf <strong>de</strong>r Metho<strong>de</strong>n <strong>de</strong>r Komponente.<br />

- Conta<strong>in</strong>er (Behälter, die mit Komponenten gefüllt wer<strong>de</strong>n, z.B. Fenster, Dialoge, Panels,...).<br />

- Events (Nachrichten über Benutzeraktionen mit Maus, Tastatur bzw. Verän<strong>de</strong>rung von<br />

Komponenten o<strong>de</strong>r Systemstatus (Zeit<strong>in</strong>tervall vergangen, Netzwerkanfragen, ...).<br />

230 vgl. 5.6<br />

255


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

3.6 <strong>Java</strong> Beans<br />

3.6.1 Wie<strong>de</strong>rverwendbare Softwarekomponenten<br />

Unter e<strong>in</strong>er Software-Komponente 231 versteht man:<br />

E<strong>in</strong>e Komponente ist e<strong>in</strong> Stück Software, das kle<strong>in</strong> genug ist, um es <strong>in</strong> e<strong>in</strong>em Stück zu erzeugen und<br />

pflegen zu können, groß genug ist, um e<strong>in</strong>e s<strong>in</strong>nvoll e<strong>in</strong>setzbare Funktionalität zu bieten und e<strong>in</strong>e<br />

<strong>in</strong>dividuelle Unterstützung zu rechtfertigen, sowie mit standardisierten Schnittstelle ausgestattet ist,<br />

um mit an<strong>de</strong>ren Komponenten zusammen zu arbeiten.<br />

Zu <strong>de</strong>n grundlegen<strong>de</strong>n Eigenschaften e<strong>in</strong>er Softwarekomponente zählen:<br />

- e<strong>in</strong>e Softwarekomponente ist e<strong>in</strong> ausführbares Programm<br />

- sie realisiert e<strong>in</strong>e klar abgegrenzte Programmfunktionalität (z.B. e<strong>in</strong> GUI-Control o<strong>de</strong>r e<strong>in</strong><br />

Berechnungsmodul)<br />

- ihre Implementierung ist nach außen nicht transparent (black-box-Mo<strong>de</strong>ll)<br />

- sie verfügt über öffentliche Schnittstellen, über die auf die Funktionalität <strong>de</strong>r Komponente zugegriffen<br />

wer<strong>de</strong>n kann (Eigenschaften, Metho<strong>de</strong>n, Ereignisse).<br />

- e<strong>in</strong>e Softwarekomponente lässt sich <strong>in</strong> umfangreichere Programme <strong>in</strong>tegrieren<br />

- im Unterschied zu Objekten s<strong>in</strong>d Komponenten auf e<strong>in</strong>e abstrakten Ebene angesie<strong>de</strong>lt und nur lose<br />

mite<strong>in</strong>an<strong>de</strong>t verkoppelt. Das soll vor allem die e<strong>in</strong>fache Wie<strong>de</strong>rverwendbarkeit von Komponenten<br />

gewährleisten<br />

Bisher gibt es ke<strong>in</strong>e e<strong>in</strong>heitliche Entwicklungsmetho<strong>de</strong> für component ware. Es<br />

existieren e<strong>in</strong>e Reihe konkurrieren<strong>de</strong>r Komponentenarchitekturen, u.a.<br />

- Microsoft ActiveX/COM/DCOM als Weiterentwicklung von OLE(object l<strong>in</strong>k<strong>in</strong>g and embedd<strong>in</strong>g)<br />

- <strong>Java</strong> Beans<br />

- CORBA Bus<strong>in</strong>ess Objects<br />

Sie s<strong>in</strong>d z.T. untere<strong>in</strong>an<strong>de</strong>r <strong>in</strong>teroperable, d.h. e<strong>in</strong> <strong>Java</strong> Beans kann als CORBAfähiges<br />

Objekt ausgestattet se<strong>in</strong> o<strong>de</strong>r im Rahmen von ActiveX e<strong>in</strong>gesetzt wer<strong>de</strong>n<br />

(über e<strong>in</strong>e ActiveX-Bridge, die das Bean als Actve-X-Komponente verpackt).<br />

Für die Komponentenentwicklung von e<strong>in</strong>zelnen Beans stehen folgen<strong>de</strong><br />

Funktionalitäten bereit:<br />

- Eigenschaften (properties) und Anpassung (customization) von Komponenten<br />

- Ereignisverarbeitung, um unterschiedliche Komponenten mite<strong>in</strong>an<strong>de</strong>r zu verknüpfen und sie<br />

kommunizieren zu lassen<br />

- Introspektion (<strong>in</strong>trospection), so dass e<strong>in</strong> Entwicklungswerkzeug erkennen kann, wie e<strong>in</strong> Bean<br />

arbeitet.<br />

- Persistenz und Verpacken von Komponenten, so dass <strong>in</strong> e<strong>in</strong>em Entwicklungswerkzeug angepasstes<br />

bzw. modifiziertes Bean <strong>in</strong> e<strong>in</strong>em neuen Zustand gespeichert wer<strong>de</strong>n kann.<br />

Die wichtigsten Merkmale e<strong>in</strong>es Beans s<strong>in</strong>d:<br />

- se<strong>in</strong>e Eigenschaften (properties)<br />

- se<strong>in</strong>e Metho<strong>de</strong>n, die von an<strong>de</strong>ren Komponenten aufgerufen wer<strong>de</strong>n können.<br />

- Ereignisse / Nachrichen, die es auslöst.<br />

231 Die Def<strong>in</strong>ition wur<strong>de</strong> zitiert nach: Griffel, Frank: Componentenware – Konzepte und Techniken e<strong>in</strong>es<br />

Softwareparadigma, dpunkt-Verlag, 1998<br />

256


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

3.6.2 Komponenten für <strong>Java</strong>: Beans<br />

<strong>Java</strong>Beans ist das Komponentenmo<strong>de</strong>ll von <strong>Java</strong>. Diese Komponenten haben e<strong>in</strong>e<br />

genauere und für Programmierwerkzeuge verständlichere Schnittstellen<strong>de</strong>f<strong>in</strong>tion als<br />

<strong>Java</strong>-Klassen. Die zur Beschreibung und Analyse benötigten Interfaces und Klassen<br />

stehen im Package java.beans. Sie arbeiten sehr eng mit <strong>de</strong>n Klassen zur<br />

Laufzeitanalyse von <strong>Java</strong>-Klassen im Paket java.lang.reflect zusammen. Die<br />

Schnittstellen von <strong>Java</strong>Beans bezeichnet man als Features. Sie besteht aus drei<br />

Teilen:<br />

<strong>Java</strong>Beans<br />

Properties Metho<strong>de</strong>n Ereignisse<br />

Abb.: Die <strong>Java</strong>Bean-Features<br />

1. Properties s<strong>in</strong>d die öffentlichen, mit Namen versehenen Attribute o<strong>de</strong>r Eigenschaften. Über diese<br />

Properties s<strong>in</strong>d Beans an die Vorstellungen <strong>de</strong>s Programmierers anpassbar, ohne daß er er <strong>de</strong>n<br />

Quellco<strong>de</strong> än<strong>de</strong>rn und darauf irgen<strong>de</strong><strong>in</strong> Zugriff haben muß. Die Implementierung von Properties<br />

erfolgt durch entsprechen<strong>de</strong> lesen<strong>de</strong> und schreiben<strong>de</strong> Metho<strong>de</strong>n, die e<strong>in</strong>er Namenskonvention<br />

(„Design-Pattern“ 232 ) gehorchen. Die <strong>Java</strong>-Beans-Architektur unterschei<strong>de</strong>t zwischen sog.<br />

In<strong>de</strong>xed, Bound und Constra<strong>in</strong>t Properties.<br />

2. Die öffentlichen Metho<strong>de</strong>n, die e<strong>in</strong>e Bean ausführen kann<br />

3. E<strong>in</strong> <strong>Java</strong>Bean erzeugt Ereignisse, z.B. dann, wenn sich ihre Properties än<strong>de</strong>rn.<br />

Diese drei Arten von Features s<strong>in</strong>d im Interface BeanInfo dokumentiert.<br />

Das Paket java.beans enthält folgen<strong>de</strong> Klassen, Schnittstellen und Ausnahmen:<br />

AppletInitalizer<br />

BeanInfo<br />

Customizer<br />

DesignMo<strong>de</strong><br />

PropertyChangeListener<br />

PropertyEditor<br />

VetoableChangeListener<br />

Visibility<br />

Metho<strong>de</strong>n für die Initialisierung von Beans, die auch Applets s<strong>in</strong>d<br />

Metho<strong>de</strong>n für die Beschreibung e<strong>in</strong>es Beans, z.B.<br />

getBeanDescriptor()<br />

Schnittstelle für Klassen, die <strong>de</strong>r Anpassung von Beans dienen<br />

(Bean-Editoren, Bean-Customizer)<br />

Metho<strong>de</strong>n, die feststellen, ob sich e<strong>in</strong> Bean <strong>in</strong> e<strong>in</strong>er Design- o<strong>de</strong>r<br />

Lauffzeitumgebung bef<strong>in</strong><strong>de</strong>t<br />

Lauscherschnittstelle, die Eigenschaftsän<strong>de</strong>rungsnachrichten<br />

abfängt<br />

Metho<strong>de</strong>n für Klassen, die das Editieren von Beans bzw. ihrer<br />

Eigenschaften erlauben.<br />

Lauscherschnittstelle für sog. "constra<strong>in</strong>t properties", d.h.<br />

Eigenschaften <strong>de</strong>ren Än<strong>de</strong>rung von "externen Faktoren" abhängt,<br />

d.h. gegen <strong>de</strong>ren Än<strong>de</strong>rung von an<strong>de</strong>ren Komponenten e<strong>in</strong> Veto<br />

e<strong>in</strong>gelegt wer<strong>de</strong>n kann.<br />

Metho<strong>de</strong>n zur Feststellung, ob e<strong>in</strong> Bean <strong>in</strong> e<strong>in</strong>em GUI ausgeführt<br />

wird.<br />

Abb.: Schnittstellen <strong>in</strong> java.beans<br />

232 Unter Design Pattern s<strong>in</strong>d e<strong>in</strong>fache Muster auf <strong>de</strong>r Basis von Metho<strong>de</strong>nnamen und Signaturen geme<strong>in</strong>t, z.B.<br />

- E<strong>in</strong> Property mit <strong>de</strong>m Namen Color, für das sowohl schreiben<strong>de</strong> als auch lesen<strong>de</strong> Zugriffe erlaubt s<strong>in</strong>d wird<br />

durch Implementierung <strong>de</strong>r Metho<strong>de</strong>n „public Color getColor()“ und „public void setColor()“ dargestellt.<br />

- Das Entwurfsmuster für das Verb<strong>in</strong><strong>de</strong>n e<strong>in</strong>es Event-Handlers mit e<strong>in</strong>er Event-Source besteht aus e<strong>in</strong>er<br />

Metho<strong>de</strong>, die mit „set“ beg<strong>in</strong>nt und mit „Listener“ en<strong>de</strong>t und e<strong>in</strong> Argument besitzt. Außer<strong>de</strong>m muß e<strong>in</strong>e<br />

Vererbung vom EventListener-Interface vorhan<strong>de</strong>n se<strong>in</strong>.<br />

257


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

BeanDescriptor<br />

Beans<br />

EventSetDescriptor<br />

FeatureDescriptor<br />

Introspector<br />

MethodDescriptor<br />

ParameterDescriptor<br />

PropertyChangeEvent<br />

PropertyChangeSupport<br />

PropertyDescriptor<br />

PropertyEditorManager<br />

PropertyEditorSupport<br />

SimpleBeanInfo<br />

VetoableChangeSupport<br />

Klasse für <strong>de</strong>n Zugriff auf allgeme<strong>in</strong>e Eigenschaften e<strong>in</strong>er<br />

Komponente<br />

Hilfsklasse mit nützlichen Metho<strong>de</strong>n um Informationen über e<strong>in</strong><br />

Bean zu erhalten (isDesignTime(), isGuiAvailable(), etc.)<br />

beschreibt Ereignistypen, die von e<strong>in</strong>em Bean ausgelöst wer<strong>de</strong>n.<br />

Superklasse aller Deskriptor-Klassen für Beans mit generischen<br />

Informationsmetho<strong>de</strong>n (z.B. getNames(), attributeNames(),<br />

getValues(), etc.).<br />

Zugriff auf Beaneigenschaften zur Design-Zeit durch Metho<strong>de</strong>n wie<br />

z.B. getBeanInfo().<br />

beschrebt e<strong>in</strong>e Mretho<strong>de</strong> e<strong>in</strong>es <strong>Java</strong> Bean, die von außen<br />

zugänglich ist<br />

zusätzliche Information über Parameter (d.h. mehr als das, was<br />

man über die reflection-Metho<strong>de</strong>n ermitteln kann<br />

Nachrichtenklasse für die Verarbeitung von Eigenschaftsän<strong>de</strong>rungsachrichten<br />

Hilfsklasse für die Verarbeitung von Eigenschaftsän<strong>de</strong>rungsnachrichten<br />

beschreibt die Eigenschaft e<strong>in</strong>es Bean, die von außen zugänglich<br />

ist.<br />

ist beim Aufbau e<strong>in</strong>es geeigneten Editor für e<strong>in</strong> Bean behilflich<br />

Hilfsklasse für <strong>de</strong>n Aufbau von Editoren<br />

Hilfsklasse für die Generierung bon BeanInfo-Objekten<br />

Hilfsklasse für die Verarbeitung von Eigenschaften, gegen die e<strong>in</strong><br />

Veto möglich ist.<br />

Abb.: Klassen <strong>in</strong> java.beans<br />

IntrospectionException<br />

PropertyVetoException<br />

tritt bei Fehlern während <strong>de</strong>r Introspektion e<strong>in</strong>es Beans auf<br />

tritt auf, falls e<strong>in</strong>e Eigenschaft e<strong>in</strong>es Bean auf e<strong>in</strong>en nicht<br />

akzeptablen Wert geän<strong>de</strong>rt wer<strong>de</strong>n soll<br />

Abb.: Ausnahmen <strong>in</strong> java.beans<br />

3.6.3 Enterprise <strong>Java</strong> Beans<br />

„<strong>Java</strong> Beans“ wur<strong>de</strong> als e<strong>in</strong> lokales Komponentenmo<strong>de</strong>ll 233 entworfen. Das <strong>Java</strong>-<br />

Beans-Mo<strong>de</strong>ll sieht aber auch die Nutzung entfernter Ressourcen vor: Enterprise<br />

<strong>Java</strong> Beans. Damit wer<strong>de</strong>n Anb<strong>in</strong>dungen zur folgen<strong>de</strong>n Application Interfaces<br />

möglich:<br />

- <strong>Java</strong> Database Connectivity -API: Es gestattet <strong>de</strong>n Zugriff auf relationale Datenbanken.<br />

- Remote Method Invocation: RMI stellt e<strong>in</strong> API zur Verfügung, mit <strong>de</strong>ssen Hilfe die Kommunikation<br />

zweier Komponenten über Adreß- o<strong>de</strong>r Masch<strong>in</strong>enräume h<strong>in</strong>weg möglich ist. RMI ist e<strong>in</strong>e Art<br />

objektorientierter RPC-Mechanismus (Remote Procedure Call), <strong>de</strong>r speziell für <strong>Java</strong> entwickelt<br />

wur<strong>de</strong>.<br />

- CORBA<br />

233 D.h.: Die Kommunikation zwischen Komponenten f<strong>in</strong><strong>de</strong>t auf e<strong>in</strong>er Masch<strong>in</strong>e statt.<br />

258


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

4. Grafik und Bildverarbeitung<br />

Mit e<strong>in</strong>er Instanz von Graphics kann gezeichnet wer<strong>de</strong>n, z.B.:<br />

Graphics me<strong>in</strong>eGrafik;<br />

me<strong>in</strong>eGrafik = getGraphics();<br />

me<strong>in</strong>eGrafik.drawStr<strong>in</strong>g("mache irgendwas",20,40);<br />

Die <strong>in</strong> <strong>de</strong>r Klasse Component als public Graphics getGraphics() <strong>de</strong>f<strong>in</strong>ierte<br />

Metho<strong>de</strong> gibt <strong>de</strong>n „Graphics“-Kontext von <strong>de</strong>r Komponente zurück bzw. Null, wenn<br />

die Komponente ke<strong>in</strong>en aktuellen Grafikbezug hat. Bei je<strong>de</strong>r Zeichenoperation muß<br />

<strong>de</strong>r grafische Kontext angegeben wer<strong>de</strong>n, <strong>de</strong>nn dieses Objekt verwaltet:<br />

- die Komponente, auf <strong>de</strong>r zu zeichnen ist.<br />

- Koord<strong>in</strong>aten <strong>de</strong>s Bildbereichs und <strong>de</strong>s Clipp<strong>in</strong>g-Bereichs.<br />

- <strong>de</strong>r aktuelle Clip-Bereich und Font, die aktuelle Farbe<br />

- die Pixeloperation (XOR o<strong>de</strong>r Pa<strong>in</strong>t)<br />

- Composite (Überschreibmodus/Alphakanal)<br />

Die meisten Zeichenvorgänge wer<strong>de</strong>n <strong>in</strong> <strong>de</strong>r pa<strong>in</strong>t()-Metho<strong>de</strong> durchgeführt. <strong>Java</strong>-<br />

Applets zeichnen sich selbst neu nach Überschreiben <strong>de</strong>r pa<strong>in</strong>t()-Metho<strong>de</strong>. Es gibt<br />

drei verschie<strong>de</strong>ne Metho<strong>de</strong>n 234 zum Neuzeichnen:<br />

public void pa<strong>in</strong>t(Graphics g)<br />

public void repa<strong>in</strong>t()<br />

public void update(Graphics g)<br />

E<strong>in</strong> Graphics-Objekt erzeugt nicht <strong>de</strong>r Entwickler, son<strong>de</strong>rn er bekommt <strong>de</strong>n Kontext<br />

gestellt, auf <strong>de</strong>m er zeichnen möchte. Die Klasse Graphics hat ke<strong>in</strong>en öffentlichen<br />

Konstruktor.<br />

234 vgl. 3.2.2<br />

259


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

.........<br />

Graphics<br />

{ abstract }<br />

><br />

protected Graphics()<br />

><br />

public abstract Graphics create()<br />

public Graphics create(<strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t width, <strong>in</strong>t height)<br />

public abstract void translate(<strong>in</strong>t x, <strong>in</strong>t y)<br />

public abstract Color getColor()<br />

public abstract void setColor(Color farbe)<br />

public abstract void setXORMo<strong>de</strong>(Color farbe)<br />

public abstract Font getFont()<br />

public abstract void setFont(Font f)<br />

public FontMetrics getFontMetrics()<br />

public abstract Rectangle getClipBounds()<br />

public abstract void clipRect(<strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t width, <strong>in</strong>t height)<br />

public abstract void setClip(<strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t width, <strong>in</strong>t height)<br />

public abstract Shape getClip()<br />

public abstract void drawL<strong>in</strong>e(<strong>in</strong>t x1, <strong>in</strong>t y1, <strong>in</strong>t x2, <strong>in</strong>t y2)<br />

public abstract fillRect(<strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t width, <strong>in</strong>t height)<br />

public void drawRect(<strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t width, <strong>in</strong>t height)<br />

public abstract void clearRect(<strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t width, <strong>in</strong>t height)<br />

public abstract void drawRoundRect(<strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t width, <strong>in</strong>t height, <strong>in</strong>t arcWidth, <strong>in</strong>t arcHeight)<br />

public abstract fillRoundRect(<strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t width, <strong>in</strong>t height, <strong>in</strong>t arcWidth, <strong>in</strong>t arcHeight)<br />

public abstract void drawOval(<strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t width, <strong>in</strong>t height)<br />

public abstract void fillOval(<strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t width, <strong>in</strong>t height)<br />

public abstract void drawArc(<strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t width, <strong>in</strong>t height, <strong>in</strong>t startAngle, <strong>in</strong>t arcAngle)<br />

public abstract void drawStr<strong>in</strong>g(Str<strong>in</strong>g str, <strong>in</strong>t x, <strong>in</strong>t y)<br />

public abstract boolean drawImage(Image bild, <strong>in</strong>t x, <strong>in</strong>t y, ImageObserver observer)<br />

Abb.: Die Klasse Graphics<br />

4.1 Allgeme<strong>in</strong>e Zeichenvorgänge<br />

4.1.1 Punkte, L<strong>in</strong>ien, Kreise, Bögen, Polygone<br />

Das Koord<strong>in</strong>atensystem. Der Ausgangspunkt (0,0) <strong>de</strong>s <strong>Java</strong>-Koord<strong>in</strong>atensystems<br />

ist die obere l<strong>in</strong>ke Ecke. Von dieser Stelle führen positive x-Werte nach rechts und<br />

positive y-Werte nach unten. Die Angaben <strong>de</strong>r Koord<strong>in</strong>aten erfolgen <strong>in</strong> Pixel. Alle<br />

Pixelwerte s<strong>in</strong>d Ganzzahlen.<br />

Punkte. E<strong>in</strong> Punkt ist durch zwei o<strong>de</strong>r mehr Koord<strong>in</strong>aten gekennzeichnet. In <strong>Java</strong> gibt<br />

es ke<strong>in</strong>e Funktion, mit <strong>de</strong>r Punkte gezeichnet wer<strong>de</strong>n. Diese können nur durch<br />

L<strong>in</strong>iebefehle erzeugt wer<strong>de</strong>n.<br />

Zeichnen e<strong>in</strong>er L<strong>in</strong>ie. Es geschieht mit <strong>de</strong>r Metho<strong>de</strong>: public abstract void<br />

drawL<strong>in</strong>e(<strong>in</strong>t x1, <strong>in</strong>t y1, <strong>in</strong>t x2, <strong>in</strong>t y2). (x1,y1) bestimmt <strong>de</strong>n<br />

Anfangspunkt, (x2,y2) bestimmt <strong>de</strong>n Endpunkt <strong>de</strong>r L<strong>in</strong>ie.<br />

260


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Bsp.: E<strong>in</strong> Applet mit zufällig verteilten L<strong>in</strong>ien 235<br />

import java.awt.*;<br />

// Top Level Deklaration <strong>de</strong>s Applets<br />

public class L<strong>in</strong>ienApplet extends java.applet.Applet<br />

implements Runnable<br />

{<br />

// Variablen-Deklaration<br />

<strong>in</strong>t x1 = 0;<br />

<strong>in</strong>t x2 = 0;<br />

<strong>in</strong>t y1 = 0;<br />

<strong>in</strong>t y2 = 0;<br />

float rot, gruen, blau;<br />

Color l<strong>in</strong>ienFarbe;<br />

// Eigene Metho<strong>de</strong>n<br />

// Metho<strong>de</strong>n, die ueberschrieben wer<strong>de</strong>n<br />

public void <strong>in</strong>it()<br />

{<br />

setBackground(Color.lightGray);<br />

}<br />

public void start()<br />

{<br />

if (fa<strong>de</strong>n == null)<br />

{<br />

fa<strong>de</strong>n = new Thread(this);<br />

fa<strong>de</strong>n.start();<br />

}<br />

}<br />

public void stop()<br />

{<br />

if (fa<strong>de</strong>n != null)<br />

{<br />

fa<strong>de</strong>n.stop();<br />

fa<strong>de</strong>n = null;<br />

}<br />

}<br />

public void run()<br />

{<br />

// Ausfuehrung <strong>de</strong>s Applet<br />

while (true)<br />

{<br />

x1 = (<strong>in</strong>t) (Math.random() * this.size().width);<br />

x2 = (<strong>in</strong>t) (Math.random() * this.size().width);<br />

y1 = (<strong>in</strong>t) (Math.random() * this.size().height);<br />

y2 = (<strong>in</strong>t) (Math.random() * this.size().height);<br />

rot = (float) Math.random();<br />

gruen = (float) Math.random();<br />

blau = (float) Math.random();<br />

l<strong>in</strong>ienFarbe = new Color(rot,gruen,blau);<br />

repa<strong>in</strong>t(); // Aufruf <strong>de</strong>r repa<strong>in</strong>t()-Metho<strong>de</strong><br />

try {fa<strong>de</strong>n.sleep(1000); } // Pause von 1000 Millisekun<strong>de</strong>n<br />

catch(InterruptedException e) {}<br />

}<br />

}<br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

g.setColor(l<strong>in</strong>ienFarbe);<br />

g.drawL<strong>in</strong>e(x1,y1,x2,y2);<br />

}<br />

}<br />

Die Metho<strong>de</strong> drawL<strong>in</strong>e zeichnet L<strong>in</strong>ien mit e<strong>in</strong>er Dicke von e<strong>in</strong>em Pixel.<br />

235 vgl. pr42005<br />

261


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Zeichnen e<strong>in</strong>es Rechtecks. Dafür gibt es die Metho<strong>de</strong>: public void<br />

drawRect(<strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t width, <strong>in</strong>t height). (x1,y1) bestimmt die<br />

obere l<strong>in</strong>ke Ecke e<strong>in</strong>es Rechtecks, (width, height) legen Breite und Höhe <strong>de</strong>s<br />

Rechtecks fest.<br />

Zeichnen e<strong>in</strong>es gefüllten Rechtecks. Es wird ermöglicht durch die Metho<strong>de</strong>: public<br />

abstract void fillRect(<strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t width, <strong>in</strong>t height). Die<br />

Farbe, mit <strong>de</strong>r das Rechteck gefüllt wer<strong>de</strong>n soll, kann mit <strong>de</strong>r folgen<strong>de</strong>n Metho<strong>de</strong><br />

gesetzt wer<strong>de</strong>n: public abstract void setColor(Color c).<br />

Bsp.: E<strong>in</strong> Applet mit zufällig verteilten Rechtecken 236<br />

// zeichne Rechtecke<br />

import java.applet.*;<br />

import java.awt.*;<br />

public class RechteckeAppl3 extends Applet<br />

{<br />

// Instanzvariable<br />

<strong>in</strong>t appletHoehe;<br />

<strong>in</strong>t appletBreite;<br />

<strong>in</strong>t rechteckHoehe;<br />

<strong>in</strong>t rechteckBreite;<br />

<strong>in</strong>t rechteckTop;<br />

<strong>in</strong>t rechteckL<strong>in</strong>ks;<br />

Color rechteckFarbe;<br />

<strong>in</strong>t anzRechtecke = 100;<br />

// Metho<strong>de</strong>n<br />

public void <strong>in</strong>it()<br />

{<br />

Dimension d = size();<br />

appletHoehe = d.height;<br />

appletBreite = d.width;<br />

repa<strong>in</strong>t();<br />

}<br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

setBackground(Color.white);<br />

g.setColor(Color.black);<br />

g.drawRect(0,0,appletBreite – 1,appletHoehe – 1);<br />

for (<strong>in</strong>t i = 0; i < anzRechtecke; i++)<br />

{<br />

rechteckTop = bestimmeZufallszahl(appletHoehe);<br />

rechteckL<strong>in</strong>ks = bestimmeZufallszahl(appletBreite);<br />

rechteckHoehe = bestimmeZufallszahl(<br />

appletHoehe - rechteckTop);<br />

rechteckBreite = bestimmeZufallszahl(<br />

appletBreite – rechteckL<strong>in</strong>ks);<br />

rechteckFarbe = new Color(bestimmeZufallszahl(255),<br />

bestimmeZufallszahl(255),<br />

bestimmeZufallszahl(255));<br />

g.setColor(rechteckFarbe);<br />

g.fillRect(rechteckL<strong>in</strong>ks,rechteckTop,rechteckBreite-1,<br />

rechteckHoehe – 1);<br />

}<br />

}<br />

private <strong>in</strong>t bestimmeZufallszahl(<strong>in</strong>t bereich)<br />

{<br />

double ausgangsgroesse;<br />

ausgangsgroesse = Math.random();<br />

return (<strong>in</strong>t) (ausgangsgroesse * bereich);<br />

}<br />

}<br />

236 vgl. pr41103<br />

262


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Löschen e<strong>in</strong>es Rechtecks. Das übernimmt die Metho<strong>de</strong> public abstract void<br />

clearRect(<strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t width, <strong>in</strong>t height).<br />

Kopieren e<strong>in</strong>es Rechtecks. Dafür gibt es die Metho<strong>de</strong> public abstract void<br />

copyArea(<strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t width, <strong>in</strong>t height, <strong>in</strong>t dx, <strong>in</strong>t dy).<br />

Zeichnen e<strong>in</strong>es 3D-Rechtecks. Es erfolgt mit Hilfe <strong>de</strong>r Metho<strong>de</strong> public void<br />

draw3DRect(<strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t width, <strong>in</strong>t height, boolean<br />

raised).<br />

Zeichnen e<strong>in</strong>es gefüllten 3D-Rechtecks.<br />

Zeichnen abgerun<strong>de</strong>ter Rechtecke. Sie können gezeichnet wer<strong>de</strong>n mit public<br />

abstract void drawRoundRect(<strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t width, <strong>in</strong>t<br />

height, <strong>in</strong>t arcWidth, <strong>in</strong>t arcHeight). „arcWidth“ bestimmt <strong>de</strong>n W<strong>in</strong>kel<br />

<strong>de</strong>r Abrundung auf <strong>de</strong>r horizontalen, „arcHeight“ <strong>de</strong>n W<strong>in</strong>kel auf <strong>de</strong>r vertikalen<br />

Ebene. Je größer die W<strong>in</strong>kel s<strong>in</strong>d, <strong>de</strong>sto stärker gerun<strong>de</strong>t ersche<strong>in</strong>t das Rechteck.<br />

Zeichnen abgerun<strong>de</strong>ter, gefüllter Rechtecke. Dafür existiert die Metho<strong>de</strong> public<br />

abstract void fillRoundRect(<strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t width, <strong>in</strong>t<br />

height, <strong>in</strong>t arcWidth, <strong>in</strong>t arcHeight).<br />

Zeichnen von Polygonen. Hierfür kann die Metho<strong>de</strong> public abstract void<br />

drawPolygon(<strong>in</strong>t[] xPunkte, <strong>in</strong>t[] yPunkte, <strong>in</strong>t nPunkte)<br />

herangezogen wer<strong>de</strong>n. Es gibt zwei Möglichkeiten beim Zeichnen von Polygonen:<br />

- Weitergabe <strong>de</strong>r bei<strong>de</strong>n Datenbereiche (Arrays) mit <strong>de</strong>n x- und y-Koord<strong>in</strong>aten <strong>de</strong>r Punkte, z.B. 237 :<br />

import java.awt.*;<br />

public class ZeichnePolyAppl1 extends java.applet.Applet<br />

{<br />

// Instanzvariable<br />

// Def<strong>in</strong>ition <strong>de</strong>s Felds mit <strong>de</strong>n x-Koord<strong>in</strong>aten<br />

<strong>in</strong>t xKoord[] = {20,50,70,40,20,20};<br />

// Def<strong>in</strong>ition <strong>de</strong>s Felds mit <strong>de</strong>n y-Koord<strong>in</strong>aten<br />

<strong>in</strong>t yKoord[] = {30,10,20,70,50,30};<br />

// Metho<strong>de</strong>n<br />

public void <strong>in</strong>it() { setBackground(Color.yellow); }<br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

// Zeichne e<strong>in</strong> 5-Eck<br />

g.setColor(Color.red);<br />

g.drawPolygon(xKoord,yKoord,6);<br />

}<br />

}<br />

- Weitergabe e<strong>in</strong>er Instanz <strong>de</strong>r Polygon-Klasse, z.B. 238 :<br />

import java.awt.*;<br />

public class ZeichnePolyAppl2 extends java.applet.Applet<br />

{<br />

// Instanzvariable<br />

// Def<strong>in</strong>ition <strong>de</strong>s Felds mit <strong>de</strong>n x-Koord<strong>in</strong>aten<br />

<strong>in</strong>t xKoord[] = {20,50,70,40,20,20};<br />

// Def<strong>in</strong>ition <strong>de</strong>s Felds mit <strong>de</strong>n y-Koord<strong>in</strong>aten<br />

<strong>in</strong>t yKoord[] = {30,10,20,70,50,30};<br />

// Anzahl Ecken<br />

<strong>in</strong>t anzEcken = xKoord.length;<br />

// Metho<strong>de</strong>n<br />

public void <strong>in</strong>it() { setBackground(Color.yellow); }<br />

public void pa<strong>in</strong>t(Graphics g)<br />

237 pr41105<br />

238 pr41105<br />

263


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

{<br />

// Zeichne e<strong>in</strong> 5-Eck<br />

g.setColor(Color.red);<br />

Polygon poly = new Polygon(xKoord,yKoord, anzEcken);<br />

g.drawPolygon(poly);<br />

}<br />

}<br />

Das Zeichnen von gefüllten Polygonen. Dazu dient die Metho<strong>de</strong> public abstract<br />

void fillPolygon(<strong>in</strong>t[] xPunkte, <strong>in</strong>t[] yPunkte, <strong>in</strong>t nPunkte).<br />

Bsp. 239 :<br />

import java.awt.*;<br />

public class ZeichnePolyAppl3 extends java.applet.Applet<br />

{<br />

// Instanzvariable<br />

// Def<strong>in</strong>ition <strong>de</strong>s Felds mit <strong>de</strong>n x-Koord<strong>in</strong>aten<br />

<strong>in</strong>t xKoord[] = {20,50,70,40,20};<br />

// Def<strong>in</strong>ition <strong>de</strong>s Felds mit <strong>de</strong>n y-Koord<strong>in</strong>aten<br />

<strong>in</strong>t yKoord[] = {30,10,20,70,50};<br />

// Metho<strong>de</strong>n<br />

public void <strong>in</strong>it() { setBackground(Color.yellow); }<br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

// Zeichne e<strong>in</strong> 5-Eck<br />

g.setColor(Color.red);<br />

g.fillPolygon(xKoord,yKoord,5);<br />

}<br />

}<br />

Zeichnen e<strong>in</strong>es L<strong>in</strong>ienzuges mit <strong>de</strong>r Metho<strong>de</strong> abstract void drawPolygon(<strong>in</strong>t<br />

xPunkte[], <strong>in</strong>t yPunkte[], <strong>in</strong>t nPunkte) durch die gegebenen<br />

Koord<strong>in</strong>aten <strong>in</strong> <strong>de</strong>r Vor<strong>de</strong>rgrundfarbe. Die Figur wird dabei nicht geschlossen, wenn<br />

nicht Strat- und Endkoord<strong>in</strong>aten gleich s<strong>in</strong>d. Mit nPunkte wer<strong>de</strong>n die Anzahl <strong>de</strong>r<br />

gezeichneten L<strong>in</strong>ien kontrolliert.<br />

Die Klasse Polygon. Sie ist e<strong>in</strong>e Erweiterung <strong>de</strong>s Interface Shape . E<strong>in</strong> Polygon-<br />

Objekt verwaltet eigenständig se<strong>in</strong>e Koord<strong>in</strong>aten, von außen können Elemente<br />

aufgenommen wer<strong>de</strong>n.<br />

239 pr41105<br />

264


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

java.awt.Polygon<br />

protected Rectangle bounds<br />

public <strong>in</strong>t npo<strong>in</strong>ts<br />

public <strong>in</strong>t[] xpo<strong>in</strong>ts<br />

public <strong>in</strong>t[] ypo<strong>in</strong>ts<br />

><br />

public Polygon()<br />

public Polygon(<strong>in</strong>t[] xpo<strong>in</strong>ts, <strong>in</strong>t[] ypo<strong>in</strong>ts, <strong>in</strong>t npo<strong>in</strong>ts)<br />

><br />

public void addPo<strong>in</strong>t(<strong>in</strong>t x, <strong>in</strong>t y)<br />

public boolean conta<strong>in</strong>s(double x, double y)<br />

public boolean conta<strong>in</strong>s(double x, double y, double w, double h)<br />

public boolean conta<strong>in</strong>s(<strong>in</strong>t x, <strong>in</strong>t y)<br />

...<br />

public Rectangle getBounds();<br />

...<br />

public boolean <strong>in</strong>si<strong>de</strong>(<strong>in</strong>t x, <strong>in</strong>t y)<br />

...<br />

public void translate(<strong>in</strong>t <strong>de</strong>ltaX, <strong>in</strong>t <strong>de</strong>ltaY)<br />

Abb.: Die Klasse Polygon<br />

Das Zeichnen von Kreisen und Ellipsen. Es erfolgt über die Metho<strong>de</strong> public<br />

abstract void drawOval(<strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t width, <strong>in</strong>t height).<br />

(x,y) gibt die Koord<strong>in</strong>aten <strong>de</strong>r oberen l<strong>in</strong>ken Ecke <strong>de</strong>s umschreiben<strong>de</strong>n Rechtecks<br />

an.<br />

Das Zeichnen von gefüllten Kreisen und Ellipsen. Es erfolgt über die Metho<strong>de</strong><br />

public abstract void fillOval(<strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t width, <strong>in</strong>t<br />

height)<br />

Das Zeichnen von Bögen. <strong>Java</strong> stellt die folgen<strong>de</strong> Metho<strong>de</strong> dafür zur Verfügung:<br />

public abstract void drawArc(<strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t breite, <strong>in</strong>t<br />

hoehe, <strong>in</strong>t startW<strong>in</strong>kel, <strong>in</strong>t bogenW<strong>in</strong>kel). „startW<strong>in</strong>kel“ bestimmt<br />

<strong>de</strong>n Anfangsw<strong>in</strong>kel von e<strong>in</strong>er (gedachten) horizontalen Mittell<strong>in</strong>ie aus gesehen, ab<br />

<strong>de</strong>m <strong>de</strong>r Bogen gezeichnet wer<strong>de</strong>n soll. „bogenW<strong>in</strong>kel“ legt fest, wie weit <strong>de</strong>r<br />

Bogen ab <strong>de</strong>m Startpunkt gezeichnet wird und <strong>in</strong> welche Richtung er geht. Die<br />

positive Richtung <strong>in</strong> <strong>Java</strong> ist entgegen <strong>de</strong>m Uhrzeigers<strong>in</strong>n.<br />

Das Zeichnen von gefüllten Bögen. Es erfolgt über die Metho<strong>de</strong> public abstract<br />

void fillArc(<strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t breite, <strong>in</strong>t hoehe, <strong>in</strong>t<br />

startW<strong>in</strong>kel, <strong>in</strong>t bogenW<strong>in</strong>kel)<br />

Das Zeichnen von Zeichenketten erfolgt mit Hilfe <strong>de</strong>r Metho<strong>de</strong>n<br />

public abstract void drawStr<strong>in</strong>g(Str<strong>in</strong>g str, <strong>in</strong>t x, <strong>in</strong>t y)<br />

public void drawChars(char[] daten, <strong>in</strong>t offset, <strong>in</strong>t length, <strong>in</strong>t x, <strong>in</strong>t y)<br />

public void drawBytes(byte[] daten, <strong>in</strong>t offset, <strong>in</strong>t length, <strong>in</strong>t x, <strong>in</strong>t y)<br />

265


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

4.1.2 Farbangaben<br />

<strong>Java</strong> setzt Farben aus sog. Primärfarben (Rot, Grün, Blau) <strong>de</strong>s Lichts zusammen<br />

(RGB-Mo<strong>de</strong>ll). E<strong>in</strong>e Farbe im RGB-Mo<strong>de</strong>ll wird durch die Angabe, wieviel rotes,<br />

grünes und blaues Licht <strong>in</strong> <strong>de</strong>r Farbe enthalten s<strong>in</strong>d, bestimmt. Dies kann entwe<strong>de</strong>r<br />

mit e<strong>in</strong>er Ganzzahl zwischen 0 und 255 o<strong>de</strong>r e<strong>in</strong>er Gleitpunktzahl zwischen 0.0 und<br />

1.0 geschehen.<br />

Farbe Rot-Anteil Grün-Anteil Blau-Anteil<br />

Weiß (white) 255 255 255<br />

Hellgrau (lightGray) 192 192 192<br />

Grau (gray) 128 128 128<br />

Dunkelgrau (darkGray) 64 64 64<br />

Schwarz (black) 0 0 0<br />

Rot (rot) 255 0 0<br />

Grün (green) 0 255 0<br />

Blau (blue) 0 0 255<br />

Gelb (yellow) 255 255 0<br />

Orange (orange) 255 200 0<br />

P<strong>in</strong>k (p<strong>in</strong>k) 255 175 175<br />

Magenta (magenta) 255 0 255<br />

Cyan (cyan) 0 255 255<br />

Abb.: Gebräuchliche Farbwerte RGB-Werte)<br />

Neben <strong>de</strong>m RGB-Farbmo<strong>de</strong>ll unterstützt <strong>Java</strong> auch das HSB-Farbmo<strong>de</strong>ll. Dieses<br />

stellt e<strong>in</strong>e Farbe durch die drei Parameter Farbton, Intensität und Helligkeit dar. Die<br />

Farbmo<strong>de</strong>lle können die gleichen Farben beschreiben und umgerechnet wer<strong>de</strong>n.<br />

Die Color-Klasse. Auf drei Arten kann e<strong>in</strong>e Farbe erzeugt wer<strong>de</strong>n:<br />

public Color(<strong>in</strong>t red, <strong>in</strong>t green, <strong>in</strong>t blue)<br />

Damit wird e<strong>in</strong>e Farbe mit Rot-, Grün- und Blau-Werten zwischen 0 und 255 erzeugt.<br />

public Color(<strong>in</strong>t rgb)<br />

erzeugt e<strong>in</strong> Color-Objekt aus <strong>de</strong>m rgb-Wert, <strong>de</strong>r die Farben rot, grün, blau kodiert. Der Rot-Anteil<br />

bef<strong>in</strong><strong>de</strong>t sich <strong>in</strong> <strong>de</strong>n Bits 16 bis 23, <strong>de</strong>r Grünanteil <strong>in</strong> 8 bis 15 und auch <strong>de</strong>r Blauanteil <strong>in</strong> 0 bis 7. Je<strong>de</strong><br />

Farbe ist durch e<strong>in</strong> Byte repräsentiert.<br />

public Color(float red, float green, float blue)<br />

E<strong>in</strong>e gängige Farbe kann schneller über die Standardfarbobjekte <strong>de</strong>r <strong>in</strong> <strong>de</strong>r Color-Klasse <strong>de</strong>f<strong>in</strong>ierten<br />

verschie<strong>de</strong>nen Klassenvariablen gewonnen wer<strong>de</strong>n, z.B.:<br />

public static Color white;<br />

public static Color lightGray;<br />

public static Color gray;<br />

public static Color darkGray;<br />

public static Color black;<br />

public static Color red;<br />

public static Color blue;<br />

public static Color green;<br />

public static Color yellow;<br />

public static Color magenta;<br />

public static Color cyan;<br />

public static Color orange;<br />

public static Color p<strong>in</strong>k;<br />

Ermitteln <strong>de</strong>r RGB-Werte von e<strong>in</strong>em bestimmten Farbobjekt. Es erfolgt über<br />

public <strong>in</strong>t getRed();<br />

public <strong>in</strong>t getGreen();<br />

266


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

public <strong>in</strong>t getBlue();<br />

Setzen von Farben. Es wird möglich durch die Metho<strong>de</strong>: public abstract void<br />

setColor(Color c). Der Parameter bestimmt das gewünschte Farbobjekt.<br />

Setzen von H<strong>in</strong>tergrundfarben. Normalerweise ist die H<strong>in</strong>tergrundfarbe e<strong>in</strong>es Applets<br />

weiß o<strong>de</strong>r dunkelgrau (je nach Conta<strong>in</strong>er). Individuell kann die H<strong>in</strong>tergrundfarbe<br />

e<strong>in</strong>es Applets gesetzt wer<strong>de</strong>n durch: public void setBackground(Color c).<br />

Parameter ist das gewünschte Farbobjekt.<br />

Setzen von Vor<strong>de</strong>rgrundfarben. Falls die Farbe für alle Zeichenobjekte <strong>in</strong>nerhalb<br />

e<strong>in</strong>es Applets pauschal festgesetzt wer<strong>de</strong>n soll, dann kann die Metho<strong>de</strong> public<br />

void setForeground(Color c) verwen<strong>de</strong>t wer<strong>de</strong>n.<br />

4.1.3 Textausgabe über <strong>de</strong>n Zeichen-Modus<br />

Die Graphics-Klasse enthält auch Metho<strong>de</strong>n zum Zeichnen von Textzeichen und<br />

Zeichenketten (z.B. die drawStr<strong>in</strong>g() Metho<strong>de</strong>). Zusätzlich spielen die Font-<br />

Klasse 240 und die Fontmetrics-Klasse 241 beim Textzeichnen e<strong>in</strong>e Rolle.<br />

Font<br />

FontMetrics<br />

{ abstract }<br />

public static f<strong>in</strong>al <strong>in</strong>t PLAIN<br />

public static f<strong>in</strong>al <strong>in</strong>t BOLD<br />

public Font getFont()<br />

public static f<strong>in</strong>al <strong>in</strong>t ITALIC<br />

public <strong>in</strong>t getLead<strong>in</strong>g()<br />

protected Str<strong>in</strong>g name<br />

public <strong>in</strong>t getAscent()<br />

protected <strong>in</strong>t style<br />

public <strong>in</strong>t getDescent()<br />

protected <strong>in</strong>t size<br />

public <strong>in</strong>t getHeight()<br />

public <strong>in</strong>t getMaxAscent()<br />

> public <strong>in</strong>t getMaxDescent()<br />

public Font(Str<strong>in</strong>g name, <strong>in</strong>t style, <strong>in</strong>t size)<br />

public <strong>in</strong>t getMaxAdvance()<br />

> public <strong>in</strong>t charWidth(<strong>in</strong>t zeichen)<br />

public Str<strong>in</strong>g getName()<br />

public <strong>in</strong>t charWidth(char zeichen)<br />

public <strong>in</strong>t getStyle()<br />

public <strong>in</strong>t str<strong>in</strong>gWidth(Str<strong>in</strong>g str)<br />

public <strong>in</strong>t getSize()<br />

public <strong>in</strong>t[] getWidths()<br />

public boolean isPla<strong>in</strong>()<br />

public Str<strong>in</strong>g toStr<strong>in</strong>g()<br />

public boolean isBold()<br />

public boolean isItalic()<br />

public static Font getFont(Str<strong>in</strong>g nm)<br />

public static Font getFont(Str<strong>in</strong>g nm, Font font)<br />

public static Font <strong>de</strong>co<strong>de</strong>(Str<strong>in</strong>g str)<br />

public <strong>in</strong>t hashCo<strong>de</strong>()<br />

public boolean equals(Object obj)<br />

public Str<strong>in</strong>g toStr<strong>in</strong>g()<br />

Abb.: Die Klassen Font und FontMetrics<br />

240 Die Font-Klasse stellt bestimmte Fonts dar (Name, Stil, Fontgröße)<br />

241 Die Fontmetrics-Klasse enthält Informationen über <strong>de</strong>n Font wie tatsächliche Höhe und Breite e<strong>in</strong>es<br />

bestimmten Zeichens.<br />

267


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Die Klasse Font<br />

Texte wer<strong>de</strong>n <strong>in</strong> e<strong>in</strong>em standarmäßig bereitgestellten Font ausgegeben. Soll e<strong>in</strong><br />

an<strong>de</strong>rer Font zur Textausgabe verwen<strong>de</strong>t wer<strong>de</strong>n, so muß e<strong>in</strong> Objekt <strong>de</strong>r Klasse<br />

Font erzeugt und <strong>de</strong>m verwen<strong>de</strong>ten Graphics-Objekt zugewiesen wer<strong>de</strong>n.<br />

Das Erzeugen neuer Font-Objekte wird über die Parameter name, style und size<br />

<strong>de</strong>s Konstruktors <strong>de</strong>r Klasse Font gesteuert:<br />

public Font(Str<strong>in</strong>g name, <strong>in</strong>t style, <strong>in</strong>t size)<br />

name: Name <strong>de</strong>s gewünschten Font. In allen <strong>Java</strong>-Systemen sollen „SansSerif“, „Serif“, und<br />

„Monospaced“ unterstützt wer<strong>de</strong>n. Unter W<strong>in</strong>dows wer<strong>de</strong>n die Standardnamen auf die „True-Type-<br />

Fonts“ „Arial“, „TimesNewRoman“ und „CourierNew“ abgebil<strong>de</strong>t.<br />

style: Auswahl <strong>de</strong>r Ausprägung (fett, kursiv)<br />

Name Wert Be<strong>de</strong>utung<br />

Font.PLAIN 0 Standard-Font<br />

Font.BOLD 1 fett 242<br />

Font.ITALIC 2 kursiv<br />

size: Angabe <strong>de</strong>r Größe <strong>de</strong>r gewünschten Schriftart <strong>in</strong> Pixel (Punktgrößen)<br />

public void setFont(Font font) wird zum E<strong>in</strong>tragen <strong>de</strong>s Font-Objekts <strong>in</strong> <strong>de</strong>n<br />

Grafikkontext verwen<strong>de</strong>t.<br />

public Font getFont() ermittelt <strong>de</strong>n aktuellen Font.<br />

Die Ermittlung, welche Zeichensätze auf e<strong>in</strong>em System <strong>in</strong>stalliert s<strong>in</strong>d, kann über<br />

Toolkit.getDefaultToolkit().getFontList() erfolgen. Str<strong>in</strong>g[]<br />

getFontList() gibt die Namen <strong>de</strong>r verfügbaren Zeichensätze zurück, z.B.: 243<br />

import java.awt.*;<br />

class ListFont<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

// herkoemmliche Metho<strong>de</strong> zur Ermittlung <strong>de</strong>r<br />

// Schriftarten <strong>in</strong> e<strong>in</strong>em <strong>Java</strong>-System<br />

Str<strong>in</strong>g fonts[] = Toolkit.getDefaultToolkit().getFontList();<br />

for (<strong>in</strong>t i = 0; i < fonts.length; i++)<br />

System.out.pr<strong>in</strong>tln(fonts[i]);<br />

}<br />

}<br />

Die Klasse FontMetrics<br />

Diese Klasse bietet Metho<strong>de</strong>n zur Bestimmung <strong>de</strong>r Größe <strong>de</strong>r angezeigten Zeichen<br />

<strong>in</strong> <strong>de</strong>r festgelegten Schrift an.<br />

Begriffe für Schriften und Text.<br />

Basel<strong>in</strong>e (Grundl<strong>in</strong>ie): Damit ist die imag<strong>in</strong>äre L<strong>in</strong>ie geme<strong>in</strong>t, auf <strong>de</strong>r <strong>de</strong>r Text steht.<br />

Descent (Unterstand): Damit ist geme<strong>in</strong>t, wie weit e<strong>in</strong> Buchstabe unter die Grundl<strong>in</strong>ie geht.<br />

Ascent (Überstand): Damit ist geme<strong>in</strong>t, wie weit e<strong>in</strong> Buchstabe über die Grundl<strong>in</strong>ie geht.<br />

242 BOLD und ITALIC können auch geme<strong>in</strong>sam verwen<strong>de</strong>t wer<strong>de</strong>n, <strong>in</strong><strong>de</strong>m bei<strong>de</strong> Konstanten mit „+“<br />

zusammengefügt wer<strong>de</strong>n.<br />

243 pr41300<br />

268


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Lead<strong>in</strong>g (Zeileabstand): Damit ist <strong>de</strong>r Raum zwischen <strong>de</strong>m Descent e<strong>in</strong>es Buchstabens und <strong>de</strong>m<br />

Ascent <strong>de</strong>r nächsten Zeile geme<strong>in</strong>t.<br />

Metho<strong>de</strong>n.<br />

Metho<strong>de</strong> Aktion<br />

str<strong>in</strong>gWidth() Gibt die volle Breite e<strong>in</strong>er Zeichenmkette <strong>in</strong> Pixel aus<br />

charWidth() Gibt die Breite e<strong>in</strong>es bestimmten zeichens aus<br />

getAscent() Gibt die Entfernung zwischen <strong>de</strong>r Grundl<strong>in</strong>ie und <strong>de</strong>r oberen Grenze <strong>de</strong>r Buchstaben<br />

aus<br />

getDescent() Gibt die Entfernung zwischen <strong>de</strong>r Grundl<strong>in</strong>ie und <strong>de</strong>r unteren Grenze <strong>de</strong>r<br />

Buchstaben aus (z.B. p und g)<br />

getLead<strong>in</strong>g() Gibt <strong>de</strong>n Abstand zwischen <strong>de</strong>m Überstand e<strong>in</strong>er Zeile und <strong>de</strong>m Überstand <strong>de</strong>r<br />

nächsten Zeile aus<br />

getHeight() gibt die Gesamthöhe <strong>de</strong>r Schrift aus, d.h. die Summe von Überstand, Unterstand<br />

und Zeilenabstand<br />

Abb.: Fontmetrics-Metho<strong>de</strong>n<br />

Bsp. 244 :<br />

import java.applet.*;<br />

import java.awt.*;<br />

public class AllAnfAppl extends Applet<br />

{<br />

private static f<strong>in</strong>al Str<strong>in</strong>g spruch = "Aller Anfang ist schwer!";<br />

private Font font;<br />

private FontMetrics fontMetrics;<br />

private <strong>in</strong>t spruchBreite;<br />

private <strong>in</strong>t spruchAscent;<br />

private <strong>in</strong>t spruchDescent;<br />

private <strong>in</strong>t spruchX;<br />

private <strong>in</strong>t spruchY;<br />

// Metho<strong>de</strong>n<br />

public void <strong>in</strong>it()<br />

{<br />

setBackground(Color.white);<br />

font = new Font("Helvetica",Font.BOLD,24);<br />

fontMetrics = null;<br />

}<br />

//<br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

// Oval mit Farbe yellow<br />

g.setColor(Color.yellow);<br />

g.fillOval(0,0,getSize().width,getSize().height);<br />

// Roter Rahmen; da <strong>Java</strong> ke<strong>in</strong>e L<strong>in</strong>ienbreite kennt,<br />

// wird die L<strong>in</strong>ienbreite durrch 4 Ovale, (die sich<br />

// um Pixelbreite unterschei<strong>de</strong>n,) simuliert<br />

g.setColor(Color.red);<br />

g.drawOval( 3, 3,getSize().width-6,getSize().height-6);<br />

g.drawOval( 2, 2,getSize().width-4,getSize().height-4);<br />

g.drawOval( 1, 1,getSize().width-2,getSize().height-2);<br />

g.drawOval( 0, 0,getSize().width,getSize().height);<br />

g.setColor(Color.black);<br />

g.setFont(font);<br />

if (fontMetrics == null)<br />

{<br />

fontMetrics = g.getFontMetrics();<br />

spruchBreite = fontMetrics.str<strong>in</strong>gWidth(spruch);<br />

spruchAscent = fontMetrics.getAscent();<br />

spruchDescent = fontMetrics.getDescent();<br />

244 pr41301<br />

269


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

}<br />

<strong>in</strong>t breite = getSize().width;<br />

<strong>in</strong>t hoehe = getSize().height;<br />

spruchX = (breite - spruchBreite) / 2;<br />

spruchY = (hoehe + spruchAscent - spruchDescent) / 2;<br />

g.drawStr<strong>in</strong>g(spruch,spruchX,spruchY);<br />

}<br />

}<br />

4.1.4 Grössenangaben<br />

Für die meisten Größenangaben wer<strong>de</strong>n <strong>in</strong>t-Werte benutzt. Darüber h<strong>in</strong>aus gibt es<br />

auch alterantive Formulierungsmöglichkeiten mit <strong>de</strong>n Klassen Dimension, Po<strong>in</strong>t<br />

und Rectangle.<br />

><br />

Shape<br />

Po<strong>in</strong>t<br />

public <strong>in</strong>t x<br />

public <strong>in</strong>t y<br />

Rectangle<br />

><br />

public <strong>in</strong>t y<br />

public Po<strong>in</strong>t()<br />

public <strong>in</strong>t y public Po<strong>in</strong>t(Po<strong>in</strong>t p)<br />

public <strong>in</strong>t width public Po<strong>in</strong>t(<strong>in</strong>t x, <strong>in</strong>t y)<br />

public <strong>in</strong>t height ><br />

public Po<strong>in</strong>t getLocation()<br />

> public void setLocation(Po<strong>in</strong>t p)<br />

public Rectangle(Rectangle r) public void setLocation(<strong>in</strong>t x, <strong>in</strong>t y)<br />

public Rectangle(<strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t width, <strong>in</strong>t height) public void move(<strong>in</strong>t x, <strong>in</strong>t y)<br />

public Rectangle(<strong>in</strong>t width, <strong>in</strong>t height) public void translate(<strong>in</strong>t x, <strong>in</strong>t y)<br />

public Rectangle(Po<strong>in</strong>t p, Dimension d)<br />

public <strong>in</strong>t hashCo<strong>de</strong>()<br />

public Rectangle(Po<strong>in</strong>t p)<br />

public boolean equals(Object obj)<br />

public Rectangle(Dimension d)<br />

public Str<strong>in</strong>g toStr<strong>in</strong>g()<br />

><br />

public Rectangle getBounds()<br />

public void setBounds(<strong>in</strong>t x,<strong>in</strong>t y, <strong>in</strong>t width, <strong>in</strong>t height)<br />

Dimension<br />

public void setBounds(Rectangle r)<br />

public Po<strong>in</strong>t getLocation()<br />

public <strong>in</strong>t width<br />

public void setLocation(Po<strong>in</strong>t p)<br />

public <strong>in</strong>t height<br />

public void setLocation((<strong>in</strong>t x, <strong>in</strong>t y)<br />

public void translate(<strong>in</strong>t x, <strong>in</strong>t y) ><br />

public Dimension getSize()<br />

public Dimension()<br />

public void setSize(<strong>in</strong>t width, <strong>in</strong>t height) public Dimension(Dimension d)<br />

public boolean conta<strong>in</strong>s(Po<strong>in</strong>t p)<br />

public Dimension(<strong>in</strong>t width, <strong>in</strong>t height)<br />

public boolean conta<strong>in</strong>s(<strong>in</strong>t x, <strong>in</strong>t y) ><br />

public boolean isEmpty()<br />

public Dimension getSize()<br />

public <strong>in</strong>t hashCo<strong>de</strong>() public void setSize(Dimension d)<br />

public boolean equals(Object obj)<br />

public boolean equals(Object obj)<br />

public Str<strong>in</strong>g toStr<strong>in</strong>g()<br />

public Str<strong>in</strong>g toStr<strong>in</strong>g()<br />

Abb.: Die Klassen Dimension und Rectangle<br />

Das Interface Shape wird von <strong>de</strong>n bisher bekannten geometrischen Formen (z.B.<br />

Rectangle, Polygon) implementiert und außer<strong>de</strong>m von <strong>de</strong>n neuen geometrischen<br />

Formen im Paket java.awt.geom.<br />

270


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

4.1.5 Clipp<strong>in</strong>g-Operationen<br />

Je<strong>de</strong>r Grafikkontext hat e<strong>in</strong>e zugeordnete Clipp<strong>in</strong>g-Region, die dazu dient, die<br />

Ausgabe auf e<strong>in</strong>en bestimmten Bereich e<strong>in</strong>zugrenzen. Clipp<strong>in</strong>g ist e<strong>in</strong>e Eigenschaft<br />

<strong>de</strong>s aktuellen „Graphic“ Objekts. Mit <strong>de</strong>r Metho<strong>de</strong> clipRect(<strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t<br />

breite, <strong>in</strong>t hoehe) läßt sich <strong>de</strong>r Bereich e<strong>in</strong>schränken, dann erfolgen alle<br />

Operationen <strong>in</strong> diesem Bereich. Mit<br />

public abstract void setClip(<strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t width, <strong>in</strong>t height)<br />

public abstract void setClip(Shape clip)<br />

kann die Clipp<strong>in</strong>g-Region auf e<strong>in</strong>en beliebigen Bereich <strong>in</strong>nerhalb <strong>de</strong>s aktuelle<br />

Fensters ausge<strong>de</strong>hnt wer<strong>de</strong>n. Die zweite Version von setClip() erlaubt die<br />

Übergabe e<strong>in</strong>es Objekts, die das Shape-Interface implementiert.<br />

4.2 Bildverarbeitung<br />

Bil<strong>de</strong>r s<strong>in</strong>d neben Text das wichtigste visuelle Gestaltungsmerkmal. In <strong>Java</strong> können<br />

Grafiken an verschie<strong>de</strong>nen Stellen e<strong>in</strong>gebun<strong>de</strong>n wer<strong>de</strong>n, z.B. als Grafiken <strong>in</strong><br />

Zeichenflächen (Canvas) o<strong>de</strong>r als Icons <strong>in</strong> Buttons. Über <strong>Java</strong> können GIF-Bil<strong>de</strong>r 245<br />

und JPEG-Bil<strong>de</strong>r gela<strong>de</strong>n wer<strong>de</strong>n.<br />

Je<strong>de</strong> Grafik wird als Exemplar <strong>de</strong>r Klasse Image erzeugt. Image dient zur<br />

Implementierung von:<br />

- ImageProducer für die Erzeugung von Bil<strong>de</strong>rn auf <strong>de</strong>r Grundlage von Bildpunkten.<br />

- ImageConsumer für die Darstellung von Bil<strong>de</strong>rn als Interpretation <strong>de</strong>r gespeicherten Bildpunkte.<br />

- ImageFilter für die Filterung <strong>de</strong>s Bil<strong>de</strong>s <strong>in</strong>sbeson<strong>de</strong>re h<strong>in</strong>sichtlich vorgebbarer Farbmo<strong>de</strong>lle und<br />

Bil<strong>de</strong>igenschaften (mit <strong>de</strong>r speziellen Subklasse RGBImageFilter).<br />

- ImageObserver bspw. für die Beobachtung, ob e<strong>in</strong> Bild vollständig gela<strong>de</strong>n ist u.ä.m. Zur<br />

vollständigen Kontrolle <strong>de</strong>s La<strong>de</strong>ns e<strong>in</strong>es Bilds stellt java.awt. die Klasse MediaTracker bereit.<br />

Bil<strong>de</strong>r e<strong>in</strong>er Applikation wer<strong>de</strong>n über die Klasse Toolkit e<strong>in</strong>gebun<strong>de</strong>n.<br />

Bsp.: Anfor<strong>de</strong>rn e<strong>in</strong>es Bilds <strong>in</strong> e<strong>in</strong>er Applikation.<br />

Image pic = Toolkit.getDefaultToolkit().getImage("");<br />

Zur Anzeige e<strong>in</strong>es Bilds <strong>in</strong> Orig<strong>in</strong>algröße wird die Metho<strong>de</strong> drawImage() mit<br />

folgen<strong>de</strong>n Argumenten aufgerufen:<br />

- Das Image-Objekt, das angezeigt wer<strong>de</strong>n soll.<br />

- die x-Koord<strong>in</strong>ate<br />

- die y-Koord<strong>in</strong>ate<br />

- Das Schlüsselwort this.<br />

245 Das GIF-Format (Graphics Interchange Format) ist e<strong>in</strong> komprimieren<strong>de</strong>s Verfahren, das 1987 von<br />

CompuServe-Betreibern zum Austausch von Bil<strong>de</strong>rn entwickelt wur<strong>de</strong>. GIF-Bil<strong>de</strong>r können bis zu 1600 mal<br />

1600 Punkte umfassen. Die Komprimierung (nach <strong>de</strong>m LZW-Verfahren) ist verlustfrei. Je<strong>de</strong>s GIF-Bild kann aus<br />

maximal 256 Farben bestehen (bei e<strong>in</strong>er Palette von 16,7 Millionen Farben).<br />

271


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

4.2.1 Klassen zur Bildverarbeitung<br />

Toolkit<br />

{ abstract }<br />

public abstract Image getImage(Str<strong>in</strong>g dateiName)<br />

public abstract Image getImage(Url url)<br />

public abstract Image createImage(ImageProducer erzeuger)<br />

public abstract boolean prepareImage(Image bild, <strong>in</strong>t breite, <strong>in</strong>t hoehe,ImageObserver observer)<br />

public abstract <strong>in</strong>t checkImage(Image bild, <strong>in</strong>t breite, <strong>in</strong>t hoehe, ImageObserver observer)<br />

....<br />

Image<br />

{ abstract }<br />

public abstract <strong>in</strong>t getHeight(ImageObserver obs)<br />

public abstract <strong>in</strong>t getWidth(ImageObserver obs)<br />

public abstract ImageProducer getSource()<br />

public abstract Graphics getGraphics()<br />

public abstract void flush()<br />

public abstract Object getProperty(Str<strong>in</strong>g attribut, ImageObserver obs)<br />

public Image getScaledInstance(<strong>in</strong>t breite, <strong>in</strong>t hoehe, <strong>in</strong>t modus)<br />

public static f<strong>in</strong>al <strong>in</strong>t ALLBITS<br />

public static f<strong>in</strong>al <strong>in</strong>t SOMEBITS<br />

public static f<strong>in</strong>al <strong>in</strong>t ABORT<br />

public static f<strong>in</strong>al <strong>in</strong>t ERROR<br />

public static f<strong>in</strong>al <strong>in</strong>t PROPERTIES<br />

><br />

ImageObserver<br />

public abstract boolean imageUpdate(Image bild, <strong>in</strong>t <strong>in</strong>foFlags, <strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t breite, <strong>in</strong>t hoehe)<br />

public static f<strong>in</strong>al <strong>in</strong>t ABORTED<br />

public static f<strong>in</strong>al <strong>in</strong>t COMPLETE<br />

public static f<strong>in</strong>al <strong>in</strong>t ERRORED<br />

public static f<strong>in</strong>al <strong>in</strong>t LOADING<br />

MediaTracker<br />

><br />

public MediaTracker(Component comp)<br />

><br />

public void addImage(Image bild, <strong>in</strong>t id)<br />

public void addImage(Image bild, <strong>in</strong>t id, <strong>in</strong>t breite, <strong>in</strong>t hoehe)<br />

public boolean checkAll()<br />

public void waitForAll() throws InterruptedException<br />

public boolean checkID(<strong>in</strong>t id)<br />

public void waitForID(<strong>in</strong>t id) throws InterruptedException<br />

...<br />

Abb.: Image-Klassen<br />

272


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Die Klassen zur Bildverarbeitung bef<strong>in</strong><strong>de</strong>n sich im Package java.awt.image. Mit<br />

Hilfe <strong>de</strong>r Klasse java.awt.Toolkit kann e<strong>in</strong> Bild aus e<strong>in</strong>er angegebenen Quelle<br />

mit getImage() gela<strong>de</strong>n wer<strong>de</strong>n. Dabei wird e<strong>in</strong> Objekt vom Typ<br />

java.awt.Image zurückgegeben. Der La<strong>de</strong>zustand e<strong>in</strong>es Objekts (z.B e<strong>in</strong>es Bilds)<br />

kann überwacht wer<strong>de</strong>n, wenn die zum Objekt zugehörige Klasse das Interface<br />

java.awt.image.ImageObserver implementiert. Das tun alle AWT- und Sw<strong>in</strong>g-<br />

Kontrollelemente. Zur vere<strong>in</strong>fachten Benutzung beim La<strong>de</strong>n mehrerer Bil<strong>de</strong>r gibt es<br />

die Klasse java.awt.MediaTracker. Die weiteren Klassen im Paket<br />

java.util.image ermöglichen Skalierung, Rotation, Filtern und an<strong>de</strong>re<br />

Verän<strong>de</strong>rungen <strong>de</strong>r Bil<strong>de</strong>r. Als Bildformate wer<strong>de</strong>n GIF und JPG unterstützt.<br />

In <strong>Java</strong> 2 kamen Klassen zum pixelorientierten Erzeugen und Bearbeiten von Bil<strong>de</strong>rn<br />

im Paket java.awt.image und <strong>de</strong>m neuen java.awt.image.ren<strong>de</strong>rable<br />

h<strong>in</strong>zu.<br />

Bsp.: E<strong>in</strong> Bildbetrachter 246<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

public class ImageViewer extends Frame implements ActionListener<br />

{<br />

private Image image;<br />

private Frame frame;<br />

public ImageViewer()<br />

{<br />

setTitle("Bildbetrachter");<br />

// konstruiere die Menuezeile<br />

MenuBar mbar = new MenuBar();<br />

Menu menue = new Menu("Datei");<br />

MenuItem menueItem = new MenuItem("Oeffnen", new MenuShortcut((<strong>in</strong>t)<br />

'O'));<br />

menueItem.addActionListener(this);<br />

menue.add(menueItem);<br />

mbar.add(menue);<br />

setMenuBar(mbar);<br />

// Schliessen <strong>de</strong>s Fenster mit X<br />

frame = this;<br />

addW<strong>in</strong>dowListener(new W<strong>in</strong>dowAdapter()<br />

{<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{ System.exit(0); }<br />

});<br />

setSize(600,400);<br />

}<br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

if (image != null)<br />

{<br />

g.drawImage(image,0,0,this);<br />

setSize(image.getWidth(this),image.getHeight(this));<br />

}<br />

}<br />

public void actionPerformed(ActionEvent e)<br />

{<br />

FileDialog d = new FileDialog(frame,"Oeffne Grafikdatei",<br />

FileDialog.LOAD);<br />

d.setFile("*.jpg;*gif");<br />

d.show();<br />

Str<strong>in</strong>g file = d.getDirectory() + d.getFile();<br />

246 pr42300<br />

273


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

image = Toolkit.getDefaultToolkit().getImage(file);<br />

if (image != null) repa<strong>in</strong>t();<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

new ImageViewer().show();<br />

}<br />

}<br />

E<strong>in</strong> Bild <strong>in</strong> e<strong>in</strong>er an<strong>de</strong>ren Größe kann durch e<strong>in</strong>e erweiterte Version von<br />

drawImage() angezeigt wer<strong>de</strong>n, falls folgen<strong>de</strong> Parameter angegeben wer<strong>de</strong>n:<br />

- Das Imgae-Objekt, das angezeigt wer<strong>de</strong>n soll<br />

- die x-Koord<strong>in</strong>ate<br />

- die y-Koord<strong>in</strong>ate<br />

- die Breite<br />

- die Höhe<br />

- das Schlüsselwort this<br />

Zwei Metho<strong>de</strong>n <strong>de</strong>r Image-Klasse s<strong>in</strong>d zur Anzeige e<strong>in</strong>es Bilds, das nich <strong>in</strong><br />

Orig<strong>in</strong>algröße gezeigt wer<strong>de</strong>n soll, hilfreich:<br />

- getHeight() gibt die Höhe <strong>de</strong>s Bilds zurück.<br />

- getWidth() gibt die Breite <strong>de</strong>s Bilds zurück.<br />

Bsp. 247 : Verkle<strong>in</strong>ern <strong>de</strong>r Orig<strong>in</strong>algröße e<strong>in</strong>es Bilds (Haus Kirchplatz 7) um 25 bzw. 50<br />

Prozent<br />

import java.awt.Graphics;<br />

import java.awt.Image;<br />

public class Kirchplatz7 extends java.applet.Applet<br />

{<br />

Image whig;<br />

public void <strong>in</strong>it()<br />

{<br />

whig = getImage(getCo<strong>de</strong>Base(),"B01000800.jpg");<br />

}<br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

<strong>in</strong>t iBreite = whig.getWidth(this);<br />

<strong>in</strong>t iHoehe = whig.getHeight(this);<br />

<strong>in</strong>t xPos = 10;<br />

// 25%<br />

g.drawImage(whig, xPos, 10, iBreite / 4, iHoehe / 4, this);<br />

// 50%<br />

xPos += (iBreite / 4) + 10;<br />

g.drawImage(whig, xPos, 10, iBreite / 2, iHoehe / 2, this);<br />

}<br />

}<br />

247 vgl. pr42300<br />

274


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Abb.: Das Haus "Am Kirchplatz 7". L<strong>in</strong>ks ist e<strong>in</strong> Viertel, rechts die Hälfte <strong>de</strong>r Orig<strong>in</strong>algröße dargestellt.<br />

Mit <strong>de</strong>r Klasse MediaTracker kann festgestellt wer<strong>de</strong>n, ob e<strong>in</strong> Bild für die Anzeige<br />

bereit ist. E<strong>in</strong> MediaTracker, z.B. für e<strong>in</strong> Applet, kann erzeugt wer<strong>de</strong>n über<br />

MediaTracker me<strong>in</strong>Tracker = new MediaTracker(this);<br />

„this“ bezieht sich auf das Applet. E<strong>in</strong> Bild wird angezeigt über die Metho<strong>de</strong><br />

getImage(), z.B. über Image bild = getImage(getDocumentBase(),"B......jpg").<br />

Der Mediatracker wird angewiesen, dieses Bild über<br />

me<strong>in</strong>Tracker.addImage(bild,0)<br />

zu beobachten. Mit waitForID() kann gewartet wer<strong>de</strong>n, bis das Bild fertig gela<strong>de</strong>n<br />

wir<strong>de</strong>, z.B. me<strong>in</strong>Tracker.waitForID(0). Mit waitForAll() kann gewartet<br />

wer<strong>de</strong>n, bis alle Bil<strong>de</strong>r gela<strong>de</strong>n s<strong>in</strong>d. Falls nicht die ganze Zeit gewartet wer<strong>de</strong>n soll,<br />

bis das Bild gela<strong>de</strong>n ist, kann <strong>de</strong>r La<strong>de</strong>vorgang mit <strong>de</strong>r Metho<strong>de</strong> statusID()<br />

überwacht wer<strong>de</strong>n. Beim Aufruf von statusID() wird e<strong>in</strong>e I<strong>de</strong>ntifikation (ID)<br />

übergeben, für die e<strong>in</strong> Status angelegt wer<strong>de</strong>n soll, und e<strong>in</strong> boolscher Operator mit<br />

e<strong>in</strong>er Angabe, ob <strong>de</strong>r La<strong>de</strong>vorgang für das Bild gestartet wer<strong>de</strong>n soll o<strong>de</strong>r nicht, z.B.:<br />

me<strong>in</strong>Tracker.statusID(0,true)<br />

Soll <strong>de</strong>r Status aller Bil<strong>de</strong>r überprüft wer<strong>de</strong>n, dann erfolgt <strong>de</strong>r Aufruf<br />

me<strong>in</strong>Tracker.statusAll(true). Die Metho<strong>de</strong>n statusID() bzw.<br />

statusAll() geben e<strong>in</strong>e ganze Zahl zurück, die durch folgen<strong>de</strong> Flags symbolisiert<br />

wird:<br />

MediaTracker.ABORTED<br />

MediaTracker.COMPLETE<br />

// Das La<strong>de</strong>n von Bil<strong>de</strong>rn wur<strong>de</strong> abgebrochen<br />

// Das Bil<strong>de</strong>r wur<strong>de</strong>n komplett gela<strong>de</strong>n<br />

275


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

MediaTracker.LOADING<br />

MediaTracker.ERRORED<br />

// Das Bil<strong>de</strong>r wer<strong>de</strong>n noch gela<strong>de</strong>n<br />

// Das La<strong>de</strong>n von Bil<strong>de</strong>rn ist fehlerhaft<br />

Mit checkID() bzw. checkALL() kann überprüft wer<strong>de</strong>n, ob e<strong>in</strong> Bild bzw. alle<br />

Bil<strong>de</strong>r gela<strong>de</strong>n wur<strong>de</strong>(n):<br />

public boolean checkAll()<br />

public boolean checkAll(boolean startLoad<strong>in</strong>g)<br />

public boolean checkID(<strong>in</strong>t id)<br />

public boolean checkID(<strong>in</strong>t id,boolean startLoad<strong>in</strong>g)<br />

Falls „startLoad<strong>in</strong>g“ true ist, wird <strong>de</strong>r La<strong>de</strong>vorgang für alle Bil<strong>de</strong>r, die noch nicht<br />

gela<strong>de</strong>n s<strong>in</strong>d, gestartet.<br />

4.2.2 Bildproduzenten und Bildkonsumenten<br />

Bildproduzenten s<strong>in</strong>d die Quellen für Bilddateien. Bildkonsumenten s<strong>in</strong>d Objekte, die<br />

Bil<strong>de</strong>r verwen<strong>de</strong>n 248 . In <strong>Java</strong> ist die Aufgabe von Bildproduzenten bzw.<br />

Bildkonsumenten durch die Schnittstelle ImageProducer und ImageConsumer<br />

abgebil<strong>de</strong>t.<br />

Das Interface ImageProducer beschreibt Metho<strong>de</strong>n zum Bereitstellen von Pixeln<br />

e<strong>in</strong>es Bil<strong>de</strong>s. Klassen, die die Schnittstelle implementieren, stellen Bild<strong>in</strong>formationen<br />

e<strong>in</strong>er speziellen Quelle dar. So ist die Klasse MemoryImageSource e<strong>in</strong>e<br />

vorgefertigte Klasse, die ImageProducer implementiert. Sie produziert<br />

Bild<strong>in</strong>formationen aus e<strong>in</strong>em Array von Pixeln, die im Speicher gehalten wer<strong>de</strong>n.<br />

Die Schnittstelle ImageConsumer beschreibt Metho<strong>de</strong>n, die e<strong>in</strong>em Objekt <strong>de</strong>n<br />

Zugriff auf die Bilddatei <strong>de</strong>s Produzenten erlauben.<br />

Das Produzenten/Konsumenten-Mo<strong>de</strong>ll verwen<strong>de</strong>t e<strong>in</strong>e ColorMo<strong>de</strong>l-Klasse. Bil<strong>de</strong>r,<br />

die zwischen Produzenten und Konsumenten ausgetauscht wer<strong>de</strong>n, wer<strong>de</strong>n aus<br />

Datenfel<strong>de</strong>rn von Ganzzahlen erstellt. Je<strong>de</strong> Ganzzahl steht für die Farbe e<strong>in</strong>es<br />

Pixels. Die ColorMo<strong>de</strong>l-Klasse verfügt über Metho<strong>de</strong>n zum Herausziehen von roten,<br />

grünen, blauen und „alpha“-Komponenten.<br />

Die „alpha“-Komponente steht für die Transparenz e<strong>in</strong>er Farbe. E<strong>in</strong> „alpha“-Wert von<br />

255 be<strong>de</strong>utet, daß die Farbe vollkommen lichtundurchlässig ist. E<strong>in</strong> „alpha“-Wert von<br />

0 zeigt an, daß die Farbe vollständig transparent ist.<br />

Das Standard-Farbmo<strong>de</strong>ll ist das RGB-Default-Mo<strong>de</strong>ll mit <strong>de</strong>m die 4<br />

Farbkomponenten <strong>in</strong> e<strong>in</strong>e Form „0xaarrggbb“ gebracht wer<strong>de</strong>n. Die 8 Bit ganz l<strong>in</strong>ks<br />

s<strong>in</strong>d <strong>de</strong>r alpha-Wert, die nächsten 8 Bit s<strong>in</strong>d die „Rot“-Komponente, danach kommt<br />

die „Grün“-Komponente und zum Schluß kommen 8 Bits für Blau.<br />

Bsp.: E<strong>in</strong>e Farbe von „0x12345678“ wür<strong>de</strong> e<strong>in</strong>e „alpha“-Komponente von 0x12<br />

(ziemlich transparent) haben, e<strong>in</strong>e Rotkomponente von 0x34, e<strong>in</strong>e Grünkomponente<br />

von 0x56 und e<strong>in</strong>e Blaukomponente von 0x78.<br />

248 typischerweise e<strong>in</strong>fache Zeichenrout<strong>in</strong>en, die das Bild auf <strong>de</strong>m Bildschirm anzeigen.<br />

276


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

4.2.3 Bildfilter<br />

ImageFilter liegen zwischen Produzenten und Konsumenten, verän<strong>de</strong>rn<br />

Bild<strong>in</strong>formationen und nehmen E<strong>in</strong>fluß auf die Größe.<br />

Subklassen <strong>de</strong>r Klasse ImageFilter:<br />

BufferedImageFilter. Mit diesem Filter läßt sich e<strong>in</strong> Objekt vom Typ BufferedImageOp<br />

übergeben, mit <strong>de</strong>m unterschiedliche Manipulationen ermöglicht wer<strong>de</strong>n. BufferedImageOp ist e<strong>in</strong>e<br />

Schnittstelle, die von Aff<strong>in</strong>eTransformOp, ConvolveOp, LookupOp implementiert wird.<br />

CropImageFilter. Bildteile wer<strong>de</strong>n herausgeschnitten.<br />

ReplicateScaleFilter. Zum Vergrößern / Verkle<strong>in</strong>ern von Bil<strong>de</strong>rn.<br />

RGBImageFilter. Dieser allgeme<strong>in</strong>e Filter ist für e<strong>in</strong>e eigene, um RGBFilter erweiterte Filterklasse<br />

gedacht.<br />

Bil<strong>de</strong>r skalieren mit getScaledInstance()<br />

Die Metho<strong>de</strong> public Image getScaledInstance(<strong>in</strong>t width,<strong>in</strong>t<br />

height,<strong>in</strong>t h<strong>in</strong>ts) liefert e<strong>in</strong> skaliertes Bild mit <strong>de</strong>n neuen Ausmaßen width<br />

und height, h<strong>in</strong>ts gibt e<strong>in</strong>en Skalierungsfaktor als Konstante an.<br />

getScaledInstance() greift auf die Filterklassen AreaAverag<strong>in</strong>gScaleFilter<br />

und ReplicateScaleFilter zu. Sie berechnen jeweils das neue Bild über <strong>de</strong>n<br />

Bildproduzenten. Bei e<strong>in</strong>er Vergrößerung wer<strong>de</strong>n die Pixel e<strong>in</strong>er Zeile und Spalte<br />

e<strong>in</strong>fach verdoppelt. Bei e<strong>in</strong>er Verkle<strong>in</strong>erung wer<strong>de</strong>n e<strong>in</strong>fach Reihen und Spalten<br />

weggelassen 249 .<br />

Beim Vergrößern o<strong>de</strong>r Verkle<strong>in</strong>ern kommt es zu Pixelfehlern und die Frage, wie Pixel<br />

vergrößert wer<strong>de</strong>n, bee<strong>in</strong>flusst das En<strong>de</strong>rgebnis und die Geschw<strong>in</strong>digkeit..<br />

getScaledInstance() verlangt nicht nur Breite und Höhe, son<strong>de</strong>rn auch e<strong>in</strong>e<br />

Konstante für die Art <strong>de</strong>r Skalierung. Der Skalierungsparameter bestimmt <strong>de</strong>n<br />

Algorithmus.<br />

Skalierungsparameter<br />

SCALE_DEFAULT<br />

SCALE_FAST<br />

SCALE_SMOOTH<br />

SCALE_REPLICATE<br />

SCALE_AREA_AVERAGING<br />

verwen<strong>de</strong>t e<strong>in</strong>en Standard-Skalierungs-Algorithmus<br />

verwen<strong>de</strong>t e<strong>in</strong>en Skalierungs-Algorithmus, <strong>de</strong>r mehr Wert auf<br />

Geschw<strong>in</strong>digkeit als auf Glätte <strong>de</strong>s Bil<strong>de</strong>s legt<br />

verwen<strong>de</strong>t e<strong>in</strong>en Algorithmus mit guter Bildqualität und legt wenig<br />

Wert auf Geschw<strong>in</strong>digkeit<br />

benutzt für <strong>de</strong>n Skalierungs-Algorithmus <strong>de</strong>n ReplicateScaleFilter<br />

verwen<strong>de</strong>t <strong>de</strong>n AreaAverag<strong>in</strong>gScaleFilter<br />

Abb.: Skalierungsparameter für getScaledInstance()<br />

Bsp. 250 :<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

import java.awt.image.*;<br />

import java.applet.*;<br />

import javax.sw<strong>in</strong>g.*;<br />

public class ScaledImage extends Applet<br />

{<br />

249 Mit e<strong>in</strong>em AreaAverag<strong>in</strong>gScaleFilter erhält man die besseren Resultate, da Pixel nicht e<strong>in</strong>fach kopiert<br />

wer<strong>de</strong>n, son<strong>de</strong>rn e<strong>in</strong>gefügte Pixel aus <strong>de</strong>r Mittelwertberechnung bestimmt wer<strong>de</strong>n. Der Algorithmus heißt auch<br />

nearest neighbor algorithm.<br />

250 pr42300<br />

277


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

private Image orig<strong>in</strong>alImage;<br />

private Image scaledImage;<br />

public void <strong>in</strong>it()<br />

{<br />

orig<strong>in</strong>alImage = new ImageIcon("B01000800.jpg").getImage();<br />

<strong>in</strong>t prozent = 50;<br />

scaledImage = orig<strong>in</strong>alImage.getScaledInstance(<br />

(orig<strong>in</strong>alImage.getWidth(this) * prozent)/100,<br />

(orig<strong>in</strong>alImage.getHeight(this)*prozent)/100,<br />

Image.SCALE_SMOOTH );<br />

}<br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

g.drawImage(scaledImage ,5,0,this);<br />

}<br />

}<br />

Bildteile ausschnei<strong>de</strong>n mit <strong>de</strong>m CropImageFilter<br />

Für die Anwendung e<strong>in</strong>es Filters wird die Klasse FilteredImageSource<br />

herangezogen. Im Konstruktor von FilteredImageSource wird das Bild und <strong>de</strong>r<br />

Filter angegeben.<br />

Bsp.: 251 Das folgen<strong>de</strong> Applet nimmt das Bild aus <strong>de</strong>r vorstehen<strong>de</strong>n Abbildung und<br />

setzt ihm e<strong>in</strong> CropImageFilter auf, damit es nur e<strong>in</strong>en Teil <strong>de</strong>s Bil<strong>de</strong>s anzeigt.<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

import java.awt.image.*;<br />

import java.applet.*;<br />

import javax.sw<strong>in</strong>g.*;<br />

public class ScaledCropImage extends Applet<br />

{<br />

251 pr42300<br />

278


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

private Image orig<strong>in</strong>alImage;<br />

private Image scaledImage;<br />

private Image croppedImage;<br />

private ImageFilter cropFilter;<br />

public void <strong>in</strong>it()<br />

{<br />

orig<strong>in</strong>alImage = new ImageIcon("B01000800.jpg").getImage();<br />

<strong>in</strong>t prozent = 50;<br />

scaledImage = orig<strong>in</strong>alImage.getScaledInstance(<br />

(orig<strong>in</strong>alImage.getWidth(this) * prozent)/100,<br />

(orig<strong>in</strong>alImage.getHeight(this)*prozent)/100,<br />

Image.SCALE_SMOOTH );<br />

cropFilter = new CropImageFilter(0,0,240,200);<br />

croppedImage = createImage(new<br />

FilteredImageSource(scaledImage.getSource(),cropFilter));<br />

}<br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

g.drawImage(croppedImage ,3,0,this);<br />

}<br />

}<br />

Manipulation über Farbfilter<br />

Die folgen<strong>de</strong> Filterklasse ist abgeleitet von RGBImageFilter. Sie umfaßt<br />

filterRGB() und zw<strong>in</strong>gt e<strong>in</strong>em Bild e<strong>in</strong>en Grauschleier auf.<br />

class GrayFilter extends RGBImageFilter<br />

{<br />

public GrayFilter() {<br />

canFilterIn<strong>de</strong>xColorMo<strong>de</strong>l = true;<br />

}<br />

}<br />

public <strong>in</strong>t filterRGB(<strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t rgb) {<br />

<strong>in</strong>t a = rgb & 0xff000000;<br />

<strong>in</strong>t r = (((rgb & 0xff0000) + 0x180000)/3) & 0xff0000;<br />

<strong>in</strong>t g = (((rgb & 0x00ff00) + 0x018000)/3) & 0x00ff00;<br />

<strong>in</strong>t b = (((rgb & 0x0000ff) + 0x000180)/3) & 0x0000ff;<br />

return a | r | g | b;<br />

}<br />

279


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Bsp. 252 : Das folgen<strong>de</strong> Applet benutzt das Bild <strong>de</strong>r vorstehen<strong>de</strong>n Abbildung und färbt<br />

es mit Hilfe <strong>de</strong>r vorstehen<strong>de</strong>n Filterklasse GrayFilter grau e<strong>in</strong>.<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

import java.awt.image.*;<br />

import java.applet.*;<br />

import javax.sw<strong>in</strong>g.*;<br />

public class ScaledCropGrayImage extends Applet<br />

{<br />

private Image orig<strong>in</strong>alImage;<br />

private Image scaledImage;<br />

private Image croppedImage;<br />

private Image grauImage;<br />

private ImageFilter cropFilter;<br />

private ImageFilter grauFilter;<br />

public void <strong>in</strong>it()<br />

{<br />

orig<strong>in</strong>alImage = new ImageIcon("B01000800.jpg").getImage();<br />

<strong>in</strong>t prozent = 50;<br />

scaledImage = orig<strong>in</strong>alImage.getScaledInstance(<br />

(orig<strong>in</strong>alImage.getWidth(this) * prozent)/100,<br />

(orig<strong>in</strong>alImage.getHeight(this)*prozent)/100,<br />

Image.SCALE_SMOOTH );<br />

cropFilter = new CropImageFilter(0,0,240,200);<br />

croppedImage = createImage(new<br />

FilteredImageSource(scaledImage.getSource(),cropFilter));<br />

grauFilter = new GrayFilter();<br />

ImageProducer erzeuger = new<br />

FilteredImageSource(croppedImage.getSource(),grauFilter);<br />

grauImage = this.createImage(erzeuger);<br />

}<br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

g.drawImage(grauImage,3,0,this);<br />

g.drawImage(croppedImage ,250,0,this);<br />

}<br />

}<br />

252 vgl. pr42300<br />

280


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

4.2.4 Kopieren von Speicher <strong>in</strong> e<strong>in</strong> Bild bzw. von Bil<strong>de</strong>rn <strong>in</strong> e<strong>in</strong>en Speicher<br />

4.2.4.1 Kopieren von Speicher <strong>in</strong> e<strong>in</strong> Bild<br />

E<strong>in</strong> möglicher Typ für das Produzieren von Bil<strong>de</strong>rn ist e<strong>in</strong> Datenfeld mit ganzen<br />

Zahlen, die für die Farbe e<strong>in</strong>es je<strong>de</strong>n Pixels stehen. Möglich wird das durch die<br />

Klasse MemoryImageSource.<br />

Bsp.: Das folgen<strong>de</strong> Applet erzeugt e<strong>in</strong> Speicherbild, e<strong>in</strong>e MemoryImageSource und<br />

zeichnet das Bild im Zeichenbereich.<br />

import java.applet.*;<br />

import java.awt.*;<br />

import java.awt.image.*;<br />

/* das Applet zeichnet e<strong>in</strong> Image und benutzt dazu<br />

e<strong>in</strong>en Array mit Pixeln<br />

*/<br />

public class SpeicherBild extends Applet<br />

{<br />

private f<strong>in</strong>al static <strong>in</strong>t b = Color.blue.getRGB();<br />

private f<strong>in</strong>al static <strong>in</strong>t r = Color.red.getRGB();<br />

private f<strong>in</strong>al static <strong>in</strong>t g = Color.green.getRGB();<br />

<strong>in</strong>t pixels[] = {<br />

b, b, b, b, b, b, b, b, b, b,<br />

b, b, b, b, b, b, b, b, b, b,<br />

b, b, g, g, g, g, g, g, b, b,<br />

b, b, g, g, g, g, g, g, b, b,<br />

b, b, g, g, r, r, g, g, b, b,<br />

b, b, g, g, r, r, g, g, b, b,<br />

b, b, g, g, g, g, g, g, b, b,<br />

b, b, g, g, g, g, g, g, b, b,<br />

b, b, b, b, b, b, b, b, b, b,<br />

b, b, b, b, b, b, b, b, b, b<br />

};<br />

Image me<strong>in</strong>Bild;<br />

public void <strong>in</strong>it()<br />

{<br />

/* Erzeigen <strong>de</strong>s Bilds aus <strong>de</strong>m Array pixels.<br />

Die Pixels wer<strong>de</strong>n zeilenweise von Postion 0<br />

aus <strong>de</strong>m Array gelesen. e<strong>in</strong>e Zeile umfasst 10<br />

Postionen.<br />

*/<br />

me<strong>in</strong>Bild = createImage(<br />

new MemoryImageSource(10,10,pixels,0,10));<br />

}<br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

// Zeichen das Bild.<br />

// Breite und Hoehe <strong>de</strong>s Bilds wer<strong>de</strong>n 10fach vergroessert<br />

g.drawImage(me<strong>in</strong>Bild,0,0,100,100,this);<br />

}<br />

}<br />

281


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Abb.: Darstellung von SpeicherBild<br />

4.2.4.2 Kopieren von Bil<strong>de</strong>rn <strong>in</strong> e<strong>in</strong>en Speicher<br />

Die PixelGrabber-Klasse nimmt e<strong>in</strong> Bild und macht aus diesem e<strong>in</strong> Datenfeld mit<br />

ganzen Zahlen. E<strong>in</strong> PixelGrabber wird auf e<strong>in</strong> Image-Objekt aufgesetzt und füllt e<strong>in</strong><br />

Ganzzahl-Feld mit <strong>de</strong>n Farbwerten, die die Anteile <strong>de</strong>r Farben rot, grün und blau<br />

enthalten.<br />

Der PixelGrabber ist für Modifikationen bereits existieren<strong>de</strong>r Bil<strong>de</strong>r nützlich.<br />

class java.awt.image.PixelGrabber implements ImageConsumer<br />

Konstruktor.<br />

public PixelGrabber(Image img, <strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t breite, <strong>in</strong>t hoehe, <strong>in</strong>t<br />

feld[], <strong>in</strong>t verschiebung, <strong>in</strong>t scanSize)<br />

erzeugt e<strong>in</strong> PixelGrabber-Objekt, das e<strong>in</strong> Rechteck von RGB-Farben aus <strong>de</strong>m Feld holt. Das<br />

Rechteck ist durch die Maße x, y, breite, hoehe beschrieben. Die Farbe für e<strong>in</strong>en Punkt (i,j) s<strong>in</strong>d im<br />

Feld an <strong>de</strong>r Position (j-y)*scanSize+(i-x)+verschiebung. Mit <strong>de</strong>r Umwandlung wird noch<br />

nicht begonnen. Sie muß mit <strong>de</strong>r Funktion grabPixels() angeregt wer<strong>de</strong>n.<br />

Metho<strong>de</strong>n.<br />

public boolean grabPixels() throws InterruptedException<br />

Die Werte von e<strong>in</strong>em Image o<strong>de</strong>r ImageProducer wer<strong>de</strong>n geholt. Die Funktion kann von außen<br />

unterbrochen wer<strong>de</strong>n.<br />

public <strong>in</strong>t getHeight()<br />

liefert die Höhe <strong>de</strong>s Pixelfelds. Ist die Höhe nicht verfügbar, dann ist das Ergebnis –1.<br />

public <strong>in</strong>t getWidth() liefert die Breite <strong>de</strong>s Pixelfelds.<br />

Bsp.: Das nachfolgen<strong>de</strong> Bild lädt e<strong>in</strong> Bild und gibt die Farb<strong>in</strong>formationen auf <strong>de</strong>r<br />

Konsole aus.<br />

import javax.sw<strong>in</strong>g.*;<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

import java.awt.image.*;<br />

public class PR42400 extends Frame<br />

{<br />

Image bild;<br />

<strong>in</strong>t breite, hoehe;<br />

<strong>in</strong>t pixels[];<br />

public PR42400()<br />

{<br />

282


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

/*<br />

bild = Toolkit.getDefaultToolkit().getImage(<br />

"d:\\jdk1.3.1\\pgj\\progr\\pr42400\\B01000800.jpg");<br />

*/<br />

bild = new ImageIcon("B01000800.jpg").getImage();<br />

if (bild == null) System.out.pr<strong>in</strong>tln("Ke<strong>in</strong> Bild");<br />

breite = bild.getWidth(this);<br />

System.out.pr<strong>in</strong>tln(breite);<br />

hoehe = bild.getHeight(this);<br />

pixels = new <strong>in</strong>t [breite * hoehe];<br />

PixelGrabber grabber = new PixelGrabber(bild,0,0,breite,hoehe,<br />

pixels,0,breite);<br />

try {<br />

grabber.grabPixels();<br />

}<br />

catch(InterruptedException e)<br />

{<br />

System.err.pr<strong>in</strong>tln("Fehler beim Holen <strong>de</strong>r Pixel");<br />

}<br />

setSize(breite,hoehe);<br />

repa<strong>in</strong>t();<br />

addMouseListener(new MouseAdapter()<br />

{<br />

public void mouseClicked(MouseEvent m)<br />

{<br />

<strong>in</strong>t pixel = pixels[breite * m.getY() + m.getX()];<br />

<strong>in</strong>t alpha = (pixel >> 24) & 0xff;<br />

<strong>in</strong>t rot = (pixel >> 16) & 0xff;<br />

<strong>in</strong>t gruen = (pixel >> 8) & 0xff;<br />

<strong>in</strong>t blau = (pixel) & 0xff;<br />

System.out.pr<strong>in</strong>tln("R=" +rot+ " G=" +gruen+ "B=" +blau);<br />

}<br />

});<br />

}<br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

if (bild != null)<br />

g.drawImage(bild,0,0,this);<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

Frame f = new PR42400();<br />

f.show();<br />

}<br />

}<br />

283


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

4.3 <strong>Java</strong> 2D<br />

Überblick<br />

<strong>Java</strong> 2D ist Bestandteil <strong>de</strong>r <strong>Java</strong> Foundation Classes (JFC) 253 , e<strong>in</strong> Teil <strong>de</strong>ssen, was<br />

Sun Sw<strong>in</strong>g nennt und sämtliche Techniken umfaßt, die die GUI-Fähigkeit von <strong>Java</strong><br />

ausmachen. Das neue <strong>Java</strong>2D-API ist e<strong>in</strong> Satz von Klassen für erweiterte 2D-Grafik<br />

und die Bildverarbeitung. <strong>Java</strong> 2D unterstützt:<br />

- Shapes (Formen): L<strong>in</strong>ien, Polygonzüge, Rechtecke, Ellipsen, parametrische Kurven, etc.<br />

- Strokes (Stricharten): Striche verschie<strong>de</strong>ner Dicke und Farbe zum Zeichnen von Shapes,<br />

ausgezogen o<strong>de</strong>r aus Punkten, etc., zusammengesetzt sptze o<strong>de</strong>r abgerun<strong>de</strong>te Ecken.<br />

- Fill<strong>in</strong>g (Ausfüllen): geschlossene Polygonzüge o<strong>de</strong>r Kurven bil<strong>de</strong>n Figuren, die mit Farben,<br />

Mustern, Texturen, etc. (Pa<strong>in</strong>ts) ausgefüllt wer<strong>de</strong>n können.<br />

- Antialias<strong>in</strong>g: Geometrische Formen ergeben auf <strong>de</strong>m Rasterschirm oft "unschöne Treppeneffekte"<br />

(Alias<strong>in</strong>g). Mit Antalias<strong>in</strong>g-Techniken können diese Effekte i. allg. gemil<strong>de</strong>rt wer<strong>de</strong>n.<br />

- Images (Rasterbil<strong>de</strong>r), z.B. e<strong>in</strong>gescannte Fotografien.<br />

- Text: Unterstützung verschie<strong>de</strong>ner Zeichensätze (Fonts) und Darstellungsarten (Text Layout).<br />

- Transformations: Translation (Verschieben), Rotation, Skalierung (Vergrössern, Verkle<strong>in</strong>ern),<br />

Verzerrung, ets. von Grafiken, Text und Bil<strong>de</strong>rn (Images)<br />

- Clipp<strong>in</strong>g (Wegschnei<strong>de</strong>n, Kappen): z.B. hat <strong>in</strong> e<strong>in</strong>em Fenster nur e<strong>in</strong> Teil e<strong>in</strong>er Grafik Platz, dann<br />

wird <strong>de</strong>r Rest entfernt ("weggeschnitten").<br />

- Composites: Bil<strong>de</strong>r, Figuren, etc. wer<strong>de</strong>n übere<strong>in</strong>an<strong>de</strong>r gelegt. Wie wer<strong>de</strong>n die e<strong>in</strong>zelnen<br />

schichten gewichtet, z.B. bzgl. <strong>de</strong>r Durchsichtigkeit (Transparenz)?<br />

- Color: E<strong>in</strong>satz von Farben, verschie<strong>de</strong>ne Farbmo<strong>de</strong>lle verwendbar.<br />

- Image Process<strong>in</strong>g (Bildverarbeitung): z.B. Verschärfen, Farbkorrekturen, etc.<br />

- Pr<strong>in</strong>t<strong>in</strong>g (Drucken): Ausgabe aller <strong>Java</strong> 2D Grafiken auf e<strong>in</strong>em Drucker, Steuerungsmöglichkeiten,<br />

etc.<br />

Ren<strong>de</strong>r<strong>in</strong>g Mo<strong>de</strong>ll<br />

Der Kern von <strong>Java</strong> 2D ist die Klasse java.awt.Graphics2D. Sie ist e<strong>in</strong>e Erweiterung<br />

<strong>de</strong>r AWT-Klasse und stellt die Implementierung <strong>de</strong>s Ren<strong>de</strong>r<strong>in</strong>g-Mo<strong>de</strong>lls von <strong>Java</strong> 2D<br />

dar.<br />

Wie wird Grafik auf e<strong>in</strong> Ausgabegerät (Bildschirm, Drucker, Puffer) abgebil<strong>de</strong>t /<br />

übertragen?<br />

1. Je<strong>de</strong>s Graphics2D-Objekt ist mit e<strong>in</strong>em Ausgabeziel verknüpft, das festlegt, woh<strong>in</strong> geren<strong>de</strong>rt wird.<br />

Die Eigenschaften <strong>de</strong>s Ausgabeziels, wie z.B. Pixelformat und Auflösung wer<strong>de</strong>n durch e<strong>in</strong><br />

GraphicsConfiguration-Objekt beschieben<br />

2. Ren<strong>de</strong>r<strong>in</strong>g:<br />

(a) Durch Setzen von Attributen wird <strong>de</strong>r grafische Kontext 254 festgelegt.<br />

(b) Das darzustellen<strong>de</strong> Grafikobjekt (Shape, Text o<strong>de</strong>r Bild) wird ausgewählt.<br />

(c) Das Ren<strong>de</strong>r<strong>in</strong>g wird durchgeführt.<br />

Da die meisten Metho<strong>de</strong>n <strong>de</strong>n Zugriff auf e<strong>in</strong> allgeme<strong>in</strong>es Graphics-Objekt liefern ist<br />

e<strong>in</strong>e Typumwandlung (Cast) nach Graphics2D nötig.<br />

253 JFC ist e<strong>in</strong>e Erweiterung <strong>de</strong>s Abstract W<strong>in</strong>dow<strong>in</strong>g Toolkit (AWT): AWT = UIToolkit (Sw<strong>in</strong>g) + Draw<strong>in</strong>g<br />

Toolkit (2D API)<br />

254 Der grafische Kontext kann durch die folgen<strong>de</strong> Attribute festgelegt wer<strong>de</strong>n: Font (Schriftart), Stroke<br />

(Strichart), Pa<strong>in</strong>t (Füllfarbe, Textur), Composite (Vermischen von mehreren Grafikobjekten), Ren<strong>de</strong>r<strong>in</strong>g H<strong>in</strong>ts<br />

(Antialias<strong>in</strong>g, Dither<strong>in</strong>g, etc.), Clipp<strong>in</strong>g (Beschränkung <strong>de</strong>s Ren<strong>de</strong>r<strong>in</strong>g auf e<strong>in</strong>en bestimmten Ausschnitt),<br />

Transform (Aff<strong>in</strong>e Transformation)<br />

284


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Graphics<br />

{ abstract }<br />

Graphics2D<br />

{ abstract }<br />

Stroke stroke // Def<strong>in</strong>ition von L<strong>in</strong>ienstiften<br />

Pa<strong>in</strong>t pa<strong>in</strong>t<br />

// Erweiterung <strong>de</strong>s Farbmo<strong>de</strong>lls<br />

Aff<strong>in</strong>eTransform transform // erlaubt Verän<strong>de</strong>rungen <strong>de</strong>r Form<br />

Composite composite // gibt an, wie Objekte verschie<strong>de</strong>ner Farben überlagert wer<strong>de</strong>n<br />

// sollen, erlaubt weiche Farbübergänge (Anti-Alias<strong>in</strong>g)<br />

><br />

protected Graphics2D()<br />

><br />

public abstract void addRen<strong>de</strong>r<strong>in</strong>gH<strong>in</strong>ts(Map h<strong>in</strong>ts)<br />

public abstract void clip(Shape s)<br />

public abstract draw(Shape s)<br />

public abstract void drawImage(Image img, Aff<strong>in</strong>eTransform xForm, ImageObserver obs)<br />

public abstract void fill(Shape s)<br />

public abstract GraphicsConfiguration getDeviceConfiguration()<br />

public abstract FontRen<strong>de</strong>rContext getFontRen<strong>de</strong>rContext()<br />

public abstract Pa<strong>in</strong>t getPa<strong>in</strong>t()<br />

public abstract Object getRen<strong>de</strong>r<strong>in</strong>gH<strong>in</strong>t(Ren<strong>de</strong>r<strong>in</strong>gH<strong>in</strong>ts.Key h<strong>in</strong>tKey)<br />

public abstract Stroke getStroke()<br />

public abstract Aff<strong>in</strong>eTransform getTransform()<br />

public abstract void rotate(double theta)<br />

public abstract void scale(double sx, double sy)<br />

public abstract void setComposite(Composite comp)<br />

public abstract setPa<strong>in</strong>t(Pa<strong>in</strong>t pa<strong>in</strong>t)<br />

public abstract void setRen<strong>de</strong>r<strong>in</strong>gH<strong>in</strong>t(Ren<strong>de</strong>r<strong>in</strong>gH<strong>in</strong>ts.Key h<strong>in</strong>tKey, Object h<strong>in</strong>tValue)<br />

public abstract void setTransform(Aff<strong>in</strong>eTransform Tx)<br />

public abstract void transform(Aff<strong>in</strong>eTransform Tx)<br />

public abstract void translate(double tx, double ty)<br />

public abstract void translate(<strong>in</strong>t x, <strong>in</strong>t y)<br />

...<br />

Abb.: Die Klasse Graphics2D<br />

4.3.1 Das Zeichnen unter <strong>Java</strong>2D API<br />

Zeichnen unter <strong>de</strong>m AWT Draw<strong>in</strong>g Mo<strong>de</strong>l<br />

Dieses Mo<strong>de</strong>ll wur<strong>de</strong> bisher zum Zeichnen verwen<strong>de</strong>t, z. B.:<br />

import java.awt.*;<br />

import java.applet.*;<br />

public class MaleGefRechteck extends Applet<br />

{<br />

// Zeichnen unter <strong>de</strong>m AWT Draw<strong>in</strong>g Mo<strong>de</strong>l<br />

public void pa<strong>in</strong>t(Graphics g)<br />

285


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

{<br />

g.setColor(Color.red);<br />

g.fillRect(300,300,200,100);<br />

}<br />

}<br />

Die Vorgehensweise beim Bearbeiten für je<strong>de</strong> Komponente kann aus diesem Bsp.<br />

unmittelbar abgeleitet wer<strong>de</strong>n:<br />

1. Spezifikation <strong>de</strong>r notwendigen Atribute für die zu zeichnen<strong>de</strong> Form, z.B. die Farbe mit „setColor“.<br />

2. Def<strong>in</strong>ition <strong>de</strong>r zu zeichnen<strong>de</strong>n Form<br />

3. Festlegen <strong>de</strong>s genauen Aussehens <strong>de</strong>r Form, z.B. über e<strong>in</strong>e passen<strong>de</strong> Grafikmetho<strong>de</strong>.<br />

Das AWT biete e<strong>in</strong>fache grafische Funktionen an. Diese AWT-Funktionen besitzen<br />

aber<br />

- e<strong>in</strong>en niedrigen Abstraktionsgrad und ke<strong>in</strong>e objektorientierte Repräsentation von grafischen<br />

Objekten.<br />

- diskrete Fenster- statt kont<strong>in</strong>uierlicher Weltkoord<strong>in</strong>aten<br />

- e<strong>in</strong> "schwaches" Mo<strong>de</strong>ll zur Abbildung von Farben und Farbverläufen.<br />

Zeichnen mit <strong>Java</strong>2D<br />

Das <strong>Java</strong>2D API umfaßt zusätzliche unterstützen<strong>de</strong> Features zur Spezifizierung von<br />

Zeichenstiften, komplexen Formen und diversen Zeichenprozessen. Zur Nutzung<br />

dieser Möglichkeit muß <strong>de</strong>r Graphics-Parameter <strong>de</strong>r pa<strong>in</strong>t()-Metho<strong>de</strong> <strong>in</strong> e<strong>in</strong><br />

Graphics2D-Objekt gebracht wer<strong>de</strong>n („gecastet“) wer<strong>de</strong>n, z.B.:<br />

import java.awt.*;<br />

import java.applet.*;<br />

import java.awt.geom.*;<br />

public class MaleGefRechteck extends Applet<br />

{<br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

Graphics2D g2d = (Graphics2D) g;<br />

// Spezifikation <strong>de</strong>r Attribute<br />

g2d.setColor(Color.red);<br />

// Def<strong>in</strong>ition <strong>de</strong>r Form (Verwen<strong>de</strong> Even-Odd-Regel)<br />

GeneralPath path = new GeneralPath();<br />

path.moveTo(300.0f,400.0f); // untere l<strong>in</strong>ke Ecke<br />

path.l<strong>in</strong>eTo(500.0f,400.0f); // untere rechte Ecke<br />

path.l<strong>in</strong>eTo(500.0f,300.0f); // obere rechte Ecke<br />

path.l<strong>in</strong>eTo(300.0f,300.0f); // obere l<strong>in</strong>ke Ecke<br />

path.closePath();<br />

// Schliessen <strong>de</strong>s Rechtecks<br />

// Fuellen <strong>de</strong>r Form<br />

g2d.fill(path);<br />

}<br />

}<br />

E<strong>in</strong>e Polygon-Klasse wie unter <strong>de</strong>m AWT gibt es <strong>in</strong> <strong>de</strong>m 2D-API nicht. Hier wird e<strong>in</strong><br />

neuer Weg e<strong>in</strong>geschlagen, <strong>de</strong>r über die Klasse GeneralPath geht. Damit lassen<br />

sich beliebige Formen bil<strong>de</strong>n. Dem Pfad wer<strong>de</strong>n verschie<strong>de</strong>ne Punkte h<strong>in</strong>zugefügt,<br />

die dann verbun<strong>de</strong>n wer<strong>de</strong>n. Die Punkte müssen nicht zw<strong>in</strong>gend, wie bei e<strong>in</strong>em<br />

Polygon mit L<strong>in</strong>ien verbun<strong>de</strong>n wer<strong>de</strong>n, son<strong>de</strong>rn lassen sich auch durch quadratische<br />

o<strong>de</strong>r kubische Kurven verb<strong>in</strong><strong>de</strong>n. Da Graphics2D e<strong>in</strong>e Unterklasse von Graphics ist,<br />

lassen sich natürlich alle AWT-Operatoren weiter verwen<strong>de</strong>n.<br />

286


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Bsp.: Aufbau e<strong>in</strong>es "roten Polygons" aus e<strong>in</strong>em Pfad 255<br />

import java.awt.*;<br />

import java.applet.*;<br />

import java.awt.geom.*;<br />

public class PolyRot extends Applet<br />

{<br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

// Erzeuegen e<strong>in</strong>es Graphics2D-Objekt<br />

Graphics2D g2d = (Graphics2D) g;<br />

g2d.setColor(Color.blue);<br />

// Def<strong>in</strong>ition <strong>de</strong>r Form<br />

GeneralPath e<strong>in</strong>Pfad = new GeneralPath();<br />

// Startpunkt festlegen (Ursprung <strong>de</strong>r sichtbaren<br />

// Zeichenflaeche <strong>de</strong>s Fensters<br />

e<strong>in</strong>Pfad.moveTo(getInsets().left,getInsets().top);<br />

for (<strong>in</strong>t i = 1, j = 10;i


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Verfahrensweise beim Zeichnen mit <strong>de</strong>n <strong>Java</strong> 2D-API-Klassen<br />

1. Spezifikation <strong>de</strong>r notwendigen beschreiben<strong>de</strong>n Attribute<br />

2. Def<strong>in</strong>ition <strong>de</strong>r Form, e<strong>in</strong>es Textstr<strong>in</strong>gs o<strong>de</strong>r e<strong>in</strong>es Bilds.<br />

Das <strong>Java</strong>2D-API behan<strong>de</strong>lt Positionsangeben (Pfa<strong>de</strong>), Texte und Bil<strong>de</strong>r gleichartig.<br />

Sie können rotiert, skaliert, verzerrt und mit diversen Metho<strong>de</strong>n zusammengesetzt<br />

wer<strong>de</strong>n. Das „Shape“-Interface <strong>de</strong>f<strong>in</strong>iert e<strong>in</strong>en ganzen Satz von Metho<strong>de</strong>n zur<br />

Beschreibung von geometrischen PATH-Objekten. GeneralPath ist e<strong>in</strong>e<br />

Implementation vom Shape-Interface, das zur Def<strong>in</strong>ition von beliebig komplexen<br />

Formen (zusammengestzt aus L<strong>in</strong>ien- und Kurvensegmenten) verwen<strong>de</strong>t wer<strong>de</strong>n<br />

kann.<br />

><br />

java.awt.Shape<br />

java.awt.Polygon java.awt.geom.L<strong>in</strong>e2D java.awt.geom.GeneralPath java.awt.geom.Area<br />

java.awt.geom.RectangularShape<br />

java.awt.geom.Arc2D java.awt.geom.Ellipse2D java.awt.geom.Rectangle2D<br />

Abb.: das Interface Shape und implementieren<strong>de</strong> Klassen<br />

draw() aus <strong>de</strong>r Klasse Graphics2D nimmt e<strong>in</strong> Shape-Objekt und zeichnet es.<br />

Shape-Objekte s<strong>in</strong>d L<strong>in</strong>ien, Polygone o<strong>de</strong>r auch Kurven.<br />

abstract public void draw(Shape s) zeichnet die Forrm im aktuellen<br />

Graphics2D-Kontext.<br />

Bsp.: E<strong>in</strong> Kreis <strong>in</strong>nerhalb von e<strong>in</strong>em Kasten. 256<br />

import javax.sw<strong>in</strong>g.*;<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

import java.awt.geom.*;<br />

public class ShapeBsp extends JPanel<br />

{<br />

private Ellipse2D.Double kreis<br />

= new Ellipse2D.Double(10,10,350,350);<br />

private Rectangle2D.Double quadrat<br />

= new Rectangle2D.Double(10,10,350,350);<br />

public void pa<strong>in</strong>tComponent(Graphics g)<br />

{<br />

clear(g);<br />

Graphics2D g2d = (Graphics2D) g;<br />

256 vgl. PR43100<br />

288


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

g2d.fill(kreis);<br />

g2d.draw(quadrat);<br />

}<br />

protected void clear(Graphics g)<br />

{<br />

super.pa<strong>in</strong>tComponent(g);<br />

// loescht die Pixelmap <strong>de</strong>s Bildschirms<br />

// per Default gibt es Double Buffer<strong>in</strong>g<br />

}<br />

protected Ellipse2D.Double getCircle()<br />

{<br />

return (kreis);<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

ShapeBsp jp = new ShapeBsp();<br />

Str<strong>in</strong>g titel = jp.getClass().toStr<strong>in</strong>g();<br />

if (titel.<strong>in</strong><strong>de</strong>xOf("class") != -1)<br />

titel = titel.substr<strong>in</strong>g(6);<br />

JFrame frame = new JFrame(titel);<br />

frame.addW<strong>in</strong>dowListener(new W<strong>in</strong>dowAdapter()<br />

{<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{<br />

System.exit(0);<br />

}<br />

});<br />

frame.getContentPane().add(jp,Bor<strong>de</strong>rLayout.CENTER);<br />

frame.setSize(380,400);<br />

frame.setVisible(true);<br />

}<br />

}<br />

3. Festlegen vom Aussehen <strong>de</strong>r Form, <strong>de</strong>s Textstr<strong>in</strong>gs o<strong>de</strong>r <strong>de</strong>s Bil<strong>de</strong>s über<br />

passen<strong>de</strong> Graphics2D-Metho<strong>de</strong>n.<br />

289


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

290


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Geometrische Objekte<br />

<strong>Java</strong>2D unterschei<strong>de</strong>t folgen<strong>de</strong> geometrische Objekte:<br />

- Shapes: beliebige offene o<strong>de</strong>r geschlossene Figuren, zusammengesetzt aus Folgen von L<strong>in</strong><strong>in</strong>en und<br />

Kurvensegmenten.<br />

- Text: Text <strong>in</strong> vorhert festgelegtem Zeichensatz (Zeichen s<strong>in</strong>d als Shapes <strong>de</strong>f<strong>in</strong>iert)<br />

- Image (rechteckiger Ausschnitt e<strong>in</strong>er Bitmap).<br />

Je<strong>de</strong>m geometrischen Objekt (Punkt, Klasse, Rechteck) ist e<strong>in</strong> <strong>Java</strong>-Klasse<br />

zugeordnet. Das konkrete Aussehen <strong>de</strong>s Objekts (Farbe, Umriss) wird erst bei <strong>de</strong>r<br />

Darstellung festgelegt. Die Darstellung erfolgt über e<strong>in</strong>en Rasterisierer 257 , <strong>de</strong>r<br />

Kompositionsvorschriften für kont<strong>in</strong>uierliche geometrische Objekte <strong>in</strong><br />

Weltkoord<strong>in</strong>aten über e<strong>in</strong> diskretes Raster umsetzt.<br />

Allgeme<strong>in</strong>e Vorgehensweise<br />

1. Setzen von Graphikattributen (ren<strong>de</strong>r<strong>in</strong>g attributes) wie L<strong>in</strong>ientyp, Füllmuster (auch<br />

komplexe Gradienten).<br />

2. Def<strong>in</strong>ition <strong>de</strong>r Form, e<strong>in</strong>er Zeichenkette (auszugeben<strong>de</strong>r Text) o<strong>de</strong>r e<strong>in</strong>es Bilds.<br />

3. Anwendung e<strong>in</strong>er Transformation (z.B. Bild verzerren o<strong>de</strong>r Filtern, Text drehen<br />

etc.).<br />

4. Ausgabe e<strong>in</strong>es graphischen Elements (z.B. durch die Graphics2D-Metho<strong>de</strong>n<br />

draw(), drawImage(), drawRectangle() o<strong>de</strong>r fill().<br />

Die eigentliche Ausgabe erfolgt <strong>in</strong> 4 Schritten:<br />

1. Das Objekt wird <strong>in</strong> Grafikprimitive umgewan<strong>de</strong>lt und auf <strong>de</strong>n Koord<strong>in</strong>atenraum <strong>de</strong>s Ausgabegerätes<br />

(<strong>de</strong>vice space) abgebil<strong>de</strong>t.<br />

- Han<strong>de</strong>lt es sich um e<strong>in</strong>e Form (Shape), so wer<strong>de</strong>n die <strong>in</strong> <strong>de</strong>r Form enthaltenen Graphikelemente<br />

bestimmt.<br />

- Han<strong>de</strong>lt es sich um e<strong>in</strong>en Text, wird die Form <strong>de</strong>r Buchstaben (Glyphen) aus <strong>de</strong>r Schrift<strong>in</strong>formation<br />

bestimmt und <strong>in</strong> e<strong>in</strong>en Umriß (outl<strong>in</strong>e) umgewan<strong>de</strong>lt, <strong>de</strong>r sich als Shape-Objekt beschreiben lässt.<br />

- Bei Bil<strong>de</strong>rn (images) wird ihre Bound<strong>in</strong>g Box <strong>in</strong> Device-Koord<strong>in</strong>aten umgewan<strong>de</strong>lt (unter<br />

Verwendung <strong>de</strong>r im Programm angegebenen Transformationen <strong>de</strong>s Graphics2D-Kontextes)<br />

2. Der aktuelle clipp<strong>in</strong>g path schränkt die Darstellungsoperation (ren<strong>de</strong>r<strong>in</strong>g operation) e<strong>in</strong>. E<strong>in</strong><br />

clipp<strong>in</strong>g path kann je<strong>de</strong> Form annehmen, die sich durch e<strong>in</strong> Shape-Objekt beschreiben lässt.<br />

Normalerweise han<strong>de</strong>lt es sich um <strong>de</strong>n Bereich <strong>de</strong>r gera<strong>de</strong> tatsächlich gezeichnet wer<strong>de</strong>n kann (z.B.<br />

sichtbare Fläche e<strong>in</strong>es Fensters).<br />

3. Die Ausgabefarbe wird bestimmt (aus <strong>de</strong>n Bilddaten bei Bil<strong>de</strong>rn, aus <strong>de</strong>m aktuellen Pa<strong>in</strong>t- o<strong>de</strong>r<br />

Color-Objekt <strong>in</strong> allen an<strong>de</strong>ren Fällen.<br />

4. Die Farbe wird aus das auszugeben<strong>de</strong> Objekt angewandt.<br />

<strong>Java</strong> 2D API-Klassen 258<br />

Außer <strong>de</strong>r Klasse java.awt.Graphics2D umfaßt <strong>Java</strong> 2D API Klassen <strong>in</strong> <strong>de</strong>n<br />

Paketen java.awt.geom (geometrische Formen, Pfa<strong>de</strong> und Transformationen),<br />

257 Die kont<strong>in</strong>uierlichen Shapes (z.B. e<strong>in</strong> Kreis) wer<strong>de</strong>n vom Rasterisierer <strong>in</strong> diskrete Rasterbelegungen<br />

umgewan<strong>de</strong>lt. Man unterschei<strong>de</strong>t Rasterisierung mit o<strong>de</strong>r ohne Alias<strong>in</strong>g:<br />

258 pr43210<br />

291


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

java.awt.font, java.awt.color (Farbräume), java.awt.image und<br />

java.awt.image.ren<strong>de</strong>rable (Bitmaps und Filter) sowie java.awt.pr<strong>in</strong>t<br />

(Drucken).<br />

Die Klassen für geometrische Objekte, die sich von <strong>de</strong>r Klasse Shape ableiten, s<strong>in</strong>d<br />

Polygon, RectangularShape, Rectangle, Area, QuadCurve2D und<br />

CubicCurve2D 259 . E<strong>in</strong>e beson<strong>de</strong>re Klasse, die auch von Shape abgeleitet ist, heißt<br />

GeneralPath. Damit lassen sich mehrere Objekte e<strong>in</strong>er Figur zusammensetzten.<br />

Die Klasse Rectangle2D, RoundRectangle2D, Aread2D und Ellipse2D erben<br />

von <strong>de</strong>r Klasse RectangularShape und s<strong>in</strong>d Objekte, die durch e<strong>in</strong>e rechteckige<br />

Box umgeben s<strong>in</strong>d.<br />

4.3.2 Eigenschaften geometrischer Objekte<br />

Dicke und Art <strong>de</strong>r L<strong>in</strong>ien bestimmen<br />

Die Metho<strong>de</strong> setStroke() kann <strong>de</strong>f<strong>in</strong>ieren<br />

- die Dicke (width)<br />

- die Eigenschaft wie e<strong>in</strong> L<strong>in</strong>iensegment beg<strong>in</strong>nt und abschließt<br />

- die Art, wie sich die L<strong>in</strong>ien verb<strong>in</strong><strong>de</strong>n<br />

- e<strong>in</strong> L<strong>in</strong>ien-Pattern (dash attributes)<br />

Unterstützt wird setStroke() durch die Schnittstelle Stroke, die konkret durch<br />

BasicStroke implementiert wird, z.B.<br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

Graphics2D g2 = (Graphics2D) g;<br />

BasicStroke pen =<br />

new BasicStroke(2.0f,BasicStroke.CAP_BUTT,BasicStroke.JOIN_ROUND)<br />

g2.setStroke(pen);<br />

}<br />

Für BasicStroke gibt es 9 Konstruktoren. Der hier verwen<strong>de</strong>te Konstruktor von<br />

BasicStroke har drei Argumente:<br />

- e<strong>in</strong>en float-Wert, <strong>de</strong>r die L<strong>in</strong>ienstärke angibt<br />

- e<strong>in</strong>en <strong>in</strong>t-Wert, <strong>de</strong>r die Art <strong>de</strong>s L<strong>in</strong>ienen<strong>de</strong>s festlegt<br />

- e<strong>in</strong>en <strong>in</strong>t-Wert, <strong>de</strong>r <strong>de</strong>n Stil <strong>de</strong>s Verb<strong>in</strong>dungsstücks zwischen zwei L<strong>in</strong>iensegmenten festlegt<br />

Beson<strong>de</strong>rs bei breiten L<strong>in</strong>ie ist es <strong>in</strong>teressant, wie die L<strong>in</strong>ie en<strong>de</strong>t. Hier läßt sich aus<br />

CAP-BUTT (ke<strong>in</strong>e Abschlußpunkte), CAP_ROUND (Anzeige von Kreisen an bei<strong>de</strong>n<br />

En<strong>de</strong>n) und CAP_SQUARE auswählen.<br />

Die möglichen Stile für Verb<strong>in</strong>dungselemente s<strong>in</strong>d JOIN_MITER (Abschluß von<br />

L<strong>in</strong>ien so, daß sie senkrecht aufe<strong>in</strong>an<strong>de</strong>r stehen), JOIN_ROUND (Abschluß <strong>de</strong>r L<strong>in</strong>ien<br />

durch abgerun<strong>de</strong>te Ecken) und JOIN-BEVEL (E<strong>in</strong>e L<strong>in</strong>ie wird zwischen <strong>de</strong>n äußeren<br />

Eckpunkten gezogen).<br />

259 Beschreibung quadratischer und kubischer Kurvensegmente. Das s<strong>in</strong>d Kurven, die durch zwei Endpunkte<br />

und durch Kontrollpunkte dazwischen gegeben s<strong>in</strong>d. Kubische Kurvensegmente wer<strong>de</strong>n auch Bézier-Kurven<br />

genannt.<br />

292


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Bsp 260 .: Das folgen<strong>de</strong> Applet zeigt Verb<strong>in</strong>dungselemente zwischen L<strong>in</strong>ien<br />

import java.awt.*;<br />

import java.applet.*;<br />

import java.awt.geom.*;<br />

public class StrokeDemo extends Applet<br />

{<br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

Graphics2D g2d = (Graphics2D) g;<br />

BasicStroke pen1 = new BasicStroke(10.0f,BasicStroke.CAP_BUTT,<br />

BasicStroke.JOIN_MITER);<br />

g2d.setStroke(pen1);<br />

g2d.draw(new L<strong>in</strong>e2D.Float(10f,10f,210f,10f));<br />

BasicStroke pen2 = new BasicStroke(10.0f,BasicStroke.CAP_ROUND,<br />

BasicStroke.JOIN_MITER);<br />

g2d.setStroke(pen2);<br />

g2d.draw(new L<strong>in</strong>e2D.Float(10f,30f,210f,30f));<br />

BasicStroke pen3 = new BasicStroke(10.0f,BasicStroke.CAP_SQUARE,<br />

BasicStroke.JOIN_MITER);<br />

g2d.setStroke(pen3);<br />

g2d.draw(new L<strong>in</strong>e2D.Float(10f,50f,210f,50f));<br />

BasicStroke pen4 = new BasicStroke(10.0f,BasicStroke.CAP_BUTT,<br />

BasicStroke.JOIN_MITER);<br />

g2d.setStroke(pen4);<br />

g2d.draw(new L<strong>in</strong>e2D.Float(10f,70f,100f,70f));<br />

g2d.draw(new L<strong>in</strong>e2D.Float(100f,70f,100f,120f));<br />

g2d.draw(new L<strong>in</strong>e2D.Float(100f,120f,10f,120f));<br />

g2d.draw(new L<strong>in</strong>e2D.Float(10f,120f,10f,70f));<br />

BasicStroke pen5 = new BasicStroke(10.0f,BasicStroke.CAP_BUTT,<br />

BasicStroke.JOIN_BEVEL);<br />

g2d.setStroke(pen5);<br />

g2d.draw(new L<strong>in</strong>e2D.Float(120f,70f,210f,70f));<br />

g2d.draw(new L<strong>in</strong>e2D.Float(210f,70f,210f,120f));<br />

g2d.draw(new L<strong>in</strong>e2D.Float(210f,120f,120f,120f));<br />

g2d.draw(new L<strong>in</strong>e2D.Float(120f,120f,120f,70f));<br />

BasicStroke pen6 = new BasicStroke(10.0f,BasicStroke.CAP_BUTT,<br />

BasicStroke.JOIN_MITER);<br />

g2d.setStroke(pen6);<br />

g2d.draw(new L<strong>in</strong>e2D.Float(30f,140f,10f,160f));<br />

g2d.draw(new L<strong>in</strong>e2D.Float(30f,140f,50f,160f));<br />

BasicStroke pen7 = new BasicStroke(10.0f,BasicStroke.CAP_BUTT,<br />

BasicStroke.JOIN_BEVEL);<br />

g2d.setStroke(pen7);<br />

g2d.draw(new L<strong>in</strong>e2D.Float(90f,140f,70f,160f));<br />

g2d.draw(new L<strong>in</strong>e2D.Float(90f,140f,110f,160f));<br />

BasicStroke pen8 = new BasicStroke(10.0f,BasicStroke.CAP_BUTT,<br />

BasicStroke.JOIN_ROUND);<br />

g2d.setStroke(pen8);<br />

g2d.draw(new L<strong>in</strong>e2D.Float(150f,140f,130f,160f));<br />

g2d.draw(new L<strong>in</strong>e2D.Float(150f,140f,170f,160f));<br />

float dash[] = {10, 2};<br />

BasicStroke pen9 = new BasicStroke(2.0f,BasicStroke.CAP_BUTT,<br />

BasicStroke.JOIN_MITER,<br />

1,<br />

dash,0);<br />

g2d.setStroke(pen9);<br />

g2d.draw(new Rectangle2D.Float(10f,180f,50f,50f));<br />

BasicStroke pen10 = new BasicStroke(10.0f,BasicStroke.CAP_BUTT,<br />

260 pr43100<br />

293


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

BasicStroke.JOIN_ROUND);<br />

g2d.setStroke(pen10);<br />

g2d.draw(new Rectangle2D.Float(10f,250f,60f,70f));<br />

BasicStroke pen11 = new BasicStroke(10.0f,BasicStroke.CAP_BUTT,<br />

BasicStroke.JOIN_BEVEL);<br />

g2d.setStroke(pen11);<br />

g2d.draw(new Rectangle2D.Float(90f,250f,60f,70f));<br />

BasicStroke pen12 = new BasicStroke(10.0f,BasicStroke.CAP_BUTT,<br />

BasicStroke.JOIN_MITER);<br />

g2d.setStroke(pen11);<br />

g2d.draw(new Rectangle2D.Float(170f,250f,60f,70f));<br />

}<br />

}<br />

Abb.<br />

294


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

4.3.3 Koord<strong>in</strong>atensysteme und Transformation<br />

Das <strong>Java</strong> 2D-API unterschei<strong>de</strong>t zwischen<br />

- <strong>de</strong>m user coord<strong>in</strong>ate space (Koord<strong>in</strong>atenraum <strong>de</strong>s Benutzers)<br />

und<br />

- <strong>de</strong>m <strong>de</strong>vice coord<strong>in</strong>ate space (Koord<strong>in</strong>atensystem <strong>de</strong>s Ausgabegeräts)<br />

Zu <strong>de</strong>n möglichen Transformationen gehören<br />

- die Drehung (rotate(), setToRotation())<br />

- die Skalierung (scale(), setToScale())<br />

- die Verschiebung (translate(), setToTranslation())<br />

- die Verzerrung (shear(), setToShear())<br />

Das folgen<strong>de</strong> Beispiel 261 zeigt e<strong>in</strong> Rechteck, das zunächst im Ursprung <strong>de</strong>s<br />

Koord<strong>in</strong>atensystems (L<strong>in</strong>ke obere Ecke) gezeichnet wird. Anschließend wird <strong>de</strong>r<br />

Koord<strong>in</strong>atenraum durch sukzessive Anwendung von Transformationen<br />

- verschoben<br />

- verschoben und gedreht<br />

- verschoben, gedreht und skaliert<br />

- verschoben, gedreht, skaliert und verzerrt<br />

import javax.sw<strong>in</strong>g.*;<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

import java.awt.geom.*;<br />

public class TransformBsp extends JPanel<br />

{<br />

public void pa<strong>in</strong>tComponent(Graphics g)<br />

{<br />

clear(g);<br />

Graphics2D g2d = (Graphics2D) g;<br />

Rectangle e<strong>in</strong>Rechteck =<br />

new Rectangle(getInsets().left,<br />

getInsets().top,50,50);<br />

g2d.draw(e<strong>in</strong>Rechteck);<br />

// neues Transformationsmo<strong>de</strong>ll erzeugen<br />

Aff<strong>in</strong>eTransform dieTransformation = new Aff<strong>in</strong>eTransform();<br />

// Koord<strong>in</strong>atenraum um 50 x-, 50 y-E<strong>in</strong>heiten verschieben<br />

dieTransformation.translate(50.0,50.0);<br />

g2d.transform(dieTransformation);<br />

g2d.setColor(Color.lightGray);<br />

g2d.fill(e<strong>in</strong>Rechteck);<br />

// Koord<strong>in</strong>atenraum um 30 Grad gegen <strong>de</strong>r Uhrzeigers<strong>in</strong>n drehen<br />

dieTransformation.rotate(-Math.PI/12.0);<br />

g2d.transform(dieTransformation);<br />

g2d.setColor(g2d.getColor().darker());<br />

g2d.fill(e<strong>in</strong>Rechteck);<br />

// Koord<strong>in</strong>atenraum um die Faktoren 1.33 * x, 0.66 * y skalieren<br />

dieTransformation.scale(1.33f,0.66f);<br />

g2d.transform(dieTransformation);<br />

g2d.setColor(Color.black);<br />

g2d.fill(e<strong>in</strong>Rechteck);<br />

// Koord<strong>in</strong>atensystem um die Faktoren 0.1 * x, 0.1 * y verzerren<br />

261 vgl. pr43100<br />

295


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

dieTransformation.shear(0.1f,0.1f);<br />

g2d.transform(dieTransformation);<br />

g2d.setColor(Color.black);<br />

g2d.fill(e<strong>in</strong>Rechteck);<br />

}<br />

protected void clear(Graphics g)<br />

{<br />

super.pa<strong>in</strong>tComponent(g);<br />

// loescht die Pixelmap <strong>de</strong>s Bildschirms<br />

// per Default gibt es Double Buffer<strong>in</strong>g<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

TransformBsp tbsp = new TransformBsp();<br />

Str<strong>in</strong>g titel = tbsp.getClass().toStr<strong>in</strong>g();<br />

if (titel.<strong>in</strong><strong>de</strong>xOf("class") != -1)<br />

titel = titel.substr<strong>in</strong>g(6);<br />

JFrame frame = new JFrame(titel);<br />

frame.addW<strong>in</strong>dowListener(new W<strong>in</strong>dowAdapter()<br />

{<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{<br />

System.exit(0);<br />

}<br />

});<br />

frame.getContentPane().add(tbsp,Bor<strong>de</strong>rLayout.CENTER);<br />

frame.setSize(380,250);<br />

frame.setVisible(true);<br />

}<br />

}<br />

Abb.: Transformation e<strong>in</strong>es Rechtecks.<br />

Nach je<strong>de</strong>r Transformation wird das Rechteck neu gezeichnet.<br />

Aff<strong>in</strong>e Transformationen. Sie können durch e<strong>in</strong>fache Transformationsmatrizen<br />

beschrieben wer<strong>de</strong>n, z.B. Translation e<strong>in</strong>es Punktes (x,y) um (dx,dy):<br />

⎛1<br />

⎜<br />

⎜0<br />

⎜<br />

⎝0<br />

0<br />

1<br />

0<br />

dx⎞<br />

⎛ x⎞<br />

⎛ x + dx ⎞<br />

⎟ ⎜ ⎟ ⎜ ⎟<br />

dy⎟<br />

⋅ ⎜ y⎟<br />

= ⎜ y + dy⎟<br />

1 ⎟ ⎜ ⎟ ⎜ ⎟<br />

⎠ ⎝ 1⎠<br />

⎝ 1 ⎠<br />

296


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Bsp. 262 : Das Haus <strong>de</strong>s Nikolaus.<br />

Gegeben ist die folgen<strong>de</strong> pa<strong>in</strong>t()- Metho<strong>de</strong><br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

Graphics2D g2d = (Graphics2D) g;<br />

// Def<strong>in</strong>ition <strong>de</strong>r Form (Verwen<strong>de</strong> Even-Odd-Regel)<br />

GeneralPath path = new GeneralPath(GeneralPath.WIND_EVEN_ODD);<br />

path.moveTo(-25,50);<br />

path.l<strong>in</strong>eTo(-25.0f,0.0f); path.l<strong>in</strong>eTo(0.0f,-30.0f);<br />

path.l<strong>in</strong>eTo(25.0f,0.0f); path.l<strong>in</strong>eTo(-25.0f,0.0f);<br />

path.l<strong>in</strong>eTo(25.0f,50.0f); path.l<strong>in</strong>eTo(-25.0f,50.0f);<br />

path.l<strong>in</strong>eTo(25.0f,0.0f); path.l<strong>in</strong>eTo(25.0f,50.0f);<br />

path.closePath();<br />

// Schliessen<br />

g2d.draw(path);<br />

}<br />

Die Implementierung dieser pa<strong>in</strong>t()-Metho<strong>de</strong> zeigt das folgen<strong>de</strong> Fenster an:<br />

E<strong>in</strong>e Translation um (100,100) führt zu <strong>de</strong>m folgen<strong>de</strong>n Fenster:<br />

Diese Form wur<strong>de</strong> durch Implementieren <strong>de</strong>r folgen<strong>de</strong> pa<strong>in</strong>t()-Metho<strong>de</strong> erreicht:<br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

Graphics2D g2d = (Graphics2D) g;<br />

// Def<strong>in</strong>ition <strong>de</strong>r Form (Verwen<strong>de</strong> Even-Odd-Regel)<br />

262 pr43100<br />

297


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

GeneralPath path = new GeneralPath(GeneralPath.WIND_EVEN_ODD);<br />

path.moveTo(-25,50);<br />

path.l<strong>in</strong>eTo(-25.0f,0.0f); path.l<strong>in</strong>eTo(0.0f,-30.0f);<br />

path.l<strong>in</strong>eTo(25.0f,0.0f); path.l<strong>in</strong>eTo(-25.0f,0.0f);<br />

path.l<strong>in</strong>eTo(25.0f,50.0f); path.l<strong>in</strong>eTo(-25.0f,50.0f);<br />

path.l<strong>in</strong>eTo(25.0f,0.0f); path.l<strong>in</strong>eTo(25.0f,50.0f);<br />

path.closePath();<br />

// Schliessen<br />

// Transformation<br />

Aff<strong>in</strong>eTransform at = new Aff<strong>in</strong>eTransform();<br />

at.setToTranslation(100.0f,100.0f);<br />

g2d.transform(at);<br />

g2d.draw(path);<br />

}<br />

Zusätzlich zur Translation soll das Haus noch um 45 Grad im Uhrzeigers<strong>in</strong>n gedreht wer<strong>de</strong>n. Dabei<br />

muß unterschie<strong>de</strong>n wer<strong>de</strong>n zwischen <strong>de</strong>m Setzen e<strong>in</strong>er e<strong>in</strong>er Aff<strong>in</strong>en Transformation und <strong>de</strong>m<br />

Erweitern e<strong>in</strong>er Aff<strong>in</strong>en Transformation.<br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

Graphics2D g2d = (Graphics2D) g;<br />

// Def<strong>in</strong>ition <strong>de</strong>r Form (Verwen<strong>de</strong> Even-Odd-Regel)<br />

GeneralPath path = new GeneralPath(GeneralPath.WIND_EVEN_ODD);<br />

path.moveTo(-25,50);<br />

path.l<strong>in</strong>eTo(-25.0f,0.0f); path.l<strong>in</strong>eTo(0.0f,-30.0f);<br />

path.l<strong>in</strong>eTo(25.0f,0.0f); path.l<strong>in</strong>eTo(-25.0f,0.0f);<br />

path.l<strong>in</strong>eTo(25.0f,50.0f); path.l<strong>in</strong>eTo(-25.0f,50.0f);<br />

path.l<strong>in</strong>eTo(25.0f,0.0f); path.l<strong>in</strong>eTo(25.0f,50.0f);<br />

path.closePath();<br />

// Schliessen<br />

// Transformation<br />

Aff<strong>in</strong>eTransform at = new Aff<strong>in</strong>eTransform();<br />

// Setzen <strong>de</strong>r aff<strong>in</strong>en Transformation<br />

at.setToTranslation(100.0f,100.0f);<br />

// Erweitern <strong>de</strong>r aff<strong>in</strong>en Transformation<br />

at.rotate(Math.PI/4);<br />

g2d.transform(at);<br />

g2d.draw(path);<br />

}<br />

Die Geometrie vom Haus <strong>de</strong>s Nikolaus soll vergrößert wer<strong>de</strong>n.<br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

Graphics2D g2d = (Graphics2D) g;<br />

// Def<strong>in</strong>ition <strong>de</strong>r Form (Verwen<strong>de</strong> Even-Odd-Regel)<br />

GeneralPath path = new GeneralPath(GeneralPath.WIND_EVEN_ODD);<br />

298


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

path.moveTo(-25,50);<br />

path.l<strong>in</strong>eTo(-25.0f,0.0f); path.l<strong>in</strong>eTo(0.0f,-30.0f);<br />

path.l<strong>in</strong>eTo(25.0f,0.0f); path.l<strong>in</strong>eTo(-25.0f,0.0f);<br />

path.l<strong>in</strong>eTo(25.0f,50.0f); path.l<strong>in</strong>eTo(-25.0f,50.0f);<br />

path.l<strong>in</strong>eTo(25.0f,0.0f); path.l<strong>in</strong>eTo(25.0f,50.0f);<br />

path.closePath();<br />

// Schliessen<br />

// Transformation<br />

Aff<strong>in</strong>eTransform at = new Aff<strong>in</strong>eTransform();<br />

// Setzen <strong>de</strong>r aff<strong>in</strong>en Transformation<br />

at.setToTranslation(100.0f,100.0f);<br />

// Erweitern <strong>de</strong>r aff<strong>in</strong>en Transformation<br />

at.rotate(Math.PI/4);<br />

// Skalieren<br />

at.scale(1.5f,1.5f);<br />

g2d.transform(at);<br />

// Zeichnen<br />

g2d.draw(path);<br />

}<br />

Das Fenster zeigt: Die Skalierung <strong>in</strong> <strong>Java</strong>2D bee<strong>in</strong>flusst auch die Stroke- und Pa<strong>in</strong>t-Eigenschaften,<br />

d.h. bei e<strong>in</strong>er Vergrößerung wird auch die L<strong>in</strong>iendicke größer.<br />

Die vollständige Transformation kann durch dir folgen<strong>de</strong>Transformationsmatrix beschrieben wer<strong>de</strong>n<br />

(Kompostion von Transformationen):<br />

⎛1<br />

⎜<br />

⎜0<br />

⎜<br />

⎝0<br />

0<br />

1<br />

0<br />

dx⎞<br />

⎛cosα<br />

⎟ ⎜<br />

dy⎟<br />

⋅ ⎜ 0<br />

1 ⎟ ⎜<br />

⎠ ⎝ 0<br />

0<br />

s<strong>in</strong>α<br />

0<br />

0⎞<br />

⎛ sx<br />

⎟ ⎜<br />

0⎟<br />

⋅ ⎜ 0<br />

1⎟<br />

⎜<br />

⎠ ⎝ 0<br />

0<br />

sy<br />

0<br />

0⎞<br />

⎟<br />

0⎟<br />

⋅ geom _ objekt<br />

1⎟<br />

⎠<br />

299


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

4.3.4 <strong>Java</strong>2D-Pipel<strong>in</strong>e<br />

<strong>Java</strong>2D stellt geometische Objekte über e<strong>in</strong>e "Ren<strong>de</strong>r-"Pipel<strong>in</strong>e dar:<br />

fill()<br />

Transformation<br />

Shapes draw() Stroke<br />

Text drawStr() Font<br />

H<strong>in</strong>ts<br />

Rasterisierer<br />

Images drawImage() Ren<strong>de</strong>r<strong>in</strong>g H<strong>in</strong>ts<br />

Clipp<strong>in</strong>g Pa<strong>in</strong>t Composition Output Device<br />

Abb.: <strong>Java</strong>2D-Pipel<strong>in</strong>e<br />

Geometrische Objekte können neben ihrer Form (Shape, z.B. Rechteck) durch drei<br />

weitere Parameter beschrieben wer<strong>de</strong>n: Translation, Rotation, Skalierung. Die<br />

Anwendung dieser Parameter auf e<strong>in</strong> geometrisches Objekt wird als Transformation<br />

bezeichnet.<br />

Kont<strong>in</strong>uierliche Shapes wer<strong>de</strong>n vom Rasterisierer <strong>in</strong> diskrete Rasterbelegungen<br />

umgewan<strong>de</strong>lt. Man unterschei<strong>de</strong>t Rasterisierung mit o<strong>de</strong>r ohne Alias<strong>in</strong>g:<br />

- Alias<strong>in</strong>g: Raster-Pixel wird als belegt gekennzeichnet, wenn das Objekt meht als x% <strong>de</strong>s Pixels<br />

belegt<br />

- Antialias<strong>in</strong>g: <strong>de</strong>m Pixel wird <strong>de</strong>r Belegungsfaktor x zugewiesen und als Transparenzwert <strong>in</strong>terpretiert.<br />

Ren<strong>de</strong>r<strong>in</strong>g H<strong>in</strong>ts s<strong>in</strong>d H<strong>in</strong>weise o<strong>de</strong>r Wünsche an <strong>Java</strong>2D, welche Qualität bzw.<br />

welche Performance man gerne erreichen wür<strong>de</strong>. <strong>Java</strong>2D entschei<strong>de</strong>t dann selber,<br />

welche dieser Wünsche erfüllt wer<strong>de</strong>n (können).<br />

Die Ausgabe von <strong>Java</strong>2D erfolgt typischerweise <strong>in</strong> e<strong>in</strong> Fenster. Dieses Fenster zeigt<br />

nur e<strong>in</strong>en Ausschnitt <strong>de</strong>r potentiell unbegrenzten (virtuellen) <strong>Java</strong>2D-Zeichenfläche.<br />

Gemalt wer<strong>de</strong>n soll natürlich nur das, was auch tatsächlich <strong>in</strong> diesem sichtbaren<br />

Ausschnitt liegt. Dieses Zuschnei<strong>de</strong>n auf e<strong>in</strong>en Bereich wird als Clipp<strong>in</strong>g bezeichnet.<br />

<strong>Java</strong>2D erlaubt neben <strong>de</strong>m normalen Clipp<strong>in</strong>g auf e<strong>in</strong>em rechteckigen (Fenster-)<br />

Bereich auch das Clipp<strong>in</strong>g auf beliebige <strong>Java</strong>2D-Flächen.<br />

Im Pa<strong>in</strong>t-Schritt wer<strong>de</strong>n Farbe und Transparenz <strong>de</strong>r Pixel festgelegt. Dazu gehört<br />

u.a.:<br />

- die Ausprägung <strong>de</strong>r Umrissl<strong>in</strong>ie (durchgehend, gestrichelt, …)<br />

- die Art <strong>de</strong>r Füllung (feste Farbe, Farbverlauf, Muster, Bitmap, …)<br />

Falls e<strong>in</strong> Image gemalt wer<strong>de</strong>n soll, entfällt dieser Schritt, da alle Informationen<br />

implizit im Image enthalten s<strong>in</strong>d.<br />

300


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

<strong>Java</strong>2D erweitert das H<strong>in</strong>zufügen von Objekten um sog. Kompostionsregeln<br />

(bekannt als Porter-Duff-Regeln). Neben <strong>de</strong>r Porter-Duff-Komposition, die e<strong>in</strong><br />

Source-Objekt mit <strong>de</strong>r gesamten Zeichenfläche verknüpft, gibt es noch die sog.<br />

CAG-Komposition (Constructive Area Geometry). CAG verknüpft jeweils 2 (CAG-)<br />

Objekte zu e<strong>in</strong>em neuen CAG-Objekt.<br />

4.3.5 Farbmanagement unter <strong>Java</strong> 2D<br />

Die Klasse Color mit <strong>de</strong>m Interface Pa<strong>in</strong>t<br />

E<strong>in</strong> Objekt <strong>de</strong>r Klasse Color stellt e<strong>in</strong>e Farbe im RGB-Farbschema dar. Color<br />

implementiert das neue Interface Pa<strong>in</strong>t (Zeichenfarbe).<br />

><br />

Pa<strong>in</strong>t<br />

public static Color black;<br />

public static Color blue;<br />

public static Color cyan;<br />

…<br />

public static Color white;<br />

public static Color yellow;<br />

Color GradientPa<strong>in</strong>t TexturePa<strong>in</strong>t<br />

><br />

public Color(float r, float g, float b)<br />

public Color(<strong>in</strong>t rgb)<br />

public Color(<strong>in</strong>t rgba,boolean hasalppha)<br />

public Color(<strong>in</strong>t r,<strong>in</strong>t g,<strong>in</strong>t b)<br />

public Color(<strong>in</strong>t r,<strong>in</strong>tg,<strong>in</strong>t b,<strong>in</strong>t a)<br />

><br />

public Color brighter()<br />

public Color darker()<br />

public boolean equals()<br />

public <strong>in</strong>t getAlpha()<br />

public <strong>in</strong>t getBlue()<br />

…<br />

public static Color getColor(Str<strong>in</strong>g nm)<br />

public static getHSBColor(float h,float s,<br />

float b)<br />

public <strong>in</strong>t getRGB()<br />

public <strong>in</strong>t getTransparency()<br />

public static <strong>in</strong>t HSBtoRGB(float hue,<br />

float saturation,float brightness)<br />

public static float RGBtoHSB(<strong>in</strong>t r,<strong>in</strong>t g,<strong>in</strong>t b,<br />

float[] hsbwerte)<br />

public Str<strong>in</strong>g toStr<strong>in</strong>g()<br />

301


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Abb.: Die Klasse Color<br />

Farbübergänge<br />

<strong>Java</strong> 2D erlaubt die Verwendung von Farbübergängen (Gradienten) und Mustern zur<br />

Füllung beliebiger Formen. Dazu existieren zwei Klassen, die die Schnittstelle Pa<strong>in</strong>t<br />

implementieren:<br />

- GradientPa<strong>in</strong>t (repäsentiert e<strong>in</strong>en glatten Übergang von e<strong>in</strong>er Farbe zu e<strong>in</strong>er an<strong>de</strong>ren)<br />

- TexturePa<strong>in</strong>t für Füllmuster auf <strong>de</strong>r Basis e<strong>in</strong>er Bilddatei, die das Muster vorgibt.<br />

public class GradientPa<strong>in</strong>t extends Object implements Pa<strong>in</strong>t<br />

Konstruktoren.<br />

public GradientPa<strong>in</strong>t(float x1,float y1,Color c1,float x2,float y2,Color c2)<br />

public GradientPa<strong>in</strong>t(float x1,float y1,Color c1,float x2,float y2,<br />

Color c2,boolean cyclic)<br />

// c1 ist die Farbe am ersten Punkt(x1,y1), c2 ist die Farbe am zweiten Punkt (x2,y2). cyclic ist true,<br />

// falls <strong>de</strong>r Übergang zwischen <strong>de</strong>n Farben wie<strong>de</strong>rholt wer<strong>de</strong>n soll<br />

Der aktuelle Gradient wird über setPa<strong>in</strong>t() <strong>de</strong>s Graphics2D-Kontextes gesetzt.<br />

Bsp. 263 : E<strong>in</strong> Kreis wird mit e<strong>in</strong>em Farbübergang ausgestattet.<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

import java.awt.geom.*;<br />

public class GradientPa<strong>in</strong>tBsp extends JPanel<br />

{<br />

private Ellipse2D.Double kreis<br />

= new Ellipse2D.Double(10,10,350,350);<br />

private GradientPa<strong>in</strong>t gradient<br />

= new GradientPa<strong>in</strong>t(0,0,Color.red,175,175,Color.yellow,true);<br />

public void pa<strong>in</strong>tComponent(Graphics g)<br />

{<br />

clear(g);<br />

Graphics2D g2d = (Graphics2D) g;<br />

g2d.setPa<strong>in</strong>t(gradient);<br />

g2d.fill(getCircle());<br />

g2d.setPa<strong>in</strong>t(Color.black);<br />

g2d.draw(getCircle());<br />

}<br />

protected void clear(Graphics g)<br />

{<br />

super.pa<strong>in</strong>tComponent(g);<br />

// loescht die Pixelmap <strong>de</strong>s Bildschirms<br />

// per Default gibt es Double Buffer<strong>in</strong>g<br />

}<br />

protected Ellipse2D.Double getCircle()<br />

{<br />

return (kreis);<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

GradientPa<strong>in</strong>tBsp gbsp = new GradientPa<strong>in</strong>tBsp();<br />

Str<strong>in</strong>g titel = gbsp.getClass().toStr<strong>in</strong>g();<br />

if (titel.<strong>in</strong><strong>de</strong>xOf("class") != -1)<br />

263 pr43100<br />

302


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

titel = titel.substr<strong>in</strong>g(6);<br />

JFrame frame = new JFrame(titel);<br />

frame.addW<strong>in</strong>dowListener(new W<strong>in</strong>dowAdapter()<br />

{<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{<br />

System.exit(0);<br />

}<br />

});<br />

frame.getContentPane().add(gbsp,Bor<strong>de</strong>rLayout.CENTER);<br />

frame.setSize(380,400);<br />

frame.setVisible(true);<br />

}<br />

}<br />

Abb.: Ausstatten e<strong>in</strong>er Form mit e<strong>in</strong>em Farbgradienten<br />

public class TexturePa<strong>in</strong>t extends Object implements Pa<strong>in</strong>t<br />

Konstruktor.<br />

public TexturePa<strong>in</strong>t(BufferedImage texture,Rectangle2D anchor)<br />

// e<strong>in</strong> Bild wird über e<strong>in</strong>e Form ge<strong>de</strong>ckt.<br />

Metho<strong>de</strong>n.<br />

public Pa<strong>in</strong>tContext createContext(ColorMo<strong>de</strong>l cm, Rectangle <strong>de</strong>viceBounds,<br />

Rectangle2D userBounds, Aff<strong>in</strong>eTransform xForm, Ren<strong>de</strong>r<strong>in</strong>g h<strong>in</strong>ts)<br />

public BufferedImage getImage()<br />

public <strong>in</strong>t getTransparency()<br />

Bsp. 264 : E<strong>in</strong> Kreis wird mit e<strong>in</strong>em Füllmuster abge<strong>de</strong>ckt.<br />

264 pr43100<br />

303


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

import javax.sw<strong>in</strong>g.*;<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

import java.awt.geom.*;<br />

import java.awt.image.*;<br />

public class TexturePa<strong>in</strong>tBsp extends JPanel<br />

{<br />

private Ellipse2D.Double kreis<br />

= new Ellipse2D.Double(10,10,350,350);<br />

public void pa<strong>in</strong>tComponent(Graphics g)<br />

{<br />

clear(g);<br />

Graphics2D g2d = (Graphics2D) g;<br />

// g2d.fill(kreis);<br />

// g2d.draw(quadrat);<br />

BufferedImage buffImage =<br />

new BufferedImage(10,10,BufferedImage.TYPE_INT_RGB);<br />

Graphics2D gg = buffImage.createGraphics();<br />

gg.setColor(Color.yellow);<br />

gg.fillRect(0,0,10,10);<br />

gg.setColor( Color.black ); // draw <strong>in</strong> black<br />

gg.drawRect(1,1,6,6); // draw a rectangle<br />

gg.setColor(Color.blue ); // draw <strong>in</strong> blue<br />

gg.fillRect(1,1,3,3); // draw a filled rectangle<br />

gg.setColor(Color.red ); // draw <strong>in</strong> red<br />

gg.fillRect(4,4,3,3); // draw a filled rectangle<br />

g2d.fill(<br />

new RoundRectangle2D.Double(<br />

155, 30, 75, 100, 50, 50 ));<br />

// pa<strong>in</strong>t buffImage onto the Frame<br />

g2d.setPa<strong>in</strong>t(new TexturePa<strong>in</strong>t(buffImage, new Rectangle(10,10)));<br />

g2d.fill(getCircle());<br />

g2d.setPa<strong>in</strong>t(Color.black);<br />

g2d.draw(getCircle());<br />

}<br />

protected void clear(Graphics g)<br />

{<br />

super.pa<strong>in</strong>tComponent(g);<br />

// loescht die Pixelmap <strong>de</strong>s Bildschirms<br />

// per Default gibt es Double Buffer<strong>in</strong>g<br />

}<br />

protected Ellipse2D.Double getCircle()<br />

{<br />

return (kreis);<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

TexturePa<strong>in</strong>tBsp tbsp = new TexturePa<strong>in</strong>tBsp();<br />

Str<strong>in</strong>g titel = tbsp.getClass().toStr<strong>in</strong>g();<br />

if (titel.<strong>in</strong><strong>de</strong>xOf("class") != -1)<br />

titel = titel.substr<strong>in</strong>g(6);<br />

JFrame frame = new JFrame(titel);<br />

frame.addW<strong>in</strong>dowListener(new W<strong>in</strong>dowAdapter()<br />

{<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{<br />

System.exit(0);<br />

}<br />

});<br />

frame.getContentPane().add(tbsp,Bor<strong>de</strong>rLayout.CENTER);<br />

frame.setSize(380,400);<br />

frame.setVisible(true);<br />

}<br />

}<br />

304


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Abb.: Füllen e<strong>in</strong>er Form mit e<strong>in</strong>em Füllmuster aus e<strong>in</strong>er Bilddatei<br />

Transparentes Zeichnen mit Hilfe <strong>de</strong>r Klasse AlphaComposite<br />

Für je<strong>de</strong> geometrische Form kann e<strong>in</strong> Transparenzwert ("Alphakanal") angegeben<br />

wer<strong>de</strong>n, <strong>de</strong>r angibt, wie stark "<strong>de</strong>ckend" gezeichnet wer<strong>de</strong>n soll. Dazu <strong>in</strong>stanziert<br />

man e<strong>in</strong> Objekt von AlphaComposite und setzt <strong>de</strong>n entsprechen<strong>de</strong>n<br />

Zeichenparameter mit <strong>de</strong>r Metho<strong>de</strong> setComposite() <strong>de</strong>s Graphics2D-Kontextes.<br />

Bsp. 265 : E<strong>in</strong> rotes, halbtransparentes Rechteck liegt über e<strong>in</strong>em Muster.<br />

import java.awt.*;<br />

import java.applet.*;<br />

import java.awt.geom.*;<br />

public class PolyTransp extends Applet<br />

{<br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

// Erzeuegen e<strong>in</strong>es Graphics2D-Objekt<br />

Graphics2D g2d = (Graphics2D) g;<br />

g2d.setColor(Color.blue);<br />

// Def<strong>in</strong>ition <strong>de</strong>r Form<br />

GeneralPath e<strong>in</strong>Pfad = new GeneralPath();<br />

// Startpunkt festlegen (Ursprung <strong>de</strong>r sichtbaren<br />

// Zeichenflaeche <strong>de</strong>s Fensters<br />

e<strong>in</strong>Pfad.moveTo(getInsets().left,getInsets().top);<br />

for (<strong>in</strong>t i = 1, j = 10;i


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

}<br />

// <strong>de</strong>r Pfad wird geschlossen<br />

e<strong>in</strong>Pfad.closePath();<br />

// Zeichnen (blau) <strong>de</strong>s Umrisses vom PfadObjekt<br />

g2d.setColor(Color.blue);<br />

g2d.draw(e<strong>in</strong>Pfad);<br />

// neues Gradientenobjekt<br />

GradientPa<strong>in</strong>t <strong>de</strong>rFarbGradient =<br />

new GradientPa<strong>in</strong>t(getInsets().left,<br />

// Startpunkt<br />

getInsets().top,<br />

// Gradient<br />

new Color(255,255,255), // Farbe<br />

getSize().width,<br />

// Endpunkt<br />

getSize().height,<br />

// Gradient<br />

new Color(0,0,0));<br />

// Endfarbe<br />

// Gradient festlegen<br />

g2d.setPa<strong>in</strong>t(<strong>de</strong>rFarbGradient);<br />

// Pfad fuellen<br />

g2d.fill(e<strong>in</strong>Pfad);<br />

// Farbe festlegen (verdraengt <strong>de</strong>n Gradienten)<br />

g2d.setColor(Color.red);<br />

// Instanz e<strong>in</strong>es Alphakanals la<strong>de</strong>n (Wert: 50 Prozent = 0.5)<br />

AlphaComposite transparenzModus =<br />

AlphaComposite.getInstance(AlphaComposite.SRC_OVER,0.5f);<br />

// Transparenzwert festlegen<br />

g2d.setComposite(transparenzModus);<br />

// Zeichnen e<strong>in</strong>es Rechtecks (50 Pixel Rand an allen Seiten)<br />

g2d.fillRect(getInsets().left + 50,<br />

getInsets().top + 50,<br />

getSize().width - getInsets().right - 100,<br />

getSize().height - getInsets().bottom - 100);<br />

// g2d.setColor(Color.red);<br />

// g2d.fill(e<strong>in</strong>Pfad);<br />

}<br />

}<br />

Abb.: Transparente Überlagerung von Formen<br />

Wenn zwei e<strong>in</strong>an<strong>de</strong>r überlappen<strong>de</strong> Formen dargestellt wer<strong>de</strong>n sollen, muß geklärt<br />

wer<strong>de</strong>n, wie die Objekte sich zue<strong>in</strong>an<strong>de</strong>r verhalten sollen. Hierzu gibt es die<br />

Composite- und CompositeContext-Interfaces. E<strong>in</strong>e Implementierung dazu ist<br />

die AlphaComposite-Klasse, <strong>in</strong> <strong>de</strong>r die SRC_OVER-Regel die meistgenutzte ist.<br />

306


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

4.3.6 Text und Fonts unter <strong>Java</strong>2D<br />

Character, Glyphs 266 und Fonts<br />

E<strong>in</strong> Str<strong>in</strong>g lässt sich auf <strong>de</strong>r Ebene <strong>de</strong>r e<strong>in</strong>zelnen Zeichen auf vielfältige Art und<br />

Weise manipulieren, z.B. durch<br />

- verschie<strong>de</strong>ne Formatierungen<br />

- Ausrichtung<br />

- Position<br />

- Schriftart<br />

- Metrik<br />

- Größe<br />

- Umrisse<br />

Je<strong>de</strong>s Font-Objekt umfasst diese Attribute, die direkt über von <strong>de</strong>r Font-Klasse zur<br />

Verfügung gestellte Metho<strong>de</strong>n ansprechbar s<strong>in</strong>d.<br />

<strong>Java</strong>2D enthält e<strong>in</strong>e erweiterte Font-Klasse. die erheblich weitgehen<strong>de</strong>re<br />

Möglichkeiten zur Kontrolle von Schriftarten bereitstellt.<br />

Bsp. 267 : Das folgen<strong>de</strong> Programm listet die von <strong>Java</strong>2D bereitgestellten Fonts auf.<br />

import java.awt.*;<br />

/** Lists the names of all available fonts. */<br />

public class ListFonts<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

GraphicsEnvironment env =<br />

GraphicsEnvironment.getLocalGraphicsEnvironment();<br />

Str<strong>in</strong>g[] fontNames = env.getAvailableFontFamilyNames();<br />

System.out.pr<strong>in</strong>tln("Available Fonts:");<br />

for(<strong>in</strong>t i = 0; i < fontNames.length; i++)<br />

System.out.pr<strong>in</strong>tln(" " + fontNames[i]);<br />

}<br />

}<br />

Transformations- und Zeichentechnik<br />

Die Transformations- und Zeichentechnik von <strong>Java</strong>2D kann auch auf Textstr<strong>in</strong>gs<br />

angewen<strong>de</strong>t wer<strong>de</strong>n.<br />

Bsp. 268 : Rotation e<strong>in</strong>es Textstr<strong>in</strong>gs<br />

import java.awt.*;<br />

import java.awt.geom.*;<br />

import java.awt.event.*;<br />

import javax.sw<strong>in</strong>g.*;<br />

public class RotationBsp extends JPanel<br />

{<br />

private Ellipse2D.Double kreis<br />

= new Ellipse2D.Double(10,10,350,350);<br />

private GradientPa<strong>in</strong>t gradient<br />

= new GradientPa<strong>in</strong>t(0,0,Color.red,175,175,Color.yellow,true);<br />

private Color[] farben = { Color.white, Color.black };<br />

public RotationBsp()<br />

266 Die Vere<strong>in</strong>igung von <strong>de</strong>m Zeichen selbst und se<strong>in</strong>em exakten Aussehen wird Glyph genannt<br />

267 pr43100<br />

268 pr43100<br />

307


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

{<br />

GraphicsEnvironment env =<br />

GraphicsEnvironment.getLocalGraphicsEnvironment();<br />

env.getAvailableFontFamilyNames();<br />

setFont(new Font("Book Antiqua", Font.PLAIN, 90));<br />

}<br />

protected void clear(Graphics g)<br />

{<br />

super.pa<strong>in</strong>tComponent(g);<br />

// loescht die Pixelmap <strong>de</strong>s Bildschirms<br />

// per Default gibt es Double Buffer<strong>in</strong>g<br />

}<br />

protected Ellipse2D.Double getCircle()<br />

{<br />

return (kreis);<br />

}<br />

public void pa<strong>in</strong>tComponent(Graphics g)<br />

{<br />

clear(g);<br />

Graphics2D g2d = (Graphics2D) g;<br />

g2d.setPa<strong>in</strong>t(gradient);<br />

g2d.fill(getCircle());<br />

g2d.setPa<strong>in</strong>t(Color.black);<br />

g2d.draw(getCircle());<br />

g2d.translate(185.0,185.0);<br />

for (<strong>in</strong>t i = 0; i < 16; i++)<br />

{<br />

g2d.rotate(Math.PI/8.0);<br />

g2d.setPa<strong>in</strong>t(farben[i%2]);<br />

g2d.drawStr<strong>in</strong>g("<strong>Java</strong>",0,0);<br />

}<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

RotationBsp rotationBsp = new RotationBsp();<br />

Str<strong>in</strong>g titel = rotationBsp.getClass().toStr<strong>in</strong>g();<br />

if (titel.<strong>in</strong><strong>de</strong>xOf("class") != -1)<br />

titel = titel.substr<strong>in</strong>g(6);<br />

JFrame frame = new JFrame(titel);<br />

frame.addW<strong>in</strong>dowListener(new W<strong>in</strong>dowAdapter()<br />

{<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{<br />

System.exit(0);<br />

}<br />

});<br />

frame.getContentPane().add(rotationBsp,Bor<strong>de</strong>rLayout.CENTER);<br />

frame.setSize(380,400);<br />

frame.setVisible(true);<br />

}<br />

}<br />

308


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Abb.:<br />

4.3.7 2D Bildverarbeitung<br />

Zeichnen e<strong>in</strong>es Bilds.<br />

Mit Hilfe <strong>de</strong>r Graphics2d-Metho<strong>de</strong><br />

public boolean drawImage(BufferedImage image, BufferedImageOp filter,<br />

<strong>in</strong>t left, <strong>in</strong>t top)<br />

kann e<strong>in</strong> BufferedImage ab <strong>de</strong>r oberen l<strong>in</strong>ken Ecke (left,top) gezeichnet<br />

wer<strong>de</strong>n.<br />

Erstellen e<strong>in</strong>es BufferedImage aus e<strong>in</strong>em Image-File.<br />

public static BufferedImage getBufferedImage(Str<strong>in</strong>g imageFile, Component c)<br />

{<br />

Image image = c.getToolkit().getImage(imageFile);<br />

MediaTracker tracker = new MediaTracker(c); // nutzt MediaTracker<br />

tracker.addImage(image,0);<br />

try {<br />

tracker.waitForAll();<br />

}<br />

catch (InterruptedException ie) { }<br />

BufferedImage bufferedImage = new BufferedImage(image.getWidth(c),<br />

image.getHeight(c),BufferedImage.TYPE_INT_RGB);<br />

Graphics2D g2d = bufferedImage.createGraphics();<br />

g2d.drawImage(image,0,0,c);<br />

return bufferedImage);<br />

}<br />

309


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

BufferedImage-Objekte<br />

E<strong>in</strong> BufferedImage-Objekt besteht aus e<strong>in</strong>em Raster-Objekt und e<strong>in</strong>em ColorMo<strong>de</strong>l-<br />

Objekt.<br />

Der Raster kümmert sich um die Bildverwaltung, wobei <strong>de</strong>r DataBuffer die Bilddaten<br />

enthält und das SampleMo<strong>de</strong>l angibt, wie die Bilddaten zu <strong>in</strong>terpretien s<strong>in</strong>d.<br />

Das ColorMo<strong>de</strong>l gibt an, wie die Pixeldaten <strong>in</strong> Bezug auf ihre Farben ausgewertet<br />

wer<strong>de</strong>n sollen. E<strong>in</strong> BufferedImage kann als OffScreen-Puffer dienen. Damit kann<br />

man das Objekt schon im Speicher ren<strong>de</strong>rn, bevor man das Bild o<strong>de</strong>r nur <strong>de</strong>n<br />

benötigten Ausschnitt auf <strong>de</strong>n Bildschirm kopiert.<br />

4.3.8 Animationen mit <strong>Java</strong>2D<br />

Man kann mit <strong>Java</strong>2D e<strong>in</strong>fache Animationen erstellen, z.B.:<br />

310


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

5. AWT<br />

5.1 Bestandteile <strong>de</strong>s AWT<br />

In <strong>Java</strong> wird die Kommunikation mit <strong>de</strong>m Anwen<strong>de</strong>r hauptsächlich über e<strong>in</strong> eigenes<br />

Konzept realisiert – <strong>de</strong>m Abstract W<strong>in</strong>dow<strong>in</strong>g Toolkit. Das AWT besitzt zur<br />

Kommunikation mit <strong>de</strong>m Anwen<strong>de</strong>r e<strong>in</strong> Application Programm<strong>in</strong>g Interface (API).<br />

Darüber können allgeme<strong>in</strong>e Komponenten <strong>de</strong>r Benutzeroberfläche, z.B.<br />

Schaltflächen o<strong>de</strong>r Menüs, plattformunabhängig genutzt wer<strong>de</strong>n.<br />

5.2 Die AWT-Komponenten<br />

Die AWT stellt Komponenten für <strong>de</strong>n Anwen<strong>de</strong>r bereit. Die von Anfang an<br />

vorhan<strong>de</strong>nen Komponenten 269 <strong>de</strong>s AWT s<strong>in</strong>d: Schaltflächen (Buttons), Labels,<br />

Kontrollkästchen (Checkbuttons), Optionsfel<strong>de</strong>r (Radiobuttons), Listen,<br />

Auswahlfel<strong>de</strong>r, Textfel<strong>de</strong>r, Menüs, Zeichenbereiche.<br />

Zusätzlich gibt es Conta<strong>in</strong>er, <strong>in</strong> die die Komponenten zum Erstellen vollständiger und<br />

s<strong>in</strong>nvoller Anwendungsschnittstellen <strong>in</strong>tegriert se<strong>in</strong> müssen. Die Conta<strong>in</strong>er im AWT<br />

s<strong>in</strong>d Fenster, Panels, Frames, Dialoge.<br />

E<strong>in</strong> weiterer Bestandteil <strong>de</strong>r AWT s<strong>in</strong>d Layout-Manager. E<strong>in</strong> Layout-Manager ist <strong>in</strong><br />

je<strong>de</strong>m Conta<strong>in</strong>er enthalten. Er f<strong>in</strong><strong>de</strong>t – angepaßt an die jeweilige Situation –<br />

automatisch heraus, an welche Stelle die Komponenten am besten passen. Der AWT<br />

stellt 5 verschie<strong>de</strong>ne Typen von Layout-Managern zur Verfügung: Flow-, Bor<strong>de</strong>r-,<br />

Grid-, Gridbag-Layout.<br />

Schließlich stellt AWT die Mittel zur Reaktion auf Ereignisse zur Verfügung, die vom<br />

Anwen<strong>de</strong>r über Komponenten ausgelöst wer<strong>de</strong>n können.<br />

Die Wurzel fast aller AWT-Komponenten ist die Klasse Component. Sie enthält die<br />

grundlegen<strong>de</strong>n Anzeige- und Event-Handl<strong>in</strong>g-Funktionen.<br />

Component<br />

Canvas Conta<strong>in</strong>er TextComponent Button<br />

Panel W<strong>in</strong>dow TextField<br />

Applet Frame Dialog<br />

Abb. AWT-Klassenhierarchie<br />

269 <strong>Java</strong> 1.2 und das Sw<strong>in</strong>g-Konzept erweitern diese Komponenten noch e<strong>in</strong>mal um e<strong>in</strong>en satz von neuen und<br />

ergänzen<strong>de</strong>n Komponenten.<br />

311


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

5.2.1 Schaltflächen (Buttons)<br />

Component<br />

Button<br />

><br />

public Button()<br />

public Button(Str<strong>in</strong>g label)<br />

><br />

public void addActionListener(ActionListener l)<br />

public void addNotify()<br />

public Str<strong>in</strong>g getActionCommand()<br />

public Str<strong>in</strong>g getLabel()<br />

protected Str<strong>in</strong>g paramStr<strong>in</strong>g()<br />

protected void processActionEvent(ActionEvent e)<br />

protected void processEvent(AWTEvent e)<br />

public void removeActionListener(ActionListener l)<br />

public void setActionCommand(Str<strong>in</strong>g kommando)<br />

public void setLabel(Str<strong>in</strong>g label)<br />

Abb. Die Klasse Button<br />

Erzeugen. Schaltflächen (Buttons) s<strong>in</strong>d Elemente e<strong>in</strong>er grafischen<br />

Benutzeroberfläche, die auf Knopfdruck Aktionen <strong>in</strong> <strong>de</strong>r Fensterklasse auslösen. Mit<br />

<strong>de</strong>n folgen<strong>de</strong>n Konstruktoren kann e<strong>in</strong>e Schaltfläche erstellt wer<strong>de</strong>n:<br />

- Button() erzeugt e<strong>in</strong>e leere Schaltfläche ohne Beschriftung<br />

- Button(Str<strong>in</strong>g label) erzeugt e<strong>in</strong>e Schaltfläche mit <strong>de</strong>r durch das Str<strong>in</strong>g-Objekt<br />

bezeichneten Beschriftung.<br />

In e<strong>in</strong>em Applet 270 reicht dafür aus: add(new Button("Auf los geht's<br />

los!"));.<br />

Beschriftungen e<strong>in</strong>er Schaltfläche können dynamisch zur Laufzeit gesetzt und wie<strong>de</strong>r<br />

verän<strong>de</strong>rt wer<strong>de</strong>n. Dazu dient die Metho<strong>de</strong>: public void setLabel(Str<strong>in</strong>g<br />

label). Welche Beschriftung die Schaltfläche angenommen hat, ermittelt die<br />

Metho<strong>de</strong> public Str<strong>in</strong>g getLabel();.<br />

Reaktionen auf die Betätigung von Schaltflächen (JDK 1.1). Falls e<strong>in</strong> Button gedrückt<br />

wird, sen<strong>de</strong>t es e<strong>in</strong> Action-Event an se<strong>in</strong>e Ereignisempänger. Diese müssen das<br />

Interface ActionListener implementieren und sich durch <strong>de</strong>n Aufruf von<br />

„addActionListener“ registrieren:<br />

public void addActionListener(ActionListener l)<br />

public void removeActionListener(ActionListener l)<br />

Das Action-Event führt im Ereignisempfänger zum Aufruf <strong>de</strong>r Metho<strong>de</strong> public<br />

void actionPerformed(ActionEvent ae) ,die die Metho<strong>de</strong><br />

getActionCommand aufrufen kann, mit <strong>de</strong>r die Beschriftung <strong>de</strong>s Button abgefragt<br />

wer<strong>de</strong>n kann. Falls das Action-Kommando nicht mit <strong>de</strong>r Beschriftung i<strong>de</strong>ntisch ist,<br />

270 Abgeleitet von <strong>de</strong>r Panel-Klasse<br />

312


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

kann es <strong>in</strong> <strong>de</strong>r Button-Klasse durch „public void setActionCommand(Str<strong>in</strong>g<br />

kommando)“ geän<strong>de</strong>rt wer<strong>de</strong>n.<br />

Bsp.: Setzen von H<strong>in</strong>tergrundfarben nach verschie<strong>de</strong>nen Buttonklicks 271<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

public class SchaltApplet extends java.applet.Applet<br />

{<br />

private Button schalt1 = new Button("Rot");<br />

private Button schalt2 = new Button("Blau");<br />

private Button schalt3 = new Button("Gruen");<br />

private Button schalt4 = new Button("Gelb");<br />

private Button schalt5 = new Button("Schwarz");<br />

public void <strong>in</strong>it()<br />

{<br />

setBackground(Color.white);<br />

schalt1.addActionListener(new ActionListener()<br />

{<br />

public void actionPerformed(ActionEvent e)<br />

{<br />

setBackground(Color.red);<br />

}<br />

});<br />

schalt2.addActionListener(new ActionListener()<br />

{<br />

public void actionPerformed(ActionEvent e)<br />

{<br />

setBackground(Color.blue);<br />

}<br />

});<br />

schalt3.addActionListener(new ActionListener()<br />

{<br />

public void actionPerformed(ActionEvent e)<br />

{<br />

setBackground(Color.green);<br />

}<br />

});<br />

schalt4.addActionListener(new ActionListener()<br />

{<br />

public void actionPerformed(ActionEvent e)<br />

{<br />

setBackground(Color.yellow);<br />

}<br />

});<br />

schalt5.addActionListener(new ActionListener()<br />

{<br />

public void actionPerformed(ActionEvent e)<br />

{<br />

setBackground(Color.black);<br />

}<br />

});<br />

this.setLayout(new Bor<strong>de</strong>rLayout(15,15));<br />

Panel p = new Panel();<br />

p.setLayout(new FlowLayout(FlowLayout.CENTER,15,15));<br />

p.add(schalt1);<br />

p.add(schalt2);<br />

p.add(schalt3);<br />

p.add(schalt4);<br />

p.add(schalt5);<br />

this.add("South",p);<br />

// this.pack();<br />

}<br />

}<br />

271 pr52105<br />

313


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Reaktionen auf die Betätigung von Schaltflächen (JDK 1.0). Die Komponenten<br />

<strong>in</strong>nerhalb <strong>de</strong>r AWT-Oberfläche besitzen e<strong>in</strong>e action()-Metho<strong>de</strong>, die aufgerufen<br />

wird, wenn bei e<strong>in</strong>er Komponente e<strong>in</strong>e Aktion ausgführt wur<strong>de</strong>. Beim Betätigen<br />

(Auslösen) e<strong>in</strong>er Schaltfläche wird die action()-Metho<strong>de</strong> aufgerufen. Die<br />

action()-Metho<strong>de</strong> gehört zu <strong>de</strong>n Ereignisbehandlungsmetho<strong>de</strong>n <strong>de</strong>s Event<br />

Handl<strong>in</strong>g. Die Syntax <strong>de</strong>r action()-Metho<strong>de</strong> ist bei allen Komponenten i<strong>de</strong>ntisch:<br />

public boolean action(Event ereignis, Object welcheAktion).<br />

Ereignis: Ereignis, das bei <strong>de</strong>r Komponente aufgetreten ist<br />

welcheAktion: steht für das, was geschehen ist<br />

Bei Schaltflächen ist die Art <strong>de</strong>r Aktion (welcheAktion) ganz e<strong>in</strong>fach über das Label<br />

(Beschriftung) <strong>de</strong>r Schaltfläche auszuwerten, die ausgelöst wur<strong>de</strong>. Der „Event“-<br />

Parameter enthält spezifische Informationen über die Aktion, z.B.:<br />

event.target (Komponente, bei <strong>de</strong>r die Aktion e<strong>in</strong>getreten ist)<br />

event.when (Zeitpunkt, zu <strong>de</strong>m die Aktion geschehen ist)<br />

Mit <strong>de</strong>m <strong>in</strong>stanceof-Operator kann die event.target-Variable überprüft wer<strong>de</strong>n,<br />

ob die Aktion auch für das Objekt erfolgt, das gewünscht wird.<br />

Bsp.: Setzen von H<strong>in</strong>tergrundfarben nach verschie<strong>de</strong>nen Buttonklicks 272<br />

import java.awt.*;<br />

public class KnopfAktApplet extends java.applet.Applet<br />

{<br />

public void <strong>in</strong>it()<br />

{<br />

setBackground(Color.white); this.setLayout(new Bor<strong>de</strong>rLayout(15,15));<br />

Panel p = new Panel();<br />

p.setLayout(new FlowLayout(FlowLayout.CENTER,15,15));<br />

p.add(new Button("Rot")); p.add(new Button("Blau"));<br />

p.add(new Button("Gruen")); p.add(new Button("Gelb"));<br />

p.add(new Button("Schwarz")); this.add("South",p);<br />

}<br />

public boolean action(Event e, Object arg)<br />

{<br />

// Test auf Schaltflaechenaktion<br />

if (e.target <strong>in</strong>stanceof Button) aen<strong>de</strong>reFarbe((Str<strong>in</strong>g) arg);<br />

return true;<br />

}<br />

void aen<strong>de</strong>reFarbe(Str<strong>in</strong>g knopfName)<br />

{<br />

// Aen<strong>de</strong>rn <strong>de</strong>r H<strong>in</strong>tergrundfarbe<br />

if (knopfName.equals("Rot"))<br />

setBackground(Color.red);<br />

else if (knopfName.equals("Blau")) setBackground(Color.blue);<br />

else if (knopfName.equals("Gruen")) setBackground(Color.green);<br />

else if (knopfName.equals("Gelb")) setBackground(Color.yellow);<br />

else if (knopfName.equals("Schwarz")) setBackground(Color.black);<br />

else;<br />

}<br />

}<br />

272 vgl. pr52105<br />

314


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

5.2.2 Labels<br />

Labels s<strong>in</strong>d Zeichenketten zur Beschriftung an<strong>de</strong>rer Komponenten <strong>de</strong>r<br />

Benutzeroberfläche. E<strong>in</strong> Label umfaßt e<strong>in</strong>e Zeile Text, die auf <strong>de</strong>m Bildschirm<br />

angezeigt wird und vom Programm verän<strong>de</strong>r wer<strong>de</strong>n kann.<br />

Konstruktoren.<br />

public Label()<br />

public Label(Str<strong>in</strong>g text)<br />

public Label(Str<strong>in</strong>g text, <strong>in</strong>t ausrichten)<br />

Der Parameter „ausrichten“ bestimmt die Ausrichtung <strong>de</strong>s Texts. Hier kann e<strong>in</strong>e <strong>de</strong>r Konstanten<br />

Label.LEFT, Label.RIGHT, Label.CENTER übergeben wer<strong>de</strong>n.<br />

Metho<strong>de</strong>n.<br />

public void setText(Str<strong>in</strong>g text)<br />

// Zugriff auf die Textzeile <strong>de</strong>s Label<br />

public Str<strong>in</strong>g getText()<br />

// Zugriff auf die Textzeile <strong>de</strong>s Label<br />

public void setAlignment(<strong>in</strong>t ausrichten)<br />

// Ausrichten <strong>de</strong>r Textzeile<br />

public <strong>in</strong>t getAlignment()<br />

// Ausrichten <strong>de</strong>r Textzeiele<br />

5.2.3 Kontrollkästchen und Optionsfel<strong>de</strong>r<br />

Erzeugen von Kontrollkästchen. E<strong>in</strong> Kontrollkästchen (Checkbutton / Checkbox)<br />

hat zwei Bestandteile: e<strong>in</strong> Label (Text, <strong>de</strong>r neben <strong>de</strong>m Kontrollkästchen angelegt<br />

wird) und e<strong>in</strong>em Zustand (boolesche Variable, die angibt, ob die Box e<strong>in</strong>geschaltet<br />

wur<strong>de</strong> o<strong>de</strong>r nicht 273 ). Zum Erzeugen von Kontrollkästchen gibt es 5 Konstruktoren<br />

- Checkbox()<br />

(Kontrollkästchen ohne Label)<br />

- Checkbox(Str<strong>in</strong>g label)<br />

(Kontrollkästchen mit Beschriftung)<br />

- Checkbox(Str<strong>in</strong>g label, boolean zustand)<br />

Checkbox mit Vorgabe <strong>de</strong>s Anfangszustands<br />

- Checkbox(Str<strong>in</strong>g label, CheckboxGroup cbg, boolean zustand)<br />

(Kontrollkästchen, das jenach gesetzter boolescher Zustandsvariable vorselektiert (true) ist o<strong>de</strong>r<br />

nicht (false). Das mittlere Argument ist bei Kontrollkästchen immer auf Null gesetzt. Falls das nicht<br />

<strong>de</strong>r Fall ist, dient <strong>de</strong>r Parameter cbg zum Gruppieren von Radiobuttons.<br />

- Checkbox(Str<strong>in</strong>g label, boolean zustand, CheckboxGroup cbg)<br />

Plazieren von Kontrollkästchen <strong>in</strong> e<strong>in</strong>em Conta<strong>in</strong>er. Es erfolgt über die Metho<strong>de</strong><br />

add(), z.B.: add(new Checkbox("<strong>Java</strong>"));.<br />

Überprüfen und Setzen von Kontrollkästchen. Mit <strong>de</strong>r Metho<strong>de</strong> public boolean<br />

getState() kann überprüft wer<strong>de</strong>n, ob e<strong>in</strong> Kontrollkästchen angeglickt wur<strong>de</strong>. Die<br />

Metho<strong>de</strong> public void setState(boolean zustand) setzt <strong>de</strong>n Status (true<br />

für gesetzt). Die getlabel()-Metho<strong>de</strong> fragt das Label <strong>de</strong>s jeweiligen<br />

Kontrollkästchens ab.<br />

Reaktion auf Zustandsän<strong>de</strong>rungen. E<strong>in</strong>e Checkbox generiert e<strong>in</strong> Item-Event, wenn<br />

die Markierung vom Anwen<strong>de</strong>r gesetzt o<strong>de</strong>r zurückgenommen wird. Zur Reaktion auf<br />

dieses Event ist durch <strong>de</strong>n Aufruf von addItemListener e<strong>in</strong> Objekt, das das<br />

Interface ItemListener implementiert, bei <strong>de</strong>r Checkbox zu registrieren. In<br />

273 Standardmäßig ist die Box ausgeschaltet (Wert ist false o<strong>de</strong>r „off“).<br />

315


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

diesem Fall wird e<strong>in</strong> Ereignisempfänger die Metho<strong>de</strong> itemStateChanged mit e<strong>in</strong>em<br />

Argument vom Typ ItemEvent aufgerufen:<br />

public abstract void itemStateChanged(ItemEvent e)<br />

Über das ItemEvent kann die Metho<strong>de</strong> getItemSelectable aufgerufen wer<strong>de</strong>n,<br />

mit <strong>de</strong>r ermittelt wer<strong>de</strong>n kann, durch welche Checkbox das Ereignis ausgelöst wur<strong>de</strong>:<br />

public ItemSelectable getItemSelectable().<br />

Der Rückgabewert kann <strong>in</strong> e<strong>in</strong> Objekt <strong>de</strong>s Typs Checkbox konvertiert wer<strong>de</strong>n. Durch<br />

Aufruf von getState() kann <strong>de</strong>r aktuelle Zustand bestimmt wer<strong>de</strong>n.<br />

Bsp.:<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

public class PR52301 extends Frame<br />

{<br />

Checkbox<br />

cb1 = new Checkbox("Checkbox 1"),<br />

cb2 = new Checkbox("Checkbox 2",true),<br />

cb3 = new Checkbox("Checkbox_3",false);<br />

public PR52301()<br />

{<br />

setLayout(new GridLayout(3,1));<br />

setBackground(Color.lightGray);<br />

cb1.addItemListener(new CBL());<br />

cb2.addItemListener(new CBL());<br />

cb3.addItemListener(new CBL());<br />

add(cb1);<br />

add(cb2);<br />

add(cb3);<br />

}<br />

public class CBL implements ItemListener<br />

{<br />

public void itemStateChanged(ItemEvent e)<br />

{<br />

Checkbox cb = (Checkbox) e.getItemSelectable();<br />

System.out.pr<strong>in</strong>tln(cb.getLabel() + ": " + cb.getState());<br />

}<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

PR52301 f = new PR52301();<br />

f.addW<strong>in</strong>dowListener(new W<strong>in</strong>dowAdapter()<br />

{<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{ System.exit(0); }<br />

});<br />

f.setSize(100,150); f.setVisible(true);<br />

}<br />

}<br />

Def<strong>in</strong>tion zur CheckboxGroup. E<strong>in</strong>e CheckboxGroup ist die <strong>Java</strong>-Variante e<strong>in</strong>er<br />

Gruppe von Radiobuttons (Optionsfel<strong>de</strong>r), von <strong>de</strong>n genau immer e<strong>in</strong>er aktiviert wird.<br />

Wird e<strong>in</strong>e an<strong>de</strong>rer Button aktiviert, so än<strong>de</strong>r er se<strong>in</strong>en <strong>in</strong>ternen Status auf „true“ und<br />

<strong>de</strong>r zuvor gesetzte wird „false“. E<strong>in</strong>e CheckboxGroup ist nichts An<strong>de</strong>res als e<strong>in</strong>e<br />

Checkbox, <strong>de</strong>ren CheckboxGroup-Parameter gesetzt ist.<br />

Erzeugen von Optionsfel<strong>de</strong>rn (Radiobuttons). Erfor<strong>de</strong>rlich ist zunächst e<strong>in</strong>e<br />

Checkbox-Gruppe, die über die betreffen<strong>de</strong>n Konstruktoren erstellt wer<strong>de</strong>n kann.<br />

Dieser Gruppe wer<strong>de</strong>n die e<strong>in</strong>zelnen Optionsfel<strong>de</strong>r h<strong>in</strong>zugefügt.<br />

316


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Plazieren von Optionsfel<strong>de</strong>rn <strong>in</strong> e<strong>in</strong>em Conta<strong>in</strong>er. Auch Optionsfel<strong>de</strong>r müssen wie<br />

alle Komponenten nach <strong>de</strong>r Erzeugung <strong>in</strong> e<strong>in</strong>en Conta<strong>in</strong>er gebracht wer<strong>de</strong>n. Das<br />

geschieht über die Metho<strong>de</strong> add().<br />

Setzen und Überprüfen von Optionsfel<strong>de</strong>rn. Die Metho<strong>de</strong>n getState() und<br />

getLabel() funktionieren auch bei Optionsfel<strong>de</strong>rn. Zum Zugriff auf die Gruppe<br />

e<strong>in</strong>es Optionsfelds und zum Än<strong>de</strong>rn gibt es die Metho<strong>de</strong>n:<br />

public CheckboxGroup getCheckboxGroup()<br />

public void setCheckboxGroup(CheckboxGroup g)<br />

Zum Holen und Setzen <strong>de</strong>s momentan ausgewählten Optionsfelds dienen<br />

public Checkbox getCurrent()<br />

public void setCurrent(Checkbox box)<br />

Bsp.:<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

public class PR52302 extends Frame<br />

{<br />

CheckboxGroup cbg = new CheckboxGroup();<br />

public PR52302()<br />

{<br />

setLayout(new GridLayout(3,1));<br />

setBackground(Color.lightGray);<br />

Checkbox cb1 = new Checkbox("rot",cbg,false);<br />

Checkbox cb2 = new Checkbox("blau",cbg,false);<br />

Checkbox cb3 = new Checkbox("gruen",cbg,false);<br />

cb1.addItemListener(new CBL());<br />

cb2.addItemListener(new CBL());<br />

cb3.addItemListener(new CBL());<br />

add(cb1); add(cb2); add(cb3);<br />

}<br />

public class CBL implements ItemListener<br />

{<br />

public void itemStateChanged(ItemEvent e)<br />

{<br />

Checkbox cb = (Checkbox) e.getItemSelectable();<br />

if (cb.getLabel() == "rot")<br />

{<br />

if (cb.getState())<br />

{ System.out.pr<strong>in</strong>tln("rot"); }<br />

}<br />

if (cb.getLabel() == "blau")<br />

{<br />

if (cb.getState())<br />

{ System.out.pr<strong>in</strong>tln("blau"); }<br />

}<br />

if (cb.getLabel() == "gruen")<br />

{<br />

if (cb.getState())<br />

{ System.out.pr<strong>in</strong>tln("gruen"); }<br />

}<br />

}<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

PR52302 f = new PR52302();<br />

f.addW<strong>in</strong>dowListener(new W<strong>in</strong>dowAdapter()<br />

{<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{ System.exit(0); }<br />

});<br />

317


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

f.setSize(100,150); f.setVisible(true);<br />

}<br />

}<br />

Reaktionen auf Kontrollfel<strong>de</strong>r und Radiobuttons im JDK1.0. Kontrollfel<strong>de</strong>r und<br />

Radiobuttons verfügen wie alle Komponenten über die action()-Metho<strong>de</strong>. Sie wird<br />

bei e<strong>in</strong>er Auswahl aufgerufen, sobald e<strong>in</strong> Feld selektiert wird.<br />

Bsp. 274 :<br />

import java.awt.*;<br />

public class CheckAktApplet extends java.applet.Applet<br />

{<br />

CheckboxGroup me<strong>in</strong>eCheckboxGruppe = new CheckboxGroup();<br />

public void <strong>in</strong>it()<br />

{<br />

add(new Checkbox("Blau",me<strong>in</strong>eCheckboxGruppe,false));<br />

add(new Checkbox("Rot",me<strong>in</strong>eCheckboxGruppe,false));<br />

add(new Checkbox("Gruen",me<strong>in</strong>eCheckboxGruppe,false));<br />

add(new Checkbox("Gelb",me<strong>in</strong>eCheckboxGruppe,true));<br />

// H<strong>in</strong>tergrundfarbe passend zur Vore<strong>in</strong>stellung<br />

setBackground(Color.yellow);<br />

}<br />

public boolean action(Event e, Object welcheAktion)<br />

{<br />

// Test auf Kontrollkaestchen o<strong>de</strong>r Radiobutton<br />

if (!(e.target <strong>in</strong>stanceof Checkbox))<br />

return false;<br />

// Instanz <strong>de</strong>r Ereignisse<br />

Checkbox welcheAuswahl = (Checkbox) e.target;<br />

// Status <strong>de</strong>s Radiobuttons zur Kontrolle<br />

boolean CheckboxStatus = welcheAuswahl.getState();<br />

// Auswahl Blau<br />

if (welcheAuswahl.getLabel() == "Blau")<br />

{<br />

if (CheckboxStatus) setBackground(Color.blue); return true;<br />

}<br />

// Auswahl Rot<br />

if (welcheAuswahl.getLabel() == "Rot")<br />

{<br />

if (CheckboxStatus) setBackground(Color.red); return true;<br />

}<br />

// Auswahl Gruen<br />

if (welcheAuswahl.getLabel() == "Gruen")<br />

{<br />

if (CheckboxStatus) setBackground(Color.green); return true;<br />

}<br />

if (welcheAuswahl.getLabel() == "Gelb")<br />

{<br />

if (CheckboxStatus) setBackground(Color.yellow);<br />

return true;<br />

}<br />

return false; // ke<strong>in</strong>e <strong>de</strong>r Optionen wird aufgefangen<br />

}<br />

}<br />

274 pr54305<br />

318


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

5.2.4 Auswahlmenüs<br />

Es han<strong>de</strong>lt sich um Popup- bzw. Pulldown-Menüs, die sich beim Anklicken öffnen<br />

und mehrere auszuwählen<strong>de</strong> Optionen anzeigen, von <strong>de</strong>nen dann e<strong>in</strong>e Option<br />

ausgewählt wer<strong>de</strong>n kann.<br />

Konstruktor: public Choice()<br />

// erstellt e<strong>in</strong> Choice-Objekt mit e<strong>in</strong>er leeren Liste.<br />

Bearbeiten <strong>de</strong>r Liste (Combobox) zum Auswahlmenü: public void<br />

addItem(Str<strong>in</strong>g item) Je<strong>de</strong>r Aufruf von addItem hängt das übergeben<strong>de</strong><br />

Element „item“ an das En<strong>de</strong> <strong>de</strong>r Liste an. Die Elemente wer<strong>de</strong>n <strong>in</strong> <strong>de</strong>r Reihenfolge<br />

<strong>de</strong>r Aufrufe von addItem e<strong>in</strong>gefügt.<br />

Zugriff auf die Elemente <strong>de</strong>r Combobox: public <strong>in</strong>t getSelectedIn<strong>de</strong>x()<br />

liefert <strong>de</strong>n In<strong>de</strong>x <strong>de</strong>s ausgewählten Elements. Durch Übergabe dieses Werts (an<br />

„getItem“ kann das aktuelle Element beschafft wer<strong>de</strong>n: public Str<strong>in</strong>g<br />

getItem(<strong>in</strong>t n). Über public Str<strong>in</strong>g getSelectedItem() kann ohne<br />

Kenntnis <strong>de</strong>r <strong>in</strong>ternen Nummer (In<strong>de</strong>x) das ausgewählte Element beschafft wer<strong>de</strong>n.<br />

Programmseitige Auswahl e<strong>in</strong>es Elements:<br />

public void select(<strong>in</strong>t)<br />

// Auswahl über <strong>de</strong>n In<strong>de</strong>x<br />

public void select(Str<strong>in</strong>g s)<br />

// Auswahl über <strong>de</strong>n angegebenen Wert von Str<strong>in</strong>g<br />

Ereignisbehandlung. Ebenso wie e<strong>in</strong>e Checkbox sen<strong>de</strong>t auch e<strong>in</strong> Choice-Element<br />

„Item“-Ereignisse, wenn e<strong>in</strong> Element ausgewählt wur<strong>de</strong>. Zur Reaktion auf dieses<br />

Element muß e<strong>in</strong> ItemListener durch Aufruf von public void<br />

addItemListener(ItemListener l) registriert se<strong>in</strong>. E<strong>in</strong> „ItemEvent“ wird<br />

immer dann gesen<strong>de</strong>t, wenn e<strong>in</strong> Element ausgewählt wur<strong>de</strong>. In diesem Fall wird e<strong>in</strong><br />

Ereignisempfänger die Metho<strong>de</strong> itemStateChanged mit e<strong>in</strong>em Argument vom Typ<br />

ItemEvent aufrufen: public abstract void<br />

itemStateChanged(ItemEvent e). Das „ItemEvent“ stellt die Metho<strong>de</strong><br />

public ItemSelectable getItemSelectable() zur Verfügung, mit <strong>de</strong>r<br />

ermittelt wer<strong>de</strong>n kann, durch welches Choice-Element das Ereignis ausgewählt<br />

wur<strong>de</strong>. Zusätzlich gibt es public Object getItem(), die das ausgewählte<br />

Element als Str<strong>in</strong>g zur Verfügung stellt.<br />

Bsp.:<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

public class PR14174 extends Frame<br />

{<br />

TextField t = new TextField(30);<br />

Choice auswahl = new Choice();<br />

public PR14174()<br />

{<br />

auswahl.addItem("rot");<br />

auswahl.addItem("blau");<br />

auswahl.addItem("gruen");<br />

auswahl.addItem("p<strong>in</strong>k");<br />

auswahl.addItem("orange");<br />

auswahl.addItem("gelb");<br />

auswahl.addItem("cyan");<br />

add(auswahl);<br />

pack();<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

PR14174 f = new PR14174();<br />

f.addW<strong>in</strong>dowListener(<br />

319


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

}<br />

new W<strong>in</strong>dowAdapter()<br />

{<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{ System.exit(0); }<br />

});<br />

f.setBackground(Color.lightGray);<br />

f.setVisible(true);<br />

}<br />

5.2.5 Listenfel<strong>de</strong>r<br />

E<strong>in</strong>e Instanz <strong>de</strong>r Klasse List ist e<strong>in</strong>e listenartige Darstellung von Werten, aus <strong>de</strong>nen<br />

Anwen<strong>de</strong>r e<strong>in</strong>en o<strong>de</strong>r mehrere auswählen kann. An<strong>de</strong>rs als e<strong>in</strong> Choice-Element ist<br />

e<strong>in</strong> Element <strong>de</strong>r Klasse List ständig <strong>in</strong> voller Größe auf <strong>de</strong>m Bildschirm sichtbar.<br />

Erzeugen von Listenfel<strong>de</strong>rn.<br />

public List()<br />

// legt e<strong>in</strong>e leere Liste an, <strong>de</strong>ren dargestellte Größe durch <strong>de</strong>n<br />

// Layoutmanager begrenzt wird.<br />

public List(<strong>in</strong>t groesse)<br />

// Der Parameter groesse legt die Anzahl <strong>de</strong>r angezeigten Zeilen fest.<br />

public List(<strong>in</strong>t groesse, boolean multiselect)<br />

// ist multiselect "true", kann <strong>de</strong>r Anwen<strong>de</strong>r nicht nur e<strong>in</strong> Element,<br />

// son<strong>de</strong>rn auch an<strong>de</strong>re Elemente auswählen<br />

Mehrfachauswahl ausgewählter Elemente. „List“ bietet nahezu dieselbe<br />

Funktionalität wie Choice an. Zusätzlich stehen zur Verfügung:<br />

public <strong>in</strong>t[] getSelectedIn<strong>de</strong>xes()<br />

// liefert e<strong>in</strong>en Array mit <strong>de</strong>n In<strong>de</strong>xpositionen <strong>de</strong>r ausgewählten<br />

// Elemente.<br />

public Str<strong>in</strong>g[] getSelectedItems()<br />

// liefert e<strong>in</strong>e Liste <strong>de</strong>r Werte.<br />

Auswahl e<strong>in</strong>zelner Elemente.<br />

public void select(<strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

public void <strong>de</strong>select(<strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

Entfernen, Verän<strong>de</strong>rn von Elementen.<br />

public void <strong>de</strong>lItem(<strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

// löscht das Element an <strong>de</strong>r Position <strong>in</strong><strong>de</strong>x<br />

public void remove(<strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

// löscht das Element an <strong>de</strong>r Position <strong>in</strong><strong>de</strong>x<br />

public void replaceItem(Str<strong>in</strong>g neuerWert,<strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

// ersetzt das Element an <strong>de</strong>r Position <strong>in</strong><strong>de</strong>x durch die Postion<br />

// neuerWert.<br />

Item- und Action-Ereignisse. E<strong>in</strong> Listenelement sen<strong>de</strong>t Item- und Action-<br />

Ereignisse. E<strong>in</strong> Action-Ereignis wird generiert, wenn e<strong>in</strong> Listenelement durch<br />

Doppelklick ausgewählt wur<strong>de</strong>. E<strong>in</strong> Item-Ereignis wird ausgelöst, nach<strong>de</strong>m <strong>in</strong> <strong>de</strong>r<br />

Liste e<strong>in</strong> Element ausgewählt wur<strong>de</strong>.<br />

Bsp.:<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

public class PR14177 extends Frame<br />

{<br />

Str<strong>in</strong>g[] farben = {"rot","blau","gruen","p<strong>in</strong>k","orange",<br />

"gelb","cyan"};<br />

List lst = new List(6,true);<br />

public PR14177()<br />

320


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

{<br />

setLayout(new FlowLayout());<br />

for (<strong>in</strong>t i = 0; i < 7; i++)<br />

lst.addItem(farben[i]);<br />

add(lst);<br />

lst.addItemListener(new ILL());<br />

lst.addActionListener(new ALL());<br />

pack();<br />

}<br />

public class ILL implements ItemListener<br />

{<br />

public void itemStateChanged(ItemEvent e)<br />

{<br />

List lst = (List) e.getItemSelectable();<br />

Str<strong>in</strong>g str = lst.getSelectedItem();<br />

<strong>in</strong>t pos = ((Integer) e.getItem()).<strong>in</strong>tValue();<br />

System.out.pr<strong>in</strong>tln("event.getSelectedItem: " + str + " " + pos);<br />

}<br />

}<br />

class ALL implements ActionListener<br />

{<br />

public void actionPerformed(ActionEvent e)<br />

{<br />

Object obj = e.getSource();<br />

if (obj <strong>in</strong>stanceof List)<br />

{<br />

System.out.pr<strong>in</strong>tln("Listenfeld-Aktion: " + e.getActionCommand());<br />

}<br />

}<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

PR14177 f = new PR14177();<br />

f.addW<strong>in</strong>dowListener(new W<strong>in</strong>dowAdapter()<br />

{<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{<br />

// f.setVisible(false);<br />

System.exit(0);<br />

}<br />

});<br />

f.setBackground(Color.lightGray);<br />

// f.setSize(100,160);<br />

f.setVisible(true);<br />

}<br />

}<br />

5.2.6 Textbereiche und Textfel<strong>de</strong>r<br />

Das AWT verfügt über Klassen zur E<strong>in</strong>gabe von Text (Klasse TextField für<br />

Textfel<strong>de</strong>r und Klasse TextArea für Textbereiche). Textfel<strong>de</strong>r begrenzen die Menge<br />

<strong>de</strong>s e<strong>in</strong>zugeben<strong>de</strong>n Textes und können nicht gescrollt wer<strong>de</strong>n. In Textbereichen<br />

können mehrere Zeilen verarbeitet wer<strong>de</strong>n, außer<strong>de</strong>m s<strong>in</strong>d Textbereiche scrollbar.<br />

TextField<br />

E<strong>in</strong> TextField dient zur Darstellung von Text. Anwen<strong>de</strong>r und Programm können <strong>de</strong>n<br />

dargestellten Text e<strong>in</strong>lesen und verän<strong>de</strong>rn.<br />

Erzeugen von Textfel<strong>de</strong>rn. Es stehen folgen<strong>de</strong> Konstruktoren zur Verfügung:<br />

321


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

public TextField()<br />

erzeugt e<strong>in</strong> leeren Textfeld, <strong>in</strong> das <strong>de</strong>r Anwen<strong>de</strong>r Text e<strong>in</strong>geben kann.<br />

public TextField(<strong>in</strong>t anzSpalten)<br />

Erzeugt e<strong>in</strong> leeres Textfeld bestimmter Breite. Die Anzahl e<strong>in</strong>zugeben<strong>de</strong>r Zeichen ist nicht begrenzt.<br />

Ist <strong>de</strong>r Text länger, so scrollt er <strong>in</strong>nerhalb <strong>de</strong>s Textfelds<br />

public TextField(Str<strong>in</strong>g text)<br />

Über <strong>de</strong>n Parameter text kann e<strong>in</strong>e Zeichenkette vorgegeben wer<strong>de</strong>n, die beim aufruf <strong>de</strong>s Textfelds<br />

vorgelegt wird.<br />

public TextField(Str<strong>in</strong>g text, <strong>in</strong>t anzSpalten)<br />

Metho<strong>de</strong>n zum Zugriff bzw. Verän<strong>de</strong>rn von Textfel<strong>de</strong>rn.<br />

public Str<strong>in</strong>g getText();<br />

public void setText(Str<strong>in</strong>g text)<br />

Mit<br />

public <strong>in</strong>t getColumns()<br />

kann die Anzahl <strong>de</strong>r darstellbaren Zeichen e<strong>in</strong>es Textfelds abgefragt und mit<br />

public void setColumns(<strong>in</strong>t anzSpalten)<br />

verän<strong>de</strong>rt wer<strong>de</strong>n.<br />

Markieren von Text.<br />

public void selectAll()<br />

//markiert <strong>de</strong>n kompletten Bereich<br />

public void select(<strong>in</strong>t erstes, <strong>in</strong>t letztes)<br />

//markiert <strong>de</strong>n Bereich von „erstes“ bis „letztes“. Das „Zählen“ beg<strong>in</strong>nt bei 0.<br />

Mit<br />

public <strong>in</strong>t getSelectionStart()<br />

bzw.<br />

public <strong>in</strong>t getSelectionEnd()<br />

kann die aktuelle Auswahl abgefragt wer<strong>de</strong>n. Mit<br />

public <strong>in</strong>t getCaretPosition()<br />

und<br />

public void setCaretPosition(<strong>in</strong>t position)<br />

kann auf die aktuelle Cursorposition zugegriffen wer<strong>de</strong>n.<br />

Bearbeiten von Textfel<strong>de</strong>rn.<br />

Über<br />

public void setEditable(boolean erlaubt)<br />

// Aufruf mit Parameter false unterb<strong>in</strong><strong>de</strong>t weitere E<strong>in</strong>gaben<br />

bzw.<br />

public boolean isEditable() // Abfrage <strong>de</strong>s aktuellen Status<br />

kann man die Verän<strong>de</strong>rung von Text verh<strong>in</strong><strong>de</strong>rn. Mit<br />

public void setEchoChar(char z)<br />

(Übergabe e<strong>in</strong>es Zeichens, das beim Tastendruck anstelle <strong>de</strong>s vom anwen<strong>de</strong>r e<strong>in</strong>gegebenen<br />

Zeichens ausgegeben wird)<br />

bzw.<br />

public char getEchoChar()<br />

besteht die Möglichkeit ver<strong>de</strong>ckter E<strong>in</strong>gaben (z.B. für Paßwörter).<br />

Generieren e<strong>in</strong>es Action-Event erfolgt beim Drücken <strong>de</strong>r Enter-Taste <strong>in</strong>nerhalb <strong>de</strong>s<br />

Textfelds. Die Metho<strong>de</strong> Str<strong>in</strong>g getActionCommand() <strong>de</strong>s Action-Events liefert<br />

<strong>de</strong>n Inhalt <strong>de</strong>s Textfelds.<br />

Generieren e<strong>in</strong>es Text-Ereignisses bei je<strong>de</strong>r Än<strong>de</strong>rung im Textfeld. E<strong>in</strong> Empfänger<br />

kann mit <strong>de</strong>r Metho<strong>de</strong> addTextListener vom Textfeld registriert wer<strong>de</strong>n. Als<br />

Argument wird e<strong>in</strong> Objekt erwartet, das das Interface TextListener implementiert.<br />

Beim Auftreten e<strong>in</strong>es Text-Ereignisses wird vom TextListener die Metho<strong>de</strong><br />

322


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

textValueChanged mit e<strong>in</strong>em „TextEvent“ als Parameter aufgerufen.<br />

„TextEvent“ ist aus „AWTEvent“ abgeleitet und erbt die Metho<strong>de</strong>n getID(),<br />

getSource(). Typischerweise wird <strong>in</strong>nerhalb von public void<br />

textValueChanged(TextEvent e) mit getSource() das zugehörige Textfeld<br />

beschafft und mit getText() auf se<strong>in</strong>en Inhalt zugegriffen.<br />

Bsp.:<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

public class PR14170 extends Frame<br />

{<br />

Button<br />

b1 = new Button("Hole Text"),<br />

b2 = new Button("Setze Text");<br />

TextField<br />

t1 = new TextField(30),<br />

t2 = new TextField(30),<br />

t3 = new TextField(30);<br />

Str<strong>in</strong>g s = new Str<strong>in</strong>g();<br />

public PR14170()<br />

{<br />

setLayout(new FlowLayout());<br />

b1.addActionListener(new B1A());<br />

b2.addActionListener(new B2A());<br />

t1.addTextListener(new T1());<br />

t1.addActionListener(new T1A());<br />

add(b1);<br />

add(b2);<br />

add(t1);<br />

add(t2);<br />

add(t3);<br />

}<br />

class T1 implements TextListener<br />

{<br />

public void textValueChanged(TextEvent e)<br />

{<br />

t2.setText(t1.getText());<br />

}<br />

}<br />

class T1A implements ActionListener<br />

{<br />

private <strong>in</strong>t zaehler = 0;<br />

public void actionPerformed(ActionEvent e)<br />

{<br />

t3.setText("t1 ActionEvent " + zaehler++);<br />

}<br />

}<br />

class B1A implements ActionListener<br />

{<br />

public void actionPerformed(ActionEvent e)<br />

{<br />

s = t1.getSelectedText();<br />

if (s.length() == 0) s = t1.getText();<br />

t1.setEditable(true);<br />

}<br />

}<br />

class B2A implements ActionListener<br />

{<br />

public void actionPerformed(ActionEvent e)<br />

{<br />

t1.setText("E<strong>in</strong>gefuegt durch Button 2: " + s);<br />

t1.setEditable(false);<br />

}<br />

323


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

PR14170 f = new PR14170();<br />

f.addW<strong>in</strong>dowListener(<br />

new W<strong>in</strong>dowAdapter()<br />

{<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{<br />

System.exit(0);<br />

}<br />

});<br />

f.setSize(300,200);<br />

f.setBackground(Color.lightGray);<br />

f.setVisible(true);<br />

}<br />

}<br />

TextArea<br />

Mit dieser Klasse können mehrzeilige Textfel<strong>de</strong>r erzeugt wer<strong>de</strong>n. Zusätzlich kann <strong>de</strong>r<br />

Text <strong>in</strong> allen Richtungen „scrollen“, so daß auch größere Texte bequem bearbeitet<br />

wer<strong>de</strong>n können.<br />

Konstruktoren.<br />

public TextArea()<br />

//erzeugt e<strong>in</strong> leeres TextArea-Objekt <strong>in</strong> e<strong>in</strong>er vom System vorgegebenen Größe.<br />

public TextArea(<strong>in</strong>t zeilen, <strong>in</strong>t spalten)<br />

//erzeugt e<strong>in</strong>e leeres TextArea-Objekt mit e<strong>in</strong>er bestimmten Anzahl von sichtbaren Zeilen<br />

//und Spalten.<br />

public TextArea(Str<strong>in</strong>g text, <strong>in</strong>t zeilen, <strong>in</strong>t spalten)<br />

//erzeugt e<strong>in</strong> TextArea-Objekt mit Text.<br />

public TextArea(Str<strong>in</strong>g text, <strong>in</strong>t zeilen, <strong>in</strong>t spalten, <strong>in</strong>t scrollbars)<br />

//erzeugt e<strong>in</strong> TextArea-Objekt, <strong>in</strong> <strong>de</strong>m über scrollbars die Ausstattung <strong>de</strong>r TextArea mit<br />

//Schieberegistern festgelegt wer<strong>de</strong>n kann. Dazu stellt TextArea die Konstanten<br />

//SCROLLBARS_BOTH, SCROLLBARS_NONE, SCROLLBARS_VERTICAL_ONLY,<br />

//SCROLLBARS_HORIZONTAL_ONLY zur Verfügung, die als Argumente übergeben wer<strong>de</strong>n können.<br />

Zusätzliche <strong>in</strong> <strong>de</strong>r Klasse bereitgestellte Metho<strong>de</strong>n zum Verän<strong>de</strong>rn von Teilen <strong>de</strong>s<br />

Texts. Neben <strong>de</strong>n bereits <strong>in</strong> <strong>de</strong>r Klasse TextField angegebenen Metho<strong>de</strong>n stehen zur<br />

Verfügung:<br />

public void <strong>in</strong>sert(Str<strong>in</strong>g str, <strong>in</strong>t pos)<br />

//verän<strong>de</strong>rt die Zeichenkette str ab Position pos. Der dah<strong>in</strong>ter stehen<strong>de</strong> Text wird entsprechend<br />

//nach h<strong>in</strong>ten verschoben.<br />

public void append(Str<strong>in</strong>g str)<br />

//hängt <strong>de</strong>n über str übergebenen Text h<strong>in</strong>ten an.<br />

public void replaceRange(Str<strong>in</strong>g text, <strong>in</strong>t start, <strong>in</strong>t en<strong>de</strong>)<br />

//ersetzt <strong>de</strong>n Text zwischen start und end durch <strong>de</strong>n Str<strong>in</strong>g text.<br />

Sen<strong>de</strong>n von Text-Events. E<strong>in</strong> Objekt <strong>de</strong>r Klasse TextArea sen<strong>de</strong>t Text-Events,<br />

so wie es bei TextField beschrieben wur<strong>de</strong>.<br />

5.2.7 Schieber und Bildlaufleisten<br />

Listen und Textbereiche besitzen standardmäßig die Eigenschaft bei Bedarf nicht <strong>in</strong><br />

<strong>de</strong>n Anzeigebereich passen<strong>de</strong>n Inhalt über Bildlaufleisten und Schieber zu scrollen.<br />

Bildlaufleisten können auch <strong>in</strong>dividuell erstellt wer<strong>de</strong>n und dienen zur (quasi-)<br />

analogen Anzeige bzw. E<strong>in</strong>gabe e<strong>in</strong>es Werts aus e<strong>in</strong>em vorgebenen Wertebereich.<br />

324


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Der Schieberegler kann horizotal und vertikal angeordnet wer<strong>de</strong>n und besitzt e<strong>in</strong>en<br />

Schieber, <strong>de</strong>ssen Größe verän<strong>de</strong>rlich ist. Der <strong>in</strong>terne Wert e<strong>in</strong>es Schiebereglers und<br />

die Anzeigeposition se<strong>in</strong>es Schiebers s<strong>in</strong>d untrennbar mite<strong>in</strong>an<strong>de</strong>r verbun<strong>de</strong>n. Än<strong>de</strong>rt<br />

<strong>de</strong>r Anwen<strong>de</strong>r die Position <strong>de</strong>s Schiebers, än<strong>de</strong>rt sich automatisch auch se<strong>in</strong> <strong>in</strong>terner<br />

Wert. Wird vom Programm <strong>de</strong>r Wert verän<strong>de</strong>rt, führt das automatisch zu e<strong>in</strong>er<br />

Positionierung <strong>de</strong>s Schiebers. Zur Än<strong>de</strong>rung <strong>de</strong>s aktuellen Werts e<strong>in</strong>er Bildlaufleiste<br />

können drei verschie<strong>de</strong>n Teile <strong>de</strong>r Bildlaufleiste verwen<strong>de</strong>t wer<strong>de</strong>n:<br />

- Pfeile an bei<strong>de</strong>n En<strong>de</strong>n zum Erhöhen / Erniedrigen <strong>de</strong>s Werts um e<strong>in</strong>e E<strong>in</strong>heit (standardmäßig 1)<br />

- E<strong>in</strong> Bereich <strong>in</strong> <strong>de</strong>r Mitte zum Erhöhen / Erniedrigen e<strong>in</strong>es Werts um je e<strong>in</strong>e größere E<strong>in</strong>heit<br />

(standardmäßig 10)<br />

- E<strong>in</strong> Bildlauffeld (Schieber) <strong>in</strong> <strong>de</strong>r Mitte, <strong>de</strong>r <strong>de</strong>n momentan gewählten Wert anzeigt. Durch<br />

Verschieben <strong>de</strong>s Bildlauffelds mit <strong>de</strong>m Mauszeiger kann <strong>in</strong>nerhalb <strong>de</strong>r Bildlaufleiste e<strong>in</strong> an<strong>de</strong>rer<br />

Wert gewählt wer<strong>de</strong>n.<br />

Nötig ist nur die Festlegung e<strong>in</strong>es Höchst- und M<strong>in</strong><strong>de</strong>stwerts für die Bildlaufleiste. Der<br />

Rest wird von <strong>Java</strong> erledigt.<br />

Konstruktoren.<br />

public Scrollbar()<br />

//erzeugt e<strong>in</strong>en vertikalen Schieberegler mit 0,0 als anfänglichen Höchst-, M<strong>in</strong><strong>de</strong>stwert.<br />

public Scrollbar(<strong>in</strong>t orientierung)<br />

//orientierung bezeichnet die Ausrichtung und kann über Scrollbar.HORIZONTAL bzw.<br />

//Scrollbar.VERTICAL festgelegt wer<strong>de</strong>n.<br />

public Scollbar(<strong>in</strong>t orientierung, <strong>in</strong>t wert, <strong>in</strong>t bh, <strong>in</strong>t m<strong>in</strong>imum, <strong>in</strong>t<br />

maximum)<br />

//orientierung...bestimmt die Ausrichtung <strong>de</strong>s Schiebereglers<br />

//wert...Anfangswert <strong>de</strong>s Schiebereglers (Wert zwischen <strong>de</strong>m Höchst- und M<strong>in</strong><strong>de</strong>stwert<br />

//bh...Breite bzw. Höhe <strong>de</strong>r <strong>de</strong>m Schieberegler zugeteilten Box (Seite)<br />

//m<strong>in</strong>imum, maximum...bezeichnet <strong>de</strong>n M<strong>in</strong><strong>de</strong>st-, Höchstwert <strong>de</strong>s Schiebereglers<br />

Metho<strong>de</strong>n <strong>de</strong>r Scrollbar-Klasse versorgen die Handhabung <strong>de</strong>r Werte <strong>de</strong>s<br />

Schiebereglers:<br />

Metho<strong>de</strong><br />

Be<strong>de</strong>utung<br />

public <strong>in</strong>t getValue()<br />

Zugriff auf <strong>de</strong>n aktuellen Wert <strong>de</strong>s Schiebereglers<br />

public void setValue(<strong>in</strong>t wert) Setzt <strong>de</strong>n aktuellen Wert <strong>de</strong>s Schiebereglers<br />

public <strong>in</strong>t getOrientation()<br />

Bestimmt die Ausrichtung <strong>de</strong>s Schiebereglers<br />

public <strong>in</strong>t getM<strong>in</strong>imum()<br />

Zugriff auf <strong>de</strong>n M<strong>in</strong><strong>de</strong>stwert<br />

public <strong>in</strong>t getMaximum()<br />

Zugriff auf <strong>de</strong>n Höchstwert<br />

public <strong>in</strong>t getUnitIncrement() Zugriff <strong>de</strong>r Parameter, die die Stärke <strong>de</strong>r Verän<strong>de</strong>rung <strong>de</strong>s<br />

Werts beim Klicken auf die Buttons bzw. die Schaltfläche<br />

zwischen Schieber und Button bestimmen.<br />

public void setUnitIncrement(<strong>in</strong>t l) Verän<strong>de</strong>rung <strong>de</strong>r Parameter, die die Stärke <strong>de</strong>r<br />

Verän<strong>de</strong>rung <strong>de</strong>s Werts beim Klicken auf die Buttons<br />

zwischen Schieber und Button bestimmen<br />

public <strong>in</strong>t getBlockIncrement() Zugriff <strong>de</strong>r Parameter, die die Stärke <strong>de</strong>r Verän<strong>de</strong>rung <strong>de</strong>s<br />

Werts beim Klicken auf die Buttons bzw. die Schaltfläche<br />

zwischen Schieber und Button bestimmen<br />

public void setBlockIncrement(<strong>in</strong>t l) Verän<strong>de</strong>rung <strong>de</strong>r Parameter, die die Stärke <strong>de</strong>r<br />

Verän<strong>de</strong>rung <strong>de</strong>s Werts beim Klicken auf die Schaltfläche<br />

zwischen Schieber und Button bestimmen<br />

Ereignisse. E<strong>in</strong> Scrollbar sen<strong>de</strong>t Adjustment-Ereignisse an se<strong>in</strong>e<br />

Ereignisempfänger. Diese müssen das Interface AdjustmentListener<br />

implementieren und sich durch Aufruf von public void<br />

addAdjustmentListener(AdjustmentListener l) registrieren. Das<br />

Adjustment-Ereignis führt im Ereignisempfänger zum Aufruf <strong>de</strong>r Metho<strong>de</strong>:<br />

325


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

public abstract void adjustmentValueChanged(AdjustmentEvent e),<br />

die e<strong>in</strong> AdjustmentEvent übergeben bekommt. Dieses besitzt die Meto<strong>de</strong><br />

getAdjustable(), mit <strong>de</strong>r <strong>de</strong>r auslösen<strong>de</strong> Scrollbar bestimmt wer<strong>de</strong>n kann.<br />

Zusätzlich gibt es die Metho<strong>de</strong> getAdjustmentType(), die Auskunft darüber gibt,<br />

welche Benutzeraktion zur Auslösung <strong>de</strong>s Ereignisses führte.<br />

Konstanten, die von getAdjustmentType<br />

zurückgegeben wer<strong>de</strong>n<br />

AdjustmentEvent.UNIT_INCREMENT<br />

AdjustmentEvent.UNIT_DECREMENT<br />

AdjustmentEvent.BLOCK_INCREMENT<br />

AdjustmentEvent.BLOCK_DECREMENT<br />

AdjustmentEvent.TRACK<br />

Be<strong>de</strong>utung<br />

Der Wert durch Klicken e<strong>in</strong>es „Button“ um e<strong>in</strong>e<br />

E<strong>in</strong>heit erhöht.<br />

Der Wert durch Klicken e<strong>in</strong>es „Button“ um e<strong>in</strong>e<br />

E<strong>in</strong>heit erniedrigt.<br />

Der Wert wur<strong>de</strong> durch Klicken <strong>de</strong>r Schaltfläche<br />

zwischen Button und Schieber um e<strong>in</strong>e Seite erhöht<br />

Der Wert wur<strong>de</strong> durch Klicken <strong>de</strong>r Schaltfläche<br />

zwischen Button und Schieber um e<strong>in</strong>e Seite<br />

verm<strong>in</strong><strong>de</strong>rt<br />

Der Wert wur<strong>de</strong> durch Ziehen <strong>de</strong>s Schiebers<br />

verän<strong>de</strong>rt<br />

Bsp.:<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

public class PR14175 extends Frame<br />

{<br />

public PR14175()<br />

{<br />

Panel p = new Panel();<br />

p.setLayout(new Bor<strong>de</strong>rLayout());<br />

Scrollbar hsb = new Scrollbar(Scrollbar.HORIZONTAL,1,10,1,100);<br />

hsb.addAdjustmentListener(new HsbAL());<br />

p.add("South",hsb);<br />

Scrollbar vsb = new Scrollbar(Scrollbar.VERTICAL,1,10,1,100);<br />

vsb.addAdjustmentListener(new VsbAL());<br />

p.add("East",vsb);<br />

add(p);<br />

pack();<br />

}<br />

public class HsbAL implements AdjustmentListener<br />

{<br />

public void adjustmentValueChanged(AdjustmentEvent e)<br />

{<br />

switch(e.getAdjustmentType())<br />

{<br />

case AdjustmentEvent.UNIT_INCREMENT:<br />

System.out.pr<strong>in</strong>tln("Adjustment.UNIT_INCREMENT");<br />

break;<br />

case AdjustmentEvent.UNIT_DECREMENT:<br />

System.out.pr<strong>in</strong>tln("Adjustment.UNIT_DECREMENT");<br />

break;<br />

case AdjustmentEvent.BLOCK_INCREMENT:<br />

System.out.pr<strong>in</strong>tln("Adjustment.BLOCK_INCREMENT");<br />

break;<br />

case AdjustmentEvent.BLOCK_DECREMENT:<br />

System.out.pr<strong>in</strong>tln("Adjustment.BLOCK_DECREMENT");<br />

break;<br />

case AdjustmentEvent.TRACK:<br />

System.out.pr<strong>in</strong>tln("Adjustment.TRACK");<br />

break;<br />

}<br />

System.out.pr<strong>in</strong>tln("Wert: " + e.getValue());<br />

}<br />

326


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

}<br />

public class VsbAL implements AdjustmentListener<br />

{<br />

public void adjustmentValueChanged(AdjustmentEvent e)<br />

{<br />

switch(e.getAdjustmentType())<br />

{<br />

case AdjustmentEvent.UNIT_INCREMENT:<br />

System.out.pr<strong>in</strong>tln("Adjustment.UNIT_INCREMENT");<br />

break;<br />

case AdjustmentEvent.UNIT_DECREMENT:<br />

System.out.pr<strong>in</strong>tln("Adjustment.UNIT_DECREMENT");<br />

break;<br />

case AdjustmentEvent.BLOCK_INCREMENT:<br />

System.out.pr<strong>in</strong>tln("Adjustment.BLOCK_INCREMENT");<br />

break;<br />

case AdjustmentEvent.BLOCK_DECREMENT:<br />

System.out.pr<strong>in</strong>tln("Adjustment.BLOCK_DECREMENT");<br />

break;<br />

case AdjustmentEvent.TRACK:<br />

System.out.pr<strong>in</strong>tln("Adjustment.TRACK");<br />

break;<br />

}<br />

System.out.pr<strong>in</strong>tln("Wert: " + e.getValue());<br />

}<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

PR14175 f = new PR14175();<br />

f.addW<strong>in</strong>dowListener(<br />

new W<strong>in</strong>dowAdapter()<br />

{<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{ System.exit(0); }<br />

});<br />

f.setBackground(Color.lightGray);<br />

f.setVisible(true);<br />

}<br />

}<br />

5.2.8 ScrollPane<br />

E<strong>in</strong> ScrollPane ist e<strong>in</strong> Conta<strong>in</strong>er für automatisches horizontales und vertikales<br />

Scroll<strong>in</strong>g. ScrollPane unterschei<strong>de</strong>t sich durch zwei Eigenschaften von e<strong>in</strong>em<br />

gewöhnlichen Panel:<br />

- Es kann genau e<strong>in</strong> Dialogelement aufnehmen und benötigt ke<strong>in</strong>en eigenen Layoutmanager<br />

- Es ist <strong>in</strong> <strong>de</strong>r Lage, e<strong>in</strong>e virtuelle Ausgabefläche zu verwalten, die größer ist als die auf <strong>de</strong>m<br />

Bildschirm zur Verfügung stehen<strong>de</strong> Ausgabefläche.<br />

Die von e<strong>in</strong>em ScrollPane angezeigte Komponente arbeitet mit e<strong>in</strong>er virtuellen<br />

Ausgabefläche (und merkt nichts von evtl. Größenbeschränkungen auf <strong>de</strong>m<br />

Bildschirm). Falls die benötigte Ausgabefläche größer ist als die anzeigbare, blen<strong>de</strong>t<br />

e<strong>in</strong> ScrollPane automatisch die erfor<strong>de</strong>rlichen Schieberegister e<strong>in</strong>, um<br />

Dialogelemente horizontal und vertikal verschieben zu können.<br />

Konstruktoren.<br />

public ScrollPane()<br />

public ScrollPane(<strong>in</strong>t scrollbarDisplayPolicy)<br />

//srollbarDisplayPolicy <strong>de</strong>f<strong>in</strong>iert die Strategie zur Anzeige <strong>de</strong>r Schieberegler entsprechend<br />

327


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

//<strong>de</strong>n <strong>in</strong> <strong>de</strong>r folgen<strong>de</strong> Liste aufgelisteten Konstanten:<br />

ScrollPane.SCROLLBARS_AS_NEEDED<br />

ScrollPane.SCROLLBARS_ALWAYS<br />

ScrollPane.SCROLLBARS_NEVER<br />

Die Schieberegler wer<strong>de</strong>n genau dann angezeigt,<br />

wenn es erfor<strong>de</strong>rlich ist, d.h.: Es wird mehr Platz<br />

benötigt, als für sie zur Anzeige zur Verfügung<br />

steht.<br />

Die Schieberegler wer<strong>de</strong>n immer angezeigt<br />

Die Schieberegler wer<strong>de</strong>n nie angezeigt, <strong>de</strong>r<br />

Bildschirm kann nur vom Programm aus verschoben<br />

wer<strong>de</strong>n<br />

5.2.9 Zeichenbereiche<br />

E<strong>in</strong> Canvas ist e<strong>in</strong> frei <strong>de</strong>f<strong>in</strong>iertes Dialogelement, das <strong>in</strong> <strong>de</strong>r Grundversion praktisch<br />

ke<strong>in</strong>e Funktionalität zur Verfügung stellt. Damit e<strong>in</strong> Canvas etwas S<strong>in</strong>nvolles tun<br />

kann, muß daraus e<strong>in</strong>e eigene Klasse abgeleitet wer<strong>de</strong>n.<br />

Konstruktor:<br />

public Canvas()<br />

Die Metho<strong>de</strong> pa<strong>in</strong>t(Graphics g). Durch Überlagerung von public void<br />

pa<strong>in</strong>t(Graphics g) sorgt e<strong>in</strong>e Canvas-Komponente für die Darstellung auf <strong>de</strong>m<br />

Bildschirm. Die vom System bereitgestellte Version zeichnet nur die Ausgabefläche<br />

<strong>in</strong> <strong>de</strong>r aktuellen H<strong>in</strong>tergrundfarbe. E<strong>in</strong>e überlagerte Version kann hier natürlich e<strong>in</strong><br />

beliebig komplexes Darstellungsverhalten erzielen. Der Punkt (0,0) <strong>de</strong>s übergebenen<br />

Graphics-Objekts entspricht dabei <strong>de</strong>r l<strong>in</strong>ken oberen Ecke <strong>de</strong>s Ausgabebereichs.<br />

Ereignisse. Da die Klasse Canvas aus Component abgeleitet ist, bekommt e<strong>in</strong><br />

Canvas-Objekt alle Ereignisse zugestellt, die auch an e<strong>in</strong>e Komponente gehen.<br />

Hierzu zählen Tastatur-, Maus-, Mausbewegungs-, Fokus- und<br />

Komponentenereignisse.<br />

Bsp.:<br />

Die von Canvas abgeleitete Klasse ZeichneCanvas<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

public class ZeichneCanvas extends Canvas<br />

{<br />

// Instanzvariable<br />

public boolean zeichneObjekte;<br />

private Font font;<br />

// Konstruktoren<br />

public ZeichneCanvas()<br />

{<br />

this(400,400);<br />

}<br />

public ZeichneCanvas(<strong>in</strong>t breite,<strong>in</strong>t hoehe)<br />

{<br />

super();<br />

setSize(breite,hoehe);<br />

setBackground(Color.white);<br />

bere<strong>in</strong>igen();<br />

font = new Font("Helvetica",Font.BOLD,24);<br />

}<br />

// Instanzmetho<strong>de</strong>n<br />

public void bere<strong>in</strong>igen()<br />

{<br />

zeichneObjekte = false;<br />

repa<strong>in</strong>t();<br />

328


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

}<br />

public void zeichnen()<br />

{<br />

zeichneObjekte = true;<br />

repa<strong>in</strong>t();<br />

}<br />

public void pa<strong>in</strong>t(Graphics g)<br />

{<br />

System.out.pr<strong>in</strong>tln(<br />

"Metho<strong>de</strong> pa<strong>in</strong>t wur<strong>de</strong> aufgerufen, zeichneObjekte = "<br />

+ zeichneObjekte);<br />

if (zeichneObjekte)<br />

{<br />

// Individuelle Auspraegung <strong>de</strong>r Zeichnung<br />

<strong>in</strong>t r = 8;<br />

<strong>in</strong>t i, j;<br />

<strong>in</strong>t x, y;<br />

g.setColor(Color.red);<br />

for (i=1; i


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

setLayout(new Bor<strong>de</strong>rLayout());<br />

Panel panelN = new Panel(); panelN.add(new Label(„x: „));<br />

xFeld = new TextField(5); xFeld.setEditable(false);<br />

panelN.add(xFeld); panelN.add(new Label("y: "));<br />

yFeld = new TextField(5); yFeld.setEditable(false);<br />

panelN.add(yFeld);<br />

add("North",panelN);<br />

zeichenflaeche = new ZeichneCanvas(breite,hoehe);<br />

zeichenflaeche.addMouseMotionListener(this);<br />

add("Center",zeichenflaeche); Panel panelS = new Panel();<br />

zeichne = new Button("Zeichne");<br />

zeichne.addActionListener(new ActionListener()<br />

{<br />

public void actionPerformed(ActionEvent e)<br />

{<br />

zeichenflaeche.zeichnen();<br />

}<br />

});<br />

panelS.add(zeichne);<br />

bere<strong>in</strong>ige = new Button("Bere<strong>in</strong>ige");<br />

bere<strong>in</strong>ige.addActionListener(new ActionListener()<br />

{<br />

public void actionPerformed(ActionEvent e)<br />

{<br />

zeichenflaeche.bere<strong>in</strong>igen();<br />

}<br />

});<br />

panelS.add(bere<strong>in</strong>ige); add("South",panelS);<br />

pack();<br />

setVisible(true);<br />

}<br />

// Implementierung <strong>de</strong>s MouseMotionListener-Interface<br />

public void mouseMoved(MouseEvent e)<br />

{<br />

xFeld.setText(" " + e.getX()); yFeld.setText(" " + e.getY());<br />

}<br />

public void mouseDragged(MouseEvent e)<br />

{<br />

xFeld.setText(" " + e.getX()); yFeld.setText(" " + e.getY());<br />

}<br />

// Start <strong>de</strong>r GUI<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

ZeichneGUI gui = new ZeichneGUI();<br />

}<br />

}<br />

330


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

5.3 Conta<strong>in</strong>er<br />

Conta<strong>in</strong>er s<strong>in</strong>d allgeme<strong>in</strong>e Komponenten, die an<strong>de</strong>re Komponenten o<strong>de</strong>r auch<br />

an<strong>de</strong>re Conta<strong>in</strong>er enthalten.<br />

5.3.1 Panels<br />

E<strong>in</strong>e sehr häufig vorkommen<strong>de</strong> Conta<strong>in</strong>er-Form ist das sog. Panel, das e<strong>in</strong>en<br />

Conta<strong>in</strong>er darstellt, <strong>de</strong>r am Bildschirm dargestellt wer<strong>de</strong>n kann. E<strong>in</strong> Panel ist e<strong>in</strong><br />

„re<strong>in</strong>er“ Conta<strong>in</strong>er, ke<strong>in</strong> eigenes Fenster. Se<strong>in</strong> e<strong>in</strong>ziger Zweck ist es, Komponenten <strong>in</strong><br />

e<strong>in</strong>em Fenster anzuordnen.<br />

Erzeugen e<strong>in</strong>es Panels. Das Erzeugen erfolgt mit Hilfe <strong>de</strong>s Konstruktors „Panel()“,<br />

z.B.: Panel me<strong>in</strong>Panel = new Panel();<br />

H<strong>in</strong>zufügen von e<strong>in</strong>em Panel <strong>in</strong> e<strong>in</strong>en an<strong>de</strong>ren Conta<strong>in</strong>er. E<strong>in</strong> Panel kann bspw. mit<br />

add(me<strong>in</strong>Panel) <strong>in</strong> e<strong>in</strong> Applet e<strong>in</strong>gebaut wer<strong>de</strong>n.<br />

Verschachteln von Panels. Panels lassen sich <strong>in</strong> beliebig vielen Ebenen<br />

verschachteln. S<strong>in</strong>n machen solche verschachtelten Panels vor allem <strong>in</strong> Verb<strong>in</strong>dung<br />

mit <strong>de</strong>n verschie<strong>de</strong>nen Layouts.<br />

5.3.2 Frames<br />

E<strong>in</strong> Frame ist e<strong>in</strong> voll funktionsfähiges Fenster mit eigenem Titel und Icon. Frames<br />

können Pulldown-Menüs haben und verschie<strong>de</strong>n gestaltete Mauszeiger verwen<strong>de</strong>n.<br />

Erzeugen von Frames. Mit <strong>de</strong>r Frame-Klasse kann e<strong>in</strong> funktionsfähiges Fenster mit<br />

Menüleiste erstellt wer<strong>de</strong>n. Zum Erzeugen von Frames stehen verschie<strong>de</strong>ne<br />

Konstruktoren zur Verfügung:<br />

public Frame()<br />

//Damit kann e<strong>in</strong> Frame erzeugt wer<strong>de</strong>n, <strong>de</strong>r zu Beg<strong>in</strong>n nicht sichtbar ist und ke<strong>in</strong>en Titel hat.<br />

public Frame(Str<strong>in</strong>g titel)<br />

//Diese Version vergibt e<strong>in</strong>en Titel bei <strong>de</strong>r Erzeugung, das Frame ist zu Beg<strong>in</strong>n nicht sichtbar.<br />

Verän<strong>de</strong>rn <strong>de</strong>r Titelzeile. Sie läßt sich <strong>in</strong> <strong>de</strong>n Klassen Frame und Dialog mit <strong>de</strong>n<br />

Metho<strong>de</strong>n<br />

public void setTitle(Str<strong>in</strong>g titel)<br />

// än<strong>de</strong>rt die Beschriftung <strong>de</strong>r Titelleiste <strong>in</strong> titel<br />

public Str<strong>in</strong>g getTitle()<br />

// Abfrage <strong>de</strong>r Titelleiste<br />

bee<strong>in</strong>flussen.<br />

E<strong>in</strong>stellen <strong>de</strong>s Standard-Font. E<strong>in</strong> Fenster hat e<strong>in</strong>en Standard-Font, <strong>de</strong>r zur Ausgabe<br />

<strong>de</strong>r Schrift verwen<strong>de</strong>t wird, (wenn nicht im Grafik-Kontext e<strong>in</strong> an<strong>de</strong>rer Font<br />

ausgewählt wird). Der Standard-Font e<strong>in</strong>es Fensters wird mit „public void<br />

setFont (Font f)“ <strong>de</strong>r Klasse Component e<strong>in</strong>gestellt.<br />

E<strong>in</strong>stellen von Vor<strong>de</strong>r- Und H<strong>in</strong>tergrundfarbe. Die Vor<strong>de</strong>rgrundfarbe dient zur<br />

Ausgabe von Grafik- und Textobjekten, wenn im Grafik-Kontext ke<strong>in</strong>e an<strong>de</strong>re Farbe<br />

331


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

gesetzt wird. Wird die E<strong>in</strong>stellung nicht geän<strong>de</strong>rt, wer<strong>de</strong>n für Vor<strong>de</strong>rgrund- und<br />

H<strong>in</strong>tergrundfarbe die unter W<strong>in</strong>dows e<strong>in</strong>gestellten Standardfarben verwen<strong>de</strong>t. Mit<br />

public void setBackground(Color farbe)<br />

public void setForeground(Color farbe)<br />

können Vor<strong>de</strong>rgrund- und H<strong>in</strong>tergrundfarbe <strong>de</strong>s Fensters geän<strong>de</strong>rt wer<strong>de</strong>n.<br />

Größe und Position e<strong>in</strong>es Fensters können bestimmt wer<strong>de</strong>n über<br />

public void setSize(<strong>in</strong>t breite, <strong>in</strong>t hoehe)<br />

// verän<strong>de</strong>rt die Größe <strong>de</strong>s Fensters auf <strong>de</strong>n Wert (breite, hoehe)<br />

public void setSize(Dimension d)<br />

public void setBounds(<strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t breite, <strong>in</strong>t hoehe)<br />

// positioniert e<strong>in</strong> Fenster <strong>de</strong>r Größe (breite,hoehe) an die Position (x,y)<br />

public void setBounds(Rectangle r)<br />

public void setLocation(<strong>in</strong>t x, <strong>in</strong>t y)<br />

// bewegt die l<strong>in</strong>ke obere Ecke an die Bildschirmposition (x,y)<br />

public void setLocation(Po<strong>in</strong>t p);<br />

Anzeigen, Wegblen<strong>de</strong>n und Löschen von Frames. Die Konstruktoren haben das<br />

Frame unsichtbar gelassen. Frames müssen vor Gebrauch sichtbar gemacht<br />

wer<strong>de</strong>n. Der 1. Schritt besteht dar<strong>in</strong>, e<strong>in</strong>en Frame e<strong>in</strong>e Größe mit <strong>de</strong>r Metho<strong>de</strong><br />

public void resize(<strong>in</strong>t breite /* Breite vom Frame <strong>in</strong> Pixel */,<br />

<strong>in</strong>t hoehe /* Hoehe <strong>in</strong> Pixel */) zu geben. Sichtbar wird <strong>de</strong>r Frame über<br />

die show()-Metho<strong>de</strong> 275 . Mit public void hi<strong>de</strong>() 276 kann <strong>de</strong>r Frame wie<strong>de</strong>r<br />

unsichtbar gemacht wer<strong>de</strong>n. Über public void dispose() wird <strong>de</strong>r Frame<br />

been<strong>de</strong>t. Mit public boolean isShow<strong>in</strong>g() kann geprüft wer<strong>de</strong>n, ob das<br />

Fenster bereits angezeigt ist.<br />

Standardlayout für Fenster: Bor<strong>de</strong>r-Layout.<br />

H<strong>in</strong>zufügen von Komponenten: Rahmen s<strong>in</strong>d Conta<strong>in</strong>er wie Panels, an<strong>de</strong>re<br />

Komponenten können mit <strong>de</strong>r add()-Metho<strong>de</strong> h<strong>in</strong>zugefügt wer<strong>de</strong>n.<br />

5.3.3 Menüs<br />

Je<strong>de</strong>s Fenster / Frame kann e<strong>in</strong>e eigene Menüleiste besitzen. Je<strong>de</strong> Menüleiste kann<br />

mehrere Menüs enthalten und je<strong>de</strong>s Menü beliebige E<strong>in</strong>träge. <strong>Java</strong> unterstützt die<br />

Konstruktion von Menüs durch e<strong>in</strong>e Reihe speziell dafür vorgesehenen Klassen:<br />

Klasse<br />

MenuBar<br />

Menu<br />

MenuItem<br />

CheckboxMenuItem<br />

Be<strong>de</strong>utung<br />

Beschreibt die Menüzeile (-leiste)e<strong>in</strong>es Fensters<br />

Beschreibet e<strong>in</strong> e<strong>in</strong>zelnes, <strong>de</strong>r <strong>in</strong> <strong>de</strong>r Menüzeile enthaltenen Menüs<br />

Bil<strong>de</strong>t die vom Anwen<strong>de</strong>r auswählbaren E<strong>in</strong>träge <strong>in</strong>nerhalb <strong>de</strong>r Menüs<br />

Die Menüleiste<br />

Sie beschreibt das Hauptmenü e<strong>in</strong>es Fensters. Sie bef<strong>in</strong><strong>de</strong>t sich unterhalb <strong>de</strong>r<br />

Titelleiste am oberen Rand <strong>de</strong>s Fensters und zeigt die Namen <strong>de</strong>r dar<strong>in</strong> enthaltenen<br />

Menüs an. E<strong>in</strong>e Menüleiste wird durch Instanzen <strong>de</strong>r Klasse MenuBar erzeugt:<br />

275 Für show() aus <strong>de</strong>m Paket java.awt.Component gibt es als neue Variante die Metho<strong>de</strong><br />

setVisible(boolean)<br />

276 hi<strong>de</strong>() ist <strong>in</strong> java.awt.Frame als „<strong>de</strong>precated“ <strong>de</strong>klariert. Statt <strong>de</strong>ssen steht setVisible(boolean) zur<br />

Verfügung<br />

332


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Konstruktor. public MenuBar()<br />

E<strong>in</strong>fügen von Menüs. public void add(Menu m).<br />

Entfernen von bestehen<strong>de</strong>n Menüs. public void remove(<strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

public void remove(MenuComponent m)<br />

Zugriff auf e<strong>in</strong> beliebiges Menü. public Menu getMenu(<strong>in</strong>t <strong>in</strong><strong>de</strong>x).<br />

getMenu liefert das Menüobjekt, das sich an <strong>de</strong>r Position mit <strong>de</strong>m angegebenen<br />

In<strong>de</strong>x bef<strong>in</strong><strong>de</strong>t. Zum B<strong>in</strong><strong>de</strong>n e<strong>in</strong>er Menüleiste an e<strong>in</strong> Fenster mit <strong>de</strong>m angegebenen<br />

In<strong>de</strong>x (<strong>in</strong><strong>de</strong>x) bef<strong>in</strong><strong>de</strong>t.<br />

B<strong>in</strong><strong>de</strong>n e<strong>in</strong>er Menüleiste an e<strong>in</strong>en Frame. public void setMenuBar(MenuBar<br />

mb) .Durch Aufruf dieser Metho<strong>de</strong> wird die angegebene Menüleiste im Fenster<br />

angezeigt und beim Auswählen <strong>de</strong>s Menüpunkts wer<strong>de</strong>n Nachrichen ausgelöst und<br />

an das Fenster gesen<strong>de</strong>t. Die Fensterklasse kann diese Nachrichen durch das<br />

Registrieren e<strong>in</strong>es Objekts vom Typ ActionListener bearbeiten.<br />

Menüs.<br />

Sie bil<strong>de</strong>n die Bestandteile e<strong>in</strong>er Menüleiste und wer<strong>de</strong>n durch Instanzen <strong>de</strong>r Klasse<br />

Menu repräsentiert.<br />

Konstruktor. public Menu(Str<strong>in</strong>g label)<br />

// label gibt <strong>de</strong>n Namen <strong>de</strong>s Menüs an<br />

Standardhilfe-Menü: public void setHelpMenu(Menu m) erzeugt e<strong>in</strong> spezielles<br />

Standardhilfemenü.<br />

Menüe<strong>in</strong>träge<br />

E<strong>in</strong>fache Menüe<strong>in</strong>träge s<strong>in</strong>d die elementaren Bestandteile e<strong>in</strong>es Menüs. Sie besitzen<br />

e<strong>in</strong>en Text, mit <strong>de</strong>m sie <strong>de</strong>m Anwen<strong>de</strong>r die dah<strong>in</strong>terstehen<strong>de</strong> Funktion anzeigen.<br />

Wenn <strong>de</strong>r zugehörige Menüpunkt aufgerufen wird, sen<strong>de</strong>t das Programm e<strong>in</strong>e<br />

Nachricht an das zugehörige Fenster, die dann zum Aufruf <strong>de</strong>r entsprechen<strong>de</strong>n<br />

Metho<strong>de</strong> führt.<br />

Erzeugen von Menüe<strong>in</strong>trägen: public MenuItem(Str<strong>in</strong>g label). „label“ ist<br />

<strong>de</strong>r Name <strong>de</strong>s Menüe<strong>in</strong>trags.<br />

Zugriff auf <strong>de</strong>n Namen bzw. Setzen <strong>de</strong>s Namens e<strong>in</strong>es Menüe<strong>in</strong>trags:<br />

public Str<strong>in</strong>g getLabel()<br />

public void setLabel(Str<strong>in</strong>g label)<br />

Neben <strong>de</strong>m Namen besitzt e<strong>in</strong> Menüe<strong>in</strong>trag e<strong>in</strong>e <strong>in</strong>terne Zustandsvariable. Sie zeigt<br />

an, ob <strong>de</strong>r Menüe<strong>in</strong>trag aktiv ist o<strong>de</strong>r nicht. Nur e<strong>in</strong> aktiver E<strong>in</strong>trag kann vom<br />

Anwen<strong>de</strong>r ausgewählt wer<strong>de</strong>n und so e<strong>in</strong>e Nachricht auslösen. Nach <strong>de</strong>m Aufruf <strong>de</strong>s<br />

Konstruktors ist e<strong>in</strong> Menüe<strong>in</strong>trag zunächst aktiviert. Er kann durch public void<br />

setEnabled(boolean b) mit Parameterwert „false“ <strong>de</strong>aktiviert und mit<br />

Parameterwert true aktiviert wer<strong>de</strong>n. Über public boolean isEnabled() kann<br />

<strong>de</strong>r aktuelle Zustand abgefragt wer<strong>de</strong>n.<br />

Metho<strong>de</strong>n für die Bearbeitung von Menüe<strong>in</strong>trägen.<br />

public void add(MenuItem m)<br />

public void add(Str<strong>in</strong>g label)<br />

public void remove(<strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

public void remove(MenuComponent Item)<br />

Seperatoren für Menüe<strong>in</strong>träge. E<strong>in</strong> Seperator wird als waagrechter Strich zur<br />

Trennung <strong>de</strong>r Menüe<strong>in</strong>träge angezeigt:<br />

public void addSeparator()<br />

// fügt <strong>de</strong>n Separator h<strong>in</strong>ter <strong>de</strong>m zuletzt e<strong>in</strong>gefügten Menüe<strong>in</strong>trag e<strong>in</strong><br />

public void <strong>in</strong>sertSeparator(<strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

// E<strong>in</strong>fügepostion kann frei angegeben wer<strong>de</strong>n.<br />

333


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Zugriff auf Menüe<strong>in</strong>trag. public MenuItem getItem(<strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

Anzahl <strong>de</strong>r E<strong>in</strong>träge. public <strong>in</strong>t getItemCount()<br />

Die Klasse CheckboxMenuItem. Sie ist aus MenuItem abgeleitet und besitzt<br />

zusätzlich e<strong>in</strong>e <strong>in</strong>terne Zustandsvariable, die zwischen true und false umgeschaltet<br />

wer<strong>de</strong>n kann. Die visuelle Darstellung <strong>de</strong>r Zustandsvariablen erfolgt durch Anfügen<br />

o<strong>de</strong>r Entfernen e<strong>in</strong>es „Häkchens“ neben <strong>de</strong>m Menüe<strong>in</strong>trag.<br />

Die Instanzierung e<strong>in</strong>es CkeckboxMenuItem erfogt wie bei e<strong>in</strong>em MenuItem.<br />

Zusätzlich stehen die bei<strong>de</strong>n Metho<strong>de</strong>n setState() und getState() zum Setzen<br />

und Abfragen <strong>de</strong>s Zustands zur Verfügung:<br />

public void setState(boolean status)<br />

public boolean getState()<br />

Menüaktionen: E<strong>in</strong> Menü macht nur S<strong>in</strong>n, wenn damit e<strong>in</strong>e Aktion ausgelöst wer<strong>de</strong>n<br />

kann. Diese Aktion kann wie je<strong>de</strong> an<strong>de</strong>re Aktion mit <strong>de</strong>r Metho<strong>de</strong> actionPerformed<br />

behan<strong>de</strong>lt wer<strong>de</strong>n.<br />

Bsp.: Zusammenstellung <strong>de</strong>r wesentlichen Elemente zur Gestaltung von Menüs<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

public class PR53331 extends Frame<br />

{<br />

// TextField t = new TextField("Ke<strong>in</strong>e Anzeige",30);<br />

public PR53331()<br />

{<br />

// Menueleiste erzeugen<br />

MenuBar mb = new MenuBar();<br />

// Menueleiste an Rahmen verankern<br />

setMenuBar(mb);<br />

// Menuepunkt erzeugen<br />

Menu m1 = new Menu("Farben"); // Menuepunkt 1<br />

// E<strong>in</strong> Untermenue erzeugen<br />

// Menuee<strong>in</strong>traege <strong>in</strong> <strong>de</strong>r Menuezeile plazieren<br />

mb.add(m1);<br />

// E<strong>in</strong>traege <strong>in</strong> Menuepunkt 1<br />

MenuItem rot = new MenuItem("Rot");<br />

m1.add(rot);<br />

rot.addActionListener(new RotL());<br />

MenuItem blau = new MenuItem("Blau");<br />

m1.add(blau);<br />

blau.addActionListener(new BlauL());<br />

MenuItem gruen = new MenuItem("Gruen");<br />

m1.add(gruen);<br />

gruen.addActionListener(new GruenL());<br />

MenuItem gelb = new MenuItem("Gelb");<br />

m1.add(gelb);<br />

gelb.addActionListener(new GelbL());<br />

// Untermenue erzeugen<br />

Menu unterMenue = new Menu("Farbanpassung");<br />

// Trennl<strong>in</strong>ie<br />

MenuItem trennL<strong>in</strong>ie = new MenuItem("-");<br />

m1.add(trennL<strong>in</strong>ie);<br />

// Untermenue h<strong>in</strong>zufuegen<br />

m1.add(unterMenue);<br />

MenuItem farbe = new MenuItem("Farbe");<br />

MenuItem mono = new MenuItem("Mono");<br />

farbe.addActionListener(new FarbeL());<br />

mono.addActionListener(new MonoL());<br />

unterMenue.add(farbe);<br />

unterMenue.add(mono);<br />

// Menue zur Dateibearbeitung<br />

334


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Menu d = new Menu("Datei");<br />

MenuItem[] datei =<br />

{<br />

//<br />

new MenuItem("Open"),<br />

// menu shortcut<br />

new MenuItem("Exit",new MenuShortcut(KeyEvent.VK_E))<br />

};<br />

ML ml = new ML();<br />

datei[0].setActionCommand("Open");<br />

datei[0].addActionListener(ml);<br />

datei[1].setActionCommand("Exit");<br />

datei[1].addActionListener(ml);<br />

for (<strong>in</strong>t i = 0; i < datei.length; i++)<br />

d.add(datei[i]);<br />

mb.add(d);<br />

// CheckboxMenuItem<br />

Menu s = new Menu("Sicherheit");<br />

CheckboxMenuItem[] sicherung;<br />

{<br />

new CheckboxMenuItem("Wache"),<br />

new CheckboxMenuItem("Versteck")<br />

};<br />

CMIL cmil = new CMIL();<br />

sicherung[0].setActionCommand("Wache");<br />

sicherung[0].addItemListener(cmil);<br />

sicherung[1].setActionCommand("Versteck");<br />

sicherung[1].addItemListener(cmil);<br />

for (<strong>in</strong>t i = 0; i < sicherung.length; i++)<br />

s.add(sicherung[i]);<br />

d.add(s);<br />

// Hilfsmenue<br />

Menu me<strong>in</strong>eHilfe = new Menu("Hilfe");<br />

mb.setHelpMenu(me<strong>in</strong>eHilfe);<br />

// E<strong>in</strong>traege im Hilfsmenue<br />

me<strong>in</strong>eHilfe.add(new MenuItem("Info"));<br />

me<strong>in</strong>eHilfe.add(new MenuItem("Hilfe"));<br />

// Textfeld <strong>in</strong>itialisieren<br />

// t.setEditable(false);<br />

// add(t,Bor<strong>de</strong>rLayout.CENTER);<br />

}<br />

class ML implements ActionListener<br />

{<br />

public void actionPerformed(ActionEvent e)<br />

{<br />

MenuItem ziel<br />

= (MenuItem) e.getSource();<br />

Str<strong>in</strong>g aktionsKommando = ziel.getActionCommand();<br />

if (aktionsKommando.equals("Open"))<br />

{<br />

System.out.pr<strong>in</strong>tln("Oeffnen");<br />

}<br />

else if (aktionsKommando.equals("Exit"))<br />

{<br />

dispatchEvent(new W<strong>in</strong>dowEvent(PR53331.this,<br />

W<strong>in</strong>dowEvent.WINDOW_CLOSING));<br />

}<br />

}<br />

}<br />

class CMIL implements ItemListener<br />

{<br />

public void itemStateChanged(ItemEvent e)<br />

{<br />

CheckboxMenuItem ziel = (CheckboxMenuItem) e.getSource();<br />

Str<strong>in</strong>g aktionsKommando = ziel.getActionCommand();<br />

if (aktionsKommando.equals("Wache"))<br />

{<br />

335


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

System.out.pr<strong>in</strong>tln("Wache " + ziel.getState());<br />

}<br />

else if (aktionsKommando.equals("Versteck"))<br />

{<br />

System.out.pr<strong>in</strong>tln("Versteck " + ziel.getState());<br />

}<br />

}<br />

}<br />

class RotL implements ActionListener<br />

{<br />

public void actionPerformed(ActionEvent e)<br />

{ setBackground(Color.red); }<br />

}<br />

class BlauL implements ActionListener<br />

{<br />

public void actionPerformed(ActionEvent e)<br />

{<br />

setBackground(Color.blue);<br />

}<br />

}<br />

class GruenL implements ActionListener<br />

{<br />

public void actionPerformed(ActionEvent e)<br />

{ setBackground(Color.green); }<br />

}<br />

class GelbL implements ActionListener<br />

{<br />

public void actionPerformed(ActionEvent e)<br />

{ setBackground(Color.yellow); }<br />

}<br />

class FarbeL implements ActionListener<br />

{<br />

public void actionPerformed(ActionEvent e)<br />

{<br />

System.out.pr<strong>in</strong>tln("Im Untermenue wur<strong>de</strong> Farbe ausgewaehlt");<br />

}<br />

}<br />

class MonoL implements ActionListener<br />

{<br />

public void actionPerformed(ActionEvent e)<br />

{<br />

System.out.pr<strong>in</strong>tln("Im Untermenue wur<strong>de</strong> Mono gewaehlt");<br />

}<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

PR53331 f = new PR53331();<br />

f.addW<strong>in</strong>dowListener(new W<strong>in</strong>dowAdapter()<br />

{<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{ System.exit(0); }<br />

});<br />

f.setSize(300,200); f.setVisible(true);<br />

}<br />

}<br />

5.3.4 Dialoge<br />

Unter e<strong>in</strong>em Dialog versteht man e<strong>in</strong> Popup-Fenster. Dialoge wer<strong>de</strong>n entwe<strong>de</strong>r<br />

„modal“ o<strong>de</strong>r „non-modal“ erzeugt. „modal“ be<strong>de</strong>utet: Das Dialogfeld blockiert an<strong>de</strong>re<br />

Fenster, wenn es angezeigt wird.<br />

336


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Erzeugen. E<strong>in</strong> modaler Dialog muß immer von <strong>de</strong>r Klasse Dialog abgeleitet se<strong>in</strong>.<br />

Dialog bietet 4 Konstruktoren an<br />

public Dialog(Frame eltern)<br />

public Dialog(Frame eltern, boolean modal)<br />

public Dialog(Frame eltern, Str<strong>in</strong>g titel)<br />

public Dialog(Frame eltern, Str<strong>in</strong>g titel, boolean modal)<br />

Der Parameter modal entschei<strong>de</strong>t, ob <strong>de</strong>r Dialog modal wird o<strong>de</strong>r nicht.<br />

Zugriff auf modale bzw. „nicht modale“ Eigenschaft. Dafür gibt es die Metho<strong>de</strong>n<br />

public boolean isModal()<br />

// Rückgabewert ist true, falls <strong>de</strong>r Dialog modal ist. An<strong>de</strong>renfalls ist er<br />

// false.<br />

public void setModal(boolean b)<br />

Unterb<strong>in</strong><strong>de</strong>n <strong>de</strong>r Verän<strong>de</strong>rbarkeit <strong>de</strong>r Größe e<strong>in</strong>es Fensters. Das kann über<br />

public void setResizable(boolean resizable)<br />

public boolean isResizable()<br />

geschehen bzw. überprüft wer<strong>de</strong>n.<br />

337


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

5.4 Die Layout-Manager<br />

Das AWT-System kümmert sich weitgehend selbstständig um Größenanpassung<br />

und Postionierung von Komponenten auf <strong>de</strong>r Oberfläche. Je nach Plattform und<br />

Bed<strong>in</strong>gungen wer<strong>de</strong>n Komponenten <strong>in</strong> die Oberfläche optimal angepaßt. Über<br />

Layout-Manager kann das AWT angewiesen wer<strong>de</strong>n, wo Komponenten im Verhältnis<br />

zu an<strong>de</strong>ren Komponenten stehen sollen. Der Layout-Manager bestimmt nach<br />

gewissen Regeln, an welche Stelle die Komponenten am besten passen und ermittelt<br />

die optimale Größe <strong>de</strong>r Komponenten.<br />

5.4.1 Layout-Regeln<br />

Drei Aspekte bestimmen das Aussehen e<strong>in</strong>er AWT-Oberfläche:<br />

1. Die Plattform und die Bed<strong>in</strong>gungen, die unter dieser Plattform vorliegen. <strong>Java</strong><br />

ist plattformunabhängig, daher kann <strong>de</strong>r Programmierer hier ke<strong>in</strong>e Aussagen<br />

machen.<br />

2. Die Reihenfolge, <strong>in</strong> <strong>de</strong>r die AWT-Komponenten <strong>in</strong> e<strong>in</strong>en Conta<strong>in</strong>er e<strong>in</strong>gefügt<br />

wer<strong>de</strong>n.<br />

3. Die Art <strong>de</strong>s Layout-Managers. Das AWT umfaßt 5 verschie<strong>de</strong>ne Typen von<br />

Layout-Managern, die die Oberfläche unterschiedlich aufglie<strong>de</strong>rn:<br />

- Flow-Layout<br />

- Grid-Layout<br />

- Bor<strong>de</strong>r-Layout<br />

- Card-Layout<br />

- GridBag-Layout<br />

Je<strong>de</strong>s Panel kann e<strong>in</strong>en eigenen Layout-Manager verwen<strong>de</strong>n.<br />

Erstellen e<strong>in</strong>es Layout-Managers. Zum Erstellen e<strong>in</strong>es Layout-Managers für e<strong>in</strong>en<br />

Panel kann ( <strong>in</strong> <strong>de</strong>r <strong>in</strong>it()-Metho<strong>de</strong>) die Metho<strong>de</strong> public void<br />

setLayout(LayoutManager mgr) verwen<strong>de</strong>t wer<strong>de</strong>n, z.B.: setLayout(new<br />

FlowLayout());<br />

338


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

5.4.2 Die e<strong>in</strong>zelnen Layout-Manager<br />

Die FlowLayout-Klasse<br />

><br />

LayoutManager<br />

FlowLayout<br />

public static <strong>in</strong>t CENTER<br />

public static <strong>in</strong>t LEADING<br />

public static <strong>in</strong>t LEFT<br />

public static <strong>in</strong>t RIGHT<br />

public static <strong>in</strong>t TRAILING<br />

><br />

public FlowLayout()<br />

public FlowLayout(<strong>in</strong>t align)<br />

public FlowLayout(<strong>in</strong>t align, <strong>in</strong>t hgap, <strong>in</strong>t vgap)<br />

//erzeugt e<strong>in</strong> FlowLayout mit angegebenen Alignment und e<strong>in</strong>em<br />

//horizontalen bzw. vertikalen Rand<br />

><br />

public void addLayoutComponent(Str<strong>in</strong>g name, Component comp)<br />

public <strong>in</strong>t getAlignment()<br />

//liefert das Alignment <strong>de</strong>s FlowLayout-Managers<br />

public void setAlignment(<strong>in</strong>t align)<br />

//setzt das Alignment mit Hilfe <strong>de</strong>r Konstanten<br />

//FlowLayout.RIGHT, FlowLayout.LEFT, FlowLayout.CENTER<br />

...<br />

Abb. Die Klasse FlowLayout<br />

FlowLayout ordnet Komponenten von l<strong>in</strong>ks nach rechts an, bis ke<strong>in</strong>e weiteren<br />

Komponenten mehr <strong>in</strong> die Zeile passen. Dann geht es zur nächsten Zeile und bewegt<br />

sich wie<strong>de</strong>r von l<strong>in</strong>ks nach rechts. Die Standard-.Ausrichtung für e<strong>in</strong> FlowLayout ist<br />

zentriert.<br />

Der FlowLayout-Manager ist <strong>de</strong>r Standard-Layout-Manager für alle Applets.<br />

Bsp.: Plazierung von Schaltflächen.<br />

Die Komponenten wer<strong>de</strong>n <strong>in</strong> <strong>de</strong>r Reihenfolge, <strong>in</strong> <strong>de</strong>r sie <strong>in</strong> <strong>de</strong>n Conta<strong>in</strong>er e<strong>in</strong>gefügt<br />

wer<strong>de</strong>n, spaltenweise von l<strong>in</strong>ks nach rechts e<strong>in</strong>geordnet, bis ke<strong>in</strong>e weiteren<br />

Komponenten mehr <strong>in</strong> e<strong>in</strong>e Zeile passen. So führt <strong>de</strong>r folgen<strong>de</strong> Quellco<strong>de</strong><br />

import java.awt.*;<br />

public class FlowLayoutTestApplet extends java.applet.Applet<br />

{<br />

public void <strong>in</strong>it()<br />

{<br />

setBackground(Color.yellow);<br />

setLayout(new FlowLayout());<br />

Button me<strong>in</strong>Knopf1 = new Button("Rot");<br />

add(me<strong>in</strong>Knopf1);<br />

Button me<strong>in</strong>Knopf2 = new Button("Blau");<br />

add(me<strong>in</strong>Knopf2);<br />

Button me<strong>in</strong>Knopf3 = new Button("Gruen");<br />

add(me<strong>in</strong>Knopf3);<br />

339


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Button me<strong>in</strong>Knopf4 = new Button("P<strong>in</strong>k");<br />

add(me<strong>in</strong>Knopf4);<br />

Button me<strong>in</strong>Knopf5 = new Button("Rosa");<br />

add(me<strong>in</strong>Knopf5);<br />

Button me<strong>in</strong>Knopf6 = new Button("Gelb");<br />

add(me<strong>in</strong>Knopf6);<br />

Button me<strong>in</strong>Knopf7 = new Button("Cyan");<br />

add(me<strong>in</strong>Knopf7);<br />

}<br />

}<br />

mit <strong>de</strong>n im Applet-Tag angegebenen Abmessungen<br />

<br />

<br />

FlowLayout-Test<br />

<br />

<br />

<br />

<br />

<br />

<br />

zur folgen<strong>de</strong>n Darstellung<br />

E<strong>in</strong>e Verän<strong>de</strong>rung <strong>in</strong> <strong>de</strong>r Größe <strong>de</strong>s Applet macht <strong>de</strong>n zeilenweisen Aufbau <strong>de</strong>utlich:<br />

<br />

<br />

FlowLayout-Test<br />

<br />

<br />

<br />

<br />

<br />

<br />

340


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Die Bor<strong>de</strong>rLayout-Klasse<br />

><br />

LayoutManager<br />

Bor<strong>de</strong>rLayout<br />

public static Str<strong>in</strong>g CENTER<br />

public static Str<strong>in</strong>g NORTH<br />

public static Str<strong>in</strong>g EAST<br />

public static Str<strong>in</strong>g SOUTH<br />

public static Str<strong>in</strong>g WEST<br />

><br />

public Bor<strong>de</strong>rLayout()<br />

public Bor<strong>de</strong>rLayout(<strong>in</strong>t hgap, <strong>in</strong>t vgap)<br />

><br />

public void addLayoutComponent(Component comp, Object constra<strong>in</strong>ts)<br />

public void layoutConta<strong>in</strong>er(Conta<strong>in</strong>er target)<br />

public Dimension preferredLayoutSize(Conta<strong>in</strong>er target)<br />

public void removeLayoutComponent(Component comp)<br />

...<br />

Abb. Die Klasse Bor<strong>de</strong>rLayout<br />

Die Bor<strong>de</strong>rLayout-Klasse unterteilt e<strong>in</strong>en Conta<strong>in</strong>er <strong>in</strong> fünf Bereiche: North, South,<br />

East, West, Center. Falls e<strong>in</strong>em Conta<strong>in</strong>er Komponenten h<strong>in</strong>zugefügt wer<strong>de</strong>n,<br />

geschieht das über e<strong>in</strong> spezielles Exemplar <strong>de</strong>r add()-Metho<strong>de</strong> ( mit e<strong>in</strong>em dieser<br />

fünf Bereichsnamen).<br />

Bsp.: Bor<strong>de</strong>rLayout-Applet, das e<strong>in</strong>em Applet e<strong>in</strong>ige Schaltfläche h<strong>in</strong>zufügt.<br />

Der Quellco<strong>de</strong> ist<br />

import java.awt.*;<br />

import java.applet.*;<br />

public class Bor<strong>de</strong>rLayoutTestApplet extends Applet<br />

{<br />

public void <strong>in</strong>it()<br />

{<br />

<strong>in</strong>t i = 0;<br />

setBackground(Color.yellow);<br />

setLayout(new Bor<strong>de</strong>rLayout());<br />

add("North",new Button("Schaltflaeche " + i++));<br />

add("South",new Button("Schaltflaeche " + i++));<br />

add("East", new Button("Schaltflaeche " + i++));<br />

341


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

add("West", new Button("Schaltflaeche " + i++));<br />

add("Center",new Button("Schaltflaeche " + i++));<br />

}<br />

}<br />

Für das Applet wur<strong>de</strong> folgen<strong>de</strong> Größe vere<strong>in</strong>bart:<br />

<br />

<br />

FlowLayout-Test<br />

<br />

<br />

<br />

<br />

<br />

<br />

Das führt zu <strong>de</strong>m folgen<strong>de</strong>n Layout:<br />

Die GridLayout-Klasse<br />

Diese Klasse teilt <strong>de</strong>n Conta<strong>in</strong>er <strong>in</strong> e<strong>in</strong> Gitter mit gleich großen Zellen e<strong>in</strong>.<br />

H<strong>in</strong>zugefügte Komponenten wer<strong>de</strong>n von l<strong>in</strong>ks nach rechts angeordnet, begonnen<br />

wird bei <strong>de</strong>n l<strong>in</strong>ken, oberen Zellen.<br />

Bsp.: Plazieren von Schaltflächen<br />

Der folgen<strong>de</strong> Quellco<strong>de</strong><br />

import java.awt.*;<br />

import java.applet.*;<br />

public class GridLayoutTestAppl extends Applet<br />

{<br />

public void <strong>in</strong>it()<br />

{<br />

setLayout(new GridLayout(7,3));<br />

for (<strong>in</strong>t i = 0; i < 20; i++)<br />

add(new Button("Schaltflaeche " + i));<br />

}<br />

}<br />

führt über die folgen<strong>de</strong> Größe im Applet-Tag<br />

<br />

<br />

FlowLayout-Test<br />

<br />

<br />

<br />

<br />

<br />

<br />

zu <strong>de</strong>r folgen<strong>de</strong>n Darstellung<br />

342


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Die GridBagLayout-Klasse und die GridBagConstra<strong>in</strong>t-Klasse<br />

Die Klasse GridBagLayout ermöglicht die Anordnung <strong>de</strong>r Komponenten <strong>in</strong> e<strong>in</strong>em<br />

rasterähnlichen Layout. Zusätzlich zum GridLayout kann kontrolliert wer<strong>de</strong>n: Die<br />

Werte e<strong>in</strong>zelner Zellen im Raster, die Proportionen zwischen Zeilen und Spalten<br />

sowie die Anordnung von Komponenten <strong>in</strong>nerhalb <strong>de</strong>r Zellen im Raster. Zur<br />

Erstellung e<strong>in</strong>es GridLayout dienen zwei Klassen:<br />

- GridBagLayout, die <strong>de</strong>n Layoutmanager bereitstellt<br />

- GridBagConstra<strong>in</strong>ts, die die Eigenschaften je<strong>de</strong>r Komponente im Raster bestimmt.<br />

Die GridBagLayout-Klasse ermöglicht e<strong>in</strong>er Komponente auch die Belegung von<br />

mehr als e<strong>in</strong>er Zelle. Der gesamte Bereich, <strong>de</strong>n die Komponente e<strong>in</strong>nimmt, wird<br />

„display area“ genannt. Bevor e<strong>in</strong>em Conta<strong>in</strong>er e<strong>in</strong>e Komponente h<strong>in</strong>zugefügt<br />

wird, müssen GridBagLayout Vorschläge unterbreitet wer<strong>de</strong>n, wo die Komponente<br />

h<strong>in</strong>gestellt wer<strong>de</strong>n soll. Die GridBagConstra<strong>in</strong>t-Klasse besitzt verschie<strong>de</strong>ne<br />

Variable zur Steuerung <strong>de</strong>r Anordnung <strong>de</strong>r Komponenten:<br />

- die Variablen gridx und gridy (Koord<strong>in</strong>aten <strong>de</strong>r Zelle <strong>in</strong> <strong>de</strong>r die nächste Komponente plaziert<br />

wer<strong>de</strong>n soll). Die obere l<strong>in</strong>ke Ecke von GridBagLayout liegt bei (0,0), <strong>de</strong>r Standardwert ist<br />

GridBagConstra<strong>in</strong>ts.RELATIVE. Für gridx ist das die erste rechte Zelle neben <strong>de</strong>r letzten<br />

h<strong>in</strong>zugefügten Komponente. Für gridy ist es genau die Zelle, die unterhalb <strong>de</strong>r letzten<br />

Komponente h<strong>in</strong>zugefügt wur<strong>de</strong>.<br />

- die Variablen gridwidth und gridheight bestimmen, wie viele Zellen hoch und breit e<strong>in</strong>e<br />

Komponente se<strong>in</strong> sollte (Standardwert ist 1). Der Wert GridBagConstra<strong>in</strong>ts.REMAINDER für<br />

gridwidth bestimmt, daß e<strong>in</strong>e Komponente die letzte <strong>in</strong> <strong>de</strong>r Spalte se<strong>in</strong> sollte.<br />

- Die Variable fill gibt an, welche Dimension e<strong>in</strong>er Komponente sich verän<strong>de</strong>rn soll, wenn e<strong>in</strong>e<br />

Komponente kle<strong>in</strong>er als <strong>de</strong>r Anzeigebereich ist. Gültige Werte s<strong>in</strong>d: NONE, BOTH, HORIZONTAL<br />

und VERTICAL. „GridBagConstra<strong>in</strong>ts.BOTH“ sorgt für e<strong>in</strong>e Streckung <strong>de</strong>r Komponenten <strong>in</strong><br />

bei<strong>de</strong>n Richtungen so, daß sie <strong>de</strong>n Anzeigebereich voll ausfüllen.<br />

- anchor wird e<strong>in</strong>gesetzt, wenn die Komponente kle<strong>in</strong>er als ihr Anzeigebereich ist. Sie zeigt an, wo<br />

die Komponente <strong>in</strong>nerhalb <strong>de</strong>s Anzeigebereichs plaziert wer<strong>de</strong>n soll. Der Standardwert ist<br />

„GridBagConstra<strong>in</strong>ts.CENTER“. Er gibt an, welche Komponente im „Zentrum <strong>de</strong>s<br />

Anzeigebereichs“ stehen soll. Die an<strong>de</strong>ren Komponenten s<strong>in</strong>d Kompasspunkte:<br />

GridBagConstra<strong>in</strong>ts.NORTH, GridBagConstra<strong>in</strong>ts.NORTHEAST etc.<br />

- Die Variable weightx und weighty <strong>de</strong>f<strong>in</strong>ieren die Gewichtung für freien Raum <strong>in</strong>nerhalb e<strong>in</strong>es<br />

Conta<strong>in</strong>ers und bestimmen die relative Größe <strong>de</strong>r Komponenten. E<strong>in</strong> Komponente mit e<strong>in</strong>em Wert<br />

343


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

von 2.0 für weightx nimmt doppelt soviel Platz <strong>in</strong> horizontaler Richtung wie e<strong>in</strong>e Komponente mit<br />

<strong>de</strong>m Wert 1.0 für weightx.<br />

- Durch „<strong>in</strong>sets“ (Objekte <strong>de</strong>r Klasse Insets) 277 wird <strong>de</strong>r freizuhalten<strong>de</strong> Rand zwischen<br />

Komponente und Gitter festgelegt. Die Standardwerte für alle 4 Seiten s<strong>in</strong>d Null.<br />

- Die Elemente können zum Teil auch vergrößert wer<strong>de</strong>n. Die Pixel, die rechts und l<strong>in</strong>ks zuzufügen<br />

s<strong>in</strong>d, gibt ipadx an. Wieviel oben und unten h<strong>in</strong>zukommt, speichert ipady. (ipadx, ipady s<strong>in</strong>d<br />

standardmäßig 0).<br />

gridwidth<br />

ipady<br />

<strong>in</strong>sets<br />

ipadx<br />

fill<br />

gridheight<br />

Bsp. 278 : Plazieren von Schaltflächen<br />

import java.awt.*;<br />

import java.util.*;<br />

import java.applet.Applet;<br />

public class GridBagBsp extends Applet<br />

{<br />

protected void macheSchalter(Str<strong>in</strong>g name,<br />

GridBagLayout gridbag,<br />

GridBagConstra<strong>in</strong>ts c)<br />

{<br />

Button schalter = new Button(name);<br />

gridbag.setConstra<strong>in</strong>ts(schalter, c);<br />

add(schalter);<br />

}<br />

public void <strong>in</strong>it()<br />

{<br />

GridBagLayout gridbag = new GridBagLayout();<br />

GridBagConstra<strong>in</strong>ts c = new GridBagConstra<strong>in</strong>ts();<br />

setFont(new Font("Helvetica", Font.PLAIN, 14));<br />

setLayout(gridbag);<br />

c.fill = GridBagConstra<strong>in</strong>ts.BOTH;<br />

c.weightx = 1.0;<br />

macheSchalter("Schaltflaeche1", gridbag, c);<br />

macheSchalter("Schaltflaeche2", gridbag, c);<br />

macheSchalter("Schaltflaeche3", gridbag, c);<br />

// letzter Schalter <strong>in</strong> <strong>de</strong>r Zeile:<br />

c.gridwidth = GridBagConstra<strong>in</strong>ts.REMAINDER;<br />

macheSchalter("Schaltflaeche4", gridbag, c);<br />

// neue Zeile, weightx wie<strong>de</strong>rherstellen:<br />

c.weightx = 0.0;<br />

macheSchalter("Schaltflaeche5", gridbag, c);<br />

// vorletztes Element <strong>de</strong>r Zeile:<br />

c.gridwidth = GridBagConstra<strong>in</strong>ts.RELATIVE;<br />

macheSchalter("Schaltflaeche6", gridbag, c);<br />

// letztes Element:<br />

c.gridwidth = GridBagConstra<strong>in</strong>ts.REMAINDER;<br />

macheSchalter("Schaltflaeche7", gridbag, c);<br />

277 Beim Erstellen e<strong>in</strong>es neuen Layout-Managers erzeugte horizontale und vertikale Abstän<strong>de</strong> dienen zur<br />

Bestimmung <strong>de</strong>s Platzes zwischen Komponenten <strong>in</strong> e<strong>in</strong>em Panel. Die Klasse Insets bietet Ecke<strong>in</strong>setzwerte für<br />

oben, unten, l<strong>in</strong>ks und rechts an, die dann verwen<strong>de</strong>t wer<strong>de</strong>n, wenn das Panel gezeichnet wird.<br />

278 vgl. pr54205<br />

344


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

// neue Zeile, Wert wie<strong>de</strong>rherstellen:<br />

c.gridwidth = 1;<br />

c.gridheight = 2;<br />

c.weighty = 1.0;<br />

macheSchalter("Schaltflaeche8", gridbag, c);<br />

c.weighty = 0.0;<br />

c.gridwidth = GridBagConstra<strong>in</strong>ts.REMAINDER;<br />

c.gridheight = 1;<br />

macheSchalter("Schaltflaeche9", gridbag, c);<br />

macheSchalter("Schaltflaeche10", gridbag, c);<br />

resize(300, 100);<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

Frame f = new Frame("GridBag Layout Beispiel");<br />

GridBagBsp bsp = new GridBagBsp();<br />

bsp.<strong>in</strong>it();<br />

f.add("Center", bsp);<br />

f.pack();<br />

f.resize(f.preferredSize());<br />

f.show();<br />

}<br />

Die CardLayout-Klasse<br />

CardLayout 279 plaziert die Elemente auf sich gegenseitig ver<strong>de</strong>cken<strong>de</strong> Karten.<br />

Jeweils e<strong>in</strong>e dieser Karten kann sichtbar gemacht wer<strong>de</strong>n.<br />

Das Null-Layout<br />

E<strong>in</strong> Aufruf <strong>de</strong>r Metho<strong>de</strong> „setLayout“ mit <strong>de</strong>m Argument null erzeugt e<strong>in</strong> Null-<br />

Layout. In diesem Fall verwen<strong>de</strong>t das Fenster ke<strong>in</strong>en Layoutmanager, son<strong>de</strong>rn<br />

überläßt die Positionierung <strong>de</strong>r Komponenten <strong>de</strong>r Anwendung. Je Komponente s<strong>in</strong>d<br />

drei Schritte auszuführen<br />

- Erzeugen <strong>de</strong>r Komponente<br />

- Festlegung von Größe und Position <strong>de</strong>r Komponente<br />

- Übergabe <strong>de</strong>r Komponente an das Fenster.<br />

Größe und Position <strong>de</strong>s Dialogelements können mit<br />

public void setBounds(<strong>in</strong>t x, <strong>in</strong>t y, <strong>in</strong>t breite, <strong>in</strong>t hoehe)<br />

279 Das CardLayout ist als Notizbuchersatz durch die E<strong>in</strong>führung <strong>de</strong>r Klasse JTabbedPane <strong>in</strong> Sw<strong>in</strong>g fast<br />

überflüssig gewor<strong>de</strong>n<br />

345


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

festgelegt wer<strong>de</strong>n. Der Punkt (x,y) bestimmt die Anfangsposition und<br />

(breite,hoehe) die Größe.<br />

Bsp.:<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

public class NL extends Frame<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

NL f = new NL();<br />

f.setVisible(true);<br />

}<br />

public NL()<br />

{<br />

addW<strong>in</strong>dowListener(new W<strong>in</strong>dowAdapter()<br />

{<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{<br />

setVisible(false);<br />

dispose();<br />

System.exit(0);<br />

}<br />

});<br />

setSize(300,250);<br />

setLayout(null);<br />

for (<strong>in</strong>t i = 0; i < 5; i++)<br />

{<br />

Button schalter = new Button("Schalter" + (i+1));<br />

schalter.setBounds(10+i*35,40+i*35,100,30);<br />

add(schalter);<br />

}<br />

}<br />

}<br />

346


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

5.5 Die Event-Mo<strong>de</strong>lle 1.0 und 1.1<br />

5.5.1 Der AWT-Handler 1.0<br />

Die Metho<strong>de</strong> handleEvent<br />

Die handleEvent()-Metho<strong>de</strong> 280 ist unter <strong>de</strong>m AWT-Event-Handler die allgeme<strong>in</strong>ste<br />

Art, wie das AWT auf irgendwelche Ereignisse e<strong>in</strong>geht, die auf <strong>de</strong>r<br />

Benutzeroberfläche stattf<strong>in</strong><strong>de</strong>n. Ereignisse wer<strong>de</strong>n <strong>in</strong>nerhalb <strong>de</strong>r handleEvent()-<br />

Metho<strong>de</strong> <strong>in</strong>terpretiert und dann gezielt passen<strong>de</strong> Metho<strong>de</strong>n aufgerufen. Damit die<br />

handleEvent()-Funktion nicht zu groß und unübersichtlich wird, ruft<br />

handleEvent() mehrere Hilfsfunktionen (mouseEnter, keydown, action, ... )<br />

auf, die die jeweils zugeordneten Events bearbeiten. Die Verarbeitung von<br />

Ereignissen stützt sich auf die Klasse Event. Die Instanzvariable „id“ <strong>de</strong>r Klasse<br />

Event enthält die Art <strong>de</strong>s Ereignisses und liefert alle notwendigen Informationen. Das<br />

Attribut „target“ enthält die Information, welches Objekt das Ereignis ausgelöst hat.<br />

Bsp.: Darstellung e<strong>in</strong>er Datei im Textbereich e<strong>in</strong>es Fensters (Frame)<br />

import java.awt.*;<br />

import java.io.*;<br />

public class DateiDarsteller extends Frame<br />

{<br />

// Instanzvariable<br />

Str<strong>in</strong>g dateiName;<br />

// Konstruktoren<br />

public DateiDarsteller() throws IOException<br />

{<br />

super("Dateidarsteller ");<br />

MenuBar menue = new MenuBar();<br />

Menu m = new Menu("Datei");<br />

m.add(new MenuItem("Oeffnen"));<br />

m.add(new MenuItem("Schliessen"));<br />

menue.add(m);<br />

// Installiere diesen Menuebalken<br />

setMenuBar(menue);<br />

this.show();<br />

}<br />

// Metho<strong>de</strong>n<br />

public void dateiDarstellen() throws IOException<br />

{<br />

try {<br />

File d = new File(dateiName);<br />

<strong>in</strong>t groesse = (<strong>in</strong>t) d.length();<br />

<strong>in</strong>t anzZeich = 0;<br />

FileInputStream e<strong>in</strong> = new FileInputStream(d);<br />

byte[] daten = new byte[groesse];<br />

while (anzZeich < groesse)<br />

anzZeich += e<strong>in</strong>.read(daten,anzZeich,groesse);<br />

Str<strong>in</strong>g s = new Str<strong>in</strong>g(daten,0);<br />

TextArea textBereich = new TextArea(s,24,80);<br />

textBereich.setFont(new Font("Helvetiva",Font.PLAIN,10));<br />

textBereich.setEditable(false);<br />

this.add("Center",textBereich);<br />

}<br />

280 Die Metho<strong>de</strong> ist <strong>in</strong> <strong>de</strong>r Klasse Component <strong>de</strong>f<strong>in</strong>iert, von <strong>de</strong>r die Klasse java.applet.Applet<br />

abgeleitet ist. Dadurch steht die Metho<strong>de</strong> allen Applets zur Verfügung,<br />

347


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

catch (IOException e) { }<br />

this.show();<br />

}<br />

public boolean handleEvent(Event e)<br />

{<br />

if ((e.id == Event.ACTION_EVENT) && (e.target <strong>in</strong>stanceof<br />

MenuItem))<br />

{<br />

if (((MenuItem) e.target).getLabel().equals("Oeffnen"))<br />

{<br />

FileDialog fd = new FileDialog(this,"Dateidialog");<br />

fd.show();<br />

dateiName = fd.getFile();<br />

System.out.pr<strong>in</strong>tln("Date<strong>in</strong>ame: " + dateiName);<br />

try { this.dateiDarstellen(); }<br />

catch (IOException a ) { }<br />

return true;<br />

}<br />

if (((MenuItem) e.target).getLabel().equals("Schliessen"))<br />

{<br />

this.hi<strong>de</strong>();<br />

this.dispose();<br />

System.exit(0);<br />

return true; }<br />

}<br />

return false;<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g argv[]) throws IOException<br />

{<br />

try {<br />

DateiDarsteller DD = new DateiDarsteller();<br />

}<br />

catch (IOException e) { System.out.pr<strong>in</strong>tln(e); }<br />

}<br />

}<br />

Behandlung von Aktionsereignissen<br />

Aktionsereignisse wer<strong>de</strong>n über public boolean action(Event e, Object<br />

arg) behan<strong>de</strong>lt. Zuerst muß <strong>in</strong>nerhalb <strong>de</strong>r metho<strong>de</strong> überprüft wer<strong>de</strong>n, welche<br />

Komponente <strong>de</strong>r Benutzeroberfläche die Aktion erzeugt hat. Zur vere<strong>in</strong>fachten<br />

Bearbeitung be<strong>in</strong>haltet das Event-Objekt, das beim Aufruf von action() erhalten<br />

wird, e<strong>in</strong>e "target"-Instanzvariable, die e<strong>in</strong>e Referenz zu <strong>de</strong>m Objekt, das das<br />

Ereignis aufgenommen hat, enthält. Mit <strong>de</strong>m <strong>in</strong>stanceof-Operator kann dann<br />

bestimmt wer<strong>de</strong>n, von welcher Komponente das Ereignis ausg<strong>in</strong>g. Das zweite<br />

Argument dient zum Bestimmen <strong>de</strong>s Labels 281 , <strong>de</strong>r Elemente, <strong>de</strong>s Inhalts <strong>de</strong>r<br />

Komponenten 282 . Anstatt <strong>de</strong>s zusätzlichen Arguments kann auch die Metho<strong>de</strong> 283<br />

getLabel verwen<strong>de</strong>t wer<strong>de</strong>n.<br />

Behandlung von Fokus-Ereignissen<br />

Für die Ereignisse "Fokuserhalt" und "Fokusverlust" können die Metho<strong>de</strong>n<br />

281 vgl. pr52105<br />

282 Nicht vergessen: Das Argument <strong>in</strong> <strong>de</strong>n richtigen Objekt-Typ zu casten<br />

283 vgl. pr54305<br />

348


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

public boolean getFocus(Enet evt, Object arg)<br />

bzw.<br />

public boolean lostFocus(Event evt, Object arg)<br />

verwen<strong>de</strong>t wer<strong>de</strong>n.<br />

Ereignisse von Listenfel<strong>de</strong>rn<br />

Listenfel<strong>de</strong>r erzeugen drei verschie<strong>de</strong>ne Ereignisarten: Auswahl bzw. Abwahl e<strong>in</strong>es<br />

E<strong>in</strong>trags <strong>in</strong> <strong>de</strong>r Liste bzw. Doppelklick auf e<strong>in</strong>en E<strong>in</strong>trag. E<strong>in</strong> Doppelklick auf e<strong>in</strong>en<br />

E<strong>in</strong>trag kann mit <strong>de</strong>r Metho<strong>de</strong> action bearbeitet wer<strong>de</strong>n. Auswahl und Abwahl e<strong>in</strong>es<br />

Listene<strong>in</strong>trags kann mit "handleEvent" und Überprüfen auf die Ereignisse mit<br />

LIST_SELECT und LIST_DESELECT erfolgen.<br />

5.5.2 Das Event-Handl<strong>in</strong>g <strong>in</strong> <strong>Java</strong> 1.1 bzw. <strong>Java</strong> 1.2<br />

Im neuen Ereignismo<strong>de</strong>ll erben die Ereignisklassen von <strong>de</strong>r Klasse<br />

java.util.EventObject. AWT-Events erben von <strong>de</strong>r Klasse<br />

java.awt.AWTEvent.<br />

EventListener<br />

Falls e<strong>in</strong>e Klasse im Ereignisbehandlungsmo<strong>de</strong>ll von <strong>Java</strong> 1.2 aus e<strong>in</strong> Ereignis<br />

reagieren will, muß diese e<strong>in</strong>e Schnittstelle implementieren, die dieses Ereignis<br />

verarbeitet. Diese Schnittstellen wer<strong>de</strong>n als EventListener bezeichnet. Je<strong>de</strong>r<br />

Listener behan<strong>de</strong>lt e<strong>in</strong>e bestimmte Ereignisart. E<strong>in</strong>e Klasse kann so viele<br />

Listener implementieren, wie benötigt wer<strong>de</strong>n. Die folgen<strong>de</strong>n EventListener<br />

stehen zur Verfügung:<br />

ActionListener<br />

Aktionsereignisse, die durch <strong>de</strong>n Benutzer ausgelöst wer<strong>de</strong>n, z.B.<br />

Klick auf e<strong>in</strong>e Schaltfläche<br />

AdjustmentListener<br />

Ereignisse, die erzeugt wer<strong>de</strong>n, wenn werte e<strong>in</strong>er Komponente<br />

e<strong>in</strong>gestellt wer<strong>de</strong>n (z.B. Bewegung e<strong>in</strong>es Schiebers e<strong>in</strong>er<br />

Bildlaufleiste)<br />

FocusListener<br />

Ereignisse, die erzeugt wer<strong>de</strong>n, wenn e<strong>in</strong>e Komponente, z.B. e<strong>in</strong><br />

textfeld, <strong>de</strong>n e<strong>in</strong>gabefokus erhält o<strong>de</strong>r verliert.<br />

ItemListener<br />

Ereignisse, die erzeugt wer<strong>de</strong>n, wenn e<strong>in</strong> Element, z.B. e<strong>in</strong><br />

Kontrollkästchen, verän<strong>de</strong>rt wur<strong>de</strong><br />

KeyListener<br />

Tastaturereignisse, die bei Tastature<strong>in</strong>gaben erzeugt wer<strong>de</strong>n.<br />

MouseListener<br />

Mausereignisse, die erzeugt wer<strong>de</strong>n, wenn mit <strong>de</strong>r maus geklickt<br />

wird, die Maus <strong>in</strong> <strong>de</strong>n Bereich e<strong>in</strong>er Komponente e<strong>in</strong>tritt bzw. diese<br />

wie<strong>de</strong>r verläßt<br />

MouseMotionListener Mausereignisse, die die Bewegung e<strong>in</strong>er Maus über e<strong>in</strong>e<br />

Komponente verfolgen<br />

W<strong>in</strong>dowListener<br />

Ereignisse, die von Fenstern erzeugt wer<strong>de</strong>n<br />

Das Paket java.awt.event be<strong>in</strong>haltet alle elementaren EventListener. Über<br />

import java.awt.event.* erfolgt das Importieren <strong>in</strong> die Anwendungen.<br />

349


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Event-Handl<strong>in</strong>g mit Hilfe lokaler Klasse<br />

Für die Ereignisbehandlung wer<strong>de</strong>n lokale Klassen herangezogen. Im JDK 1.0<br />

wur<strong>de</strong>n Klassen nur auf Paketen <strong>de</strong>f<strong>in</strong>iert, e<strong>in</strong>e Schachtelung war nicht möglich. Im<br />

JDK 1.1 kann <strong>in</strong>nerhalb bestehen<strong>de</strong>r Klassen e<strong>in</strong>e neue Klasse <strong>de</strong>f<strong>in</strong>iert wer<strong>de</strong>n<br />

(Konzept <strong>de</strong>r „Inner Classes“), die nur <strong>in</strong>nerhalb <strong>de</strong>r bestehen<strong>de</strong>n Klasse sichtbar ist.<br />

Objekt<strong>in</strong>stanzen <strong>de</strong>r <strong>in</strong>neren Klasse können nur aus <strong>de</strong>r umfassen<strong>de</strong>n Klasse heraus<br />

erzeugt wer<strong>de</strong>n. Allerd<strong>in</strong>gs kann die <strong>in</strong>nere Klasse auf die Instanzvariablen <strong>de</strong>r<br />

äußeren Klasse zugreifen.<br />

Mit Hilfe lokaler Klassen wer<strong>de</strong>n die benötigten EventListener implementiert. Dazu<br />

wird <strong>in</strong> <strong>de</strong>m GUI-Objekt, das e<strong>in</strong>en Event-Handler benötigt, e<strong>in</strong>e lokale Klasse<br />

<strong>de</strong>f<strong>in</strong>iert (und aus e<strong>in</strong>er passen<strong>de</strong>n AdapterKlasse abgeleitet).<br />

E<strong>in</strong> e<strong>in</strong>führen<strong>de</strong>s Bsp.: Event-Handl<strong>in</strong>g nach <strong>de</strong>r Bearbeitung von Schaltflächen <strong>de</strong>s<br />

AWT <strong>in</strong> Version 1.0 und 1.1<br />

// E<strong>in</strong>fangen von Klicks auf Schaltflaechen<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

import java.applet.*;<br />

public class SchalterAppl extends Applet<br />

{<br />

Button sch1 = new Button("Schaltflaeche 1");<br />

Button sch2 = new Button("Schaltflaeche 2");<br />

public void <strong>in</strong>it()<br />

{<br />

sch1.addActionListener(new Sch1());<br />

sch2.addActionListener(new Sch2());<br />

add(sch1);<br />

add(sch2);<br />

}<br />

class Sch1 implements ActionListener<br />

{<br />

public void actionPerformed(ActionEvent e)<br />

{<br />

getAppletContext().showStatus("Schaltflaeche 1");<br />

}<br />

}<br />

class Sch2 implements ActionListener<br />

{<br />

public void actionPerformed(ActionEvent e)<br />

{<br />

getAppletContext().showStatus("Schaltflaeche 2");<br />

}<br />

}<br />

/*<br />

public boolean action(Event e, Object welcheAktion)<br />

{<br />

if (e.target.equals(sch1))<br />

getAppletContext().showStatus("Schaltflaeche 1");<br />

else if (e.target.equals(sch2))<br />

getAppletContext().showStatus("Schaltflaeche 2");<br />

else<br />

return super.action(e,welcheAktion);<br />

return true;<br />

}<br />

350


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

*/<br />

}<br />

E<strong>in</strong>fügen / Entfernen passen<strong>de</strong>r Listener<br />

Das neue Event-Mo<strong>de</strong>ll von <strong>Java</strong> 1.1 bzw. 1.2 hat mit <strong>de</strong>m JDK 1.0 offensichtlich nur<br />

noch wenig geme<strong>in</strong>sam. Alle AWT-Komponenten haben im neuen AWT-Event-<br />

Handl<strong>in</strong>g „addXXXListener()“- und „removeXXXListener()“-Metho<strong>de</strong>n<br />

erhalten. „XXX“ beschreibt <strong>de</strong>n Typ <strong>de</strong>s Events. Die folgen<strong>de</strong> Tabelle zeigt<br />

Ereignisse, „Listeners“, Metho<strong>de</strong>n und Komponenten, die über addXXXListener()<br />

bzw. removeXXXListener() auf spezifische Ereignisse reagieren:<br />

Event, „listener <strong>in</strong>terface“ und „add“- bzw.<br />

„remove“-Metho<strong>de</strong>n<br />

ActionEvent<br />

ActionListener<br />

addActionListener()<br />

removeActionListener()<br />

AdjustmentEvent<br />

AdjustmentListener<br />

addAdjustmentListener()<br />

removeAdjustmentListener()<br />

ComponentEvent<br />

ComponentListener<br />

addComponentListener()<br />

removeComponentListener()<br />

Conta<strong>in</strong>erEvent<br />

Conta<strong>in</strong>erListener<br />

addConta<strong>in</strong>erListener()<br />

removeConta<strong>in</strong>erListener()<br />

FocusEvent<br />

FocusListener<br />

addFocusListener()<br />

removeFocusListener()<br />

KeyEvent<br />

KeyListener<br />

addKeyListener()<br />

removeKeyListener()<br />

MouseEvent<br />

MouseListener<br />

addMouseListener()<br />

removeMouseListener()<br />

MouseEvent<br />

MouseMotionListener<br />

addMouseMotionListener()<br />

removeMouseMotionListener()<br />

W<strong>in</strong>dowEvent<br />

W<strong>in</strong>dowListener<br />

addW<strong>in</strong>dowListener()<br />

removeW<strong>in</strong>dowListener()<br />

ItemEvent<br />

ItemListener<br />

addItemListener()<br />

removeItemListener()<br />

Komponenten, die dieses Ereignis unterstützen<br />

Button, List, TextField, MenuItem und se<strong>in</strong>e<br />

Abkömml<strong>in</strong>ge e<strong>in</strong>schl. CheckboxMenuItem, Menu<br />

und PopupMenu<br />

Scrollbar,<br />

alles was mit <strong>de</strong>r Erzeugung vom „Adjustable“-<br />

Interface zu tun hat<br />

Component und se<strong>in</strong>e Abkömml<strong>in</strong>ge, e<strong>in</strong>schl.<br />

Button, Canvas, Checkbox, Choice, Conta<strong>in</strong>er,<br />

Panel, Applet, ScrollPane, W<strong>in</strong>dow, Dialog,<br />

FileDialog, Frame, Label, List, Scrollbar,<br />

TextArea, TextField<br />

Conta<strong>in</strong>er und se<strong>in</strong>e Abkömml<strong>in</strong>ge, e<strong>in</strong>schl.<br />

Panel, Applet, ScrollPane, W<strong>in</strong>dow, Dialog,<br />

FileDialog, Frame<br />

Component und se<strong>in</strong>e Abkömml<strong>in</strong>ge e<strong>in</strong>schl.<br />

Button, Canvas, Conta<strong>in</strong>er, Panel, Applet,<br />

ScrollPane, W<strong>in</strong>dow, Dialog, FileDialog, Frame,<br />

Label, List, Scrollbar, TextArea, TextField<br />

Component und se<strong>in</strong>e Abkömml<strong>in</strong>ge e<strong>in</strong>schl.<br />

Button, Canvas, Checkbox, Choice, Conta<strong>in</strong>er,<br />

Panel, Applet, ScrollPane, W<strong>in</strong>dow, Dialog,<br />

FileDialog, Frame, Label, List, Scrollbar,<br />

TextArea, TextField<br />

Component und se<strong>in</strong>e Abkömml<strong>in</strong>ge e<strong>in</strong>schl.<br />

Button, Canvas, Checkbox, Choice, Conta<strong>in</strong>er,<br />

Panel, Applet, ScrollPane, W<strong>in</strong>dow, Dialog,<br />

FileDialog, Frame, Label, List, Scrollbar,<br />

TextArea, TextField<br />

Component und se<strong>in</strong>e Abkömml<strong>in</strong>ge e<strong>in</strong>schl.<br />

Button, Canvas, Checkbox, Choice, Conta<strong>in</strong>er,<br />

Panel, Applet, ScrollPane, W<strong>in</strong>dow, Dialog,<br />

FileDialog, Frame, Label, List, Scrollbar,<br />

TextArea, TextField<br />

W<strong>in</strong>dow und se<strong>in</strong>e Abkömml<strong>in</strong>ge e<strong>in</strong>schl. Dialog,<br />

FileDialog und Frame<br />

Checkbox, CkeckboxMenuItem, Choice, List und<br />

alles, was das ItemSelectable Interface<br />

implementiert<br />

351


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

TextEvent<br />

TextListener<br />

addTextListener()<br />

removeTextListener()<br />

Alles was von TextComponent e<strong>in</strong>schl. TextArea<br />

und TextField abgeleitet ist<br />

Abb.: Event- und Listener-Typen<br />

Je<strong>de</strong>r Komponenten-Typ unterstützt nur bestimmte Ereignis-Typen:<br />

Komponenten-Typ<br />

Ereignis, das durch die Komponente unterstützt<br />

wird<br />

Adjustable<br />

AdjustmentEvent<br />

Applet Conta<strong>in</strong>erEvent, FocusEvent, KeyEvent,<br />

MouseEvent, ComponentEvent<br />

Button<br />

ActionEvent,<br />

Canvas FocusEvent, KeyEvent, MouseEvent,<br />

ComponentEvent<br />

Checkbox<br />

ItemEvent, FocusEvent, KeyEvent, MouseEvent,<br />

ComponentEvent<br />

CheckboxMenuItem<br />

ActionEvent, ItemEvent<br />

Choice<br />

ItemEvent, FocusEvent, KeyEvent, MouseEvent,<br />

ComponentEvent<br />

Component FocusEvent, KeyEvent, MouseEvent,<br />

ComponentEvent<br />

Conta<strong>in</strong>er Conta<strong>in</strong>erEvent, FocusEvent, KeyEvent,<br />

MouseEvent, ComponentEvent<br />

Dialog Conta<strong>in</strong>erEvent, W<strong>in</strong>dowEvent, FocusEvent,<br />

KeyEvent, MouseEvent, ComponentEvent<br />

FileDialog Conta<strong>in</strong>erEvent, W<strong>in</strong>dowEvent, FocusEvent,<br />

KeyEvent, MouseEvent, ComponentEvent<br />

Frame Conta<strong>in</strong>erEvent, W<strong>in</strong>dowEvent, FocusEvent,<br />

KeyEvent, MouseEvent, ComponentEvent<br />

Label FocusEvent, KeyEvent, MouseEvent,<br />

ComponentEvent<br />

List ActionEvent, FocusEvent, KeyEvent,<br />

MouseEvent, ItemEvent, ComponentEvent<br />

Menu<br />

ActionEvent<br />

MenuItem<br />

ActionEvent<br />

Panel Conta<strong>in</strong>erEvent, FocusEvent, KeyEvent,<br />

MouseEvent, ComponentEvent<br />

PopupMenu<br />

ActionEvent<br />

Scrollbar AdjustmentEvent, FocusEvent, KeyEvent,<br />

MouseEvent, ComponentEvent<br />

ScrollPane Conta<strong>in</strong>erEvent, FocusEvent, KeyEvent,<br />

MouseEvent, ComponentEvent<br />

TextArea<br />

TextEvent, FocusEvent, KeyEvent, MouseEvent,<br />

ComponentEvent<br />

TextComponent<br />

TextEvent, FocusEvent, KeyEvent, MouseEvent,<br />

TextField<br />

ComponentEvent<br />

ActionEvent, TextEvent, FocusEvent, KeyEvent,<br />

MouseEvent, ComponentEvent<br />

W<strong>in</strong>dow Conta<strong>in</strong>erEvent, W<strong>in</strong>dowEvent, FocusEvent,<br />

KeyEvent, MouseEvent, ComponentEvent<br />

Abb.: Komponenten-Typ und Ereignisse<br />

Metho<strong>de</strong>n für die Ereignisbehandlung<br />

352


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Da nun bekannt ist, welche Ereignisse e<strong>in</strong>e bestimmte Komponente unterstützt, kann<br />

das zugehörige „Listener“-Interface angegeben wer<strong>de</strong>n:<br />

„Listener“-Interface<br />

ActionListener<br />

AdjustmentListener<br />

ComponentListener<br />

ComponentAdapter<br />

Conta<strong>in</strong>erListener<br />

Conta<strong>in</strong>erAdapter<br />

FocusListener<br />

FocusAdapter<br />

KeyListener<br />

KeyAdapter<br />

MouseListener<br />

MouseAdapter<br />

MouseMotionListener<br />

MouseMotionAdapter<br />

W<strong>in</strong>dowListener<br />

W<strong>in</strong>dowadapter<br />

ItemListener<br />

TextListener<br />

Metho<strong>de</strong>n<br />

actionPerformed(ActionEvent)<br />

AdjustmentValueChanged(<br />

AdjustmentEvent)<br />

componentHid<strong>de</strong>n(ComponentEvent)<br />

componentShown(ComponentEvent)<br />

componentMoved(ComponentEvent)<br />

componentResized(ComponentEvent)<br />

componentAd<strong>de</strong>d(Conta<strong>in</strong>erEvent)<br />

componentRemoved(Conta<strong>in</strong>erEvent)<br />

focusGa<strong>in</strong>ed(FocusEvent)<br />

focusLost(FocusEvent)<br />

keyPressed(KeyEvent)<br />

keyReleased(KeyEvent)<br />

keyTyped(KeyEvent)<br />

mouseClicked(MouseEvent)<br />

mouseEntered(MouseEvent)<br />

mouseExited(MouseEvent)<br />

mousePressed(MouseEvent)<br />

mouseReleased(MouseEvent)<br />

mouseDragged(MouseEvent)<br />

mouseMoved(MouseEvent)<br />

w<strong>in</strong>dowOpened(W<strong>in</strong>dowEvent)<br />

w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent)<br />

w<strong>in</strong>dowClosed(W<strong>in</strong>dosEvent)<br />

w<strong>in</strong>dowActivated(W<strong>in</strong>dowEvent)<br />

w<strong>in</strong>dowDeactivated(W<strong>in</strong>dowEvent)<br />

w<strong>in</strong>dowIconified(W<strong>in</strong>dowEvent)<br />

w<strong>in</strong>dowDeiconified(W<strong>in</strong>dowEvent)<br />

itemStateChanged(ItemEvent)<br />

textValueChanged(TextEvent)<br />

Abb.: Listener-Interface<br />

Adapter besorgen Default-Metho<strong>de</strong>n für je<strong>de</strong> Metho<strong>de</strong> im Interface. Nur die Metho<strong>de</strong>,<br />

die geän<strong>de</strong>rt wird, muß überschrieben wer<strong>de</strong>n.<br />

Komponenten <strong>de</strong>s AWT <strong>in</strong> Fenstern bzw. Applets unter <strong>Java</strong> 1.1<br />

Die <strong>in</strong> <strong>de</strong>n folgen<strong>de</strong>n Programmen verwen<strong>de</strong>ten Komponenten können <strong>in</strong> e<strong>in</strong>em<br />

Fenster (Frame) über die Instanz e<strong>in</strong>es Applet dargestellt wer<strong>de</strong>n. Das Programm<br />

enthält <strong>de</strong>shalb zusätzlich zu <strong>de</strong>n für Applets nötigen Metho<strong>de</strong>n e<strong>in</strong>e ma<strong>in</strong>()-<br />

Metho<strong>de</strong>, die e<strong>in</strong>e Instanz e<strong>in</strong>es Applets <strong>in</strong>nerhalb e<strong>in</strong>es Frame aufbaut.<br />

Bsp.: Bearbeitung von zwei Schaltflächen und e<strong>in</strong>em Textfeld 284<br />

// E<strong>in</strong>e Anwendung und e<strong>in</strong> Applet<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

import java.applet.*;<br />

public class SchalterApplet extends Applet<br />

284 pr55202<br />

353


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

{<br />

Button sch1 = new Button("Schaltflaeche 1");<br />

Button sch2 = new Button("Schaltflaeche 2");<br />

TextField t = new TextField(20);<br />

public void <strong>in</strong>it()<br />

{<br />

sch1.addActionListener(new Sch1());<br />

sch2.addActionListener(new Sch2());<br />

add(sch1);<br />

add(sch2);<br />

add(t);<br />

}<br />

class Sch1 implements ActionListener<br />

{<br />

public void actionPerformed(ActionEvent e)<br />

{<br />

t.setText("Schaltflaeche 1");<br />

}<br />

}<br />

class Sch2 implements ActionListener<br />

{<br />

public void actionPerformed(ActionEvent e)<br />

{<br />

t.setText("Schaltflaeche 2");<br />

}<br />

}<br />

// Schliessen <strong>de</strong>r Applikation<br />

static class WL extends W<strong>in</strong>dowAdapter<br />

{<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{ System.exit(0); }<br />

}<br />

// ma<strong>in</strong>()-Metho<strong>de</strong> fuer die Applikation<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

SchalterApplet applet = new SchalterApplet();<br />

Frame e<strong>in</strong>Rahmen = new Frame("Schalterdarstellung");<br />

e<strong>in</strong>Rahmen.addW<strong>in</strong>dowListener(new WL());<br />

e<strong>in</strong>Rahmen.add(applet,Bor<strong>de</strong>rLayout.CENTER);<br />

e<strong>in</strong>Rahmen.setSize(300,200);<br />

applet.<strong>in</strong>it();<br />

applet.start();<br />

e<strong>in</strong>Rahmen.setVisible(true);<br />

}<br />

}<br />

354


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

5.6 Benutzerschnittstellen mit Sw<strong>in</strong>g<br />

Die Oberflächentechnik „Sw<strong>in</strong>g“ (Paket: javax.sw<strong>in</strong>g) von <strong>Java</strong> 2 umfaßt e<strong>in</strong>e<br />

Vielzahl mächtiger Klassen (, z.B. Tabellen, Baumansichten). Sw<strong>in</strong>g benutzt <strong>de</strong>n<br />

Mo<strong>de</strong>l-View-Controller 285 -Ansatz, d.h. die konsequente Trennung <strong>de</strong>r Daten und <strong>de</strong>r<br />

Darstellung. Sw<strong>in</strong>g-Elemente fallen <strong>in</strong> zwei Gruppen:<br />

- Fenster, die direkt vom Betriebssystem dargestellt wer<strong>de</strong>n, z.B. JFrame, JDialog o<strong>de</strong>r JApplet.<br />

Diese s<strong>in</strong>d von <strong>de</strong>n jeweiligen AWT-Komponenten abgeleitet.<br />

- Kontrollelemente, die von <strong>Java</strong> selbst geren<strong>de</strong>rt wer<strong>de</strong>n. Diese s<strong>in</strong>d Subklassen von JComponent.<br />

javax.sw<strong>in</strong>g, javax.sw<strong>in</strong>g.event, javax.sw<strong>in</strong>g.table, javax.sw<strong>in</strong>g.tree, javax.sw<strong>in</strong>g.text<br />

Interfaces<br />

Klassen<br />

- ButtonMo<strong>de</strong>l Ereignis- Kontroll- Fenster Menüs<br />

- ComboBoxMo<strong>de</strong>l behandlung Elemente<br />

- ListMo<strong>de</strong>l<br />

- TreeMo<strong>de</strong>l<br />

- TableMo<strong>de</strong>l<br />

- TableColumnMo<strong>de</strong>l<br />

- Boun<strong>de</strong>dRangeMo<strong>de</strong>l<br />

- Ren<strong>de</strong>rer<br />

- ListCellRen<strong>de</strong>rer<br />

- TableCellRen<strong>de</strong>rer<br />

- TableCellEditor<br />

- TreeCellRen<strong>de</strong>rer<br />

- TreeCellEditor<br />

- ComboBoxEditor<br />

- TreeNo<strong>de</strong><br />

- MutableTreeNo<strong>de</strong><br />

- Bor<strong>de</strong>r<br />

- Scrollable<br />

- MenuElement<br />

- Document<br />

- ListDataEvent<br />

- ListSelectionEvent<br />

- TableMo<strong>de</strong>lEvent<br />

- TreeMo<strong>de</strong>lEvent<br />

- TreeExpansionEvent<br />

- TreeSelectionEvent<br />

- CaretEvent<br />

- MenuEvent<br />

- PopupMenuEvent<br />

- JComponent<br />

- JButton<br />

- JCheckBox<br />

- JRadioButton<br />

- JLabel<br />

- JTextField<br />

- JPasswordField<br />

- JTextArea<br />

- JComboBox<br />

- JList<br />

- JScrollBar<br />

- JSli<strong>de</strong>r<br />

- JProgressBar<br />

- JTree<br />

- JTable<br />

- JFrame<br />

- JDialog<br />

- JPanel<br />

- JApplet<br />

- JFileChooser<br />

- JColorChooser<br />

- JDesktopPane<br />

- JInternalFrame<br />

- JOptionPane<br />

- JScrollPane<br />

- JTabbedPane<br />

- JMenuBar<br />

- JMenuItem<br />

- JCheckbox-<br />

MenuItem<br />

- JRadioBox-<br />

MenuItem<br />

- JMenu<br />

- JPopupMenu<br />

- JToolBar<br />

- JSeparator<br />

- Caret Mo<strong>de</strong>l- Bor<strong>de</strong>r Bil<strong>de</strong>r Sonstiges<br />

- Style implementierung<br />

- StyledDocument<br />

- DesktopManager - AbstractListMo<strong>de</strong>l - Bor<strong>de</strong>rFactory - ...... - ImageIcon<br />

- Icon - DefaultListMo<strong>de</strong>l - TitledBor<strong>de</strong>r -ButtonGroup<br />

- Sw<strong>in</strong>gConstants - AbstractTableMo<strong>de</strong>l - L<strong>in</strong>eBor<strong>de</strong>r - KeyStroke<br />

- *Listener - ... - ... - ...<br />

285 Beim Mo<strong>de</strong>l-View-Controller-Entwurfsmuster (MVC) unterschei<strong>de</strong>t man drei Arten von<br />

Applikationselementen:<br />

Mo<strong>de</strong>l (Datenmo<strong>de</strong>ll)<br />

Im Datenmo<strong>de</strong>ll wer<strong>de</strong>n Zustand und Funktionalität gespeichert. Es kann Anfragen vom View über se<strong>in</strong>en<br />

Zustand beantworten und Zustandsän<strong>de</strong>rungen vom Controller o<strong>de</strong>r an<strong>de</strong>ren Objekten verarbeiten.<br />

View (Darstellung „look“)<br />

Die Ansicht weiß nichts o<strong>de</strong>r nur sehr wenig (über das Datenmo<strong>de</strong>ll und stellt lediglich die Daten dar).<br />

Controller (Steuerung „feel“)<br />

Die Steuerung stellt e<strong>in</strong> Mo<strong>de</strong>ll <strong>de</strong>r realen Welt dar und reagiert auf Benutzere<strong>in</strong>gaben von Maus, Tastatur und<br />

an<strong>de</strong>ren E<strong>in</strong>gabegeräten.<br />

355


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

5.6.1 Die Komponentenstruktur<br />

javax.sw<strong>in</strong>g.JComponent ist von java.awt.Conta<strong>in</strong>er abgeleitet. Als<br />

Conta<strong>in</strong>er können alle JComponents auch an<strong>de</strong>re Komponenten enthalten.<br />

Conta<strong>in</strong>er<br />

JComponent<br />

public void addAncestorListener(AncestorListener l)<br />

public void addNotify()<br />

...<br />

public boolean conta<strong>in</strong>s(<strong>in</strong>t x, <strong>in</strong>t y)<br />

public JToolTip createToolTip()<br />

...<br />

public float getAlignmentX()<br />

public float getAlignmentY()<br />

...<br />

public Bor<strong>de</strong>r getBor<strong>de</strong>r()<br />

public void setBor<strong>de</strong>r(Bor<strong>de</strong>r bor<strong>de</strong>r)<br />

public Rectangle getBounds(Rectangle rv)<br />

protected Graphics getComponentGraphics (Graphics g)<br />

...<br />

public Graphics getGraphics()<br />

public <strong>in</strong>t getHeight()<br />

public Insets getInsets()<br />

...<br />

public Po<strong>in</strong>t getLocation(Po<strong>in</strong>t rv)<br />

public Dimension getMaximumSize()<br />

public Dimension getM<strong>in</strong>imumSize()<br />

...<br />

…<br />

public Dimension getSize(Dimension rv)<br />

public Po<strong>in</strong>t getToolTipLocation(MouseEvent event)<br />

public Str<strong>in</strong>g getToolTipText()<br />

public Str<strong>in</strong>g getToolTipText(MouseEvent event)<br />

...<br />

public <strong>in</strong>t getWidth()<br />

public <strong>in</strong>t getX()<br />

public <strong>in</strong>t getY()<br />

...<br />

public void pa<strong>in</strong>t(Graphics g)<br />

...<br />

public void repa<strong>in</strong>t(Rectangle r)<br />

...<br />

public void setToolTipText(Str<strong>in</strong>g text)<br />

protected void setUI(ComponentUI newUI)<br />

public void setVisible(boolean aFlag)<br />

...<br />

public void update(Graphics g)<br />

public void updateUI()<br />

…<br />

Abb. Die Klasse JComponent<br />

356


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Viele <strong>de</strong>r Sw<strong>in</strong>g-Komponenten s<strong>in</strong>d direkt o<strong>de</strong>r <strong>in</strong>direkt aus <strong>de</strong>r Klasse JComponent<br />

abgeleitet<br />

Instanzen von JComponent können ausgestattet wer<strong>de</strong>n mit Umrandungen,<br />

Tooltips, transparentem H<strong>in</strong>tergrund, Doppelpufferung, etc.<br />

Umrandungen<br />

Instanzen von JComponent können über die Metho<strong>de</strong> setBor<strong>de</strong>r() Umrandungen<br />

erhalten.<br />

public void setBor<strong>de</strong>r(Bor<strong>de</strong>r bor<strong>de</strong>r)<br />

Bor<strong>de</strong>r ist e<strong>in</strong> Interface, zu <strong>de</strong>m es verschie<strong>de</strong>ne Implementierungen gibt. Die<br />

wichtigsten s<strong>in</strong>d:<br />

Klassenname<br />

EmptyBor<strong>de</strong>r<br />

L<strong>in</strong>eBor<strong>de</strong>r<br />

BevelBor<strong>de</strong>r<br />

EtchedBor<strong>de</strong>r<br />

CompoundBor<strong>de</strong>r<br />

TitledBor<strong>de</strong>r<br />

Beschreibung<br />

Unsichtbarer Rand mit e<strong>in</strong>stellbarer Dicke<br />

E<strong>in</strong>fache L<strong>in</strong>ie mit e<strong>in</strong>stellbarer Farbe und Dicke<br />

Erhabener und vertiefter 3D-Effekt<br />

E<strong>in</strong>gelassene L<strong>in</strong>e mit 3D-Effekt<br />

Aus 2 an<strong>de</strong>ren Umrandungen zusammengesetzt<br />

Umrandung mit e<strong>in</strong>em e<strong>in</strong>geketteten Text<br />

Abb.: Bor<strong>de</strong>r-Implementierungen<br />

Die Klassen besitzen sehr unterschiedliche Konstruktoren, mit <strong>de</strong>nen ihre jeweiligen<br />

Eigenschaften festgelegt wer<strong>de</strong>n. Bor<strong>de</strong>r-Instanzen können mit new über die Klasse<br />

Bor<strong>de</strong>rFactory mit <strong>de</strong>n Metho<strong>de</strong>n createEmptyBor<strong>de</strong>r(),<br />

createL<strong>in</strong>eBor<strong>de</strong>r(), usw. erzeugt wer<strong>de</strong>n.<br />

Bsp. 286 :<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

import javax.sw<strong>in</strong>g.*;<br />

import javax.sw<strong>in</strong>g.bor<strong>de</strong>r.*;<br />

public class Raen<strong>de</strong>r extends JPanel<br />

{<br />

public Raen<strong>de</strong>r()<br />

{<br />

setLayout(new GridLayout(2,4));<br />

add(showBor<strong>de</strong>r(new TitledBor<strong>de</strong>r("Titel")));<br />

add(showBor<strong>de</strong>r(new EtchedBor<strong>de</strong>r()));<br />

add(showBor<strong>de</strong>r(new L<strong>in</strong>eBor<strong>de</strong>r(Color.blue)));<br />

add(showBor<strong>de</strong>r(new MatteBor<strong>de</strong>r(5,5,30,30,Color.green)));<br />

add(showBor<strong>de</strong>r(new BevelBor<strong>de</strong>r(BevelBor<strong>de</strong>r.RAISED)));<br />

add(showBor<strong>de</strong>r(new SoftBevelBor<strong>de</strong>r(BevelBor<strong>de</strong>r.LOWERED)));<br />

add(showBor<strong>de</strong>r(new CompoundBor<strong>de</strong>r(new EtchedBor<strong>de</strong>r(),<br />

new L<strong>in</strong>eBor<strong>de</strong>r(Color.red))));<br />

}<br />

static JPanel showBor<strong>de</strong>r(Bor<strong>de</strong>r b)<br />

{<br />

JPanel jp = new JPanel();<br />

jp.setLayout(new Bor<strong>de</strong>rLayout());<br />

Str<strong>in</strong>g nm = b.getClass().toStr<strong>in</strong>g();<br />

nm = nm.substr<strong>in</strong>g(nm.lastIn<strong>de</strong>xOf('.')+1);<br />

jp.add(new JLabel(nm,JLabel.CENTER),Bor<strong>de</strong>rLayout.CENTER);<br />

286 pr56001<br />

357


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

jp.setBor<strong>de</strong>r(b);<br />

return jp;<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

JFrame frame = new JFrame("Begrenzungen");<br />

// frame.addW<strong>in</strong>dowListener(new W<strong>in</strong>dowClos<strong>in</strong>gAdapter(true));<br />

frame.getContentPane().add(new Raen<strong>de</strong>r(),Bor<strong>de</strong>rLayout.CENTER);<br />

frame.setLocation(100,100);<br />

frame.setSize(500,300);<br />

frame.pack();<br />

/* Die Metho<strong>de</strong> pack() sorgt fuer die Reduzierung <strong>de</strong>r<br />

Groesse <strong>de</strong>s Frame, dass alle Komponenten gera<strong>de</strong> dar<strong>in</strong><br />

noch Platz f<strong>in</strong><strong>de</strong>n<br />

*/<br />

frame.setVisible(true);<br />

/* Macht <strong>de</strong>n Frame sichtbar */<br />

}<br />

}<br />

Abb.<br />

Tooltips<br />

JComponent kann Komponenten e<strong>in</strong> Tooltip-Text zuweisen. Dieser wird angezeigt,<br />

wenn die Maus über das Dialogelement bewegt und dort angehalten wird. Tooltip-<br />

Texte wer<strong>de</strong>n mit setToolTipText() zugewiesen bzw. mit getToolTipText()<br />

abgefragt:<br />

public void setToolTipText(Str<strong>in</strong>g text)<br />

public void getToolTipText()<br />

Das Fenster mit <strong>de</strong>m Hilfetext ist immer im Sw<strong>in</strong>g-Fenster und nie außerhalb. Passt<br />

die Hilfe nicht auf das Fenster wird sie gar nicht dargestellt, und die Komponente<br />

macht durch "Flackern" auf sie aufmerksam<br />

Bsp. 287 :<br />

import java.awt.*;<br />

import javax.sw<strong>in</strong>g.*;<br />

public class TooltipDemo<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args [])<br />

{<br />

JFrame frame = new JFrame();<br />

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);<br />

Str<strong>in</strong>g text = "Ich brauche Hilfe.Tempo";<br />

JButton button = new JButton(text);<br />

Str<strong>in</strong>g hilfe = "Hier ist sie, die Hilfe:" +<br />

"Cool bleibenHandbuch lesen";<br />

287 pr56001<br />

358


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

button.setToolTipText(hilfe);<br />

frame.getContentPane().add(button);<br />

frame.setSize(250, 250);<br />

frame.show();<br />

}<br />

}<br />

Abb.: Tooltip-Demonstration<br />

Bildschirmdarstellung <strong>de</strong>r Komponenten<br />

Normalerweise braucht e<strong>in</strong>e GUI-Anwendung sich um die konkrete Darstellung nicht<br />

zu kümmern. Das än<strong>de</strong>rt sich, wenn e<strong>in</strong>e eigene Komponente entwickelt wer<strong>de</strong>n soll.<br />

Bei AWT-Anwendungen wur<strong>de</strong> dies aus Canvas abgeleitet, <strong>in</strong> überlagerten Varianten<br />

von pa<strong>in</strong>t() o<strong>de</strong>r update() wur<strong>de</strong> die nötige Bildschirmausgabe bereit gestellt.<br />

Die Metho<strong>de</strong> pa<strong>in</strong>t() hat <strong>in</strong> JComponent e<strong>in</strong>e recht aufwendige Implementierung<br />

und wird normalerweise nicht mehr überlagert. Im Pr<strong>in</strong>zip ruft sie nache<strong>in</strong>an<strong>de</strong>r ihre<br />

Metho<strong>de</strong>n pa<strong>in</strong>tComponent(), pa<strong>in</strong>tBor<strong>de</strong>r() (Zeichnen <strong>de</strong>r Umrandung) und<br />

pa<strong>in</strong>tChildren() (Zeichnen <strong>de</strong>r Dialogelemente) auf. Für die Darstellung <strong>de</strong>r<br />

eigenen Komponenten ist<br />

protected void pa<strong>in</strong>tComponent(Graphics g)<br />

zuständig. In JComponent wird je<strong>de</strong>r Aufruf von pa<strong>in</strong>tComponent() an das<br />

ComponentUI <strong>de</strong>legiert. Je<strong>de</strong> Sw<strong>in</strong>g-Komponente besitzt e<strong>in</strong> ComponentUI, das je<br />

Look and Feel unterschiedlich se<strong>in</strong> kann. E<strong>in</strong>e selbst<strong>de</strong>f<strong>in</strong>ierte Komponente muß also<br />

entwe<strong>de</strong>r für je<strong>de</strong>s Look and Feel e<strong>in</strong> passen<strong>de</strong>s ComponentUI zur Verfügung<br />

stellen o<strong>de</strong>r die Bildschirmdarstellung durch Überlagern von pa<strong>in</strong>tComponent()<br />

selbst erledigen.<br />

Doppelpufferung<br />

Sw<strong>in</strong>g-Komponenten, die aus JComponent abgeleitet s<strong>in</strong>d, können automatisch<br />

doppelgepuffert wer<strong>de</strong>n. Dazu ist lediglich <strong>de</strong>r Aufruf von<br />

public void setDoubleBuffered(boolean e<strong>in</strong>Flag)<br />

mit Übergabe von "true" erfor<strong>de</strong>rlich. Mit<br />

359


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

public boolean isDoubleBuffered()<br />

kann <strong>de</strong>r aktuelle Zustand abgefragt wer<strong>de</strong>n.<br />

Größenangaben<br />

In <strong>de</strong>r Klasse Component s<strong>in</strong>d die Metho<strong>de</strong>n getM<strong>in</strong>imumSize(),<br />

getPreferredSize(), getMaximumSize() <strong>de</strong>f<strong>in</strong>iert. Sie wer<strong>de</strong>n <strong>in</strong> abgeleiteten<br />

Klassen überlagert, um <strong>de</strong>m Layoutmanager die m<strong>in</strong>imale, optimale, maximale<br />

Größe mitzuteilen. In JComponent gibt es zusätzlich<br />

public void setM<strong>in</strong>imumSize(Dimension m<strong>in</strong>imumSize)<br />

public void setPreferredSize(Dimension preferredSize)<br />

public void setMaximumSize(Dimension maximumSize)<br />

zur Verän<strong>de</strong>rung <strong>de</strong>r Größenangaben bestehen<strong>de</strong>r Komponenten.<br />

Invalidierung / Validierung<br />

Zur Mitteilung an <strong>de</strong>n Conta<strong>in</strong>er, dass das Layout se<strong>in</strong>er Komponenten komplett<br />

aufgebaut wer<strong>de</strong>n soll, ist validate() aufzurufen.<br />

public void validate()<br />

Zuvor muß <strong>de</strong>r Conta<strong>in</strong>er, auf <strong>de</strong>m <strong>de</strong>r Aufruf erfolgte, zuvor mit<br />

public void <strong>in</strong>validate()<br />

als ungültig <strong>de</strong>klariert se<strong>in</strong>.<br />

In JComponent gibt es zusätzlich revalidate(), das die bei<strong>de</strong>n Schritte<br />

nache<strong>in</strong>an<strong>de</strong>r durchführt.<br />

360


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

5.6.2 Fenster<br />

Conta<strong>in</strong>er<br />

W<strong>in</strong>dow<br />

JComponent<br />

Frame<br />

Dialog<br />

JFrame<br />

JDialog<br />

Conta<strong>in</strong>er contentPane<br />

Int <strong>de</strong>faultCloseOperation<br />

> ><br />

public JFrame()<br />

public JDialog()<br />

public JFrame(Str<strong>in</strong>g titel)<br />

Abb.: Die Klassen JFrame und JDialog<br />

Die Sw<strong>in</strong>g-Klassen für Hauptfenster (JFrame) und Dialogboxen (JDialog) s<strong>in</strong>d<br />

direkt von <strong>de</strong>n AWT-Klassen Frame und Dialog abgeleitet und erben all ihre<br />

Metho<strong>de</strong>n. Der Hauptunterschied ist, daß ke<strong>in</strong>e Kontrollelemente mehr direkt auf das<br />

Fenster platziert wer<strong>de</strong>n, son<strong>de</strong>rn auf e<strong>in</strong>en Client-Conta<strong>in</strong>er (mit <strong>de</strong>m Namen<br />

„ContentPane“). Nun können bei<strong>de</strong> Klassen Menüleisten aufnehmen (Typ<br />

JMenuBar) und besitzen Vorgaben beim Schließen e<strong>in</strong>es Fensters.<br />

5.6.2.1 JFrame<br />

class javax.sw<strong>in</strong>g.Jframe extends Frame implements W<strong>in</strong>dowConstants,<br />

Accessible, RootPaneConta<strong>in</strong>er<br />

JFrame dient für <strong>de</strong>n Ersatz <strong>de</strong>r schwergewichtigen Komponente Frame und ist<br />

Ausgangsbasis für die meisten Sw<strong>in</strong>g-Oberflächen. Die Klasse JFrame erbt die<br />

wichtigsten Metho<strong>de</strong>n zum Adm<strong>in</strong>istrieren von Rahmen von ihren Vorgängern. Die<br />

wichtigste Metho<strong>de</strong> ist die von Component geerbte Metho<strong>de</strong> setVisible(true)<br />

(o<strong>de</strong>r show()), da Fenster standardmäßig nicht sichtbar s<strong>in</strong>d. „dispose()“ macht<br />

Fenster unsichbar und gibt damit alle verbun<strong>de</strong>nen Ressourcen frei.<br />

E<strong>in</strong> JFrame ist e<strong>in</strong> vollwertiges Fenster. Es besitzt e<strong>in</strong>en Rahmen mit Titelleiste und<br />

u.U.a. e<strong>in</strong>e MenuBar (Attribut JMenuBar). „resizable()“ gibt an, ob <strong>de</strong>r Benutzer<br />

die Größe e<strong>in</strong>es Fensters verän<strong>de</strong>rn kann und iconImage() dient zur Festlegung<br />

e<strong>in</strong>es Symbols, falls das Fenster <strong>de</strong>n Zustand (state) Frame.ICONIFIED<br />

annimmt.<br />

361


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Unterschied zum AWT-Frame. Mit frame.add(komponente) wird <strong>in</strong> e<strong>in</strong>em AWT-<br />

Programm e<strong>in</strong> Objekt zu e<strong>in</strong>em Fenster h<strong>in</strong>zugefügt. Bei e<strong>in</strong>em JFrame muß das<br />

Component-Objekt erst zu <strong>de</strong>r Zeichenfläche, die ContentPane genammt wird,<br />

zugefügt wer<strong>de</strong>n, z.B.:<br />

Conta<strong>in</strong>er con = frame.getContentPane();<br />

con.add(komponente);<br />

Das läßt sich abkürzen zu: frame.getContentPane().add(komponente)<br />

Auch beim Schließen e<strong>in</strong>es Fensters verhält sich e<strong>in</strong> JFrame etwas an<strong>de</strong>rs als e<strong>in</strong><br />

AWT-Frame. Beim JFrame verschw<strong>in</strong><strong>de</strong>t das Fenster <strong>in</strong> <strong>de</strong>n H<strong>in</strong>tergrund. Dieses<br />

Verhalten kann mit <strong>de</strong>r Funktion „void setDefaultCloseOperation(<strong>in</strong>t<br />

operation)“ geän<strong>de</strong>rt wer<strong>de</strong>n. So verhält sich bspw. mit<br />

setDefaultCloseOperation(W<strong>in</strong>dowConstants.DO_NOTHING_ON_CLOSE)<br />

e<strong>in</strong> JFrame wie e<strong>in</strong> Frame. Jetzt lässt sich mit Hilfe e<strong>in</strong>es W<strong>in</strong>dowListener-Objekts<br />

auf w<strong>in</strong>dowClos<strong>in</strong>g() reagieren. Neben DO_NOTHING_ON_CLOSE existieren drei<br />

weitere Konstanten:<br />

W<strong>in</strong>dowConstants.HIDE_ON_CLOSE<br />

Das Fenster wird automatisch ver<strong>de</strong>ckt, nach<strong>de</strong>m die W<strong>in</strong>dowListener aufgerufen wer<strong>de</strong>n (Standard).<br />

W<strong>in</strong>dowConstants.DISPOSE_ON_CLOSE<br />

Bewirkt die Freigabe <strong>de</strong>r Ressourcen, nach<strong>de</strong>m die Listener abgearbeitet s<strong>in</strong>d und das Fenster<br />

geschlossen wird.<br />

W<strong>in</strong>dowConstants.EXIT_ON_CLOSE<br />

Ruft System.exit() auf und schließt die Anwendung.<br />

„getDefaultCloseOperation()“ liefert die e<strong>in</strong>gestellte Eigenschaft beim<br />

Schließen <strong>de</strong>s Fensters.<br />

Das Applikationsgerüst. Für das E<strong>in</strong>fügen <strong>in</strong> e<strong>in</strong>en Sw<strong>in</strong>g-Frame ist folgen<strong>de</strong>s<br />

Applikationsgerüst zweckmäßig: Conta<strong>in</strong>er und Komponenten müssen <strong>in</strong> e<strong>in</strong>em<br />

Zwischen-Conta<strong>in</strong>er mit <strong>de</strong>m Namen „ContentPane“ (Inhaltsbereich) e<strong>in</strong>gefügt<br />

wer<strong>de</strong>n. Der JFrame ist <strong>in</strong> verschie<strong>de</strong>n Bereiche (panes) unterteilt. Der<br />

Hauptbereich ist <strong>de</strong>r Inhaltsbereich, <strong>de</strong>r die gesamte Fläche <strong>de</strong>s Frame repräsentiert,<br />

<strong>in</strong> <strong>de</strong>n Komponenten e<strong>in</strong>gefügt wer<strong>de</strong>n können. Das Erzeugen e<strong>in</strong>er Komponenten <strong>in</strong><br />

<strong>de</strong>n Inhaltsbereich umfasst:<br />

- Erzeugen e<strong>in</strong>es JPanel-Objekts<br />

- E<strong>in</strong>fügen aller Komponenten <strong>in</strong> das JPanel-Objekt über <strong>de</strong>ssen add (Component)-Metho<strong>de</strong><br />

- Implementieren <strong>de</strong>s JPanel-Objekts über die Metho<strong>de</strong> setContentPane(Conta<strong>in</strong>er).<br />

Bsp. 288 :<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

import javax.sw<strong>in</strong>g.*;<br />

public class Rahmenwerk extends JFrame<br />

{<br />

public Rahmenwerk()<br />

{<br />

super("Anwendung - Titel");<br />

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);<br />

setLocation(100,100);<br />

setSize(300,200);<br />

pack();<br />

288 pr56110<br />

362


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

/* pack() sorgt fuer die Reduzierung <strong>de</strong>r<br />

Groesse <strong>de</strong>s Frame, dass alle Komponenten gera<strong>de</strong> dar<strong>in</strong><br />

noch Platz f<strong>in</strong><strong>de</strong>n<br />

*/<br />

setVisible(true);<br />

/* Macht <strong>de</strong>n Frame sichtbar */<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

JFrame frame = new Rahmenwerk();<br />

}<br />

}<br />

Layout. LayouManager verwalten die Anordnung <strong>de</strong>r Komponenten <strong>in</strong> e<strong>in</strong>em<br />

Conta<strong>in</strong>er. Das Layout wird durch die Metho<strong>de</strong> setLayout(LayoutManager lm)<br />

angegeben.<br />

Bsp. 289 : Anwendung <strong>de</strong>s LayoutManagers Bor<strong>de</strong>rLayout<br />

Das Programm fügt <strong>in</strong> das JFrame e<strong>in</strong> Texfeld mit e<strong>in</strong>em Scrollbalken, e<strong>in</strong>em JColorChooser und 2<br />

"Karteikarten mit jeweils e<strong>in</strong>er Tabelle" e<strong>in</strong>.<br />

Das Textfeld wird durch das Anlegen e<strong>in</strong>es JTextArea-Objekts erzeugt. Durch Übergabe dieses<br />

Objekts an <strong>de</strong>n Konstruktor e<strong>in</strong>es JScrollPane-Objekts wird das Textfeld mit e<strong>in</strong>em Scrollbalken auf<br />

<strong>de</strong>r rechten Seite ausgestattet.<br />

import javax.sw<strong>in</strong>g.*;<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

public class Pr56352 extends JFrame<br />

{<br />

Color farbe;<br />

JColorChooser jcc;<br />

JButton jw;<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

Pr56352 pr56352 = new Pr56352();<br />

}<br />

public Pr56352()<br />

{<br />

super("Bor<strong>de</strong>rLayout");<br />

JTextArea ja = new JTextArea("Nor<strong>de</strong>n");<br />

JScrollPane jsp =<br />

new JScrollPane(ja, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,<br />

JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);<br />

jsp.setPreferredSize(new Dimension((<strong>in</strong>t) this.getWidth(),100));<br />

jcc = new JColorChooser();<br />

jw = new JButton("Westen");<br />

jw.addActionListener(new ActionListener()<br />

{<br />

public void actionPerformed(ActionEvent e)<br />

{<br />

farbe = jcc.getColor();<br />

jw.setBackground(farbe);<br />

}<br />

});<br />

JTabbedPane jtp = new JTabbedPane(2);<br />

JTable jt1 = new JTable(13,2);<br />

JTable jt2 = new JTable(10,1);<br />

jt2.setBackground(Color.green);<br />

jtp.addTab("2",jt1);<br />

jtp.addTab("1",jt2);<br />

JButton js = new JButton("Sue<strong>de</strong>n");<br />

289 vgl. pr56352 <strong>in</strong> pr56350<br />

363


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

}<br />

}<br />

js.setBackground(Color.yellow);<br />

addW<strong>in</strong>dowListener(new W<strong>in</strong>dowAdapter()<br />

{<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{<br />

System.exit(0);<br />

}<br />

} );<br />

getContentPane().setLayout(new Bor<strong>de</strong>rLayout());<br />

getContentPane().add(Bor<strong>de</strong>rLayout.NORTH,jsp);<br />

getContentPane().add(Bor<strong>de</strong>rLayout.WEST,jw);<br />

getContentPane().add(Bor<strong>de</strong>rLayout.EAST,jtp);<br />

getContentPane().add(Bor<strong>de</strong>rLayout.CENTER,jcc);<br />

getContentPane().add(Bor<strong>de</strong>rLayout.SOUTH,js);<br />

setSize(700,400);<br />

setVisible(true);<br />

Abb. Grafische Benutzroberfläche, die mit Bor<strong>de</strong>rLayout erzeugt wur<strong>de</strong><br />

364


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Interne Struktur von JFrame. Die <strong>in</strong>terne Struktur weist mehrere übere<strong>in</strong>an<strong>de</strong>rliegen<strong>de</strong><br />

Bereiche auf, <strong>de</strong>nen verschie<strong>de</strong>ne Aufgaben zugeteilt wur<strong>de</strong>n. Im<br />

wesentlichen s<strong>in</strong>d dies:<br />

- Wurzelbereich<br />

- Schichtbereich<br />

(dient hauptsächlich zur Darstellung von Menüs)<br />

- Inhaltsbereich<br />

(Conta<strong>in</strong>er für die meisten visuellen Elemente, kann mit getContentPane() angefor<strong>de</strong>rt wer<strong>de</strong>n)<br />

- Glasbereich<br />

(Komponenten, die <strong>in</strong> <strong>de</strong>n Glasbereich e<strong>in</strong>gefügt wer<strong>de</strong>n, ersche<strong>in</strong>en im Vor<strong>de</strong>rgrund und<br />

über<strong>de</strong>cken die darunter liegen<strong>de</strong>n Schichten. Diese Eigenschaft ist vor allem für die Darstellung<br />

von PopUp-Menüs von Be<strong>de</strong>utung.)<br />

JFrame<br />

JRoot<br />

JLayeredPane<br />

Inhaltsbereich<br />

Glasbereich<br />

Abb.: Interne Struktur von JFrame<br />

Die Hauptkomponente wird als RootPane bezeichnet und ist vom Typ JRootPane.<br />

E<strong>in</strong>e RootPane enthält folgen<strong>de</strong> Komponenten:<br />

- e<strong>in</strong>e aus Conta<strong>in</strong>er abgeleitete GlassPane<br />

- e<strong>in</strong>e aus JLayeredPane abgeleitete LayeredPane.<br />

E<strong>in</strong>e LayeredPane enthält ihrerseits zwei Unterkomponenten:<br />

- e<strong>in</strong>e aus Conta<strong>in</strong>er abgeleitete ContentPane<br />

- e<strong>in</strong>e aus JMenuBar abgeleitete Menüleiste.<br />

Die RootPane und mit ihr die dar<strong>in</strong> enthaltene GlassPane, LayeredPane und<br />

ContentPane wer<strong>de</strong>n beim Anlegen <strong>de</strong>s Fensters automatisch erzeugt (nur die<br />

Menüleiste bleibt standardmäßig leer). Außer<strong>de</strong>m implementieren alle Hauptfenster<br />

das Interface RootPaneConta<strong>in</strong>er, das <strong>de</strong>n Zugriff auf die RootPane vere<strong>in</strong>facht.<br />

E<strong>in</strong>ige se<strong>in</strong>er Metho<strong>de</strong>n s<strong>in</strong>d:<br />

public JRootPane getRootPane()<br />

public Conta<strong>in</strong>er getContentPane()<br />

public JLayeredPane getLayeredPane()<br />

public Component getClassPane()<br />

Neben <strong>de</strong>n “getter”-Metho<strong>de</strong>n gibt es auch „setter“-Metho<strong>de</strong>n, die <strong>de</strong>n strukturellen<br />

Aufbau <strong>de</strong>r RootPane vollständig verän<strong>de</strong>rn können.<br />

365


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

5.6.2.2 JW<strong>in</strong>dow<br />

JW<strong>in</strong>dow ist aus W<strong>in</strong>dow abgeleitet und dient dazu, e<strong>in</strong> rahmenloses Fenster zu<br />

erzeugen, das an beliebiger Stelle und <strong>in</strong> beliebiger Größe auf <strong>de</strong>m Bildschirm<br />

platziert wer<strong>de</strong>n kann.<br />

5.6.2.3 JDialog, JFileChooser, JColorChooser<br />

JDialog<br />

Mit <strong>de</strong>r aus Dialog abgeleiteten Klasse JDialog stehen auch im Sw<strong>in</strong>g Dialogfenster<br />

zur Verfügung. Dialogfenster unterschei<strong>de</strong>n sich von Hauptfenstern, dass sie ke<strong>in</strong><br />

Menü und nur e<strong>in</strong>geschränkte Systemfunktionen besitzen. Sie besitzen <strong>de</strong>nselben<br />

strukturellen Aufbau wie JFrame und JW<strong>in</strong>dow und implementieren ebenfalls das<br />

Interface RootPaneConta<strong>in</strong>er. Auch hier erfolgt das H<strong>in</strong>zufügen und Anordnen<br />

von Komponenten nicht auf <strong>de</strong>m Fenster selbst, son<strong>de</strong>rn auf se<strong>in</strong>er ContentPane.<br />

Die (wichigsten) Konstruktoren s<strong>in</strong>d:<br />

public JDialog(Frame owner)<br />

public JDialog(Frame owner, bollean modal)<br />

public JDialog(Frame owner, Str<strong>in</strong>g titel)<br />

public JDialog(Frame owner, Str<strong>in</strong>g titel, boolean modal)<br />

Als "owner" sollte <strong>de</strong>r Aufrufer das Fenster übergeben, zu <strong>de</strong>m <strong>de</strong>r Dialog logisch<br />

gehört. Alle Konstruktoren gibt es auch owner vom Typ Dialog. Wahlweise kann e<strong>in</strong><br />

JDialog auch ohne owner konstruiert wer<strong>de</strong>n (mit <strong>de</strong>m parameterlosen<br />

Konstruktor), doch kann es dann u.U. Fokusprobleme beim Wechsel zwischen <strong>de</strong>n<br />

Anwendungen geben.<br />

Modaler bzw. nicht-modaler Dialog. Bei e<strong>in</strong>em modalen Dialog wird <strong>de</strong>r Aufruf von<br />

show() (bzw. setVisible(true)) erst dann been<strong>de</strong>t, wenn <strong>de</strong>r Dialog<br />

geschlossen wur<strong>de</strong>. Bei e<strong>in</strong>em nicht-modalen Dialog fährt das Programm dagegen<br />

unmittelbar nach <strong>de</strong>r nächsten Anweisung h<strong>in</strong>ter show() fort.<br />

JFileChooser<br />

Diese Klasse ermöglicht e<strong>in</strong>en Standarddialog, mit <strong>de</strong>ssen Hilfe komfortable und<br />

Betriebssystem unabhängige Datei- und Verzeichnisauswahl möglich ist.<br />

JColorChooser<br />

Diese Klasse erlaubt e<strong>in</strong>e e<strong>in</strong>fache, visuelle und kontextunabhängige Auswahl von<br />

Farb<strong>in</strong>formationen.<br />

366


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Bsp. 290 : JColorChooserTest<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

import javax.sw<strong>in</strong>g.*;<br />

public class JColorChooserTest extends JFrame<br />

implements ActionListener<br />

{<br />

public JColorChooserTest()<br />

{<br />

super("Us<strong>in</strong>g JColorChooser");<br />

// W<strong>in</strong>dowUtilities.setNativeLookAndFeel();<br />

addW<strong>in</strong>dowListener(new W<strong>in</strong>dowAdapter()<br />

{<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{ System.exit(0); }<br />

});<br />

Conta<strong>in</strong>er content = getContentPane();<br />

content.setBackground(Color.white);<br />

content.setLayout(new FlowLayout());<br />

JButton colorButton<br />

= new JButton("Choose Background Color");<br />

colorButton.addActionListener(this);<br />

content.add(colorButton);<br />

setSize(300, 100);<br />

setVisible(true);<br />

}<br />

public void actionPerformed(ActionEvent e)<br />

{<br />

// Args are parent component, title, <strong>in</strong>itial color<br />

Color bgColor<br />

= JColorChooser.showDialog(this,<br />

"Choose Background Color",<br />

getBackground());<br />

if (bgColor != null)<br />

getContentPane().setBackground(bgColor);<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

new JColorChooserTest();<br />

}<br />

}<br />

Nach Drücken <strong>de</strong>s Button "ChooseBackgroundColor" ersche<strong>in</strong>t<br />

290 pr56370<br />

367


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

zur Auswahl <strong>de</strong>r gewünschten Farbe, die hier zum Füllen <strong>de</strong>s H<strong>in</strong>tergrunds benutzt wird.<br />

Mit <strong>de</strong>m Dialog lassen sich Farben <strong>in</strong> drei unterschiedlichen Dialogen auswählen.<br />

Der Benutzer hat die Auswahl unter <strong>de</strong>n vor<strong>de</strong>f<strong>in</strong>ierten Farben, HSB-Werten und<br />

RGB-Werten. Der Farbauswahl-Dialog ersche<strong>in</strong>t auf <strong>de</strong>m Bildschirm nach <strong>de</strong>m Aufruf<br />

JColorChooser.showDialog() mit drei Argumenten:<br />

e<strong>in</strong>em Component-Objekt, auf <strong>de</strong>m gezeichnet wer<strong>de</strong>n kann<br />

<strong>de</strong>m Titel<br />

e<strong>in</strong>er Anfangsfarbe<br />

Rückgabewert ist nach Been<strong>de</strong>n <strong>de</strong>s Dialogs die ausgewählte Farbe (bei Abbruch:<br />

null).<br />

5.6.2.4 JOptionPane<br />

Die Klasse javax.sw<strong>in</strong>g.JOptionPane ist <strong>in</strong> <strong>de</strong>r Lage, e<strong>in</strong>fache Dialoge, die<br />

lediglich e<strong>in</strong> Icon, e<strong>in</strong>en Text o<strong>de</strong>r e<strong>in</strong> E<strong>in</strong>gabefeld und e<strong>in</strong>e Anzahl <strong>de</strong>r Schaltflächen<br />

"Yes", "No" und "Cancel" enthalten, mit e<strong>in</strong>em e<strong>in</strong>zigen Aufruf e<strong>in</strong>er statischen<br />

Metho<strong>de</strong> zu erzeugen.<br />

368


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

5.6.2.5 JInternalFrame<br />

Bei vielen Programmen gibt es e<strong>in</strong> e<strong>in</strong>ziges Hauptfenster und zahlreiche geöffnete<br />

K<strong>in</strong>dfenster <strong>in</strong>nerhalb <strong>de</strong>s Hauptfensters. Diese unter W<strong>in</strong>dows als MDI (Multiple<br />

Document Interface) bezeichnete Technik ist mittlerweise weit verbreitet.<br />

In Sw<strong>in</strong>g können MDI-Anwendungen entwickelt wer<strong>de</strong>n. Dazu wer<strong>de</strong>n zwei Arten von<br />

Komponenten benötigt:<br />

- das als Desktop bezeichnete Hauptfenster <strong>de</strong>r Anwendung<br />

- das Subfenster <strong>de</strong>r Anwendung<br />

5.6.2.6 JPanel und JLayeredPane<br />

JPanel<br />

E<strong>in</strong> JPanel ist e<strong>in</strong> Fensterbereich, <strong>in</strong> <strong>de</strong>m an<strong>de</strong>re Kontrollelemente platziert wer<strong>de</strong>n<br />

können (Conta<strong>in</strong>er). Hauptaufgabe ist die Gestaltung und Glie<strong>de</strong>rung von<br />

Benutzeroberflächen. JPanel ist die Basisklasse für GUI-Conta<strong>in</strong>er, die nicht<br />

Hauptfenster s<strong>in</strong>d. Standardmäßig ist e<strong>in</strong>em JPanel e<strong>in</strong> FlowLayout als<br />

LayoutManager zugeordnet. Neben <strong>de</strong>n geerbten Metho<strong>de</strong>n von JComponent,<br />

Conta<strong>in</strong>er, Component und Object kommen ke<strong>in</strong>e nenneswerten Funktionen<br />

h<strong>in</strong>zu.<br />

Konstruktoren.<br />

public JPanel()<br />

// erzeugt e<strong>in</strong> neues JPanel mit Double-Buffer<strong>in</strong>g<br />

public JPanel(boolean isDoubleBuffered)<br />

// erzeugt e<strong>in</strong> neues JPanel mit FlowLayout und <strong>de</strong>r angegebenen Puffer-Strategie<br />

public JPanel(LayoutManager layout)<br />

// erzeugt e<strong>in</strong>en JPanel mit Double-Buffer<strong>in</strong>g und <strong>de</strong>m angegebenen Layout-Manager<br />

public JPanel(LayoutManager layout, boolean isDoubleBuffered)<br />

// erzeugt e<strong>in</strong> neues JPanel mit <strong>de</strong>m angegebenen LayoutManager und <strong>de</strong>r angegebenen Puffer-<br />

// Strategie<br />

JLayeredPane<br />

JLayeredPane fügt <strong>de</strong>n Dialogen e<strong>in</strong>e dritte Dimension h<strong>in</strong>zu und ermöglicht mit<br />

Hilfe e<strong>in</strong>es Layerkonzepts die kontrollierte Anordnung von Komponenten<br />

übere<strong>in</strong>an<strong>de</strong>r. JLayeredPane ist die Elternklasse von JDesktopPane und wichtiger<br />

Bestandteil <strong>de</strong>r Struktur von Hauptfenstern.<br />

369


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

5.6.3 Menüs<br />

5.6.3.1 Grundlagen<br />

Menüs dienen zur Auswahl von Funktionen und Optionen <strong>in</strong> Anwendungen. Sie<br />

können entwe<strong>de</strong>r an <strong>de</strong>r Oberseite von Fenstern o<strong>de</strong>r als PopUp-Menüs an e<strong>in</strong>er<br />

beliebigen Stelle auftauchen.<br />

JMenuBar<br />

In Sw<strong>in</strong>g können alle Hauptfenster mit Ausnahme von JW<strong>in</strong>dow e<strong>in</strong>e Menüleiste<br />

haben.<br />

JMenu<br />

Die e<strong>in</strong>zelnen Menüs e<strong>in</strong>er Menüleiste s<strong>in</strong>d Instanzen <strong>de</strong>r Klasse JMenu, die aus<br />

JMenuItem abgeleitet ist.<br />

JMenuItem<br />

Die Klasse JMenuItem repräsentiert Menüe<strong>in</strong>träge, also Elemente, die sich <strong>in</strong> e<strong>in</strong>em<br />

Menu bef<strong>in</strong><strong>de</strong>n. Dabei han<strong>de</strong>lt es sich um Texte, die wahlweise mit e<strong>in</strong>em Icon o<strong>de</strong>r<br />

mit e<strong>in</strong>em Häkchen versehen wer<strong>de</strong>n können.<br />

5.6.3.2 Kontextmenüs<br />

370


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

JComponent<br />

AbstractButton<br />

JMenuItem<br />

><br />

public JMenuItem()<br />

public JMenuItem(Icon icon)<br />

public JMenuItem(Str<strong>in</strong>g text)<br />

public JMenuItem(Str<strong>in</strong>g text, Icon icon)<br />

public JMenuItem(Str<strong>in</strong>g text, <strong>in</strong>t mnemonic)<br />

><br />

public void addMenuDragMouseListener(MenuDragMouseListener l)<br />

public void addMenuKeyListener(MenuKeyListener l)<br />

public KeyStroke getAccelerator()<br />

…<br />

public MenuKeyListener [] getMenuKeyListeners()<br />

public MenuElement [] getSubElements()<br />

…<br />

public void <strong>in</strong>it(Str<strong>in</strong>g text, Icon icon)<br />

…<br />

public void removeKeyListener(MenuKeyListener l)<br />

public void setAccelerator(KeyStroke keyStroke)<br />

…<br />

public void setEnabled(boolean b)<br />

public void setUI(MenuItem ui)<br />

public void updateUI()<br />

JMenu<br />

JCheckBoxMenuItem<br />

> JRadioButtonMenuItem<br />

public JMenu()<br />

public JMenu(Action a)<br />

public JMenu(Str<strong>in</strong>g s)<br />

public JMenu(Str<strong>in</strong>g s, boolean b)<br />

><br />

public JMenuItem add(Action a)<br />

public JMenuItem add(JMenuItem menuItem)<br />

public JMenuItem add(Str<strong>in</strong>g s)<br />

public addmenuListener(MenuListener l)<br />

public void addSeparator()<br />

…<br />

public void doClick(<strong>in</strong>t pressTime)<br />

…<br />

public JMenuItem getItem(<strong>in</strong>t pos)<br />

public <strong>in</strong>t getItemCount()<br />

…<br />

public JMenuItem <strong>in</strong>sert(Action a, <strong>in</strong>t pos)<br />

Abb.: Die Sw<strong>in</strong>g-Menüklassen<br />

371


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

JComponent<br />

JMenuBar<br />

JPopupMenu<br />

> ><br />

public JMenuBar()<br />

public JPopupMenu()<br />

> public JPopupMenu(Str<strong>in</strong>g label)<br />

public void add(JMenu c) ><br />

public void addNotify() public JMenuItem add(Action a)<br />

… public JMenuItem add(JMenuItem m)<br />

public JMenu getMenu(<strong>in</strong>t <strong>in</strong><strong>de</strong>x) public JMenuItem add(Str<strong>in</strong>g s)<br />

…<br />

public MenuElement [] getSubElements()<br />

public MenuBarUI getUI()<br />

…<br />

public boolean isSelected()<br />

…<br />

public void setHelpMenu(JMenu menu)<br />

…<br />

public void setUI(MenuBarUI ui)<br />

public void updateUI()<br />

372


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

5.6.4 Die Conta<strong>in</strong>er-Klassen JToolBar, JSplitPane, JScrollPane,<br />

JTabbedPane<br />

JToolBar<br />

Mit JToolBar können Werkzeugleisten erstellt wer<strong>de</strong>n, die Buttons enthalten.<br />

JSplitPane<br />

Das Kontrollelment teilt e<strong>in</strong>e Fläche <strong>in</strong> zwei Teile.<br />

JScrollPane<br />

Der Conta<strong>in</strong>er JScrollPane ermöglicht die Anzeige e<strong>in</strong>es<br />

Komponentenausschnitts. In Verb<strong>in</strong>dung mit <strong>de</strong>n Klassen JTextArea und JList<br />

besteht die Ausgabe von JScrollPane dar<strong>in</strong>, Dialogelemente, die zu groß für <strong>de</strong>n zur<br />

Verfügung stehen<strong>de</strong>n Platz s<strong>in</strong>d, mit Hilfe e<strong>in</strong>es verschiebbaren Fensters<br />

ausschnittsweise sichtbar zu machen.<br />

Konstruktoren.<br />

public JScrollPane(Component view)<br />

public JScrollPane(Component view, <strong>in</strong>t vsbPolicy, <strong>in</strong>t hsbPolicy)<br />

Die Argumente vsbPolicy und hsbPolicy geben an, wann e<strong>in</strong> horiz<strong>in</strong>taler bzw. vertikaler<br />

Schieberegler e<strong>in</strong>geblen<strong>de</strong>t wird. Es können folgen<strong>de</strong> Werte angegeben wer<strong>de</strong>n:<br />

Konstante<br />

VERTICAL_SCROLLBAR_NEVER<br />

VERTICAL_SCROLLBAR_ALLWAYS<br />

VERTICAL_SCROLLBAR_AS_NEEDED<br />

HORIZONTAL_SCROLLBAR_NEVER<br />

HORIZONTAL_SCROLLBAR_ALLWAYS<br />

HORIZONTAL_SCROLLBAR_AS_NEEDED<br />

Be<strong>de</strong>utung<br />

Der vertikale Schieberegler wird nie angezeigt.<br />

Der vertikale Schieberegler wird immer angezeigt.<br />

Der vertikale Schieberegler wird nur angezeigt,<br />

wenn er benötigt wird<br />

Der horizontale Schieberegler wird nie angezeigt<br />

Der horizontale Schieberegler wird immer<br />

angezeigt<br />

Der horizontale Schieberegler wird nur angezeigt,<br />

wenn er benötigt wird<br />

Falls die Argumente vsbPolicy und hsbPolicy nicht angegeben wer<strong>de</strong>n, blen<strong>de</strong>t JScrollPane<br />

die Schieberegler nur dann e<strong>in</strong>, wenn sie wirklich benötigt wer<strong>de</strong>n.<br />

Metho<strong>de</strong>n.<br />

public void setColumnHea<strong>de</strong>rView(Component view)<br />

Angabe e<strong>in</strong>er Komponente für <strong>de</strong>n Spaltenkopf. Sie wird über <strong>de</strong>m eigentlichen Dialogelement<br />

angezeigt, bei horozontalen Bewegungen mit diesem verschoben. Bei vertikalen<br />

Schieberbewegungen bleiben sie dagegen am Platz.<br />

public void setRowhea<strong>de</strong>rView(Component view)<br />

Angabe e<strong>in</strong>es Zeilenkopfs l<strong>in</strong>ks neben <strong>de</strong>r eigentlichen Komponente, wird bei vertikalen Bewegungen<br />

verschoben, behält bei horizontalen Bewegungen se<strong>in</strong>en Platz bei.<br />

373


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

public void setCorner(Str<strong>in</strong>g key, Component corner)<br />

E<strong>in</strong> Dialogelement kann <strong>in</strong> e<strong>in</strong>e beliebige <strong>de</strong>r vier ungenutzten Ecken 291 e<strong>in</strong>er JScrollPane plaziert<br />

wer<strong>de</strong>n. Der Parameter key gibt dabei an, welche Ecke belegt wer<strong>de</strong>n soll. Argument kann e<strong>in</strong>er <strong>de</strong>r<br />

Konstanten LOWER_LEFT_CORNER, LOWER_RIGHT_CORNER, UPPER_LEFT_CORNER,<br />

UPPER_RIGHT_CORNER se<strong>in</strong>.<br />

Eckfläche<br />

Zeilenhea<strong>de</strong>r<br />

Eckfläche<br />

Abb.: Aufbau e<strong>in</strong>er JScrollpane<br />

Spaltenhea<strong>de</strong>r<br />

ViewPort<br />

Horizontaler<br />

Schieberegler<br />

Eckfläche<br />

Vertikaler<br />

Schieberegler<br />

Eckfläche<br />

Der sichtbare Ausschnitt heißt ViewPort und wird durch folgen<strong>de</strong> Metho<strong>de</strong>n e<strong>in</strong>gestell:<br />

public void setViewPort(JViewPort viewport)<br />

public void setViewPortView(Component view)<br />

e<strong>in</strong>gestellt.<br />

JTabbedPane<br />

E<strong>in</strong> JTabbedPane erlaubt die Darstellung mehrerer Conta<strong>in</strong>er, die über Kartenreiter<br />

zugänglich s<strong>in</strong>d.<br />

291 u.U. stehen Eckflächen nicht zur Verfügung. Die bei<strong>de</strong>n Ecken auf <strong>de</strong>r l<strong>in</strong>ken Seite s<strong>in</strong>d bspw. nur<br />

vorhan<strong>de</strong>n, wenn e<strong>in</strong> Zeilenkopf e<strong>in</strong>geblen<strong>de</strong>t wur<strong>de</strong>. Die rechte obere ist nur vorhan<strong>de</strong>n, wenn e<strong>in</strong> vertikaler<br />

Schieberegler e<strong>in</strong>geblen<strong>de</strong>t wur<strong>de</strong>, und die rechte untere erfor<strong>de</strong>rt sogar die Anwesenheit bei<strong>de</strong>r Schieberegler.<br />

Auch kann die Anwendung praktisch ke<strong>in</strong>en E<strong>in</strong>fluß auf die Größe <strong>de</strong>r Ecken nehmen. Diese wird<br />

ausschließlich die Aus<strong>de</strong>hnung <strong>de</strong>r Schieberegler und <strong>de</strong>s Zeilenkopfes bestimmt.<br />

374


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

5.6.5 Sw<strong>in</strong>g-Komponenten<br />

5.6.5.1 Label und Textfel<strong>de</strong>r<br />

JLabel<br />

E<strong>in</strong> JLabel ist e<strong>in</strong> Dialogelement zur Anzeige und Beschriftung <strong>in</strong>nerhalb e<strong>in</strong>es GUI-<br />

Comta<strong>in</strong>ers, besitzt Text und Icon, die <strong>in</strong> beliebiger Anordnung dargestellt wer<strong>de</strong>n<br />

können. Die bei<strong>de</strong>n Elemente s<strong>in</strong>d optional, e<strong>in</strong> JLabel kann auch nur e<strong>in</strong> Icon<br />

enthalten. Auf Benutzere<strong>in</strong>geben reagiert e<strong>in</strong> JLabel nicht. In <strong>de</strong>r Sw<strong>in</strong>g-Bibliothek<br />

gibt es mit <strong>de</strong>m Interface Icon bzw. <strong>de</strong>r Klasse ImageIcon Unterstützung zur<br />

Bildverarbeitung, wobei <strong>de</strong>r La<strong>de</strong>vorgang mit Hilfe von Mediatracker <strong>in</strong>tern erledigt<br />

wird.<br />

Konstruktoren.<br />

public JLabel(Str<strong>in</strong>g text)<br />

public JLabel(Icon image)<br />

public JLabel(Str<strong>in</strong>g text, Icon icon,<strong>in</strong>t horizontalAlignment)<br />

// Der Parameter horizontalAlignment gibt an, wie das Label horizontal plaziert wer<strong>de</strong>n soll, falls<br />

// l<strong>in</strong>ks und rechts mehr Platz als erfor<strong>de</strong>rlich zur Verfügung steht. Hier kann e<strong>in</strong>e <strong>de</strong>r Konstanten<br />

// LEFT, CENTER o<strong>de</strong>r RIGHT aus <strong>de</strong>m Interface Sw<strong>in</strong>gConstants angegeben wer<strong>de</strong>n.<br />

Metho<strong>de</strong>n.<br />

public void setHorizonatTextPosition(<strong>in</strong>t textPosition)<br />

public void getHorizontalTextPosition()<br />

public void setHorizontalAlignment(<strong>in</strong>t alignment)<br />

// horizontale Ausrichtung, mögliche Parameter: LEFT, CENTER, RIGHT<br />

public <strong>in</strong>t getHorizontalAlignment()<br />

public void setVerticalAlignment(<strong>in</strong>t alignment)<br />

// vertikale Ausrichtung, mögliche Parameter: TOP, CENTER, BOTTOM<br />

public <strong>in</strong>t getVerticalAlignment()<br />

Anzeige von Bil<strong>de</strong>rn (im Gegensatz zur AWT-Implementierung). Angegeben wer<strong>de</strong>n<br />

zur Anzeige von Bil<strong>de</strong>rn Icon-Objekte. Die Klasse ImageIcon implemntiert die Icon-<br />

Schnittstelle.<br />

<strong>in</strong>terface java.awt.sw<strong>in</strong>g.Icon<br />

public <strong>in</strong>t getIconWidth()<br />

// liefert die Breite e<strong>in</strong>es Icons<br />

public <strong>in</strong>t getIconHeight()<br />

// liefert die Höhe e<strong>in</strong>es Icons<br />

public void pa<strong>in</strong>tIcon(Component c, Graphics g, unt x, <strong>in</strong>t y)<br />

// zeichnet das Icon an die angegebene Position.<br />

Die Klasse ImageIcon implementiert die Icon-Schnittstelle und nutzt die Klasse<br />

Image (und <strong>de</strong>ren verwandte Klassen. E<strong>in</strong> ImageIcon ist serialisierbar<br />

(implementiert das Interface Serializable). E<strong>in</strong>e Referenz auf das Image-Objekt kann<br />

durch <strong>de</strong>n Aufruf von getImage() <strong>de</strong>r Klasse Toolkit hergestellt wer<strong>de</strong>n. Mit <strong>de</strong>r<br />

Metho<strong>de</strong> protected void loadImage(Image image) wartet man dann mit<br />

Hilfe e<strong>in</strong>es Mediatrackers auf das Bild. Dabei setzt <strong>de</strong>r Mediatracker Höhe und<br />

375


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Breite, die sich über Icon-Metho<strong>de</strong>n abfragen lassen. Zur Serialisierbarkeit s<strong>in</strong>d die<br />

Metho<strong>de</strong>n readObject() und writeObject() implementiert. Im zugehörigen<br />

Dateistrom bef<strong>in</strong><strong>de</strong>n sich Breite und Höhe und anschließend e<strong>in</strong> Integer-Feld mit<br />

Pixelwerten. Bei readObject() liest s.readObject() – wobei s <strong>de</strong>r aktuelle<br />

ObjectInputStream ist -, das Feld wie<strong>de</strong>r e<strong>in</strong>. Über die Toolkit-Funktion<br />

createImage() wird die Klasse MemoryImage genutzt, um das Feld wie<strong>de</strong>r bzu<br />

e<strong>in</strong>em Image-Objekt zu konvertieren. Umgekehrt geht es genauso e<strong>in</strong>fach:<br />

writeObject() schreibt Breite und Höhe und anschließend das Integer-Feld, das<br />

es über e<strong>in</strong> PixelGrabber bekommen hat.<br />

Die ImageIcon-Klasse kann somit das La<strong>de</strong>n von Icon-Objekten mit e<strong>in</strong>er<br />

Programmzeilen beschreiben:<br />

Image image = new ImageIcon(("bild.jpg").getImage())<br />

Da e<strong>in</strong> Image immer wie<strong>de</strong>r <strong>in</strong> e<strong>in</strong> ImageIcon umgewan<strong>de</strong>lt wer<strong>de</strong>n kann, eignet<br />

sich die Klasse hervorragend zum La<strong>de</strong>n und Speichern.<br />

Bsp. 292 : E<strong>in</strong>ige Labels mit unterschiedlichen Eigenschaften<br />

import java.awt.*;<br />

import javax.sw<strong>in</strong>g.*;<br />

class LabelDemo extends JFrame<br />

{<br />

public LabelDemo()<br />

{<br />

super("JLabel Demo");<br />

setSize(600, 100);<br />

JPanel content = (JPanel) getContentPane();<br />

content.setLayout(new GridLayout(1, 4, 4, 4));<br />

JLabel label = new JLabel();<br />

label.setText("JLabel");<br />

label.setBackground(Color.white);<br />

content.add(label);<br />

label = new JLabel("JLabel",Sw<strong>in</strong>gConstants.CENTER);<br />

label.setOpaque(true);<br />

label.setBackground(Color.white);<br />

content.add(label);<br />

label = new JLabel("JLabel");<br />

label.setFont(new Font("Helvetica", Font.BOLD, 18));<br />

label.setOpaque(true);<br />

label.setBackground(Color.white);<br />

content.add(label);<br />

ImageIcon image = new ImageIcon("flight.gif");<br />

label = new JLabel("JLabel", image,Sw<strong>in</strong>gConstants.RIGHT);<br />

label.setVerticalTextPosition(Sw<strong>in</strong>gConstants.TOP);<br />

label.setOpaque(true);<br />

label.setBackground(Color.white);<br />

content.add(label);<br />

setVisible(true);<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

new LabelDemo();<br />

}<br />

}<br />

292 pr56360<br />

376


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Abb.: Demonstration zu <strong>de</strong>n Klassen JLabel und IconImage<br />

JTextField<br />

E<strong>in</strong> JTextField ist e<strong>in</strong> e<strong>in</strong>zeiliges E<strong>in</strong>gabefeld.<br />

Konstruktoren.<br />

public JTextField(<strong>in</strong>t columns)<br />

// e<strong>in</strong> leeres Textfeld mit <strong>de</strong>r angegebenen Anzahl Spalten<br />

public JTextField(Str<strong>in</strong>g text)<br />

// erzeugt e<strong>in</strong> Textfeld mit angegebenem Text<br />

public JTextField(Str<strong>in</strong>g text,<strong>in</strong>t columns)<br />

// erzeugt e<strong>in</strong> Textfeld nach Vorgabe von Spaltenanzahl und Text<br />

// Die Spaltenanzahl wird zur Berechnung <strong>de</strong>r Breite <strong>de</strong>r Textfel<strong>de</strong>r verwen<strong>de</strong>t<br />

Metho<strong>de</strong>n.<br />

public Str<strong>in</strong>g getText()<br />

public void setText()<br />

public Str<strong>in</strong>g getSelectedText()<br />

public <strong>in</strong>t getSelectionStart()<br />

public void setSelectionStart()<br />

public void setSelectionEnd(<strong>in</strong>t selectionEnd)<br />

public <strong>in</strong>t getCaretPosition()<br />

public void setCaretPosition(<strong>in</strong>t pos)<br />

public void moveCaretPosition(<strong>in</strong>t pos)<br />

// verän<strong>de</strong>rt die Position <strong>de</strong>r E<strong>in</strong>fügemarke und markiert dabei <strong>de</strong>n Bereich zwischen alter und<br />

// neuer Position. Für alle Positionsangaben gilt, dass <strong>de</strong>r Platz vor <strong>de</strong>m ersten Zeichen die<br />

// Position 0, und <strong>de</strong>r nach <strong>de</strong>m letzten Textzeichen die Position Länge <strong>de</strong>s Texts hat<br />

Listener.<br />

public void addActionListener(ActionListener l)<br />

// wird immer aufgerufen, wenn im E<strong>in</strong>gabefeld ENTER gedrückt wird.<br />

public void addCaretListener(CaretListener l)<br />

// wir aufgerufen, wenn sich die Position <strong>de</strong>r E<strong>in</strong>fügemarke geän<strong>de</strong>rt hat<br />

Bsp. 293 :<br />

import javax.sw<strong>in</strong>g.*;<br />

import java.awt.*;<br />

public class JTextFieldTest extends JFrame<br />

{<br />

public JTextFieldTest()<br />

{<br />

super("JTextField Test");<br />

getContentPane().setLayout(new FlowLayout());<br />

JTextField textField1 = new JTextField("m",1);<br />

JTextField textField2 = new JTextField("mm",2);<br />

JTextField textField3 = new JTextField("mmm",3);<br />

JTextField textField4 = new JTextField("mmmm",4);<br />

293 pr56370<br />

377


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

JTextField textField5 = new JTextField("mmmmm",5);<br />

JTextField textField6 = new JTextField("mmmmmm",6);<br />

JTextField textField7 = new JTextField("mmmmmmm",7);<br />

JTextField textField8 = new JTextField("mmmmmmmm",8);<br />

JTextField textField9 = new JTextField("mmmmmmmmm",9);<br />

JTextField textField10 = new JTextField("mmmmmmmmmm",10);<br />

JTextField textField11 = new JTextField("mmmmmmmmmmm",11);<br />

JTextField textField12 = new JTextField("mmmmmmmmmmmm",12);<br />

JTextField textField13 = new JTextField("mmmmmmmmmmmmm",13);<br />

JTextField textField14 = new JTextField("mmmmmmmmmmmmmm",14);<br />

getContentPane().add(textField1);<br />

getContentPane().add(textField2);<br />

getContentPane().add(textField3);<br />

getContentPane().add(textField4);<br />

getContentPane().add(textField5);<br />

getContentPane().add(textField6);<br />

getContentPane().add(textField7);<br />

getContentPane().add(textField8);<br />

getContentPane().add(textField9);<br />

getContentPane().add(textField10);<br />

getContentPane().add(textField11);<br />

getContentPane().add(textField12);<br />

getContentPane().add(textField13);<br />

getContentPane().add(textField14);<br />

setSize(300,170);<br />

setVisible(true);<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g argv[])<br />

{<br />

new JTextFieldTest();<br />

}<br />

}<br />

Abb.: Demonstration zur Klasse JTextField<br />

JTextArea<br />

JTextArea ist e<strong>in</strong>e Komponente zur Anzeige und E<strong>in</strong>gabe von mehrzeiligen Texten.<br />

Wie die AWT-Klasse TextArea dient sie dazu, unformatierte Texte zu bearbeiten.<br />

Diese können Zeilenumbrüche und Tabulatoren, nicht aber unterschiedliche<br />

Schriften, Farben o<strong>de</strong>r grafische Elemente enthalten 294 .<br />

Konstruktoren.<br />

public JTextArea(Str<strong>in</strong>g text)<br />

public JTextArea(<strong>in</strong>t rows, <strong>in</strong>t columns)<br />

public JTextArea(Str<strong>in</strong>g text,<strong>in</strong>t rows,<strong>in</strong>t columns)<br />

294 Für diesen Zweck gibt es die Klassen JEditorPane und JTextPane<br />

378


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Metho<strong>de</strong>n.<br />

public <strong>in</strong>t getL<strong>in</strong>eCount()<br />

// liefert Anzahl <strong>de</strong>r Zeilen<br />

public <strong>in</strong>t getL<strong>in</strong>eStartOffset(<strong>in</strong>t l<strong>in</strong>e)<br />

public <strong>in</strong>t getL<strong>in</strong>eEndOffset(<strong>in</strong>t l<strong>in</strong>e)<br />

// Zu e<strong>in</strong>er beliebigen Zeile kann Anfangs- und Endoffset bestimmt wer<strong>de</strong>n.<br />

public <strong>in</strong>t getL<strong>in</strong>eOfOffset(<strong>in</strong>t offset)<br />

// ermittelt die Nummer e<strong>in</strong>er Zeile, wenn <strong>de</strong>r Offset e<strong>in</strong>es dar<strong>in</strong> enthaltenen Zeichens bekannt ist.<br />

public void setTabSize(<strong>in</strong>t size)<br />

public <strong>in</strong>t getTabSize()<br />

// Zugriff auf die Tabulatorwerte (standardmäßig mit Wert 8 295 )<br />

public void setL<strong>in</strong>eWrap(boolean wrap)<br />

// Festlegung, ob zu breite Spalten automatisch unterbrochen wer<strong>de</strong>n sollen<br />

public void setWrapStyleWord(boolean word)<br />

// Umbruch an Wortgrenzen o<strong>de</strong>r mitten im Wort<br />

Automatisches Scrollen. JTextArea kann Text nicht automatisch scrollen. Wird das<br />

gewünscht, muß das Textfeld <strong>in</strong> e<strong>in</strong>er Komponente von JScrollPane e<strong>in</strong>gebettet<br />

wer<strong>de</strong>n.<br />

Bsp. 296 :<br />

import java.awt.*;<br />

import javax.sw<strong>in</strong>g.*;<br />

import javax.sw<strong>in</strong>g.bor<strong>de</strong>r.*;<br />

public class BspScroll<strong>in</strong>g extends JFrame<br />

{<br />

public BspScroll<strong>in</strong>g()<br />

{<br />

super();<br />

JScrollPane scroller = new JScrollPane();<br />

getContentPane().add(scroller);<br />

Str<strong>in</strong>gBuffer bigBuffer = new Str<strong>in</strong>gBuffer();<br />

for (<strong>in</strong>t i = 0; i < 50; i++)<br />

{<br />

bigBuffer.append(Integer.toStr<strong>in</strong>g(i));<br />

bigBuffer.append(' ');<br />

}<br />

JLabel longLabel = new JLabel(bigBuffer.toStr<strong>in</strong>g());<br />

scroller.getViewport().add(longLabel);<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g [] args)<br />

{<br />

BspScroll<strong>in</strong>g e<strong>in</strong>Frame = new BspScroll<strong>in</strong>g();<br />

e<strong>in</strong>Frame.setVisible(true);<br />

}<br />

}<br />

JEditorPane und JTextPane<br />

Die Klasse JEditorPane und <strong>de</strong>ren Subklasse JTextPane erlauben verschie<strong>de</strong>ne<br />

Typen von Text darzustellen. Die bei<strong>de</strong>n Klassen unterstützen HTML und Rich Text<br />

Format (RTF). Eigene Implementierungen lassen sich h<strong>in</strong>zufügen (sog. Editor-Kits).<br />

Der Editor stellt Text dar, <strong>de</strong>r ihm mit<br />

295 Zur Umrechnung <strong>in</strong> Bildschirmpixel wird er mit <strong>de</strong>r Breite <strong>de</strong>s breitesten Buchstabens im aktuellen Font<br />

multipliziert.<br />

296 pr56110<br />

379


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

public void setContentType(Str<strong>in</strong>g type)<br />

übergeben wird. Ohne eigene Erweiterung s<strong>in</strong>d text/html (Standard),<br />

text/pla<strong>in</strong> und text/rtf erlaubt 297 . Wird mit Standardkonstruktor gearbeitet,<br />

dann kann mit<br />

public void setPage(Str<strong>in</strong>g url)<br />

bzw.<br />

public void setPage(URL page)<br />

e<strong>in</strong> URL-Objekt o<strong>de</strong>r e<strong>in</strong> Str<strong>in</strong>g e<strong>in</strong>e Seite belegen. Auch<br />

public void setText(Str<strong>in</strong>g t)<br />

erlaubt Setzen <strong>de</strong>s spezifizierten Inhalts von Text ohne Textkomponente.<br />

Konstruktoren.<br />

public JEditorPane()<br />

public JEditorPane(Str<strong>in</strong>g url)<br />

public JEditorPane(Str<strong>in</strong>g type,Str<strong>in</strong>g text)<br />

public JEditorPane(URL <strong>in</strong>itialPage)<br />

Bsp. 298 :Anzeige e<strong>in</strong>er HTML-Seite<br />

import java.io.*;<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

import javax.sw<strong>in</strong>g.*;<br />

public class PR56510 extends Frame<br />

{<br />

Str<strong>in</strong>g url = "file:///d:/dok/pgj/ws02/progr/pr56510/Skript48.html";<br />

public PR56510()<br />

{<br />

try {<br />

JEditorPane htmlPane = new JEditorPane(url);<br />

htmlPane.setEditable(false);<br />

add(new JScrollPane(htmlPane));<br />

}<br />

catch(IOException e) { System.out.pr<strong>in</strong>tln("Error" + url);};<br />

addW<strong>in</strong>dowListener(new W<strong>in</strong>dowAdapter()<br />

{<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{ System.exit(0);}<br />

});<br />

setSize(300,200);<br />

setVisible(true);<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

}<br />

}<br />

PR56510 pr56510 = new PR56510();<br />

297 Soll nur Text ohne Formatierungen und ohne Attribute dargestellt wer<strong>de</strong>n, lässt sich gleich TextField<br />

verwen<strong>de</strong>n.<br />

298 pr56510<br />

380


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

JTextComponent<br />

JTextArea<br />

JEditorPane<br />

> ><br />

public JTextArea()<br />

public JEditorPane()<br />

public JTextArea (Document doc,<br />

public JEditorPane(Str<strong>in</strong>g type, Str<strong>in</strong>g<br />

Str<strong>in</strong>g text,<br />

text)<br />

<strong>in</strong>t rows, <strong>in</strong>t columns)<br />

public JEditorPane(URL <strong>in</strong>itialPage)<br />

public JTextArea(<strong>in</strong>t rows,<strong>in</strong>t columns)<br />

…<br />

public JTextArea(Str<strong>in</strong>g text) ><br />

public JTextArea(Str<strong>in</strong>g text,<br />

public Str<strong>in</strong>g getContentType()<br />

<strong>in</strong>t rows, <strong>in</strong>t columns)<br />

public EditorKit getEditorKit()<br />

> …<br />

public void append(Str<strong>in</strong>g str)<br />

public URL getPage()<br />

…<br />

public Dimension getPreferedSize()<br />

public <strong>in</strong>t getColumns()<br />

…<br />

…<br />

public Str<strong>in</strong>g getText()<br />

public <strong>in</strong>t getColumnCount()<br />

…<br />

…<br />

public void setPage(Str<strong>in</strong>g url)<br />

public <strong>in</strong>t getRows()<br />

public void setPage(URL page)<br />

… public void setText(Str<strong>in</strong>g t)<br />

public void <strong>in</strong>sert(Str<strong>in</strong>g str, <strong>in</strong>t pos)<br />

…<br />

public void replaceRange(Str<strong>in</strong>g str,<strong>in</strong>t start,<strong>in</strong>t end)<br />

public void setColumns(<strong>in</strong>t columns)<br />

public void setFont(Font f)<br />

JTextPane<br />

…<br />

public void setRows(<strong>in</strong>t rows)<br />

><br />

public JTextPane()<br />

public JTextPane(StyledDocument doc)<br />

><br />

public Style addStyle(Str<strong>in</strong>g nm, Style<br />

parent)<br />

…<br />

public Str<strong>in</strong>g getUIClassID()<br />

public void setStyleDocument(<br />

StyledDocument doc)<br />

Abb.: Die Klassen JTextArea und JTextPane<br />

381


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

5.6.5.2 Schaltflächen<br />

Die Klasse JButton und das Interface ButtonMo<strong>de</strong>l<br />

Buttons (Schaltflächen) s<strong>in</strong>d rechteckige Knöpfe, die e<strong>in</strong>e Aktion auslösen, wenn <strong>de</strong>r<br />

Benutzer sie anklickt.<br />

Die visuelle Komponente wird durch die Klasse JButton repräsentiert, die das<br />

Mo<strong>de</strong>ll und die Ansicht speichert. Gleichzeitig dient die Klasse als Vermittler beim<br />

Zugriff auf das Mo<strong>de</strong>ll o<strong>de</strong>r bei Anfor<strong>de</strong>rungen an die Ansicht. Das jeweilige Mo<strong>de</strong>ll<br />

ist durch e<strong>in</strong>e gleichnamige Schnittstelle implementiert. So <strong>de</strong>f<strong>in</strong>iert z.B. die<br />

Schnittstelle ButtonMo<strong>de</strong>l <strong>de</strong>n Zustand <strong>de</strong>r Schaltflächen-Klassen mit <strong>de</strong>n<br />

Zugriffsmetho<strong>de</strong>n:<br />

public Str<strong>in</strong>g getActionCommand()<br />

// liefert <strong>de</strong>n Namen <strong>de</strong>s Aktionsfel<strong>de</strong>s<br />

public boolean isArmed()<br />

public boolean isEnabled()<br />

public boolean isPressed()<br />

public boolean isRollover()<br />

public boolean isSelected()<br />

Der Zugriff auf das Mo<strong>de</strong>ll <strong>de</strong>r Komponente muß mit Hilfe <strong>de</strong>r Metho<strong>de</strong><br />

public ButtonMo<strong>de</strong>l getMo<strong>de</strong>l()<br />

<strong>de</strong>r Klasse JButton angefor<strong>de</strong>rt wer<strong>de</strong>n bzw. kann mit<br />

public void setMo<strong>de</strong>l(ButtonMo<strong>de</strong>l newMo<strong>de</strong>l)<br />

<strong>de</strong>r Klasse JButton angefor<strong>de</strong>rt wer<strong>de</strong>n. In Standardfällen ist <strong>de</strong>r direkte Zugriff auf<br />

das Mo<strong>de</strong>ll nicht erfor<strong>de</strong>rlich, da die Klasse JButton über Zugriffsmetho<strong>de</strong>n auf die<br />

meisten Inhalte <strong>de</strong>s Mo<strong>de</strong>lls verfügt und diese kapselt. Alle Mo<strong>de</strong>lle mit<br />

"Schaltflächen"-Funktionalität (Schaltflächen, Optionsfel<strong>de</strong>r, Kontrollkästchen)<br />

können dasselbe Mo<strong>de</strong>ll verwen<strong>de</strong>n.<br />

382


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

JComponent<br />

AbstractButton<br />

protected ActionListener actionListener<br />

protected ChangeEvent changeEvent<br />

protected ChangeListener changeListener<br />

protected ItemListener itemListener<br />

public void addActionListener(ActionListener l)<br />

public void addChangeListener (ChangeListener l)<br />

public void addItemListener(ItemListener l)<br />

…<br />

public void doClick()<br />

…<br />

public <strong>in</strong>t getHorizontalTextPosition()<br />

public <strong>in</strong>t getHorizontalAlignment()<br />

public <strong>in</strong>t getVerticalAlignment()<br />

…<br />

public Icon getIcon()<br />

…<br />

public Insets getMarg<strong>in</strong>()<br />

public <strong>in</strong>t getMnemonic()<br />

public ButtonMo<strong>de</strong>l getMo<strong>de</strong>l()<br />

…<br />

public Icon getSelectedIcon()<br />

public Str<strong>in</strong>g getText()<br />

public ButtonUI getUI();<br />

…<br />

public void setUI(ButtonUI ui)<br />

…<br />

public void updateUI()<br />

JButton<br />

><br />

public JButton()<br />

public JButton(Str<strong>in</strong>g text)<br />

public JButton(Icon icon)<br />

public JButton(Str<strong>in</strong>g text,Icon icon)<br />

><br />

…<br />

public boolean isDefaultButton()<br />

…<br />

public void removeNotify()<br />

…<br />

Abb.: Die Klasse JButton<br />

383


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Die Klassen JCheckBox und JRadioButton<br />

In Sw<strong>in</strong>g wer<strong>de</strong>n run<strong>de</strong> und eckige Optionsfel<strong>de</strong>r nicht mehr <strong>in</strong> e<strong>in</strong>er Klasse<br />

zusammengefasst, son<strong>de</strong>rn durch die Klassen JCheckBox und JRadioButton<br />

dargestellt.<br />

JCheckBox<br />

Die Klasse JCheckBox stellet e<strong>in</strong>en Button dar, <strong>de</strong>r vom Anwen<strong>de</strong>r wahlweise ano<strong>de</strong>r<br />

ausgeschaltet wer<strong>de</strong>n kann. JCheckBox ist von <strong>de</strong>r Klasse JToggleButton<br />

abgeleitet, die als Abstraktion von Buttons, die ihren Zustand än<strong>de</strong>rn können, auch<br />

Basisklasse von JRadioButton ist.<br />

JCheckBox kann e<strong>in</strong>e textuelle Beschriftung o<strong>de</strong>r e<strong>in</strong> Icon o<strong>de</strong>r bei<strong>de</strong>s enthalten.<br />

Konstruktoren.<br />

public JCheckBox(Str<strong>in</strong>g text)<br />

public JCheckBox(Str<strong>in</strong>g text,boolean selected)<br />

// Mit <strong>de</strong>m Parameter "selected" kann bereits bei <strong>de</strong>r Instanzierung angegeben wer<strong>de</strong>n,<br />

// ob die Checkbox aktiviert o<strong>de</strong>r <strong>de</strong>aktiviert se<strong>in</strong> soll.<br />

Metho<strong>de</strong>n.<br />

public void getSelected(boolean b)<br />

public boolean isSelected()<br />

Wie e<strong>in</strong> JButton sen<strong>de</strong>t e<strong>in</strong>e JCheckBox bei je<strong>de</strong>r Bestätigung e<strong>in</strong> ActionEvent<br />

an registrierte Listener. Zu<strong>de</strong>m wird bei Zustandsän<strong>de</strong>rungen e<strong>in</strong> ItemEvent<br />

versen<strong>de</strong>t, auf das e<strong>in</strong> ItemListener reagieren kann.<br />

JRadioButton<br />

Die Klasse JRadioButton stellt e<strong>in</strong>e Button dar, <strong>de</strong>r wahlweise an- und<br />

ausgeschaltet wer<strong>de</strong>n kann. An<strong>de</strong>rs als bei <strong>de</strong>r JCheckBox ist <strong>in</strong> e<strong>in</strong>er Gruppe von<br />

Radiobuttons allerd<strong>in</strong>gs immer nur e<strong>in</strong> Button zur Zeit aktiviert, alle an<strong>de</strong>ren s<strong>in</strong>d<br />

<strong>de</strong>aktiviert.<br />

Konstruktoren.<br />

public JRadioButton(Str<strong>in</strong>g text)<br />

public JRadioButton(Str<strong>in</strong>g text, boolean selected)<br />

Metho<strong>de</strong>n.<br />

public void add(AbstractButton b)<br />

public ButtonMo<strong>de</strong>l getSelection()<br />

public Enumeration getElements()<br />

// liefert alle Buttons <strong>de</strong>r Gruppe<br />

384


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

AbstractButton<br />

ButtonGroup<br />

protected Vector Buttons<br />

><br />

public ButtonGroup()<br />

JToggleButton ><br />

public void add(AbstractButton b)<br />

public <strong>in</strong>t getButtonCount()<br />

public Enumeration getElement()<br />

public ButtonMo<strong>de</strong>l getSelection()<br />

public boolean isSelected(ButtonMo<strong>de</strong>l m)<br />

public void remove(AbstractButton b)<br />

public void setSelected(ButtonMo<strong>de</strong>l m,<br />

boolean b)<br />

JCheckBox<br />

JRadioButton<br />

> ><br />

public JCheckBox()<br />

public JRadioButton(Str<strong>in</strong>g text)<br />

public JCheckBox(Str<strong>in</strong>g text))<br />

public JRadionButton(Str<strong>in</strong>g text,<br />

public JCheckBox(Str<strong>in</strong>g text,<br />

boolean selected)<br />

boolean selected)<br />

> ><br />

…<br />

…<br />

Abb. JCheckBox und JRadioButton<br />

385


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

5.6.5.3 Listen und Comboboxen<br />

JList<br />

JList ist e<strong>in</strong> typisches Beispiel für das Mo<strong>de</strong>l-View-Controller-Pr<strong>in</strong>zip. Dabei stehen<br />

für die View (JList) mehrere Mo<strong>de</strong>lle zur Verfügung. Am wichtigsten ist das<br />

Datenmo<strong>de</strong>ll vom Typ ListMo<strong>de</strong>l, das Auswahlmo<strong>de</strong>ll (ListSelectionMo<strong>de</strong>l)<br />

und die Klassen für die Darstellung <strong>de</strong>r E<strong>in</strong>träge (ListCellRen<strong>de</strong>rer).<br />

Konstruktoren: JList verfügt über Konstruktoren, die e<strong>in</strong>e Liste aus e<strong>in</strong>em Vector<br />

o<strong>de</strong>r Array 299 erzeugen. Da die Daten nicht direkt <strong>in</strong> <strong>de</strong>r Liste gespeichert wer<strong>de</strong>n,<br />

son<strong>de</strong>rn <strong>in</strong> e<strong>in</strong>em separaten Vektor, ist je<strong>de</strong>rzeit e<strong>in</strong>Zugriff auf diese Daten möglich.<br />

Die Daten können also, nach<strong>de</strong>m sie <strong>in</strong> <strong>de</strong>r Liste angezeigt wur<strong>de</strong>n, nachträglich<br />

verän<strong>de</strong>rt wer<strong>de</strong>n. Das Problem ist dabei, dass die Komponenten von <strong>de</strong>r Än<strong>de</strong>rung<br />

nichts erfahren, da e<strong>in</strong> sehr triviales Mo<strong>de</strong>ll, ohne jegliche Vorkehrungen o<strong>de</strong>r<br />

Benachrichtigungen im Fall e<strong>in</strong>er Än<strong>de</strong>rung, durch <strong>de</strong>n Konstruktor verwen<strong>de</strong>t wur<strong>de</strong>.<br />

Darstellung und Auswahl <strong>de</strong>r <strong>in</strong> <strong>de</strong>r Liste enthaltenen Elemente: Die Anzahl <strong>de</strong>r<br />

Elemente, die gleichzeitig angezeigt wer<strong>de</strong>n sollen, kann mit <strong>de</strong>r Metho<strong>de</strong><br />

public void setVisibleRowCount(<strong>in</strong>t setVisibleRowCount)<br />

e<strong>in</strong>gestellt wer<strong>de</strong>n. Die Listenanzeige ist aber völlig von an<strong>de</strong>ren Mechanismen wie<br />

z.B. Bildlauf getrennt, d.h. e<strong>in</strong> Listenfeld führt ke<strong>in</strong>en automatischen Bildlauf durch.<br />

Zur Realisierung e<strong>in</strong>es Bildlaufbereichs muß e<strong>in</strong> JScrollPane-Objekt mit <strong>de</strong>r Liste<br />

als Parameter erzeugt wer<strong>de</strong>n und diese dann anschließend e<strong>in</strong>em Panel<br />

h<strong>in</strong>zugefügt wer<strong>de</strong>n.<br />

Entfernen, Modifizieren von E<strong>in</strong>trägen <strong>in</strong> bzw. aus <strong>de</strong>r Liste: Das Listenobjekt selbst<br />

verfügt über ke<strong>in</strong>e Metho<strong>de</strong>n, die es erlauben, e<strong>in</strong>en neuen E<strong>in</strong>trag <strong>in</strong> die List<br />

e<strong>in</strong>zufügen. Daten wer<strong>de</strong>n nicht <strong>in</strong> die Liste selbst gespeichert, son<strong>de</strong>rn wer<strong>de</strong>n über<br />

e<strong>in</strong>e Mo<strong>de</strong>ll-Schnittstelle ListMo<strong>de</strong>l verwaltet. Das ListMo<strong>de</strong>l besitzt folgen<strong>de</strong><br />

Metho<strong>de</strong>, mit <strong>de</strong>ren Hilfe auf die Daten zugegriffen wer<strong>de</strong>n kann:<br />

public void getElementAt(<strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

public <strong>in</strong>t getSize()<br />

Es besteht die Möglichkeit, die Daten<strong>in</strong>halte, statt sie <strong>in</strong> <strong>de</strong>r Liste selbst zu speichern<br />

o<strong>de</strong>r aus e<strong>in</strong>em Vektor zu lesen, mit Hilfe e<strong>in</strong>er Neu<strong>de</strong>f<strong>in</strong>ition von getElementAt()<br />

und getSize() z.B. onl<strong>in</strong>e zu berechnen o<strong>de</strong>r aus e<strong>in</strong>er Datenbank anzufor<strong>de</strong>rn.<br />

Die zur Anzeige benötigten Inhalte wer<strong>de</strong>n dann dynamisch durch die Liste vom<br />

vorhan<strong>de</strong>nen ListMo<strong>de</strong>l über <strong>de</strong>n zugehörigen In<strong>de</strong>x angefor<strong>de</strong>rt.<br />

Mit Hilfe <strong>de</strong>r Metho<strong>de</strong><br />

public void addDataListener(ListDataListener l)<br />

kann e<strong>in</strong> Listener zum Mo<strong>de</strong>ll h<strong>in</strong>zugefügt wer<strong>de</strong>n, <strong>de</strong>r dafür Sorge tragen kann, dass<br />

e<strong>in</strong>e Benachrichtigung <strong>de</strong>r Listener-Komponente stattf<strong>in</strong><strong>de</strong>t, falls e<strong>in</strong> Element<br />

modifiziert wur<strong>de</strong>. Da die Liste nichts über die Elemente, die angezeigt wer<strong>de</strong>n<br />

sollen, weiß, muß für je<strong>de</strong>s Element Breite und Höhe neu berechnet wer<strong>de</strong>n. Mit Hilfe<br />

von<br />

public void setFixedCellHeight(<strong>in</strong>t height)<br />

299 Übergabe beim Erstellen <strong>de</strong>s Elements<br />

386


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

public void setFixedCellWidth(<strong>in</strong>t width)<br />

<strong>de</strong>r Klasse JList können konstante Abmaße <strong>de</strong>r darzustellen<strong>de</strong>n Elemente <strong>de</strong>f<strong>in</strong>iert<br />

wer<strong>de</strong>n, wodurch die Berechnung bei gleichartigen Elementen, was die<br />

überwiegen<strong>de</strong> Mehrheit <strong>de</strong>r Fälle se<strong>in</strong> dürfte, entfallen kann.<br />

Dynamische Speicherung <strong>de</strong>r Objekte im Mo<strong>de</strong>ll durch E<strong>in</strong>satz von DefaultListMo<strong>de</strong>l:<br />

Das DefaulListMo<strong>de</strong>l verfügt über die Metho<strong>de</strong>n<br />

public void addElement(Object element)<br />

public boolean removeElement(Object obj)<br />

, die zum E<strong>in</strong>fügen und Entfernen von Listenelementen e<strong>in</strong>gesetzt wer<strong>de</strong>n können.<br />

Darstellung beliebiger, benutzer<strong>de</strong>f<strong>in</strong>ierter Zeichnungen und Objekte: Hierzu muß e<strong>in</strong><br />

Listenzellen-Ren<strong>de</strong>rer <strong>de</strong>f<strong>in</strong>iert wer<strong>de</strong>n. Dieser kann dafür Sorge tragen, dass<br />

entsprechen<strong>de</strong> Elemente benutzer<strong>de</strong>f<strong>in</strong>iert <strong>in</strong>nerhalb <strong>de</strong>r Liste gezeichnet wer<strong>de</strong>n.<br />

E<strong>in</strong> Listenzellen-Ren<strong>de</strong>rer kann e<strong>in</strong>e beliebige Klasse se<strong>in</strong>, die e<strong>in</strong>e Schnittstelle mit<br />

public Component getListCellRen<strong>de</strong>rerComponent(JList list,Object value,<br />

<strong>in</strong>t <strong>in</strong><strong>de</strong>x, boolean isSelected,boolean hasFocus)<br />

implementiert. Die Schnittstelle gibt e<strong>in</strong> Objekt vom Typ Component zurückgeben,<br />

das wie<strong>de</strong>rum die Metho<strong>de</strong>n pa<strong>in</strong>tComponent() und getPreferredSize()<br />

implementiert. pa<strong>in</strong>tComponent() sorgt für das Zeichnen <strong>de</strong>r Komponente,<br />

getPreferredSize() ist für das Berechnen <strong>de</strong>r Abmaße erfor<strong>de</strong>rlich, falls ke<strong>in</strong>e<br />

feste Größen <strong>de</strong>f<strong>in</strong>iert wur<strong>de</strong>n.<br />

387


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

JComponent<br />

JList<br />

...<br />

><br />

public JList()<br />

public JList(ListMo<strong>de</strong>l dataMo<strong>de</strong>l)<br />

public JList(Object[] listData)<br />

public JList(Vector listData)<br />

><br />

public void addListSelectionListener(ListSelectionListener l)<br />

public void addSelectionInterval(<strong>in</strong>t anchor,<strong>in</strong>t lead)<br />

public void clearSelection()<br />

protected ListSelectionMo<strong>de</strong>l createSelectionMo<strong>de</strong>l()<br />

…<br />

public AccessibleContext getAccessibleContext()<br />

public <strong>in</strong>t getAnchorSelectionIn<strong>de</strong>x()<br />

public Rectangle getCellBounds(<strong>in</strong>t <strong>in</strong><strong>de</strong>x0,<strong>in</strong>t <strong>in</strong><strong>de</strong>x1)<br />

public ListCellRen<strong>de</strong>rer getCellRen<strong>de</strong>rer()<br />

…<br />

public void getSelectionMo<strong>de</strong>(<strong>in</strong>t selectionMo<strong>de</strong>)<br />

public <strong>in</strong>t getSelectedIn<strong>de</strong>x()<br />

public <strong>in</strong>t [] getSelectedIndices()<br />

public Object getSelectedValue()<br />

public Object [] getSelectedValues()<br />

public boolean isSelectedIn<strong>de</strong>x(<strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

…<br />

public void ListSelectionListener[] getSelectionListeners()<br />

public <strong>in</strong>t getMaxSelectionIn<strong>de</strong>x()<br />

public <strong>in</strong>t getM<strong>in</strong>SelectionIn<strong>de</strong>x()<br />

public ListMo<strong>de</strong>l getMo<strong>de</strong>l()<br />

…<br />

public ListUI getUI()<br />

…<br />

public Str<strong>in</strong>g paramStr<strong>in</strong>g()<br />

…<br />

public void setListData(Object[] listData)<br />

public void setListData(Vector listData)<br />

public void setMo<strong>de</strong>l(ListMo<strong>de</strong>l mo<strong>de</strong>l)<br />

…<br />

public void setSelectionMo<strong>de</strong>l(ListSelectionMo<strong>de</strong>l selectionMo<strong>de</strong>l)<br />

public void setUI(ListUI ui)<br />

public void setValueIsAdjust<strong>in</strong>g(boolean b)<br />

public void setVisibleRowCount(<strong>in</strong>t visibleRowCount)<br />

public void updateUI()<br />

Abb.: Die Klasse JList<br />

388


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

><br />

ListMo<strong>de</strong>l<br />

public void addListDataListener(ListDataListener l)<br />

public void setUI(ListUI ui)<br />

public void setValueIsAdjust<strong>in</strong>g(boolean b)<br />

public void setVisibleRowCount(<strong>in</strong>t visibleRowCount)<br />

public void updateUI()<br />

AbstractListMo<strong>de</strong>l<br />

{ abstract }<br />

public void addListDataListener(ListDataListener l)<br />

…<br />

public void removeDataListener(ListDataListener l)<br />

DefaultListMo<strong>de</strong>ll<br />

><br />

public DefaultListMo<strong>de</strong>l()<br />

><br />

public void add(<strong>in</strong>t <strong>in</strong><strong>de</strong>x,Object element)<br />

public void addElement(Object obj)<br />

public void clear()<br />

public void conta<strong>in</strong>s(Object elem)<br />

public void copyInto(Object[] anArray)<br />

public Object elementAt(<strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

public Enumeration elements()<br />

…<br />

public Object get(<strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

public Object getElementAt(<strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

…<br />

public void <strong>in</strong>sertElementAt(Object obj,<strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

public boolean isEmpty()<br />

public Object lastElement()<br />

…<br />

public Object remove(<strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

public void removeAllElements()<br />

…<br />

public Object set(<strong>in</strong>t <strong>in</strong><strong>de</strong>x,Object element)<br />

public void setElementAt(Object obj,<strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

public voiud setSize(<strong>in</strong>t newSize)<br />

public <strong>in</strong>t size()<br />

public Object[] toArray()<br />

public Str<strong>in</strong>g toStr<strong>in</strong>g()<br />

public void trimToSize()<br />

389


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

><br />

ListSelectionMo<strong>de</strong>l<br />

public void addListSelectionListener(ListSelectionListener x)<br />

public void addSelectionInterval(<strong>in</strong>t <strong>in</strong><strong>de</strong>x0,<strong>in</strong>t <strong>in</strong><strong>de</strong>x1)<br />

public void clearSelection()<br />

public <strong>in</strong>t getAnchorSelectionIn<strong>de</strong>x()<br />

public <strong>in</strong>t getMaxSelectionIn<strong>de</strong>x()<br />

public <strong>in</strong>t getM<strong>in</strong>SelectionIn<strong>de</strong>x()<br />

public <strong>in</strong>t getSelectionMo<strong>de</strong>()<br />

…<br />

public boolean isSelectionEmpty()<br />

…<br />

public void setSelectionMo<strong>de</strong>()<br />

public void setValueIsAdjust<strong>in</strong>g(boolean valueIsAdjust<strong>in</strong>g)<br />

…<br />

protected EventListenerList listenerList<br />

DefaultListSelectionMo<strong>de</strong>l<br />

><br />

public DefaultListSelectionMo<strong>de</strong>l()<br />

><br />

public void addListSelectionListener(ListSelectionListener l)<br />

public void addSelectionInterval(<strong>in</strong>t <strong>in</strong><strong>de</strong>x0,<strong>in</strong>t <strong>in</strong><strong>de</strong>x1)<br />

public void clearSelection()<br />

public Object clone()<br />

…<br />

public Str<strong>in</strong>g toStr<strong>in</strong>g()<br />

><br />

ListCellRen<strong>de</strong>rer<br />

public Componet getListCellRen<strong>de</strong>rerComponent(JList list,Object value,<br />

<strong>in</strong>t <strong>in</strong><strong>de</strong>x, boolean isSelected,boolean hasFocus)<br />

DefaultListCellRen<strong>de</strong>rer<br />

><br />

public DefaultListCellRen<strong>de</strong>rer()<br />

><br />

public void firePropertyChange(Str<strong>in</strong>g propertyName,boolean oldValue,boolean newValue)<br />

…<br />

public void validate()<br />

390


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

JComboBox<br />

Die JComboBox teilt dasselbe Darstellungsmo<strong>de</strong>ll (ListCellRen<strong>de</strong>rer) mit <strong>de</strong>r<br />

JList. Das Datenmo<strong>de</strong>ll ComboBoxMo<strong>de</strong>l ist von ListMo<strong>de</strong>l abgeleitet und<br />

erweitert es um Metho<strong>de</strong>n zum H<strong>in</strong>zufügen und Löschen von E<strong>in</strong>trägen. In e<strong>in</strong>er<br />

JComboBox kann immer nur e<strong>in</strong> E<strong>in</strong>trag ausgewählt se<strong>in</strong>.<br />

5.6.5.4 Quasi analoge Komponenten<br />

In Sw<strong>in</strong>g gibt es drei Kontrollelemente zur E<strong>in</strong>gabe und Visualisierung von<br />

Zahlenwerten aus e<strong>in</strong>em festgelegten Wertbereich: JScrollBar (e<strong>in</strong> e<strong>in</strong>facher<br />

Scrollbalken), JSli<strong>de</strong>r (e<strong>in</strong> komplexer Schieberegler mit Skala), JProgressBar<br />

(e<strong>in</strong>e Fortschrittsanzeige). Alle drei unterstützen das Boun<strong>de</strong>dRangeMo<strong>de</strong>l-<br />

Interface, das <strong>de</strong>n Zugriff auf die Attribute value, m<strong>in</strong>imum, maximum, extent bzw.<br />

valueAdjust<strong>in</strong>g (e<strong>in</strong> Flag, das anzeigt, ob <strong>de</strong>r Wert gera<strong>de</strong> e<strong>in</strong>gestellt wird) und<br />

die Implementierung von ChangeEvents sicherstellt.<br />

><br />

Boun<strong>de</strong>dRangeMo<strong>de</strong>l<br />

public void addChangeListener(ChangeListener x)<br />

public <strong>in</strong>t getExtent()<br />

public <strong>in</strong>t getMaximum()<br />

public <strong>in</strong>t getM<strong>in</strong>imum()<br />

public <strong>in</strong>t getValue()<br />

public boolean getValueIsAdjust<strong>in</strong>g()<br />

public void removeChangeListener(ChangeListener x)<br />

public void setExtent(<strong>in</strong>t newExtent)<br />

public void setMaximum(<strong>in</strong>t newMaximum)<br />

public void setRangeProperties(<strong>in</strong>t value, <strong>in</strong>t extent, <strong>in</strong>t m<strong>in</strong>,<br />

<strong>in</strong>t max, boolean adjust<strong>in</strong>g)<br />

public void setValue(<strong>in</strong>t newValue)<br />

public void setValueIsAdjust<strong>in</strong>g(boolean b)<br />

DefaultBoun<strong>de</strong>dRangeMo<strong>de</strong>l<br />

Abb.: Interface Boun<strong>de</strong>dRangeMo<strong>de</strong>l<br />

JScrollBar<br />

Attrribute. Die Klasse JScrollBar besitzt zusätzlich die Attribute unitIncrement für<br />

die Schrittweite, wenn auf die Pfeiltatsen gedrückt wird (Standard ist 1) und<br />

blockIncrement für e<strong>in</strong>en Mausklickj auf <strong>de</strong>n weißen H<strong>in</strong>tergrung <strong>de</strong>s<br />

Scrollbalkens (Standard ist 10).<br />

Konstruktor. Der wichtigste Konstruktor ist<br />

public JScrollBar(<strong>in</strong>t orientation, // Ausrichtung <strong>de</strong>s Schiebereglers<br />

<strong>in</strong>t value, // Anfangswert <strong>de</strong>s Schiebers<br />

<strong>in</strong>t extent, // Aus<strong>de</strong>hnung <strong>de</strong>s Schiebereglers<br />

<strong>in</strong>t m<strong>in</strong>, <strong>in</strong>t max)<br />

391


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Mit orientation wird die Ausrichtung (HORIZONTAL o<strong>de</strong>r VERTICAL) <strong>de</strong>s Schiebereglers<br />

festgelegt, m<strong>in</strong> gibt <strong>de</strong>n kle<strong>in</strong>sten, max <strong>de</strong>n größten möglichen Wert an.<br />

Mit extent wird die Aus<strong>de</strong>hnung <strong>de</strong>s Schiebers festgelegt. Sie muß m<strong>in</strong><strong>de</strong>stens e<strong>in</strong>s, kann auch<br />

größer se<strong>in</strong>.<br />

value ist <strong>de</strong>r Anfangswert <strong>de</strong>s Schiebers. Er muß zwischen m<strong>in</strong> und max – extent liegen.<br />

Metho<strong>de</strong>n.<br />

public <strong>in</strong>t getMaximum()<br />

public void setMaximum(<strong>in</strong>t maximum)<br />

public <strong>in</strong>t getVisibleAmount()<br />

// liefert die Aus<strong>de</strong>hnung <strong>de</strong>s Schiebers<br />

public void setVisibleAmount(<strong>in</strong>t extent)<br />

public <strong>in</strong>t getValue()<br />

public void setValue(<strong>in</strong>t value)<br />

public <strong>in</strong>t getUnitIncrement()<br />

// gibt an, um welchen Betrag <strong>de</strong>r Wert <strong>de</strong>s Schiebereglers sich verän<strong>de</strong>rt, wenn <strong>de</strong>r Anwen<strong>de</strong>r<br />

// e<strong>in</strong>en Pfeilbutton betätigt<br />

public void setUnitIncrement(<strong>in</strong>t unitIncrement)<br />

public <strong>in</strong>t getBlockIncremnent()<br />

// ermittelt <strong>de</strong>n Betrag <strong>de</strong>r Än<strong>de</strong>rung, wenn zwischen Schieber und Pfeiltatsen geklickt wird.<br />

public void setBlockIncrement(<strong>in</strong>t blockIncrement)<br />

Ereignisbehandlung. Aus Kompatibilitätsgrün<strong>de</strong>n zur AWT-Scrollbar implementiert<br />

die Klasse das Interface Adjustable und unterstützt <strong>de</strong>n AdjustmentListener.<br />

Wird e<strong>in</strong> Wert e<strong>in</strong>er JScrollBar verän<strong>de</strong>rt, sen<strong>de</strong>t sie e<strong>in</strong> AdjustmentEvent an<br />

registrierte Listener. Diese müssen das Interface AdjustmentListener<br />

implementieren und wer<strong>de</strong>n durch addjustmentListener() registiert. Mit<br />

getValueAdjust<strong>in</strong>g() kann festgestellt wer<strong>de</strong>n, auf welche Weise <strong>de</strong>r Wert<br />

verän<strong>de</strong>rt wird. Rückgabe von true erfolgt dann, wenn <strong>de</strong>r Anwen<strong>de</strong>r <strong>de</strong>n Schieber<br />

betätigt. false wird zurückgegeben, wenn die Än<strong>de</strong>rung durch e<strong>in</strong>en Mausklick auf<br />

e<strong>in</strong>en Button o<strong>de</strong>r auf die Fläche zwischen Button und Schieber ausgelöst wird.<br />

JSli<strong>de</strong>r<br />

Wichtiger (konzeptioneller) Unterschied zum JScrollbar:<br />

1. E<strong>in</strong> JSli<strong>de</strong>r kann e<strong>in</strong>e Anzeigeskala mit grober und fiener Beschriftung haben.<br />

2. E<strong>in</strong> JSli<strong>de</strong>r kennt ke<strong>in</strong>e unterschiedlichen Schiebergrößen. Ihre Aus<strong>de</strong>hung ist immer 1.<br />

Die Klasse JSli<strong>de</strong>r ist e<strong>in</strong> Schieberegler mit optionaler Skala. Die Skalenstriche<br />

nennen sich Ticks und existieren <strong>in</strong> zwei Längen: majorTicks, m<strong>in</strong>orTicks.<br />

Wichtigster Konstruktor.<br />

public JSli<strong>de</strong>r(<strong>in</strong>t orientation, <strong>in</strong>t m<strong>in</strong>, <strong>in</strong>t max, <strong>in</strong>t value)<br />

Metho<strong>de</strong>n.<br />

public <strong>in</strong>t getMaximum()<br />

public void setMaximum(<strong>in</strong>t maximum)<br />

public <strong>in</strong>t getM<strong>in</strong>imum()<br />

public void setM<strong>in</strong>imum(<strong>in</strong>t m<strong>in</strong>imum)<br />

392


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

public <strong>in</strong>t getValue()<br />

public void setValue(<strong>in</strong>t value)<br />

public <strong>in</strong>t getMajorTickSpac<strong>in</strong>g()<br />

// Vorgabe <strong>de</strong>s Abstands <strong>de</strong>r großen Markierung<br />

public void setMajorTockSpac<strong>in</strong>g(<strong>in</strong>t n)<br />

public <strong>in</strong>t getM<strong>in</strong>orTickSpac<strong>in</strong>g()<br />

public void setM<strong>in</strong>orTickSpac<strong>in</strong>g(<strong>in</strong>t n)<br />

// Vorgabe <strong>de</strong>s Abstands <strong>de</strong>r kle<strong>in</strong>en Markierung<br />

public void setPa<strong>in</strong>tTicks(boolen b)<br />

// Anzeige <strong>de</strong>r Anzeigeskala durch Übergabe von true<br />

public void setPa<strong>in</strong>tLabels(boolean b)<br />

// Anzeige <strong>de</strong>r Beschriftung durch Übergabe von true<br />

Ereignisbehandlung. E<strong>in</strong> JSli<strong>de</strong>r sen<strong>de</strong>t e<strong>in</strong> ChangeEvent, wenn se<strong>in</strong> Wert<br />

verän<strong>de</strong>rt wird. Zur Reaktion auf dieses Ereignis muß das Interface<br />

ChangeListener implementiert se<strong>in</strong>, und das zu implementieren<strong>de</strong> Objekt durch<br />

<strong>de</strong>n Aufruf addChangeListener() registriert se<strong>in</strong>. Mit getValueIsAdjust<strong>in</strong>g()<br />

kann festgestellt wer<strong>de</strong>n, ob die Än<strong>de</strong>rung Bestandteil e<strong>in</strong>er Kette von Än<strong>de</strong>rungen<br />

sit o<strong>de</strong>r ob sie e<strong>in</strong>zeln aufgetreten ist.<br />

Bsp. 300 :<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

import javax.sw<strong>in</strong>g.*;<br />

public class JSli<strong>de</strong>rs extends JFrame<br />

{<br />

public JSli<strong>de</strong>rs()<br />

{<br />

super("Us<strong>in</strong>g JSli<strong>de</strong>r");<br />

// W<strong>in</strong>dowUtilities.setNativeLookAndFeel();<br />

addW<strong>in</strong>dowListener(new W<strong>in</strong>dowAdapter()<br />

{<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{ System.exit(0); }<br />

});<br />

Conta<strong>in</strong>er content = getContentPane();<br />

content.setBackground(Color.white);<br />

JSli<strong>de</strong>r sli<strong>de</strong>r1 = new JSli<strong>de</strong>r();<br />

sli<strong>de</strong>r1.setBor<strong>de</strong>r(Bor<strong>de</strong>rFactory.createTitledBor<strong>de</strong>r<br />

("JSli<strong>de</strong>r without Tick Marks"));<br />

content.add(sli<strong>de</strong>r1, Bor<strong>de</strong>rLayout.NORTH);<br />

JSli<strong>de</strong>r sli<strong>de</strong>r2 = new JSli<strong>de</strong>r();<br />

sli<strong>de</strong>r2.setBor<strong>de</strong>r(Bor<strong>de</strong>rFactory.createTitledBor<strong>de</strong>r<br />

("JSli<strong>de</strong>r with Tick Marks"));<br />

sli<strong>de</strong>r2.setMajorTickSpac<strong>in</strong>g(20);<br />

sli<strong>de</strong>r2.setM<strong>in</strong>orTickSpac<strong>in</strong>g(5);<br />

sli<strong>de</strong>r2.setPa<strong>in</strong>tTicks(true);<br />

content.add(sli<strong>de</strong>r2, Bor<strong>de</strong>rLayout.CENTER);<br />

JSli<strong>de</strong>r sli<strong>de</strong>r3 = new JSli<strong>de</strong>r();<br />

sli<strong>de</strong>r3.setBor<strong>de</strong>r(Bor<strong>de</strong>rFactory.createTitledBor<strong>de</strong>r<br />

("JSli<strong>de</strong>r with Tick Marks & Labels"));<br />

sli<strong>de</strong>r3.setMajorTickSpac<strong>in</strong>g(20);<br />

sli<strong>de</strong>r3.setM<strong>in</strong>orTickSpac<strong>in</strong>g(5);<br />

sli<strong>de</strong>r3.setPa<strong>in</strong>tTicks(true);<br />

sli<strong>de</strong>r3.setPa<strong>in</strong>tLabels(true);<br />

content.add(sli<strong>de</strong>r3, Bor<strong>de</strong>rLayout.SOUTH);<br />

pack();<br />

300 vgl. pr56370<br />

393


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

setVisible(true);<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

new JSli<strong>de</strong>rs();<br />

}<br />

}<br />

JProgressBar<br />

Die Klasse JProgressBar zeigt e<strong>in</strong>en im Programm berechneten Fortschritt e<strong>in</strong>es<br />

Vorgangs an. Im Fortschritts-Balken kann e<strong>in</strong> Text angezeigt wer<strong>de</strong>n<br />

Konstruktoren.<br />

public JProgressBar(<strong>in</strong>t orient)<br />

public JProgressBar(<strong>in</strong>t m<strong>in</strong>, <strong>in</strong>t max)<br />

public JProgressBar(<strong>in</strong>t orient,<strong>in</strong>t m<strong>in</strong>,<strong>in</strong>t max)<br />

Metho<strong>de</strong>n.<br />

public void setStr<strong>in</strong>gPa<strong>in</strong>ted(boolean b)<br />

// Standardmäßig wird die Fortschrittsanzeige ohne Beschriftung dargestellt. Durch Aufruf von<br />

// setStr<strong>in</strong>gPa<strong>in</strong>ted() mit Übergabe true kann e<strong>in</strong> prozentualer Fortschrittswert angezeigt<br />

// wer<strong>de</strong>n.<br />

public void setValue(<strong>in</strong>t n)<br />

// Verän<strong>de</strong>rung <strong>de</strong>r grafischen darstellung <strong>de</strong>s Fortschritts<br />

public <strong>in</strong>t getValue()<br />

// Abfrage <strong>de</strong>s Werts <strong>de</strong>s grafischen Fortschritts<br />

Bsp. 301 :<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

import javax.sw<strong>in</strong>g.*;<br />

import javax.sw<strong>in</strong>g.event.*;<br />

public class JProgressBarDemo extends JFrame<br />

{<br />

protected <strong>in</strong>t m_m<strong>in</strong> = 0;<br />

protected <strong>in</strong>t m_max = 100;<br />

protected <strong>in</strong>t m_counter = 0;<br />

protected JProgressBar jpb;<br />

public JProgressBarDemo()<br />

{<br />

301 pr56370<br />

394


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

super("JProgressBar Demo");<br />

setSize(300,50);<br />

UIManager.put("ProgressBar.selectionBackground", Color.black);<br />

UIManager.put("ProgressBar.selectionForeground", Color.white);<br />

UIManager.put("ProgressBar.foreground", new Color(8,32,128));<br />

jpb = new JProgressBar();<br />

jpb.setM<strong>in</strong>imum(m_m<strong>in</strong>);<br />

jpb.setMaximum(m_max);<br />

jpb.setStr<strong>in</strong>gPa<strong>in</strong>ted(true);<br />

JButton start = new JButton("Start");<br />

start.addActionListener(new ActionListener() {<br />

public void actionPerformed(ActionEvent e) {<br />

Thread runner = new Thread() {<br />

public void run() {<br />

m_counter = m_m<strong>in</strong>;<br />

while (m_counter


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

JComponente<br />

JScrollBar<br />

protected <strong>in</strong>t blockIncrement<br />

protected BoundRangeMo<strong>de</strong>l mo<strong>de</strong>l<br />

protected <strong>in</strong>t orientation<br />

protected <strong>in</strong>t unitIncrement<br />

JProgressBar<br />

protected ChangeEvent changeEvent<br />

protected ChangeListener changeListener<br />

protected BoundRangeMo<strong>de</strong>l mo<strong>de</strong>l<br />

protected <strong>in</strong>t orientation<br />

protected boolean pa<strong>in</strong>tBor<strong>de</strong>r<br />

protected boolean pa<strong>in</strong>tStr<strong>in</strong>g<br />

protected Str<strong>in</strong>g progressStr<strong>in</strong>g<br />

> ><br />

public JScrollBar()<br />

public JProgressBar()<br />

public JScrollBar(<strong>in</strong>t orientation)<br />

…<br />

public JScrollBar(<strong>in</strong>t orientation, ><br />

<strong>in</strong>t value, <strong>in</strong>t extent,<br />

public void addChangeListener(<br />

<strong>in</strong>t m<strong>in</strong>, <strong>in</strong>t max) ChangeListener l)<br />

> protected ChangeListener<br />

public void AddAdjustmentListener(<br />

createChangeListener<br />

AdjustmentListener l)<br />

…<br />

….<br />

public <strong>in</strong>t getBlockIncrement()<br />

public <strong>in</strong>t getMaximum()<br />

public Dimension getMaximumSize()<br />

…<br />

JSli<strong>de</strong>r<br />

public ChangeEvent changeEvent<br />

public ChangeListener changeListener<br />

><br />

public JSli<strong>de</strong>r()<br />

><br />

…<br />

Abb.: Die Klassen JProgressBar, JScrollBar und JSli<strong>de</strong>r<br />

396


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

5.6.5.5 Tabellen<br />

Das Mo<strong>de</strong>ll für JTable muß das Interface TableMo<strong>de</strong>l implementieren. Die Klasse<br />

AbstractTableMo<strong>de</strong>l kann als Ableitungsbasis für die konkrete Datenklasse<br />

verwen<strong>de</strong>t wer<strong>de</strong>n. Die Klasse DefaultTableMo<strong>de</strong>l enthält e<strong>in</strong>e Implementierung<br />

aus <strong>in</strong>e<strong>in</strong>an<strong>de</strong>r verschachtelten Vector-Objekten.<br />

Konstruktoren.<br />

public JTable(Object[][] rowData,Object ColumnNanes)<br />

Im zweidimensionalen Array enthält die erste Dimension die Zeilen, die zweite die Spalten. Zur<br />

Darstellung <strong>in</strong> <strong>de</strong>r Tabelle wer<strong>de</strong>n die Arrayelemente mit toStr<strong>in</strong>g() <strong>in</strong> Str<strong>in</strong>gs umgewan<strong>de</strong>lt. Das 2.<br />

Array enthält e<strong>in</strong> Array mit Str<strong>in</strong>gs, die als Spaltenköpfe angezeigt wer<strong>de</strong>n.<br />

public JTable(Vector rowData,Vector columnNames)<br />

Daten und Spalten wer<strong>de</strong>n <strong>in</strong> e<strong>in</strong>em Vector übergeben. Der Datenvektor rowData muß für je<strong>de</strong> Zeile<br />

e<strong>in</strong>en Subvektor mit <strong>de</strong>n Datenelementen <strong>de</strong>r Zeile enthalten.<br />

public JTable(TableMo<strong>de</strong>l dm, TableColumnMo<strong>de</strong>l cm, ListSelectionMo<strong>de</strong>l sm)<br />

Das TableMo<strong>de</strong>l stellt Daten zur Verfügung.<br />

Das TableColumnMo<strong>de</strong>l <strong>de</strong>f<strong>in</strong>iert die Spalten.<br />

Das ListSelectionMo<strong>de</strong>l ist für die Auswahl von Tabellenelelementen zuständig.<br />

Bsp.: Erzeugen e<strong>in</strong>er e<strong>in</strong>fachen Tabelle<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

import javax.sw<strong>in</strong>g.*;<br />

public class SimpleTable extends JFrame<br />

{<br />

public static f<strong>in</strong>al Str<strong>in</strong>g[][] DATEN =<br />

{<br />

{"Uebungsblatt 1", "B<strong>in</strong>aere Baumknoten <strong>in</strong> <strong>Java</strong>"},<br />

{"Uebungsblatt 1a", "Baumansichten <strong>de</strong>r <strong>Java</strong> Foundation Classes"},<br />

{"Uebungsblatt 2", "Verarbeitung b<strong>in</strong>aerer Suchbaumknoten"},<br />

{"Uebungsblatt 3", "Die generische Klasse B<strong>in</strong>aererSuchbaum <strong>in</strong> <strong>Java</strong>"},<br />

{"Uebungsblatt 4", "Die generische Klasse AvlBaum <strong>in</strong> <strong>Java</strong>"},<br />

{"Uebungsblatt 5", "Perfekt ausgeglichene Baeume"},<br />

{"Uebungsblatt 6", "Rot-Schwarz-Baeume"},<br />

{"Uebungsblatt 7", "AA-Baeume"},<br />

{"Uebungsblatt 8", "Splay-Baeume"},<br />

{"Uebungsblatt 9", "Bayer-Baeume"},<br />

{"Uebungsplatt 10", "Auf Platte / Diskette gespeicherte Bayer-Bäume"},<br />

{"Uebungsblatt 11", "Hash-Tabellen"},<br />

{"Uebungsblatt 12", "Heap-Algorithmen"},<br />

{"Uebungsblatt 13", "Vorrangwarteschlangen"},<br />

{"Uebungsblatt 14", "B<strong>in</strong>omial Queue, Fibonacci Heap"},<br />

{"Uebungsblatt 15", "Backtrack<strong>in</strong>g-Algorithmen"},<br />

{"Uebungsblatt 16",<br />

"Ansichten <strong>de</strong>r <strong>Java</strong> Foundation Classes JList und JTable"},<br />

{"Uebungsblatt 17", "Skip Lists"}<br />

};<br />

public static f<strong>in</strong>al Str<strong>in</strong>g[] COLHEADS =<br />

{<br />

"Ausgabe", "Titelthema"<br />

};<br />

// Konstruktor<br />

public SimpleTable()<br />

{<br />

super("Uebersicht zu <strong>de</strong>n Uebungen <strong>in</strong> AD");<br />

JTable table = new JTable(DATEN, COLHEADS);<br />

Conta<strong>in</strong>er cp = getContentPane();<br />

cp.add(new JLabel("Uebungen zu AD im WS 02"), "North");<br />

cp.add(new JScrollPane(table), "Center");<br />

}<br />

397


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

SimpleTable frame = new SimpleTable();<br />

frame.addW<strong>in</strong>dowListener(new W<strong>in</strong>dowAdapter()<br />

{<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

{<br />

System.exit(0);<br />

}<br />

});<br />

frame.setLocation(100, 100);<br />

frame.setSize(300, 200);<br />

frame.setVisible(true);<br />

}<br />

}<br />

Das Programm erzeugt die folgen<strong>de</strong> Tabelle:<br />

Abb.: Tabelle, die von SimpleTable erzeugt wird<br />

Konfiguration <strong>de</strong>r Tabelle.<br />

Mit public void setRowHeight(<strong>in</strong>t rowHeight) wird die Gesamthöhe e<strong>in</strong>er Zeile festgelegt.<br />

Alle Zeilen s<strong>in</strong>d dabei gleich hoch.<br />

Mit public void setRowMarg<strong>in</strong>(<strong>in</strong>t rowMartg<strong>in</strong>) wird <strong>de</strong>r am oberen Rand und unteren<br />

Rand je<strong>de</strong>r Zeile frei bleiben<strong>de</strong> Platz bestimmt. Der für <strong>de</strong>n Inhalt <strong>de</strong>r Zelle verfügbare Platz ergibt<br />

sich aus <strong>de</strong>r Zellenhöhe m<strong>in</strong>us oberem und unterem Rand. Durch Aufruf von public void<br />

setIntercellSpac<strong>in</strong>g(Dimension newSpac<strong>in</strong>g) kann (zusammen mit <strong>de</strong>m vertikalen) auch<br />

<strong>de</strong>r horizontale Rand <strong>de</strong>r Zellenelemente festgelegt wer<strong>de</strong>n.<br />

Standardmäßig wer<strong>de</strong>n die Zellen e<strong>in</strong>er JTable mit senkrechten und waagrechten Begrenzungsl<strong>in</strong>ien<br />

vone<strong>in</strong>an<strong>de</strong>r getrennt. Mit public void setShowGrid(boolean b) können bei<strong>de</strong> L<strong>in</strong>ienarten<br />

zugleich an- o<strong>de</strong>r ausgeschaltet wer<strong>de</strong>n. Sollen die horizontalen o<strong>de</strong>r vertikalen L<strong>in</strong>ien separat<br />

aktiviert o<strong>de</strong>r <strong>de</strong>aktiviert wer<strong>de</strong>n, können<br />

public void setShowHorizontalL<strong>in</strong>es(boolean b)<br />

public void setShowVerticalL<strong>in</strong>es(boolean b)<br />

verwen<strong>de</strong>t wer<strong>de</strong>n. Das Verän<strong>de</strong>rn <strong>de</strong>r Zellen ist <strong>in</strong> begrenzter Weise mit folgen<strong>de</strong>n Metho<strong>de</strong>n<br />

möglich:<br />

public void setGridColor(Color newColor)<br />

// verän<strong>de</strong>rt die Farbe, mit <strong>de</strong>r Gitterl<strong>in</strong>ien angezeigt wer<strong>de</strong>n<br />

public void setSelectionForeground(Color selectionForeground)<br />

public void setSelectionBackground(Color selectionBackground)<br />

Das Verhalten <strong>de</strong>r Tabelle, nach<strong>de</strong>m die Breite e<strong>in</strong>er e<strong>in</strong>zelnen Spalte verän<strong>de</strong>rt wur<strong>de</strong>, bestimmt<br />

public void setAutoResize(<strong>in</strong>t mo<strong>de</strong>)<br />

398


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Der dadurch frei wer<strong>de</strong>n<strong>de</strong> o<strong>de</strong>r zusätzlich benötigte Platz kann auf unterschiedliche Weise <strong>de</strong>n<br />

übrigen Spalten zugeordnet wer<strong>de</strong>n. Der Parameter mo<strong>de</strong> kann folgen<strong>de</strong> Werte annehmen:<br />

Modus<br />

AUTO_RESIZE_OFF<br />

Be<strong>de</strong>utung<br />

ke<strong>in</strong>e automatische Größenanpassung <strong>de</strong>r Spalten.<br />

Wur<strong>de</strong> die Tabelle <strong>in</strong> JScrollPane verpackt, bekommt<br />

sie nötigenfalls e<strong>in</strong>en horizontale Schieberegler.<br />

AUTO_RESIZE_LAST_COLUMN Die letzte Spalte wird zum Größenausgleich<br />

verwen<strong>de</strong>t. Dadurch reduziert sich <strong>de</strong>r Platz für die<br />

letzte Spalte, wenn e<strong>in</strong>e an<strong>de</strong>re Spalte vergrößert<br />

wird, und er erhöht sich, wenn sie verkle<strong>in</strong>ert wird.<br />

AUTO_RESIZE_NEXT_COLUMN<br />

AUTO_RESIZE_SUBSEQUENT_COLUMNS<br />

AUTO_RESIZE_ALL_COLUMNS<br />

Selektieren von Elementen<br />

Die recht neben <strong>de</strong>r modifizierten Spalte liegen<strong>de</strong><br />

Spalte wird zum Größenausgleich verwen<strong>de</strong>t.<br />

Die Größenän<strong>de</strong>rung wird gleichmäßig auf alle<br />

nachfolgen<strong>de</strong>n Spalten verteilt.<br />

Die Größenän<strong>de</strong>rung wird auf alle Spalten <strong>de</strong>r Tabelle<br />

verteilt.<br />

Selektionsmodi: Die folgen<strong>de</strong>n Metho<strong>de</strong>n <strong>de</strong>r Klasse JTable regeln die Selektion:<br />

public void setRowSelectionAllowed(boolean flag)<br />

Zeilenweise Selektion nach Aufruf dieser Metho<strong>de</strong> mit true als Argument.<br />

public void setColumnSelectionAllowed(boolean flag)<br />

Spaltenweise Selektion nach Aufruf dieser Metho<strong>de</strong> mit true als Argument.<br />

Durch Übergabe von false können Selektionsmarken ausgeschaltet wer<strong>de</strong>n und nur noch e<strong>in</strong>zelne<br />

Zellen selektiert wer<strong>de</strong>n. Standardmäßig kann zeilen-, aber nicht spaltenweise selektiert wer<strong>de</strong>n.<br />

public void setSelectionMo<strong>de</strong>(<strong>in</strong>t selectionMo<strong>de</strong>)<br />

legt fest, ob e<strong>in</strong> e<strong>in</strong>zelnes Element, e<strong>in</strong> zusammenhängen<strong>de</strong>r Bereich o<strong>de</strong>r mehrere Bereiche<br />

selektiert wer<strong>de</strong>n können. Hier ist e<strong>in</strong>e <strong>de</strong>r Konstanten SINGLE_SELECTION,<br />

SINGLE_INTERVAL_SELECTION, o<strong>de</strong>r MULTIPLE_INTERNAL_SELECTION <strong>de</strong>r Klasse<br />

ListSelectionMo<strong>de</strong>l zu übergeben.<br />

public void setSelectionEnabled(boolean flag)<br />

Der Aufruf dieser Metho<strong>de</strong> mit true als Argument bewirkt: Zeile und Spalten können gleichzeitig<br />

markiert und so zusammenhängen<strong>de</strong> rechteckige Bereiche von Zellen (e<strong>in</strong>schl. e<strong>in</strong>er e<strong>in</strong>zelnen)<br />

selektiert wer<strong>de</strong>n.<br />

Abfragen <strong>de</strong>r Selektion. Die folgen<strong>de</strong>n Metho<strong>de</strong>n <strong>de</strong>r Klasse JTable bestimmen, welche Elemente<br />

selektiert wur<strong>de</strong>n:<br />

public <strong>in</strong>t getSelectionRow()<br />

public <strong>in</strong>t getSelectionColumn()<br />

Die bei<strong>de</strong>n Metho<strong>de</strong>n liefern die selektierte Zeile bzw. Spalte, wenn <strong>de</strong>r Selektionsmodus<br />

SINGLE_SELECTION ist. Die 1. Zeile und Spalte haben jeweils <strong>de</strong>n In<strong>de</strong>x 0. Erlaubt <strong>de</strong>r aktuelle<br />

Selektionsmodus das Selektieren ganzer Zeilen und Spalten, dann impliziert das Ergebnis: Alle<br />

Elemente dieser Zeile bzw. Spalte s<strong>in</strong>d selektiert. Falls ke<strong>in</strong>e Elemente selektiert s<strong>in</strong>d, wird –1<br />

zurückgegeben.<br />

public <strong>in</strong>t [] getSelectedRows()<br />

public <strong>in</strong>t [] getSelectedCloumns()<br />

Falls e<strong>in</strong>er <strong>de</strong>r Mehrfachselektionsmodi aktiviert ist, können über diese bei<strong>de</strong>n Metho<strong>de</strong>n Arrays mit<br />

allen selektierten Zeilen und Spalten beschafft wer<strong>de</strong>n. Falls ke<strong>in</strong>e Elemente selektiert s<strong>in</strong>d, wird e<strong>in</strong><br />

leeres Array zurückgegeben.<br />

Verän<strong>de</strong>rn <strong>de</strong>r Selektion. JTable stellt Metho<strong>de</strong>n bereit mit <strong>de</strong>nen die Selektion programmgesteuert<br />

verän<strong>de</strong>rt wer<strong>de</strong>n kann:<br />

public void selectAll()<br />

public void clearSelection()<br />

public void setRowSelectionIntervall(<strong>in</strong>t <strong>in</strong><strong>de</strong>x0, <strong>in</strong>t <strong>in</strong><strong>de</strong>x1)<br />

399


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Markieren e<strong>in</strong>es zusammenhängen<strong>de</strong>n Bereichs von Zeilen<br />

public void addRowSelectionIntervall(<strong>in</strong>t <strong>in</strong><strong>de</strong>x0, <strong>in</strong>t <strong>in</strong><strong>de</strong>x1)<br />

H<strong>in</strong>zufügen e<strong>in</strong>es zusammenhängen<strong>de</strong>n Bereichs zur aktuellen Selektion<br />

public void removeRowSelectionIntervall(<strong>in</strong>t <strong>in</strong><strong>de</strong>x0, <strong>in</strong>t <strong>in</strong><strong>de</strong>x1)<br />

Entferenen e<strong>in</strong>es zusammenhängen<strong>de</strong>n Bereichs aus <strong>de</strong>r aktuellen Selektion<br />

public void setColumnSelectionIntervall(<strong>in</strong>t <strong>in</strong><strong>de</strong>x0, <strong>in</strong>t <strong>in</strong><strong>de</strong>x1)<br />

public void addColumnSelectionIntervall(<strong>in</strong>t <strong>in</strong><strong>de</strong>x0, <strong>in</strong>t <strong>in</strong><strong>de</strong>x1)<br />

public void removeColumnSelectionIntervall(<strong>in</strong>t <strong>in</strong><strong>de</strong>x0, <strong>in</strong>t <strong>in</strong><strong>de</strong>x1)<br />

Zugriff auf <strong>de</strong>n Inhalt <strong>de</strong>r Tabelle<br />

Daten <strong>in</strong> <strong>de</strong>r Tabelle. Unabhängig von <strong>de</strong>r aktuellen Selektion kann auf <strong>de</strong>n Inhalt <strong>de</strong>r Tabelle<br />

zugegriffen wer<strong>de</strong>n.<br />

pulic <strong>in</strong>t getRowCount()<br />

public <strong>in</strong>t getColumnCount()<br />

liefern die aktuelle Zeilen- bzw. Spaltenanzahl <strong>de</strong>r Tabelle.<br />

Editieren von Tabellenelementen. Nach e<strong>in</strong>em Doppelklick auf e<strong>in</strong>e Zelle kann <strong>de</strong>r Anwen<strong>de</strong>r die <strong>in</strong><br />

diesem Element enthaltenen Daten verän<strong>de</strong>rn. JTable besitzt eigene Metho<strong>de</strong>n, mit <strong>de</strong>nen abgefragt<br />

wer<strong>de</strong>n kann, ob und <strong>in</strong> welcher Zelle die Tabelle gera<strong>de</strong> editiert wird.<br />

Das Tabellenmo<strong>de</strong>ll. E<strong>in</strong> eigenes Tabellenmo<strong>de</strong>ll muß das Interface TableMo<strong>de</strong>l<br />

implementieren und bei <strong>de</strong>r Instanzierung an <strong>de</strong>n Konstruktor <strong>de</strong>r JTable<br />

übergeben. Wahlweise kann nach Instanzierung auf das Mo<strong>de</strong>ll mit folgen<strong>de</strong>n<br />

Metho<strong>de</strong>n <strong>de</strong>r Klasse JTable zugegriffen wer<strong>de</strong>n:<br />

public void setMo<strong>de</strong>l(TableMo<strong>de</strong>l newMo<strong>de</strong>l)<br />

public TableMo<strong>de</strong>l getMo<strong>de</strong>l()<br />

Das Interface TableMo<strong>de</strong>l <strong>de</strong>f<strong>in</strong>iert folgen<strong>de</strong> Metho<strong>de</strong>n:<br />

public void addTableMo<strong>de</strong>lListener(TableMo<strong>de</strong>lListener l)<br />

public Class getColumnClass(<strong>in</strong>t columnIn<strong>de</strong>x)<br />

public <strong>in</strong>t getColumnCount()<br />

public <strong>in</strong>t getRowCount()<br />

public Object getValueAt(<strong>in</strong>t rowIn<strong>de</strong>x,<strong>in</strong>t columnIn<strong>de</strong>x)<br />

public boolean isCellEditable(<strong>in</strong>t rowIn<strong>de</strong>x,<strong>in</strong>t columnIn<strong>de</strong>x)<br />

public void removeTableMo<strong>de</strong>lLister(TableMo<strong>de</strong>lListener l)<br />

public void setValueAt(Object aValue,<strong>in</strong>t rowIn<strong>de</strong>x,<strong>in</strong>t columnIn<strong>de</strong>x)<br />

Das Spaltenmo<strong>de</strong>ll. Das Spaltenmo<strong>de</strong>ll e<strong>in</strong>er JTable muß das Interface<br />

TableColumnMo<strong>de</strong>l aus <strong>de</strong>m Paket javax.sw<strong>in</strong>g.table implementieren. Es wird bei<br />

<strong>de</strong>r Instanzierung e<strong>in</strong>er JTable an <strong>de</strong>n Konstruktor übergeben. Ohne weitere<br />

Ableitung kann die Standard-Implementierung DefaultTableColumnMo<strong>de</strong>l<br />

verwen<strong>de</strong>t wer<strong>de</strong>n. Sie stellt Metho<strong>de</strong>n zum H<strong>in</strong>zufügen bzw. Entfernen von<br />

Spaltenobjekten (Typ TableColumn) bereit.<br />

public void addColumn(TableColumn aColumn)<br />

public void removeColumn(TableColumn aColumn)<br />

Bsp.:<br />

import java.awt.*;<br />

import java.awt.event.*;<br />

import javax.sw<strong>in</strong>g.*;<br />

import javax.sw<strong>in</strong>g.table.*;<br />

public class SimpleTable2 extends JFrame<br />

{<br />

400


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

public static f<strong>in</strong>al Str<strong>in</strong>g[][] DATEN =<br />

{<br />

{"Uebungsblatt 1", "B<strong>in</strong>aere Baumknoten <strong>in</strong> <strong>Java</strong>"},<br />

{"Uebungsblatt 1a", "Baumansichten <strong>de</strong>r <strong>Java</strong> Foundation Classes"},<br />

{"Uebungsblatt 2", "Verarbeitung b<strong>in</strong>aerer Suchbaumknoten"},<br />

{"Uebungsblatt 3", "Die generische Klasse B<strong>in</strong>aererSuchbaum <strong>in</strong> <strong>Java</strong>"},<br />

{"Uebungsblatt 4", "Die generische Klasse AvlBaum <strong>in</strong> <strong>Java</strong>"},<br />

{"Uebungsblatt 5", "Perfekt ausgeglichene Baeume"},<br />

{"Uebungsblatt 6", "Rot-Schwarz-Baeume"},<br />

{"Uebungsblatt 7", "AA-Baeume"},<br />

{"Uebungsblatt 8", "Splay-Baeume"},<br />

{"Uebungsblatt 9", "Bayer-Baeume"},<br />

{"Uebungsplatt 10", "Auf Platte / Diskette gespeicherte Bayer-Bäume"},<br />

{"Uebungsblatt 11", "Hash-Tabellen"},<br />

{"Uebungsblatt 12", "Heap-Algorithmen"},<br />

{"Uebungsblatt 13", "Vorrangwarteschlangen"},<br />

{"Uebungsblatt 14", "B<strong>in</strong>omial Queue, Fibonacci Heap"},<br />

{"Uebungsblatt 15", "Backtrack<strong>in</strong>g-Algorithmen"},<br />

{"Uebungsblatt 16",<br />

"Ansichten <strong>de</strong>r <strong>Java</strong> Foundation Classes JList und JTable"},<br />

{"Uebungsblatt 17", "Skip Lists"}<br />

};<br />

public static f<strong>in</strong>al Str<strong>in</strong>g[] COLHEADS =<br />

{<br />

"Ausgabe", "Titelthema"<br />

};<br />

// Konstruktor<br />

public SimpleTable2()<br />

{<br />

super("Uebersicht zu <strong>de</strong>n Uebungen <strong>in</strong> AD");<br />

//Spaltenmo<strong>de</strong>ll erzeugen<br />

DefaultTableColumnMo<strong>de</strong>l cm = new DefaultTableColumnMo<strong>de</strong>l();<br />

for (<strong>in</strong>t i = 0; i < COLHEADS.length; i++)<br />

{<br />

TableColumn col = new TableColumn(i, i == 2 ? 150 : 60);<br />

col.setHea<strong>de</strong>rValue(COLHEADS[i]);<br />

cm.addColumn(col);<br />

}<br />

//Tabellenmo<strong>de</strong>ll erzeugen<br />

TableMo<strong>de</strong>l tm = new AbstractTableMo<strong>de</strong>l()<br />

{<br />

public <strong>in</strong>t getRowCount()<br />

{<br />

return DATEN.length;<br />

}<br />

public <strong>in</strong>t getColumnCount()<br />

{<br />

return DATEN[0].length;<br />

}<br />

public Object getValueAt(<strong>in</strong>t row, <strong>in</strong>t column)<br />

{<br />

return DATEN[row][column];<br />

}<br />

};<br />

JTable table = new JTable(tm, cm);<br />

Conta<strong>in</strong>er cp = getContentPane();<br />

cp.add(new JLabel("Uebungen zu AD im WS 02"), "North");<br />

cp.add(new JScrollPane(table), "Center");<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

SimpleTable2 frame = new SimpleTable2();<br />

frame.addW<strong>in</strong>dowListener(new W<strong>in</strong>dowAdapter()<br />

{<br />

public void w<strong>in</strong>dowClos<strong>in</strong>g(W<strong>in</strong>dowEvent e)<br />

401


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

{<br />

System.exit(0);<br />

}<br />

});<br />

frame.setLocation(100, 100);<br />

frame.setSize(300, 200);<br />

frame.setVisible(true);<br />

}<br />

}<br />

Ren<strong>de</strong>r<strong>in</strong>g 302 <strong>de</strong>r Zellen<br />

public TableCellRen<strong>de</strong>rer getDefaultRen<strong>de</strong>rer(Class columnClass)<br />

public void setDefaultCellRen<strong>de</strong>rer(Class columnClass,<br />

TableCellRen<strong>de</strong>rer ren<strong>de</strong>rer)<br />

Sofern nicht <strong>in</strong> <strong>de</strong>n Tabellenspalten e<strong>in</strong> eigener Ren<strong>de</strong>rer bestimmt ist, ist <strong>de</strong>r<br />

Standard-Ren<strong>de</strong>rer für die Darstellung aller Tabellenelemente zuständig. Es muß das<br />

Interface TableCellRen<strong>de</strong>rer implementieren, das nur e<strong>in</strong>e e<strong>in</strong>zige Metho<strong>de</strong> enthält.<br />

public Component getTableCellRen<strong>de</strong>rerComponent(<br />

JTable table,Object value,<br />

boolean isSelected,<br />

boolean hasFocus,<br />

<strong>in</strong>t row,<br />

<strong>in</strong>t column)<br />

Diese Metho<strong>de</strong> arbeitet als Factory-Metho<strong>de</strong> und wird nur dann aufgerufen, wenn zur<br />

Darstellung e<strong>in</strong>er Zelle e<strong>in</strong> Ren<strong>de</strong>rer benötigt wird.<br />

Standardmäßig wird als Ren<strong>de</strong>rer e<strong>in</strong>e Instanz <strong>de</strong>r Klasse DefaultTableCellRen<strong>de</strong>rer<br />

verwen<strong>de</strong>t. Sie ist e<strong>in</strong>e Ableitung von JLabel mit <strong>de</strong>ren Hilfe Farbe, H<strong>in</strong>tergrund und<br />

das Look-and-Feel <strong>de</strong>r Tabelle an die Erfor<strong>de</strong>rnisse <strong>de</strong>r jeweiligen Zelle angepasst<br />

wer<strong>de</strong>n.<br />

Reaktion auf Ereignisse. JTable generiert e<strong>in</strong>e Vielzahl vom Ereignissen, um<br />

registrierte Listener über Än<strong>de</strong>rungen <strong>de</strong>s Tabellenzustands zu <strong>in</strong>formieren. Will e<strong>in</strong><br />

Objekt beispielsweise darüber <strong>in</strong>formiert wer<strong>de</strong>n, daß sich die Selektion geän<strong>de</strong>rt<br />

hat, muß es zwei ListSelectionListener registrieren. E<strong>in</strong>er davon wird auf <strong>de</strong>m<br />

Selektionsmo<strong>de</strong>ll registriert, das mit getSelectionMo<strong>de</strong>l() ermittelt wer<strong>de</strong>n kann.<br />

Da dieser nur Informationen über Än<strong>de</strong>rungen an <strong>de</strong>r Zeilenselektion versen<strong>de</strong>t, muß<br />

e<strong>in</strong> zweiter Listener auf <strong>de</strong>m Mo<strong>de</strong>ll für die Spaltenselektion registriert wer<strong>de</strong>n. Es<br />

kann durch Aufruf von getColumnMo<strong>de</strong>l() beschafft wer<strong>de</strong>n, und auf se<strong>in</strong><br />

Selektionsmo<strong>de</strong>ll kann ebenfalls mit getSelectionMo<strong>de</strong>l() zugegriffen wer<strong>de</strong>n.<br />

Bei je<strong>de</strong>r Än<strong>de</strong>rung <strong>de</strong>r Selektion wird nun valueChanged() aufgerufen und kann<br />

mit Hilfe <strong>de</strong>r oben erläuterten Metho<strong>de</strong>n herausf<strong>in</strong><strong>de</strong>n, welche Zeilen und Spalten<br />

selektiert s<strong>in</strong>d.<br />

Die Tabelle <strong>in</strong>formiert auch über Än<strong>de</strong>rungen ihrer Daten. Dazu muß auf <strong>de</strong>m<br />

Tabellenmo<strong>de</strong>ll (das mit getMo<strong>de</strong>l() beschafft wird) durch Aufruf von<br />

addTableMo<strong>de</strong>lListener() e<strong>in</strong> TableMo<strong>de</strong>lListener registriert wer<strong>de</strong>n. Bei<br />

je<strong>de</strong>r Än<strong>de</strong>rung <strong>de</strong>s Mo<strong>de</strong>lls wird dann <strong>de</strong>ssen Metho<strong>de</strong> tableChanged()<br />

aufgerufen.<br />

Schließlich können alle <strong>in</strong> <strong>de</strong>n Superklassen von JTable <strong>de</strong>f<strong>in</strong>ierten Listener<br />

registriert wer<strong>de</strong>n.<br />

302 Vorgang, <strong>de</strong>r dafür sorgt, dass die Zellen auf <strong>de</strong>m Bildschirm dargestellt wer<strong>de</strong>n. Die dafür verantwortlichen<br />

Komponenten wer<strong>de</strong>n als Ren<strong>de</strong>rer bezeichnet.<br />

402


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

JComponent<br />

JTable<br />

...<br />

protected TableCellEditor cellEditor<br />

protected boolean cellSelectionEnabled<br />

protected TableCloumnMo<strong>de</strong>l columnMo<strong>de</strong>l<br />

protected TableMo<strong>de</strong>l dataMo<strong>de</strong>l<br />

…<br />

protected Color selectionBackground<br />

potected Color selectionForeground<br />

protected ListSelectionMo<strong>de</strong>l selectionMo<strong>de</strong>l<br />

protected boolean showHorizontalL<strong>in</strong>es<br />

protected boolean showVerticalL<strong>in</strong>es<br />

><br />

public JTable()<br />

public JTable(<strong>in</strong>t numRows, <strong>in</strong>t numColumns)<br />

public JTable(Object[][] neuDaten,Object [] columnNames)<br />

public JTable(TableMo<strong>de</strong>l dm)<br />

public JTable(TableMo<strong>de</strong>l dm, TableColumnMo<strong>de</strong>l cm)<br />

public JTable(TableMo<strong>de</strong>l dm, TableColumnMo<strong>de</strong>l cm, ListSelectionMo<strong>de</strong>l sm)<br />

public JTable(Vector rowData, Vector columnNames)<br />

><br />

public void addColumn(TableColumn aColumn)<br />

…<br />

public void addNotify()<br />

…<br />

public void clearSelection()<br />

public void columnAd<strong>de</strong>d(TableColumnMo<strong>de</strong>lEvent e)<br />

public <strong>in</strong>t columnAtPo<strong>in</strong>t(Po<strong>in</strong>t po<strong>in</strong>t)<br />

…<br />

public void doLayout()<br />

public boolean editCellAt(<strong>in</strong>t row, <strong>in</strong>t column)<br />

…<br />

public TableCellEditor getCellEditor()<br />

public TableCellEditor getCellEditor(<strong>in</strong>t row,<strong>in</strong>t column)<br />

public Rectangle getCellRect(<strong>in</strong>t row,<strong>in</strong>t column,boolean <strong>in</strong>clu<strong>de</strong>Spac<strong>in</strong>g)<br />

public TableCellRen<strong>de</strong>rer getCellRen<strong>de</strong>rer(<strong>in</strong>t row,<strong>in</strong>t column)<br />

public TableColumn getColumnClass(<strong>in</strong>t column)<br />

public <strong>in</strong>t getColumnCount()<br />

public TableColumnMo<strong>de</strong>l getColumnMo<strong>de</strong>l()<br />

public Str<strong>in</strong>g columnName(<strong>in</strong>t column)<br />

public boolean getSelectionEnabled()<br />

public Class getColumnClass(<strong>in</strong>t column)<br />

public <strong>in</strong>t getColumnCount()<br />

public TableColumnMo<strong>de</strong>l getColumnMo<strong>de</strong>l()<br />

public Str<strong>in</strong>g getColumnName(<strong>in</strong>t column)<br />

public boolean getColumnSelectionAllowed()<br />

…<br />

403


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

public TableMo<strong>de</strong>l getMo<strong>de</strong>l()<br />

public <strong>in</strong>t getRowCount()<br />

public <strong>in</strong>t getRowHeight()<br />

public <strong>in</strong>t getRowHeight(<strong>in</strong>t row)<br />

…<br />

public ListSelectionMo<strong>de</strong>l getSelectionMo<strong>de</strong>l()<br />

public boolean getShowHorizontalL<strong>in</strong>es()<br />

public boolean getShowVerticalL<strong>in</strong>es()<br />

…<br />

public JTableHea<strong>de</strong>r getTableHea<strong>de</strong>r()<br />

…<br />

public TableUI getUI()<br />

…<br />

public Object getValueAt(<strong>in</strong>t row,<strong>in</strong>t column)<br />

…<br />

public boolean isCellEditable(<strong>in</strong>t row,<strong>in</strong>t column)<br />

…<br />

public Str<strong>in</strong>g paramStr<strong>in</strong>g()<br />

…<br />

public void selectAll()<br />

…<br />

public void setMo<strong>de</strong>l(TableMo<strong>de</strong>l dataMo<strong>de</strong>l)<br />

…<br />

public void setRowHeight(<strong>in</strong>t rowHeight)<br />

public void setRowHeight(<strong>in</strong>t row,<strong>in</strong>t rowHeight)<br />

…<br />

public void setUI(TableUI ui)<br />

…<br />

public void tableChanged(TableMoveEvent e)<br />

…<br />

public void updateUI()<br />

public void valueChanged(ListSelectionEvent e)<br />

Abb.: Die Klasse JTable<br />

404


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

><br />

TableMo<strong>de</strong>l<br />

public void addTableMo<strong>de</strong>lListener(TableMo<strong>de</strong>lListener l)<br />

public Class getColumnClass(<strong>in</strong>t columnIn<strong>de</strong>x)<br />

public <strong>in</strong>t getColumnCount()<br />

public <strong>in</strong>t getRowCount()<br />

public Object getValueAt(<strong>in</strong>t rowIn<strong>de</strong>x,<strong>in</strong>t columnIn<strong>de</strong>x)<br />

public boolean isCellEditable(<strong>in</strong>t rowIn<strong>de</strong>x,<strong>in</strong>t columnIn<strong>de</strong>x)<br />

public void removeTableMo<strong>de</strong>lLister(TableMo<strong>de</strong>lListener l)<br />

public void setValueAt(Object aValue,<strong>in</strong>t rowIn<strong>de</strong>x,<strong>in</strong>t columnIn<strong>de</strong>x)<br />

AbstractTableMo<strong>de</strong>l<br />

{ abstract }<br />

…<br />

public <strong>in</strong>t f<strong>in</strong>dColumn(Str<strong>in</strong>g columnName)<br />

public void fireTableCellUpdated(<strong>in</strong>t row,<strong>in</strong>t column)<br />

public void fireTableChanged(TableMo<strong>de</strong>lEvent e)<br />

public void fireTableChanged()<br />

…<br />

public EventListener[] getListeners(Class listenerType)<br />

public TableMo<strong>de</strong>lListener[] getTableMo<strong>de</strong>lListener()<br />

…<br />

DefaultTableMo<strong>de</strong>ll<br />

proteced Vector columnI<strong>de</strong>ntifiers<br />

protected Vector dataVector<br />

><br />

public DefaultTableMo<strong>de</strong>l()<br />

public DefaultTableMo<strong>de</strong>l(<strong>in</strong>t rowCount,<strong>in</strong>t ColumnCount)<br />

public DefaultTableMo<strong>de</strong>l(Object[] columnNames,<strong>in</strong>t rowCount)<br />

public DefaultTableMo<strong>de</strong>l(Vector columnNames,<strong>in</strong>t rowCount)<br />

public DefaultTableMo<strong>de</strong>l(Vector data,Vector columnNames)<br />

><br />

public void addColumn(Object columnName)<br />

public void addColumn(Object columnName,Object[] columnData)<br />

public void addColumn(Object columnName,Vector columnData)<br />

public void addRow(Object[] rowData)<br />

public void addRow(Vector data)<br />

…<br />

public <strong>in</strong>t getColumnCount()<br />

public Str<strong>in</strong>g getColumnName()<br />

public Vector getDataVector()<br />

public <strong>in</strong>t getRowCount()<br />

public Object getValueAt(<strong>in</strong>t row,<strong>in</strong>t column)<br />

public void <strong>in</strong>sertRow(<strong>in</strong>t row,Object[] rowData)<br />

public void <strong>in</strong>sertRow(<strong>in</strong>t row,Vector rowData)<br />

…<br />

public void setDateVector(Object[][] dataVector,Object[] columnI<strong>de</strong>ntifiers)<br />

public setDataVector(Vector dataVector,Vector columnI<strong>de</strong>ntifiers)<br />

405


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

public void setNumRows(<strong>in</strong>t rowCount)<br />

public void setRowCount(<strong>in</strong>t rowCount)<br />

public void setValueAt(Object aValue,<strong>in</strong>t row,<strong>in</strong>t column)<br />

><br />

TableCellEditor<br />

public Component getTableCellEditorComponent(JTable table,Object value,<br />

boolean isSelected,<strong>in</strong>t row,<strong>in</strong>t column)<br />

DefaultCellEditor<br />

…<br />

protected JComponent editorComponent<br />

><br />

public DefaultCellEditor(JCheckBox checkBox)<br />

public DefaultCellEditor(JComboBox checkBox)<br />

public DefaultCellEditor(JTextField textField)<br />

><br />

…<br />

public Object getCellEditorValue()<br />

…<br />

public Component getComponent()<br />

…<br />

public boolean isCellEditable(EventObject anEvent)<br />

…<br />

><br />

TableCellRen<strong>de</strong>rer<br />

public Componet getTableCellRen<strong>de</strong>rerComponent(JTable table,Object value,<br />

boolean isSelected, boolean hasFocus,<strong>in</strong>t row,<strong>in</strong>t column)<br />

DefaultTableCellRen<strong>de</strong>rer<br />

><br />

public DefaultTableCellRen<strong>de</strong>rer()<br />

><br />

public void firePropertyChange(Str<strong>in</strong>g propertyName,boolean oldValue,boolean newValue)<br />

…<br />

public void validate()<br />

406


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

5.6.5.6 Hierarchische Strukturen<br />

Die Klasse JTree dient zur Darstellung, Navigation und Berabeitung baumartiger,<br />

hierarchischer Strukturen. Die Baumdarstellung basiert auf e<strong>in</strong>em hierarchischen<br />

Datenmo<strong>de</strong>ll. Je<strong>de</strong> Mo<strong>de</strong>llklasse zur Baumdarstellung muß sich auf das Interface<br />

TreeMo<strong>de</strong>l beziehen. JTree besitzt dafür Konstruktoren bzw. die Zugriffsmetho<strong>de</strong>n<br />

getMo<strong>de</strong>l() und setMo<strong>de</strong>l(). Die e<strong>in</strong>zelnen Baumknoten müssen die Interfaces<br />

TreeNo<strong>de</strong> o<strong>de</strong>r MutableTreeNo<strong>de</strong> implementieren. Die Klasse<br />

DefaulMutableTreeNo<strong>de</strong> enthält e<strong>in</strong>e universelle Implementierung (<strong>in</strong>klusive<br />

Navigationsmetho<strong>de</strong>n) für Baumknoten<br />

Erzeugen e<strong>in</strong>es Baums. Liegt e<strong>in</strong> geeignetes Datenmo<strong>de</strong>ll vor, ist das Instanzieren<br />

e<strong>in</strong>es JTree e<strong>in</strong>fach. Die bei<strong>de</strong>n wichtigsten Konstruktoren <strong>de</strong>r Klasse JTree s<strong>in</strong>d:<br />

public JTree(TreeMo<strong>de</strong>l neuesMo<strong>de</strong>ll)<br />

// erwartet wird e<strong>in</strong> vor<strong>de</strong>f<strong>in</strong>iertes TreeMo<strong>de</strong>l zur Darstellung <strong>de</strong>r Baumelemente. E<strong>in</strong> TreeMo<strong>de</strong>l<br />

// kapselt alle relevanten Informationen über die Baumstruktur. Es liefert auf Anfrage die Wurzel<br />

// <strong>de</strong>s Baums, stellt Informationen über e<strong>in</strong>en bestimmten Knoten bereit o<strong>de</strong>r liefert <strong>de</strong>ssen<br />

// Nachfolgeknoten<br />

public JTree(TreeNo<strong>de</strong> wurzel)<br />

// Die Wurzel wird automatisch <strong>in</strong> e<strong>in</strong> geeignetes TreeMo<strong>de</strong>l e<strong>in</strong>gebettet. Der JTree erfragt die zur<br />

// Darstellung <strong>de</strong>r Navigation erfor<strong>de</strong>rlichen Daten immer beim TreeMo<strong>de</strong>l. Das über wurzel<br />

// Baumwurzel) <strong>in</strong>stanzierte DefaultTreeMo<strong>de</strong>l kann diese Informationen aus <strong>de</strong>n Knoten und <strong>de</strong>n<br />

// dar<strong>in</strong> gespeicherten Verweisen auf ihre Nachfolgeknoten entnehmen.<br />

Zugriffsmetho<strong>de</strong>n auf das Datenmo<strong>de</strong>ll.<br />

public TreeMo<strong>de</strong>l getMo<strong>de</strong>l()<br />

public void setMo<strong>de</strong>l(TreeMo<strong>de</strong>l neuesMo<strong>de</strong>ll)<br />

Anzeige bzw. Unterdrücken <strong>de</strong>r Wurzel bei <strong>de</strong>r Baumdarstellung. E<strong>in</strong>e wichtige<br />

Konfigurationsoption regelt, ob die Wurzel <strong>de</strong>s Baums bei se<strong>in</strong>er Darstellung<br />

angezeigt o<strong>de</strong>r unterdrückt wer<strong>de</strong>n soll. Auf sie kann mit <strong>de</strong>n Metho<strong>de</strong>n<br />

public void setRootVisible(boolean rootVisible)<br />

public boolean isRootVisible()<br />

zugegriffen wer<strong>de</strong>n.<br />

Zur Beschriftung e<strong>in</strong>es Knotens bei <strong>de</strong>r visuellen Darstellung verwen<strong>de</strong>t e<strong>in</strong> JTree<br />

die Metho<strong>de</strong> toStr<strong>in</strong>g().<br />

Abfragen zur Selektion von Baumknoten. JTree stellt e<strong>in</strong>e Reihe von Metho<strong>de</strong>n für<br />

Abfragen bereit, ob und welche Knoten selektiert s<strong>in</strong>d, z.B.:<br />

public TreePath getSelectionPath()<br />

// ermittelt das selektierte Element. Bei aktivierter Mehrfachselektion liefert die Metho<strong>de</strong> das erste aller<br />

// selektierten Elemente. Falls ke<strong>in</strong> Knoten selektiert ist, wird null zurückgeliefert.<br />

public TreePath[] getSelectionPaths()<br />

// zurückgegeben wird e<strong>in</strong> Array mit allen selektierten Knoten<br />

public TreePath getLeadSelectionPath()<br />

// liefert das markierte Element<br />

Die angegebenen Metho<strong>de</strong>n liefern Objekte <strong>de</strong>r Klasse TreePath. Diese Klasse<br />

beschreibt e<strong>in</strong>en Knoten im Baum über <strong>de</strong>n Pfad, <strong>de</strong>r von <strong>de</strong>r Wurzel aus beschritten<br />

wer<strong>de</strong>n muß, um zu <strong>de</strong>n Knoten zu gelangen. Mit<br />

public Object getLastSelectedPathComponent()<br />

407


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

<strong>de</strong>r Klasse TreePath kann das letzte Element dieses Pfads bestimmt wer<strong>de</strong>n. Mit<br />

public Object[] getPath()<br />

<strong>de</strong>r Klasse TreePath kann <strong>de</strong>r vollständige Pfad ermittelt wer<strong>de</strong>n. An erster Stelle<br />

liegt die Wurzel <strong>de</strong>s Baums, an letzter Stelle das selektierte Element. Solle ermittelt<br />

wer<strong>de</strong>n, ob und welche Elemente im Baum selektiert s<strong>in</strong>d, können die Metho<strong>de</strong>n<br />

public boolean isSelectionEmpty()<br />

public boolean isPathSelected(TreePath pfad)<br />

aufgerufen wer<strong>de</strong>n.<br />

Verän<strong>de</strong>rn <strong>de</strong>r Selektion. Die Selektion kann programmgesteuert verän<strong>de</strong>rt wer<strong>de</strong>n:<br />

public void clearSelection()<br />

// Löschen <strong>de</strong>r Selektion<br />

public void addSelectionPath(TreePath path)<br />

// Erweitern <strong>de</strong>r Selektion um e<strong>in</strong> e<strong>in</strong>zelnes Element<br />

public void addSelectionPaths(TreePath [] pfa<strong>de</strong>)<br />

// Erweitern <strong>de</strong>r Selektion um e<strong>in</strong>e Menge Knoten<br />

public void setSelectionPath(TreePath path)<br />

// Selektion <strong>de</strong>r als Argument übergebenen Knoten)<br />

public void setSelectionPaths(TreePath[] paths)<br />

// Selektion <strong>de</strong>r als Argument übergebenen Knoten<br />

Zur unmittelbaren Reaktion auf Än<strong>de</strong>rungen <strong>de</strong>r Selektion durch <strong>de</strong>n Anwen<strong>de</strong>r kann<br />

e<strong>in</strong> TreeSelectionListener <strong>in</strong>stanziert wer<strong>de</strong>n (Registrierung mit<br />

addTreeSelectionListener() beim JTree). Bei je<strong>de</strong>r Selektionsän<strong>de</strong>rung wird<br />

public void valueChanged(TreeSelectionEvent event)<br />

aufgerufen. "event" stellt<br />

public TreePath getOldLeadSelectionPath()<br />

public TreePath getNewLeadSelectionPath()<br />

zur Verfügung, um auf <strong>de</strong>n vorherigen und aktuellen Selektionspfad zuzugreifen.<br />

Öffnen und Schließen <strong>de</strong>r Knoten. Der Anwen<strong>de</strong>r kann die Knoten mit Maus- o<strong>de</strong>r<br />

Tastaturkommandos öffnen o<strong>de</strong>r schließen. Dadurch wer<strong>de</strong>n Nachfolgeknoten<br />

sichtbar o<strong>de</strong>r versteckt.<br />

public boolean isExpan<strong>de</strong>d(TreePath pfad)<br />

// liefert true, wenn <strong>de</strong>r Knoten geöffnet ist<br />

public boolean isCollapsed()<br />

// liefert true, wenn <strong>de</strong>r Knoten geschlossen ist<br />

public boolean hasBeenExpan<strong>de</strong>d(TreePath path)<br />

// gibt an, ob <strong>de</strong>r Knoten überhaupt schon e<strong>in</strong>mal geöffnet wur<strong>de</strong><br />

public boolean isVisible(TreePath pfad)<br />

// liefert true, wenn <strong>de</strong>r Knoten sichtbar ist, d.h. all se<strong>in</strong>e Elternknoten geöffnet s<strong>in</strong>d<br />

public void makeVisible(TreePath pfad)<br />

// Sichtbar Machen e<strong>in</strong>es Knoten<br />

public void expandPath(TreePath pfad)<br />

// Öffnen e<strong>in</strong>es Knoten<br />

public void collapsePath(TreePath pfad)<br />

// Schließen e<strong>in</strong>es Knoten<br />

Verän<strong>de</strong>rn <strong>de</strong>r Baumstruktur. Es können neue Knoten e<strong>in</strong>gefügt, bestehen<strong>de</strong> entfernt<br />

o<strong>de</strong>r vorhan<strong>de</strong>ne modifiziert wer<strong>de</strong>n.<br />

408


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Interfaces zur Mo<strong>de</strong>llierung und Sicht auf e<strong>in</strong>en Baum: Drei Interfaces erlauben<br />

Mo<strong>de</strong>llierung und Sicht auf e<strong>in</strong>en Baum: TreeMo<strong>de</strong>l, TreeSelectionMo<strong>de</strong>l,<br />

TreeCellRen<strong>de</strong>rer.<br />

E<strong>in</strong> viertes Interface TreeNo<strong>de</strong> beschreibt, was <strong>in</strong> je<strong>de</strong>m Knoten <strong>de</strong>s Baums<br />

dargestellt wird.<br />

TreeMo<strong>de</strong>l<br />

Dieses Interface beschreibt das e<strong>in</strong>em JTree zugeordnete Datenmo<strong>de</strong>ll. Das<br />

TreeMo<strong>de</strong>l-Interface spezifiziert, wie e<strong>in</strong> Baum (über e<strong>in</strong>e Datenstruktur) abgebil<strong>de</strong>t<br />

wird.<br />

public Object getChild(Object parent, <strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

public <strong>in</strong>t getChildCount(Object parent)<br />

public <strong>in</strong>t getIn<strong>de</strong>xOfChild(Object parent,Object child)<br />

public Object getRoot()<br />

public Object isLeaf(Object no<strong>de</strong>)<br />

Drei weitere Metho<strong>de</strong>n<br />

public void addTreeMo<strong>de</strong>lListener(TreeMo<strong>de</strong>lListener l)<br />

public void removeTreeMo<strong>de</strong>lListener(TreeMol<strong>de</strong>lListener l)<br />

public void valueForPathChanged(TreePath pfad, Object neuerWert)<br />

behan<strong>de</strong>ln H<strong>in</strong>zufügen, Entfernen und die Kentnisnahme von Event-Listeners. Diese<br />

Listeners bemerken Än<strong>de</strong>rungen im TreeMo<strong>de</strong>l nach Empfang von TreeMo<strong>de</strong>lEvent-<br />

Nachrichten.<br />

E<strong>in</strong> Objekt, das diese Metho<strong>de</strong>n <strong>de</strong>f<strong>in</strong>iert, kann Mo<strong>de</strong>ll für e<strong>in</strong>en JTree se<strong>in</strong>. Die<br />

DefaultTreeMo<strong>de</strong>l-Klasse ist e<strong>in</strong>e e<strong>in</strong>fache Implementierung <strong>de</strong>s TreeMo<strong>de</strong>l,<br />

das explizit TreeNo<strong>de</strong>- und MutableTreeNo<strong>de</strong>-Objekte benutzt.<br />

TreeNo<strong>de</strong><br />

JTree-Objekte wer<strong>de</strong>n aus TreeNo<strong>de</strong>-Objekten gebil<strong>de</strong>t (e<strong>in</strong>fache Repräsentationen<br />

e<strong>in</strong>es Baumknoten). Die wichtigsten Metho<strong>de</strong>n von TreeNo<strong>de</strong> s<strong>in</strong>d:<br />

public <strong>in</strong>t getChildCount()<br />

// ermittelt die Anzahl <strong>de</strong>r Nachfolgeknoten. Sie wer<strong>de</strong>n von 0 an durchnummeriert.<br />

public TreeNo<strong>de</strong> getChildAt(<strong>in</strong>t childIn<strong>de</strong>x)<br />

// liefert e<strong>in</strong>en beliebigen Nachfolgeknoten<br />

public TreeNo<strong>de</strong> getParent()<br />

// E<strong>in</strong> Knoten kennt se<strong>in</strong>en Vaterknoten, <strong>de</strong>r mit getParent() ermittelt wer<strong>de</strong>n kann<br />

public boolean isLeaf()<br />

// Abfrage, ob e<strong>in</strong> Knoten e<strong>in</strong> Blatt ist o<strong>de</strong>r weitere Nachfolgeknoten enthält<br />

Die Klasse DefaultMutableTreeNo<strong>de</strong> <strong>de</strong>f<strong>in</strong>iert Metho<strong>de</strong>n zum Anschauen und<br />

Manipulieren von Baumknoten. Diese Klasse implementiert das Interface<br />

MutableTreeNo<strong>de</strong> (Extension <strong>de</strong>s Interface TreeNo<strong>de</strong>).<br />

Anwendungsbezogene Informationen wer<strong>de</strong>n bereits <strong>de</strong>m Konstruktor übergeben:<br />

public DefaultMutableTreeNo<strong>de</strong>(Object benutzerObjekt)<br />

409


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

DefaultMutableTreeNo<strong>de</strong> hat Metho<strong>de</strong>n zum E<strong>in</strong>fügen und Löschen von Knoten<br />

implementiert:<br />

public void add(MutableTreeNo<strong>de</strong> neuesK<strong>in</strong>d)<br />

// e<strong>in</strong> K<strong>in</strong>dknoten wird an das En<strong>de</strong> <strong>de</strong>r Liste <strong>de</strong>r Nachfolgeknoten angefügt<br />

public void <strong>in</strong>sert(MutableTreeNo<strong>de</strong> neuesK<strong>in</strong>d, <strong>in</strong>t K<strong>in</strong>dIn<strong>de</strong>x)<br />

// E<strong>in</strong>fügen e<strong>in</strong>es neuen K<strong>in</strong>dknoten an beliebiger Stelle<br />

public void remove(<strong>in</strong>t k<strong>in</strong>dIn<strong>de</strong>x)<br />

// Entfernen e<strong>in</strong>es beliebigen Knoten<br />

public void removeAllChildren()<br />

// Entfernen aller K<strong>in</strong>dknoten<br />

/* Zugriff auf Infornationen, die <strong>in</strong> e<strong>in</strong>em benutzerObjekt gehalten wer<strong>de</strong>n */<br />

public void setUserObject(Object benutzerObjekt)<br />

public Object getUserObject()<br />

// benutzerObjekt ist auch <strong>de</strong>r Lieferant von Knotenbeschriftungen. Je<strong>de</strong>r Aufruf von toStr<strong>in</strong>g()<br />

// wird an benutzerObjekt weitergeleitet<br />

TreeSelectionMo<strong>de</strong>l<br />

Diese Interface spezifiziert, wie e<strong>in</strong> Benutzer e<strong>in</strong>en Pfad bestimmter Objekte<br />

auswählen kann. JTree benutzt dieses Mo<strong>de</strong>ll zur Auswahl von Regeln.<br />

DefaultTreeSelectionMo<strong>de</strong>l ist e<strong>in</strong>e Implementierung von<br />

TreeSelectionMo<strong>de</strong>l.<br />

TreeSelectionMo<strong>de</strong>l steuert das Selektieren von Knoten. Die Zugriffsmetho<strong>de</strong>n<br />

auf TreeSelectionMo<strong>de</strong>l s<strong>in</strong>d:<br />

public void setSelectionMo<strong>de</strong>l(TreeSelectionMo<strong>de</strong>l selectionMo<strong>de</strong>l)<br />

public TreeSelectionMo<strong>de</strong>l getSelectionMo<strong>de</strong>l()<br />

Standardmäßig erlaubt e<strong>in</strong> JTree das Selektieren mehrerer Knoten. Soll die<br />

Selektionsmöglichkeit auf e<strong>in</strong>en Knoten beschränk wer<strong>de</strong>n, muß e<strong>in</strong> eigenes<br />

TreeSelectionMo<strong>de</strong>l an setSelectionMo<strong>de</strong>l() übergeben wer<strong>de</strong>n. Dazu<br />

kann e<strong>in</strong>e Instanz <strong>de</strong>r Klasse DefaultTreeSelectionMo<strong>de</strong>l erzeugt wer<strong>de</strong>n und<br />

durch Aufruf von<br />

public void setSelectionMo<strong>de</strong>(<strong>in</strong>t mo<strong>de</strong>)<br />

und Übergabe e<strong>in</strong>er <strong>de</strong>r Konstanten<br />

SINGLE_TREE_SELECTION<br />

CONTIGUOUS_TREE_SELECTION<br />

DISCONTIGUOUS_TREE_SELECTION<br />

konfiguriert wer<strong>de</strong>n.<br />

TreeCellRen<strong>de</strong>rer<br />

Dieses Interface wird zur visuellen Repräsentation von Baumknoten benutzt. Es<br />

umfasst nur e<strong>in</strong>e Metho<strong>de</strong>.<br />

Anwendungen<br />

410


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

1. E<strong>in</strong> Baum, <strong>de</strong>r aus DefaultMutableTreeNo<strong>de</strong>s aufgebaut ist 303 .<br />

import java.awt.*;<br />

import javax.sw<strong>in</strong>g.*;<br />

import javax.sw<strong>in</strong>g.tree.*;<br />

public class SimpleTree extends JFrame<br />

{<br />

public SimpleTree()<br />

{<br />

super("Creat<strong>in</strong>g a Simple JTree");<br />

// W<strong>in</strong>dowUtilities.setNativeLookAndFeel();<br />

/* try {<br />

UIManager.setLookAndFeel(<br />

UIManager.getSystemLookAndFeelClassName());<br />

}<br />

catch(Exception e)<br />

{<br />

System.out.pr<strong>in</strong>tln("Error sett<strong>in</strong>g native L&F: " + e);<br />

} */<br />

// addW<strong>in</strong>dowListener(new ExitListener());<br />

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);<br />

Conta<strong>in</strong>er content = getContentPane();<br />

Object[] hierarchy =<br />

{ "javax.sw<strong>in</strong>g",<br />

"javax.sw<strong>in</strong>g.bor<strong>de</strong>r",<br />

"javax.sw<strong>in</strong>g.colorchooser",<br />

"javax.sw<strong>in</strong>g.event",<br />

"javax.sw<strong>in</strong>g.filechooser",<br />

new Object[] { "javax.sw<strong>in</strong>g.plaf",<br />

"javax.sw<strong>in</strong>g.plaf.basic",<br />

"javax.sw<strong>in</strong>g.plaf.metal",<br />

"javax.sw<strong>in</strong>g.plaf.multi" },<br />

"javax.sw<strong>in</strong>g.table",<br />

new Object[] { "javax.sw<strong>in</strong>g.text",<br />

new Object[] { "javax.sw<strong>in</strong>g.text.html",<br />

"javax.sw<strong>in</strong>g.text.html.parser" },<br />

"javax.sw<strong>in</strong>g.text.rtf" },<br />

"javax.sw<strong>in</strong>g.tree",<br />

"javax.sw<strong>in</strong>g.undo" };<br />

DefaultMutableTreeNo<strong>de</strong> root = processHierarchy(hierarchy);<br />

JTree tree = new JTree(root);<br />

content.add(new JScrollPane(tree), Bor<strong>de</strong>rLayout.CENTER);<br />

setSize(275, 300);<br />

setVisible(true);<br />

}<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

new SimpleTree();<br />

}<br />

/** Small rout<strong>in</strong>e that will make no<strong>de</strong> out of the first entry<br />

* <strong>in</strong> the array, then make no<strong>de</strong>s out of subsequent entries<br />

* and make them child no<strong>de</strong>s of the first one. The process is<br />

* repeated recursively for entries that are arrays.<br />

*/<br />

private DefaultMutableTreeNo<strong>de</strong><br />

processHierarchy(Object[] hierarchy)<br />

{<br />

DefaultMutableTreeNo<strong>de</strong> no<strong>de</strong> =<br />

new DefaultMutableTreeNo<strong>de</strong>(hierarchy[0]);<br />

DefaultMutableTreeNo<strong>de</strong> child;<br />

for(<strong>in</strong>t i=1; i


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Object no<strong>de</strong>Specifier = hierarchy[i];<br />

if (no<strong>de</strong>Specifier <strong>in</strong>stanceof Object[])<br />

// Knoten mit Nachfolgeknoten<br />

child = processHierarchy((Object[])no<strong>de</strong>Specifier);<br />

else<br />

child = new DefaultMutableTreeNo<strong>de</strong>(no<strong>de</strong>Specifier);<br />

// Blatt<br />

no<strong>de</strong>.add(child);<br />

}<br />

return(no<strong>de</strong>);<br />

}<br />

}<br />

Sw<strong>in</strong>g besitzt e<strong>in</strong>en Manager für Benutzerschnittstellen, <strong>de</strong>r das "Look and Feel"<br />

(<strong>Java</strong> L&F) von Komponenten kontrolliert. Das Management <strong>de</strong>s Look and Feel wird<br />

von <strong>de</strong>r Klasse UIManager übernommen. Folgen<strong>de</strong> Auswahlmöglichkeiten stehen <strong>in</strong><br />

Abhängigkeit von <strong>de</strong>r <strong>Java</strong>-Entwicklungsumgebung zur Verfügung:<br />

- das W<strong>in</strong>dows-95- und W<strong>in</strong>dows-NT-Look-and-Feel<br />

- das Motif-x-W<strong>in</strong>dows-System-Look-and-Feel<br />

- Metal<br />

Die Auswahl <strong>de</strong>s Look-and-Feel erfolgt mit <strong>de</strong>r Metho<strong>de</strong><br />

getSystemLookAndFeelClassName() als Argument für die Metho<strong>de</strong><br />

setLookandFeel. Es wer<strong>de</strong>n unterschiedliche Ergebnisse auf unterschiedlichen<br />

Betriebssystemen erzeugt.<br />

412


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

413


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

JComponent<br />

JTree<br />

...<br />

protected TreeCellEditor cellEditor<br />

protected TreeCellRendrer cellRen<strong>de</strong>rer<br />

…<br />

protected boolean rootVisible<br />

protected TreeSelectionMo<strong>de</strong>l selectionMo<strong>de</strong>l<br />

…<br />

protected TreeMo<strong>de</strong>l treeMo<strong>de</strong>l<br />

protected TreeMo<strong>de</strong>llListener treeMo<strong>de</strong>lListener<br />

…<br />

><br />

JTree()<br />

JTree(Hashtable wert)<br />

JTree(TreeMo<strong>de</strong>l neuesMo<strong>de</strong>l)<br />

JTree(TreeNo<strong>de</strong> root)<br />

JTree(Vector wert)<br />

JTree(Object[] wert)<br />

…<br />

><br />

…<br />

public void addSelectionPath(TreePath pfad)<br />

public void addSelectionPaths(TreePath[] pfa<strong>de</strong>)<br />

public TreePath getSelectionPath()<br />

// ermittelt das ausgewählte Element<br />

public TreePath[] getSelectionPaths()<br />

public void setSelectionPath(TreePath pfad)<br />

public void setSelectionPaths(TreePath[] pfa<strong>de</strong>)<br />

public TreePath getLeadSelectionPath()<br />

// liefert das markierte Element<br />

…<br />

public void setRootVisible(boolean rootVisible)<br />

public boolean isRootVisible()<br />

public TreeMo<strong>de</strong>l getMo<strong>de</strong>l()<br />

public void setMo<strong>de</strong>l(TreeMo<strong>de</strong>l neuesMo<strong>de</strong>l)<br />

public TreeCellRen<strong>de</strong>rer getCellRen<strong>de</strong>rer ()<br />

public void setCellRen<strong>de</strong>rer(TreeCellren<strong>de</strong>rer x)<br />

…<br />

public void setUI(TreeUI ui)<br />

public void updateUI()<br />

…<br />

public void setSelectionMo<strong>de</strong>l(TreeSelectionMo<strong>de</strong>l selectionMo<strong>de</strong>l)<br />

…<br />

Abb.: Die Klasse JTree<br />

414


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

><br />

TreeMo<strong>de</strong>l<br />

public Object getRoot()<br />

public Object getChild(Object parent, <strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

public boolean isLeaf(Object knoten)<br />

public void valueForPathChanged(TreePath pfad,Object neuerWert)<br />

public <strong>in</strong>t getIn<strong>de</strong>xOfChild(Object parent, Object child)<br />

public void addTreeMo<strong>de</strong>lListener(TreeMo<strong>de</strong>lListener l)<br />

public void removeTreeListener(TreeMo<strong>de</strong>lListener l)<br />

DefaultTreeMo<strong>de</strong>ll<br />

…<br />

proteced TreeNo<strong>de</strong> root<br />

protected EventListenerList listenerList<br />

><br />

public DefaultTreeMo<strong>de</strong>l()<br />

public DefaultTreeMo<strong>de</strong>l(TreeNo<strong>de</strong> root,boolean asksAllowsChildren)<br />

><br />

public void addTreeMo<strong>de</strong>lListener l)<br />

public void removeTreeMo<strong>de</strong>lListener(TreeMo<strong>de</strong>lListener l)<br />

…<br />

public Object getChild(Object parent, <strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

public <strong>in</strong>t getChildCount(Object parent)<br />

public <strong>in</strong>t getIn<strong>de</strong>xOfChild(Object parent,Object child)<br />

…<br />

public TreeNo<strong>de</strong>[] getPathToRoot(TreeNo<strong>de</strong> e<strong>in</strong>Knoten)<br />

…<br />

public Object getRoot()<br />

….<br />

public void <strong>in</strong>sertInfo(MutableTreeNo<strong>de</strong> neuesK<strong>in</strong>d, MutableTreeNo<strong>de</strong><br />

parent,<strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

public boolean isLeaf(Object knoten)<br />

public void no<strong>de</strong>Changed(TreeNo<strong>de</strong> knoten)<br />

…<br />

public void removeNo<strong>de</strong>FromParent(MutableTreeNo<strong>de</strong> knoten)<br />

…<br />

public void setRoot(TreeNo<strong>de</strong> root)<br />

public void valueForPathChanged(TreePath path,Object neuerWert)<br />

415


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

><br />

TreeSelectionMo<strong>de</strong>l<br />

static <strong>in</strong>t CONTIGUOUS_TREE_SELECTION<br />

static <strong>in</strong>t DISCONTIGUOUS_TREE_SELECTION<br />

static <strong>in</strong>t SINGLE_TREE_SELECTION<br />

public void setSelectionMo<strong>de</strong>(<strong>in</strong>t no<strong>de</strong>)<br />

public <strong>in</strong>t setSelectionPath(TreePath path)<br />

public void setSelectionPaths(TreePath [] paths)<br />

public void addSelectionPath(TreePath path)<br />

public void addSelectionPaths(TreePath [] paths)<br />

public void removeSelectionPath(TreePath path)<br />

public void removeSelectionPaths(TreePaths [] paths)<br />

public TreePath getSelectionPath()<br />

public TreePath[] getSelectionPaths()<br />

public <strong>in</strong>t getSelectionCount()<br />

public boolean isPathSelected(TreePath path)<br />

public boolean isSelectionEmpty()<br />

public void clearSelection()<br />

public <strong>in</strong>t [] getSelectionRows()<br />

public boolean isRowSelected(<strong>in</strong>t row)<br />

public void resetRowSelection()<br />

public <strong>in</strong>t getLeadSelecttionRow()<br />

public TreePath getSelectionPath()<br />

public void addPropertyChangeListener(PropertyChangeListener listener)<br />

DefaultTreeSelectionMo<strong>de</strong>l<br />

...<br />

protected <strong>in</strong>t leadIn<strong>de</strong>x<br />

...<br />

protected TreePath [] selection<br />

…<br />

static Str<strong>in</strong>g SELECTION_MODE_PROPERTY<br />

protected <strong>in</strong>t selectionMo<strong>de</strong><br />

…<br />

><br />

public DefaultTreeSelectionMo<strong>de</strong>l()<br />

><br />

public void addSelectionPath(TreePath path)<br />

public void addSelectionPaths(TreePath [] paths)<br />

public void removeSelectionPath(TreePath path)<br />

…<br />

public void addTreeSelectionListener(TreeSelectionListener x)<br />

public void removeTreeSelectionListener(TreeSelectionListener x)<br />

…<br />

public void updateLeadIn<strong>de</strong>x()<br />

public void <strong>in</strong>sureUniqueness<br />

public Str<strong>in</strong>g toStr<strong>in</strong>g()<br />

public Object clone() throws CloneNotSupportedException<br />

416


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

><br />

TreeNo<strong>de</strong><br />

public Enumertion children()<br />

public boolean getAllowsChildren()<br />

public TreeNo<strong>de</strong> getChildAt(<strong>in</strong>t childIn<strong>de</strong>x)<br />

public <strong>in</strong>t getChildCount()<br />

public <strong>in</strong>t getIn<strong>de</strong>x(TreeNo<strong>de</strong> no<strong>de</strong>)<br />

public TreeNo<strong>de</strong> getParent()<br />

public boolean isLeaf()<br />

><br />

MutableTreeNo<strong>de</strong><br />

public void <strong>in</strong>sert(MutableTreeNo<strong>de</strong> child,<strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

public void remove(<strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

public void remove(MutableTreeNo<strong>de</strong> no<strong>de</strong>)<br />

public void removeFromParent()<br />

public void setParent(MutableTreeNo<strong>de</strong> newParent)<br />

public void setUserObject(Object objekt)<br />

DefaultMutableTreeNo<strong>de</strong><br />

….<br />

protected MutableTreeNo<strong>de</strong> parent<br />

><br />

DefaultMutableTreeNo<strong>de</strong>()<br />

DefaultMutableTreeNo<strong>de</strong>(Object objekt)<br />

DefaultMutableTreeNo<strong>de</strong>(Object benutzerObjekt)<br />

><br />

public void add(MutableTreeNo<strong>de</strong> neuesK<strong>in</strong>d)<br />

public Enumeartion breadFirstEnumeration()<br />

…<br />

public Enumeration <strong>de</strong>pthFirstEnumeration()<br />

…<br />

public TreeNo<strong>de</strong> getChildAfter(TreeNo<strong>de</strong> e<strong>in</strong>K<strong>in</strong>d)<br />

…<br />

public TreeNo<strong>de</strong> getChildBefore(TreeNo<strong>de</strong> e<strong>in</strong>K<strong>in</strong>d)<br />

…<br />

public <strong>in</strong>t getDepth()<br />

…<br />

public <strong>in</strong>t getLevel()<br />

…<br />

public TreeNo<strong>de</strong> getPath()<br />

protected TreeNo<strong>de</strong>[] getPathToRoot(TreeNo<strong>de</strong> e<strong>in</strong>Knoten, <strong>in</strong>t tiefe)<br />

…<br />

public TreeNo<strong>de</strong> getRoot()<br />

…<br />

public boolean isRoot()<br />

…<br />

public void removeAllChildren()<br />

…<br />

public Str<strong>in</strong>g toStr<strong>in</strong>g()<br />

417


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

><br />

TreeCellEditor<br />

public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected,<br />

Boolean expan<strong>de</strong>d, boolean leaf, <strong>in</strong>t row)<br />

DefaultTreeCellEditor<br />

protected Color bor<strong>de</strong>rSelectionColor<br />

protected booleab canEdit<br />

protected Component edit<strong>in</strong>gComponent<br />

protected Conta<strong>in</strong>er edit<strong>in</strong>gConta<strong>in</strong>er<br />

protected Icob edit<strong>in</strong>gIcon<br />

protected Font font<br />

protected TreePath lstPath<br />

…<br />

protected Timer timer<br />

protected JTree tree<br />

><br />

public DefaultTreeCellEditor(JTree tree, DefaultTreeCellRen<strong>de</strong>rer ren<strong>de</strong>rer)<br />

public DefaultTreeCellEditor(JTree tree, DefaultTreeCellRen<strong>de</strong>rer ren<strong>de</strong>rer,TreeCellEditor editor)<br />

><br />

public void setBor<strong>de</strong>rSelectionColor(Color newColor)<br />

public Color getBor<strong>de</strong>rSelectionColor()<br />

public void setFont(Font font)<br />

public Font getFont()<br />

…<br />

418


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

><br />

TreeCellRen<strong>de</strong>rer<br />

public Component getTreeCellRen<strong>de</strong>rerComponent( JTree tree, Object value, boolean selected,<br />

boolean expan<strong>de</strong>d, bollean leaf, <strong>in</strong>t row,<br />

boolean lostFocus)<br />

DefaultTreeCellRen<strong>de</strong>rer<br />

protected Color backGroundSelectionColor<br />

protected Color bor<strong>de</strong>rSelectionColor<br />

protected Icon closedIcon<br />

protected boolean hasFocus<br />

protected Icon leafIcon<br />

protected Icon openIcon<br />

protected boolean selected<br />

protected Color textNonSelectionColor<br />

protected Color textSelectionColor<br />

><br />

public TreeCellRen<strong>de</strong>rer()<br />

><br />

public void setFont(Font font)<br />

public void setLeafIcon(Icon newIcon)<br />

public void setOpenIcon(Icon newIcon)<br />

public void setTextNonSelectionColor(Color newColor)<br />

public void setTextSelectionColor(Color newColor)<br />

public void validate()<br />

419


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

5.6.6 Ereignisbehandlung unter Sw<strong>in</strong>g<br />

420


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

6. Utilities<br />

6.1 Kollektionen (Collections)<br />

Kollektionen (Collections) s<strong>in</strong>d Datenstrukturen zur Aufnahme und Verarbeitung von<br />

Datenmengen. Typische Collections s<strong>in</strong>d Stacks, Queues, Priority Queues, Listen<br />

o<strong>de</strong>r Trees (Bäume).<br />

In <strong>Java</strong> existieren seit <strong>de</strong>r Version 1.0 die Collections Vector, Stack, Hashtable<br />

und Bitset. Die Kritik an diesem Collection-Konzept führte zu e<strong>in</strong>er Sammlung von<br />

20 Klassen und Interfaces im Paket java.util <strong>de</strong>s JDK 1.2. Im Wesentlichen s<strong>in</strong>d<br />

dies: Set, List und Map.<br />

- E<strong>in</strong>e List ist e<strong>in</strong>e beliebig große Liste von Elementen beliebigen Typs, auf die wahlfrei und<br />

sequentiell zugegriffen wer<strong>de</strong>n kann<br />

- E<strong>in</strong> Set ist e<strong>in</strong>e Menge von Elementen (ohne Duplikate), auf die mit typischen Mengenoperationen<br />

zugegriffen wer<strong>de</strong>n kann.<br />

- E<strong>in</strong>e Map ist e<strong>in</strong>e Abbildung von Elementen e<strong>in</strong>es Typs auf Elemente e<strong>in</strong>es an<strong>de</strong>ren Typs (Menge<br />

zusammengehöriger Paare).<br />

Je<strong>de</strong> dieser Grundformen ist als Interface und <strong>de</strong>n angegebenen Namen<br />

implementiert. Zu<strong>de</strong>m gibt es jeweils e<strong>in</strong>e o<strong>de</strong>r mehrere konkrete<br />

Implementierungen. So gibt es bspw. für das Interface List die<br />

Implementierungsvarianten L<strong>in</strong>kedList bzw. die abstrakte Implementierung<br />

AbstractList.<br />

6.1.1 Durchwan<strong>de</strong>rn von Datenstrukturen mit Iteratoren<br />

Bei Datenstrukturen gibt es e<strong>in</strong>e Möglichkeit, gespeicherte Daten unabhängig von<br />

<strong>de</strong>r Implementierung immer mit <strong>de</strong>r gleichen Technik abzufragen. Bei<br />

Datenstrukturen han<strong>de</strong>lt es sich meistens um Daten <strong>in</strong> Listen, Bäumen o<strong>de</strong>r<br />

ähnlichem und oft wird nur die Frage nach <strong>de</strong>r Zugehörigkeit e<strong>in</strong>es Worts zum<br />

Datenbestand gestellt (z.B. „Gehört das Wort dazu?“). Auch die Möglichkeit Daten <strong>in</strong><br />

irgen<strong>de</strong><strong>in</strong>er Form aufzuzählen, ist e<strong>in</strong>e häufig gestellte Aufgabe. Hierfür bieten sich<br />

Iteratoren an. In <strong>Java</strong> umfaßt das Interface Enumeration die bei<strong>de</strong>n Funktionen<br />

hasMoreElements() und nextElement(), mit <strong>de</strong>nen durch e<strong>in</strong>e Datenstruktur<br />

iteriert wer<strong>de</strong>n kann.<br />

public <strong>in</strong>terface Enumeration<br />

{<br />

public boolean hasMoreElements();<br />

// Test, ob noch e<strong>in</strong> weiteres Element aufgezählt wer<strong>de</strong>n kann<br />

public Object nextElement() throws NoSuchElementException;<br />

/* setzt <strong>de</strong>n <strong>in</strong>ternen Zeiger auf das nächste Element, d. h. liefert das<br />

das nächste Element <strong>de</strong>r Enumertion zurück. Diese Funktion kann e<strong>in</strong>e<br />

NoSuchException auslösen, wenn nextElement() aufgerufen wird, obwohl<br />

hasMoreElements() unwahr ist<br />

*/<br />

}<br />

421


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Die Aufzählung erfolgt meistens über<br />

for (Enumeration e = ds.elements(); e.hasMoreElements(); )<br />

System.out.pr<strong>in</strong>tln(e.nextElements());<br />

Die Datenstruktur ds besitzt e<strong>in</strong>e Metho<strong>de</strong> elements(), die e<strong>in</strong> Enumeration-Objekt<br />

zurückgibt, das die Aufzählung erlaubt.<br />

6.1.2 Die Klasse Vector<br />

class java.util.Vector extends AbstractList<br />

implements List, Cloneable, Collection, Serializable<br />

Die Klasse Vector beschreibt e<strong>in</strong> Array mit variabler Länge. Objekte <strong>de</strong>r Klasse<br />

Vector s<strong>in</strong>d Repräsentationen e<strong>in</strong>er l<strong>in</strong>earen Liste. Die Liste kann Elemente<br />

beliebigen Typs enthalten, ihre Länge ist zur Laufzeit verän<strong>de</strong>rbar (Array mit variabler<br />

Länge). Vector erlaubt das E<strong>in</strong>fügen von Elementen an beliebiger Stelle, bietet<br />

sequentiellen und wahlfreien Zugriff auf die Elemente. Das JDK realisiert Vector als<br />

Array von Elementen <strong>de</strong>s Typs Object. Der Zugriff auf Elemente erfolgt über<br />

Indizes. Es wird dazu aber ke<strong>in</strong> Operator [], son<strong>de</strong>rn es wer<strong>de</strong>n Metho<strong>de</strong>n benutzt,<br />

die e<strong>in</strong>en In<strong>de</strong>x als Parameter annehmen.<br />

Anlegen e<strong>in</strong>es neuen Vektors (Konstruktor): public Vector()<br />

public Vector(<strong>in</strong>t <strong>in</strong>itialCapacity, <strong>in</strong>t capacityIncrement)<br />

// E<strong>in</strong> Vector vergrößert sich automatisch, falls mehr Elemente aufgenommen wer<strong>de</strong>n, als<br />

// ursprünglich vorgesehen (Resiz<strong>in</strong>g). Dabei sollen <strong>in</strong>itialCapacity und capacityIncrement<br />

// passend gewählt wer<strong>de</strong>n.<br />

E<strong>in</strong>fügen von Elementen: public void addElement(Object obj)<br />

// Anhängen an <strong>de</strong>s En<strong>de</strong> <strong>de</strong>r bisher vorliegen<strong>de</strong>n Liste von Elementen<br />

Eigenschaften: public boolean isEmpty()<br />

// Prüfen, ob <strong>de</strong>r Vektor leer ist<br />

public <strong>in</strong>t size()<br />

// bestimmt die Anzahl <strong>de</strong>r Elemente<br />

public <strong>in</strong>t capacity()<br />

// bestimmt die <strong>in</strong>terne Größe <strong>de</strong>s Arrays. Sie kann mit ensureCapacity() geän<strong>de</strong>rt<br />

// wer<strong>de</strong>n<br />

E<strong>in</strong>fügen an beliebiger Stelle <strong>in</strong>nerhalb <strong>de</strong>r Liste:<br />

public void <strong>in</strong>sertElementAt(Object obj, <strong>in</strong>t <strong>in</strong><strong>de</strong>x) throws<br />

ArrayIn<strong>de</strong>xOutOfBoundsException<br />

// fügt obj an die Position <strong>in</strong><strong>de</strong>x <strong>in</strong> <strong>de</strong>n "Vector" e<strong>in</strong>.<br />

Zugriff auf Elemente: Für <strong>de</strong>n sequentiellen Zugriff steht e<strong>in</strong> Iterator zur Verfügung.<br />

Wahlfreier Zugriff erfolgt über:<br />

public Object firstElement() throws NoSuchElementException;<br />

public Object lastElement() throws NoSuchElementException;<br />

public Object elementAt(<strong>in</strong>t <strong>in</strong><strong>de</strong>x) throws ArrayIn<strong>de</strong>xOutOfBoundException;<br />

firstElement() liefert das erste, lastElement() das letzte Element- Mit<br />

elementAt() wird auf das Element an <strong>de</strong>r Position <strong>in</strong><strong>de</strong>x zugegriffen. Alle 3<br />

Metho<strong>de</strong>n verursachen e<strong>in</strong>e Ausnahme, wenn das gewünschte Element nicht<br />

vorhan<strong>de</strong>n ist.<br />

Arbeitsweise <strong>de</strong>s <strong>in</strong>ternen Arrays. Der Vector vergrößert sich automatisch, falls mehr<br />

Elemente aufgenommen wer<strong>de</strong>n. Die Operation heißt Resiz<strong>in</strong>g.<br />

422


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Die Größe <strong>de</strong>s Felds. Mit capacity() erhält man die <strong>in</strong>terne Größe <strong>de</strong>s Arrays. Sie kann mit<br />

ensureCapacity() geän<strong>de</strong>rt wer<strong>de</strong>n. ensureCapacity(<strong>in</strong>t m<strong>in</strong>imumCapacity) bewirkt bei<br />

e<strong>in</strong>em Vector, daß er m<strong>in</strong><strong>de</strong>stens m<strong>in</strong>Capacity Elemente aufnehmen soll.<br />

Der Vektor verkle<strong>in</strong>ert nicht die aktuelle Kapazität, falls sie schon höher als m<strong>in</strong>Capacity ist. Zur<br />

Verän<strong>de</strong>rung dieser Größe, dient die Metho<strong>de</strong> trimToSize(). Sie reduziert die Kapazität <strong>de</strong>s<br />

Vectors auf die Anzahl <strong>de</strong>r Elemente, die gera<strong>de</strong> im Vector s<strong>in</strong>d.<br />

Die Anzahl <strong>de</strong>r Elemente kann über die Metho<strong>de</strong> size() erfragt wer<strong>de</strong>n. Sie kann über<br />

setSize(<strong>in</strong>t newSize) geän<strong>de</strong>rt wer<strong>de</strong>n. Ist die neue Größe kle<strong>in</strong>er als die alte, so wer<strong>de</strong>n die<br />

Elemente am En<strong>de</strong> <strong>de</strong>s Vectors abgeschnitten. Ist newSize größer als die alte Größe, wer<strong>de</strong>n die<br />

neu angelegten Elemente mit null <strong>in</strong>itialisiert.<br />

Bereitstellen <strong>de</strong>s Interface Enumerartion. In <strong>de</strong>r Klasse Vector liefert die Metho<strong>de</strong> public<br />

Enumeration elements() e<strong>in</strong>en Enumerator (Iterator) für alle Elemente, die sich <strong>in</strong> Vector bef<strong>in</strong><strong>de</strong>n.<br />

Vector<br />

><br />

public Vector()<br />

// E<strong>in</strong> Vector <strong>in</strong> <strong>de</strong>r Anfangsgröße von 10 Elementen wird angelegt<br />

public Vector(<strong>in</strong>t startKapazitaet)<br />

// E<strong>in</strong> Vector enthält Platz für startKapazitaet Elemente<br />

public Vector(<strong>in</strong>t startKapazitaet, <strong>in</strong>t kapazitaetsSchrittweite)<br />

><br />

public Object elementAt(<strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

// Das an <strong>de</strong>r Stelle <strong>in</strong><strong>de</strong>x bef<strong>in</strong>dliche Objekt wird zurückgegeben<br />

public <strong>in</strong>t size()<br />

public Object firstElement()<br />

public Object lastElement();<br />

public void <strong>in</strong>sertElementAt(Object obj, <strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

// fügt Object obj an <strong>in</strong><strong>de</strong>x e<strong>in</strong> und verschiebt die an<strong>de</strong>ren Elemente<br />

public void setElementAt(Object obj, <strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

public copyInto(Object e<strong>in</strong>Array[])<br />

// kopiert die Elemente <strong>de</strong>s Vektors <strong>in</strong> das Array e<strong>in</strong>Array<br />

// Falls das bereitgestellte Objektfeld nicht so groß ist wie <strong>de</strong>r Vektor,<br />

// dann tritt e<strong>in</strong>e In<strong>de</strong>xOutOfBoundsException auf<br />

public boolean conta<strong>in</strong>s(Object obj)<br />

// sucht das Element, liefert true zurück wenn o im Vector vorkommt<br />

public <strong>in</strong>t <strong>in</strong><strong>de</strong>xOf(Object obj)<br />

// sucht im Vector nach <strong>de</strong>m Objekt obj. Falls obj nicht <strong>in</strong> <strong>de</strong>r Liste ist, wird<br />

// -1 übergeben<br />

public <strong>in</strong>t lastIn<strong>de</strong>xOf(Object obj)<br />

public boolean removeElement(Object obj)<br />

// entfernt obj aus <strong>de</strong>r Liste. Konnte es entfernt wer<strong>de</strong>n, wird true<br />

// zurückgeliefert<br />

public void removeElementAt(<strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

// entfernt das Element an Stelle <strong>in</strong><strong>de</strong>x<br />

public void removeAllElements()<br />

// löscht alle Elemente<br />

public <strong>in</strong>t capacity()<br />

// gibt an, wieviel Elemente im Vektor Patz haben<br />

// (, ohne daßautomatische Größenanpassung erfolgt)<br />

public Object clone()<br />

// Implementierung <strong>de</strong>r clone()-metho<strong>de</strong> von Object, d.h. e<strong>in</strong>e Referenz<br />

// <strong>de</strong>s kopierten Fel<strong>de</strong>s wird zurückgegeben. Die Kopie ist flach.<br />

public Str<strong>in</strong>g toStr<strong>in</strong>g()<br />

Abb.: Die Klasse Vector<br />

423


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

6.1.3 Die Klasse Stack<br />

class java.util.Stack extends Vector<br />

E<strong>in</strong> Stack ist e<strong>in</strong>e nach <strong>de</strong>m LIFO-Pr<strong>in</strong>zip arbeiten<strong>de</strong> Datenstruktur. Elemente<br />

wer<strong>de</strong>n vorn (am vor<strong>de</strong>ren En<strong>de</strong> <strong>de</strong>r Liste) e<strong>in</strong>gefügt und von dort auch wie<strong>de</strong>r<br />

entnommen. In <strong>Java</strong> ist e<strong>in</strong> Stack e<strong>in</strong>e Ableitung von Vector mit neuen<br />

Zugriffsfunktionen für die Implementierung <strong>de</strong>s typischen Verhaltens von e<strong>in</strong>em<br />

Stack.<br />

Konstruktor: public Stack();<br />

H<strong>in</strong>zufügen neuer Elemente: public Object push(Object item);<br />

Zugriff auf das oberste Element:<br />

public Object pop();<br />

// Zugriff und Entfernen <strong>de</strong>s obersten Element<br />

public Object peek()<br />

// Zugriff auf das oberste Element<br />

Suche im Stack: public <strong>in</strong>t search(Object o)<br />

// Suche nach beliebigem Element,<br />

// Rueckgabewert: Distanz zwischen gefun<strong>de</strong>nem und<br />

// obersten Stack-Element bzw. –1,<br />

// falls das Element nicht da ist.<br />

Test:public boolean empty()<br />

// bestimmt, ob <strong>de</strong>r Stack leer ist<br />

Vector<br />

Stack<br />

public Stack()<br />

public Object push(Object obj)<br />

public Object pop()<br />

public Object peek()<br />

public <strong>in</strong>t search(Object obj)<br />

public boolean empty()<br />

Abb.: Die Klasse Stack<br />

Anwendungen:<br />

1. Umrechnen von Dezimalzahlen <strong>in</strong> an<strong>de</strong>re Basisdarstellungen<br />

Aufgabenstellung: Defaultmäßig wer<strong>de</strong>n Zahlen <strong>de</strong>zimal ausgegeben. E<strong>in</strong> Stapel, <strong>de</strong>r Ganzzahlen<br />

aufnimmt, kann dazu verwen<strong>de</strong>t wer<strong>de</strong>n, Zahlen bezogen auf e<strong>in</strong>e an<strong>de</strong>re Basis als 10 darzustellen.<br />

Die Funktionsweise <strong>de</strong>r Umrechnung von Dezimalzahlen <strong>in</strong> e<strong>in</strong>e Basis e<strong>in</strong>es an<strong>de</strong>ren Zahlensystem<br />

zeigen die folgen<strong>de</strong>n Beispiele:<br />

424


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

2810 = 3⋅ 8 + 4 = 348<br />

7210 = 1⋅ 64 + 0⋅ 16 + 2 ⋅ 4 + 0 = 10204<br />

= ⋅ + ⋅ + ⋅ + ⋅ + ⋅ + =<br />

53 1 32 1 16 0 8 1 4 0 2 1 110101<br />

10 2<br />

Mit e<strong>in</strong>em Stapel läßt sich die Umrechnung folgen<strong>de</strong>rmaßen unterstützen:<br />

6<br />

7 7<br />

4 4 4<br />

1 1 1 1<br />

leerer Stapel n%8=1 n%8=4 n%8=7 n%8=6<br />

n = 3553 10<br />

n/8=444 n/8=55 n/8=6 n/6=0<br />

n = 444 10<br />

n = 55 10<br />

n = 6 10<br />

n = 0 10<br />

Abb.: Umrechnung von 3553 10 <strong>in</strong> 6741 8 mit Hilfe e<strong>in</strong>es Stapel<br />

Algorithmus zur Lösung <strong>de</strong>r Aufgabe:<br />

1) Die am weitesten rechts stehen<strong>de</strong> Ziffer von n ist n%b. Sie ist auf <strong>de</strong>m Stapel abzulegen.<br />

2) Die restlichen Ziffern von n s<strong>in</strong>d bestimmt durch n/b. Die Zahl n wird ersetzt durch n/b.<br />

3) Wie<strong>de</strong>rhole die Arbeitsschritte 1) und 2) bis ke<strong>in</strong>e signifikanten Ziffern mehr übrig bleiben.<br />

4) Die Darstellung <strong>de</strong>r Zahl <strong>in</strong> <strong>de</strong>r neuen Basis ist aus <strong>de</strong>m Stapel abzulesen. Der Stapel ist zu diesem<br />

Zweck zu entleeren.<br />

Implementierung: Das folgen<strong>de</strong> kle<strong>in</strong>e Testprogramm 304 realisiert <strong>de</strong>n Algorithmus und benutzt dazu<br />

e<strong>in</strong>e Instanz von Stack.<br />

import java.util.*;<br />

public class PR61210<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

<strong>in</strong>t zahl = 3553;<br />

// Dezimalzahl<br />

<strong>in</strong>t b = 8; // Basis<br />

Stack s = new Stack(); // Stapel<br />

do<br />

{<br />

s.push(new Integer(zahl % b));<br />

zahl /= b;<br />

} while (zahl != 0);<br />

while (!s.empty())<br />

{<br />

System.out.pr<strong>in</strong>t(s.pop());<br />

}<br />

System.out.pr<strong>in</strong>tln();<br />

}<br />

}<br />

304 pr61210<br />

425


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

2. E<strong>in</strong>lesen e<strong>in</strong>er Folge ganzer Zahlen, Ausgabe <strong>de</strong>r Zahlen <strong>in</strong> umgekehrter<br />

Reihenfolge 305<br />

import java.io.*;<br />

import java.util.*;<br />

public class Reverse<br />

{<br />

public static void ma<strong>in</strong> (Str<strong>in</strong>g args[]) throws IOException<br />

{<br />

<strong>in</strong>t a[]=new <strong>in</strong>t[20];<br />

//Stapel<br />

Stack s = new Stack();<br />

// E<strong>in</strong>lesen <strong>de</strong>r ganzen Zahlen, Aufnahme <strong>de</strong>r Zahlen <strong>in</strong> e<strong>in</strong>en ganzzahligen<br />

// Array, Aufnahme <strong>de</strong>r im ganzzahligen Array gespeicherten Zahlen <strong>in</strong><br />

// e<strong>in</strong>en Stapel<br />

BufferedRea<strong>de</strong>r e<strong>in</strong>=new BufferedRea<strong>de</strong>r(new InputStreamRea<strong>de</strong>r(System.<strong>in</strong>));<br />

for ( <strong>in</strong>t i=0;i


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

6.1.4 Die Klasse Bitset für Bitmengen<br />

class java.util.BitSet implements Cloneable, Serializable<br />

Die Klasse Bitset bietet komfortable Möglichkeiten zur bitweisen Manipulation von<br />

Daten.<br />

Bitset anlegen und füllen. Mit zwei Metho<strong>de</strong>n lassen sich die Bits <strong>de</strong>s Bitsets leicht<br />

än<strong>de</strong>rn: set(<strong>in</strong>t bitNummer) und clear(<strong>in</strong>t bitNummer).<br />

Mengenor<strong>in</strong>tierte Operationen. Das Bitset erlaubt mengenorientierte Operationen mit<br />

e<strong>in</strong>er weiteren Menge.<br />

BitSet<br />

Abb.: Die Klasse BitSet<br />

public void and(BitSet bs)<br />

public void or(BitSet bs)<br />

public void xor(BitSet bs)<br />

public void andNot(Bitset set)<br />

// löscht alle Bits im Bitset, <strong>de</strong>ssen Bit <strong>in</strong> set gesetzt s<strong>in</strong>d<br />

public void clear(<strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

// Löscht e<strong>in</strong> Bit. Ist <strong>de</strong>r In<strong>de</strong>x negativ, kommt es<br />

// zur Auslösung von In<strong>de</strong>xOutOfBoundsException<br />

public void set(<strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

// Setzt e<strong>in</strong> Bit. Ist <strong>de</strong>r In<strong>de</strong>x negativ, kommt es<br />

// zur Auslösung von In<strong>de</strong>xOutOfBoundsException<br />

public boolean get(<strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

// liefert <strong>de</strong>n Wert <strong>de</strong>s Felds am übergebenen In<strong>de</strong>x,<br />

// kann In<strong>de</strong>xOutOfBoundsException auslösen.<br />

public <strong>in</strong>t size()<br />

public boolean equals(Object o)<br />

// Vergleicht sich mit e<strong>in</strong>em an<strong>de</strong>ren Bitset-Objekt o.<br />

Bsp. 306 : Anwendung <strong>de</strong>r Klasse BitSet bei <strong>de</strong>r Konstruktion e<strong>in</strong>er Menge von<br />

Primzahlen<br />

import java.util.*;<br />

public class Primtest<br />

{<br />

f<strong>in</strong>al static <strong>in</strong>t MAXPRIM = 30;<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

BitSet b = new BitSet();<br />

for (<strong>in</strong>t i = 2; i


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

}<br />

}<br />

6.1.5 Die Klasse Hashtable und assoziative Speicher<br />

E<strong>in</strong>e Hashtabelle (Hashtable) ist e<strong>in</strong> assoziativer Speicher, <strong>de</strong>r Schlüssel (keys) mit<br />

Werten verknüpft. Die Datenstruktur ist mit e<strong>in</strong>em Wörterbuch vergleichbar. Die<br />

Hashtabelle arbeitet mit Schlüssel/Werte Paaren. Aus <strong>de</strong>m Schlüssel wird nach e<strong>in</strong>er<br />

Funktion – <strong>de</strong>r sog. Hashfunktion – e<strong>in</strong> Hashco<strong>de</strong> berechnet. Dieser dient als In<strong>de</strong>x<br />

für e<strong>in</strong> <strong>in</strong>ternes Array. Dieses Array hat zu Anfang e<strong>in</strong> feste Grösse. Lei<strong>de</strong>r hat dieses<br />

Technik e<strong>in</strong>en entschei<strong>de</strong>n<strong>de</strong>n Nachteil. Besitzen zwei Wörter <strong>de</strong>nselben Hashco<strong>de</strong>,<br />

dann kommt es zu e<strong>in</strong>er Kollision. Auf ihn muß die Datenstruktur vorbereitet se<strong>in</strong>.<br />

Hier gibt es verschie<strong>de</strong>ne Lösungsansätze. Die unter <strong>Java</strong> implementierte Variante<br />

benutzt e<strong>in</strong>e verkettete Liste (separate Cha<strong>in</strong><strong>in</strong>g). Falls e<strong>in</strong>e Kollision auftritt, so wird<br />

<strong>de</strong>r Hashco<strong>de</strong> beibehalten und <strong>de</strong>r Schlüssel bzw. Wert <strong>in</strong> e<strong>in</strong>em Listenelement an<br />

<strong>de</strong>n vorhan<strong>de</strong>nen E<strong>in</strong>trag angehängt. Wenn allerd<strong>in</strong>gs irgendwann e<strong>in</strong>mal e<strong>in</strong>e Liste<br />

durchsucht wer<strong>de</strong>n muß, dann wird die Datenstruktur langsam. E<strong>in</strong> Maß für <strong>de</strong>n<br />

Füllgrad ist <strong>de</strong>r Füllfaktor (Load Factor). Dieser liegt zwischen 0 und 100 %. 0<br />

be<strong>de</strong>utet: ke<strong>in</strong> Listenelement wird verwen<strong>de</strong>t. 100 % be<strong>de</strong>utet: Es ist ke<strong>in</strong> Platz mehr<br />

im Array und es wer<strong>de</strong>n nur noch Listen für alle zukommen<strong>de</strong>n Werte erweitert. Der<br />

Füllfaktor sollte für effiziente Anwendungen nicht höher als 75% se<strong>in</strong>. Ist e<strong>in</strong><br />

Füllfaktor nicht explizit angegeben, dann wird die Hashtabelle „rehashed“, wenn mehr<br />

als 75% aller Plätze besetzt s<strong>in</strong>d.<br />

class java.util.Hashtable extends Dictionary implements Map,<br />

Cloneable, Serializable<br />

Erzeugen von e<strong>in</strong>em Objekt <strong>de</strong>r Klasse Hashtable:<br />

public Hashtable()<br />

/* Die Hashtabelle enthält e<strong>in</strong>e Kapazität von 11 E<strong>in</strong>trägen und e<strong>in</strong>en Füllfaktor von 75 % */<br />

public Hashtable(<strong>in</strong>t <strong>in</strong>itialCapacity)<br />

/* erzeugt e<strong>in</strong>e Hashtabelle mit e<strong>in</strong>er vorgebenen Kapazität und <strong>de</strong>m Füllfaktor 0.75 */<br />

public Hashtable(<strong>in</strong>t <strong>in</strong>itialCapacity, float loadFactor)<br />

/* erzeugt e<strong>in</strong>e Hashtabelle mit e<strong>in</strong>er vorgebenen Kapazität und <strong>de</strong>m angegebenen Füllfaktor */<br />

Daten e<strong>in</strong>fügen: public Object put(Object key, Object value)<br />

/* speichert <strong>de</strong>n Schlüssel und <strong>de</strong>n Wert <strong>in</strong> <strong>de</strong>r Hashtabelle. Falls sich zu <strong>de</strong>m<br />

Schlüssel schon e<strong>in</strong> E<strong>in</strong>trag <strong>in</strong> <strong>de</strong>r Hashtabelle befand, so wird dieser<br />

zurückgegeben. An<strong>de</strong>renfalls ist <strong>de</strong>r Rückgabewert null. Die Metho<strong>de</strong> ist<br />

vorgegeben vom Interface Map. Es überschreibt die Metho<strong>de</strong> von <strong>de</strong>r Superklasse<br />

Dictionary. */<br />

Daten holen: public Object get(Object key)<br />

Schlüssel entfernen. public Object remove(Object key)<br />

Löschen <strong>de</strong>r Werte. public void clear()<br />

Test. public boolean conta<strong>in</strong>sKey(Object key)<br />

// Test auf e<strong>in</strong>en bestimmten Schlüssel<br />

public boolean conta<strong>in</strong>sValue(Object value)<br />

// Test auf e<strong>in</strong>en bestimmten Wert<br />

Aufzählen <strong>de</strong>r Elemente. Mit keys() und elements() bietet die Hashtabelle zwei<br />

Metho<strong>de</strong>n an, die e<strong>in</strong>e Aufzählung zurückgeben:<br />

public Enumeration keys()<br />

// liefert e<strong>in</strong>e Aufzählung aller Schlüssel, überschreibt keys() <strong>in</strong> Dictionary.<br />

428


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

public Enumeration elements()<br />

// liefert e<strong>in</strong>e Aufzählung <strong>de</strong>r Werte, überschreibt elements() <strong>in</strong> Dictionary<br />

Wie üblich liefern bei<strong>de</strong> Iteratoren e<strong>in</strong> Objekt, welches das Interface Enumeration<br />

implementiert. Der Zugriff erfolgt daher mit Hilfe <strong>de</strong>r Metho<strong>de</strong>n hasMoreElements()<br />

und nextElement().<br />

Dictionary<br />

{abstract}<br />

public abstract Object put (Object key, Object value)<br />

public abstract Object get(Object key)<br />

public abstract Enumeration elements()<br />

public abstract Enumeration keys()<br />

public abstract <strong>in</strong>t size()<br />

public abstract boolean isEmpty()<br />

public abstract Object remove(Object key)<br />

Hashtable<br />

Map<br />

><br />

public Hashtable(<strong>in</strong>t <strong>in</strong>itialKapazitaet)<br />

public Hashtable(<strong>in</strong>t <strong>in</strong>itialKapazitaet, float La<strong>de</strong>faktor)<br />

public Hashtable()<br />

><br />

public boolean conta<strong>in</strong>s(Object wert)<br />

public boolean conta<strong>in</strong>sKey(Object key)<br />

public void clear()<br />

public Object clone()<br />

protected void rehash()<br />

public Str<strong>in</strong>g toStr<strong>in</strong>g()<br />

Properties<br />

><br />

public Properties()<br />

// legt e<strong>in</strong>en leeren Conta<strong>in</strong>er an<br />

public Properties(Properties <strong>de</strong>faults)<br />

// füllt e<strong>in</strong>e Property-Liste mit <strong>de</strong>n angegebenen Default-Werten<br />

><br />

public Str<strong>in</strong>g getProperty(Str<strong>in</strong>g key)<br />

public Str<strong>in</strong>g getProperty(Str<strong>in</strong>g key, Str<strong>in</strong>g <strong>de</strong>faultKey)<br />

public void load(InputStream <strong>in</strong>) throws IOException<br />

// Hier muß e<strong>in</strong> InputStream übergeben wer<strong>de</strong>n, <strong>de</strong>r die daten <strong>de</strong>r<br />

// Property-Liste zur Verfügung stellt.<br />

public void store(OutputStream out, Str<strong>in</strong>g hea<strong>de</strong>r)<br />

public void list(Pr<strong>in</strong>tStream out)<br />

public void list(Pr<strong>in</strong>tWriter out)<br />

public Enumeration propertyNames()<br />

// beschafft e<strong>in</strong> Enumerations-Objekt mit <strong>de</strong>nen Eigenschaften<br />

// <strong>de</strong>r Property-Liste aufgezählt wer<strong>de</strong>n können<br />

429


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Abb.: Die Klassen Hashtable und Properties<br />

Die Klasse Hashtable ist e<strong>in</strong>e Konkretisierung <strong>de</strong>r abstrakten Klasse Dictionary.<br />

Diese Klasse beschreibt e<strong>in</strong>en assoziativen Speicher, <strong>de</strong>r Schlüssel auf Werte<br />

abbil<strong>de</strong>t und über <strong>de</strong>n Schlüsselbegriff e<strong>in</strong>en effizienten Zugriff auf <strong>de</strong>n Wert<br />

ermöglicht. E<strong>in</strong>fügen und <strong>de</strong>r Zugriff auf Schlüssel erfolgt nicht auf <strong>de</strong>r Basis <strong>de</strong>s<br />

Operators „==“, son<strong>de</strong>rn mit Hilfe <strong>de</strong>r Metho<strong>de</strong> „equals“. Schlüssel müssen daher<br />

lediglich <strong>in</strong>haltlich gleich se<strong>in</strong>, um als i<strong>de</strong>ntisch angesehen zu wer<strong>de</strong>n.<br />

Beispiele.<br />

1. Hashtabelle zum Test <strong>de</strong>r von Zufallszahlen <strong>de</strong>r Metho<strong>de</strong> Math.random() 307 .<br />

import java.util.*;<br />

class Zaehler<br />

{<br />

<strong>in</strong>t i = 1;<br />

public Str<strong>in</strong>g toStr<strong>in</strong>g()<br />

{<br />

return Integer.toStr<strong>in</strong>g(i);<br />

}<br />

}<br />

public class Statistik<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

Hashtable h = new Hashtable();<br />

for (<strong>in</strong>t i = 0; i < 10000; i++)<br />

{<br />

// Erzeuge e<strong>in</strong>e Zahl zwischen 0 und 20<br />

Integer r = new Integer((<strong>in</strong>t)(Math.random() * 20));<br />

if (h.conta<strong>in</strong>sKey(r))<br />

((Zaehler) h.get(r)).i++;<br />

else h.put(r,new Zaehler());<br />

}<br />

System.out.pr<strong>in</strong>tln(h);<br />

}<br />

}<br />

2. Hashtabelle für direkten Zugriff auf Daten 308<br />

import java.io.*;<br />

import java.util.*;<br />

public class HashTabTest<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g [ ] args)<br />

{<br />

// Map map = new HashMap();<br />

Hashtable h = new Hashtable();<br />

Str<strong>in</strong>g e<strong>in</strong>gabeZeile = null;<br />

BufferedRea<strong>de</strong>r e<strong>in</strong>gabe = null;<br />

try {<br />

e<strong>in</strong>gabe = new BufferedRea<strong>de</strong>r(<br />

new FileRea<strong>de</strong>r("e<strong>in</strong>g.txt"));<br />

}<br />

catch (FileNotFoundException io)<br />

307 pr61310<br />

308 pr61310<br />

430


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

{<br />

System.out.pr<strong>in</strong>tln("Fehler beim E<strong>in</strong>lesen!");<br />

}<br />

try {<br />

while ( (e<strong>in</strong>gabeZeile = e<strong>in</strong>gabe.readL<strong>in</strong>e() ) != null)<br />

{<br />

Str<strong>in</strong>gTokenizer str = new Str<strong>in</strong>gTokenizer(e<strong>in</strong>gabeZeile);<br />

if (e<strong>in</strong>gabeZeile.equals("")) break;<br />

Str<strong>in</strong>g key = str.nextToken();<br />

Str<strong>in</strong>g daten = str.nextToken();<br />

System.out.pr<strong>in</strong>tln(key);<br />

h.put(key,daten);<br />

// map.put(key,daten);<br />

}<br />

}<br />

catch (IOException ioe)<br />

{<br />

System.out.pr<strong>in</strong>tln("E<strong>in</strong>gefangen <strong>in</strong> ma<strong>in</strong>()");<br />

}<br />

try {<br />

e<strong>in</strong>gabe.close();<br />

}<br />

catch(IOException e)<br />

{<br />

System.out.pr<strong>in</strong>tln(e);<br />

}<br />

System.out.pr<strong>in</strong>tln("Uebersicht zur Hash-Tabelle");<br />

// System.out.pr<strong>in</strong>tln(map);<br />

System.out.pr<strong>in</strong>tln(h);<br />

//h.pr<strong>in</strong>tHashTabelle();<br />

System.out.pr<strong>in</strong>tln("Abfragen bzw. Modifikationen");<br />

// Wie<strong>de</strong>rauff<strong>in</strong><strong>de</strong>n<br />

Str<strong>in</strong>g e<strong>in</strong>gabeKey = null;<br />

BufferedRea<strong>de</strong>r e<strong>in</strong> = new BufferedRea<strong>de</strong>r(<br />

new InputStreamRea<strong>de</strong>r(System.<strong>in</strong>));<br />

System.out.pr<strong>in</strong>tln("Wie<strong>de</strong>rauff<strong>in</strong><strong>de</strong>n von Elementen");<br />

while (true)<br />

{<br />

try {<br />

System.out.pr<strong>in</strong>t("Bitte Schluessel e<strong>in</strong>geben, ! be<strong>de</strong>utet En<strong>de</strong>: ");<br />

e<strong>in</strong>gabeKey = e<strong>in</strong>.readL<strong>in</strong>e();<br />

// System.out.pr<strong>in</strong>tln(e<strong>in</strong>gabeKey);<br />

if (e<strong>in</strong>gabeKey.equals("!")) break;<br />

Str<strong>in</strong>g e<strong>in</strong>tr = (Str<strong>in</strong>g) h.get(e<strong>in</strong>gabeKey);<br />

// Str<strong>in</strong>g e<strong>in</strong>tr = (Str<strong>in</strong>g) map.get(e<strong>in</strong>gabeKey);<br />

if (e<strong>in</strong>tr == null)<br />

System.out.pr<strong>in</strong>tln("Ke<strong>in</strong> E<strong>in</strong>trag!");<br />

else<br />

{<br />

System.out.pr<strong>in</strong>tln(e<strong>in</strong>tr);<br />

System.out.pr<strong>in</strong>tln("Soll dieser E<strong>in</strong>trag geloescht wer<strong>de</strong>n? ");<br />

Str<strong>in</strong>g antwort = e<strong>in</strong>.readL<strong>in</strong>e();<br />

// System.out.pr<strong>in</strong>tln(antwort);<br />

if ((antwort.equals("j")) || (antwort.equals("J")))<br />

{<br />

// System.out.pr<strong>in</strong>tln("E<strong>in</strong>trag wird entfernt!");<br />

h.remove(e<strong>in</strong>gabeKey);<br />

// map.remove(e<strong>in</strong>gabeKey);<br />

}<br />

}<br />

}<br />

catch(IOException ioe)<br />

{<br />

System.out.pr<strong>in</strong>tln(e<strong>in</strong>gabeKey +<br />

" konnte nicht korrekt e<strong>in</strong>gelesen wer<strong>de</strong>n!");<br />

}<br />

431


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

}<br />

System.out.pr<strong>in</strong>tln(h);<br />

// System.out.pr<strong>in</strong>tln(map);<br />

// System.out.pr<strong>in</strong>tln("Sortierte Tabelle");<br />

// Map sortedMap = new TreeMap(map);<br />

// System.out.pr<strong>in</strong>tln(sortedMap);<br />

}<br />

}<br />

Die Klasse Hashtable benutzt das Verfahren <strong>de</strong>r Schlüsseltransformation (Hash-<br />

Funktion) zur Abbildung von Schlüsseln auf In<strong>de</strong>xpostionen e<strong>in</strong>es Arrays. Die<br />

Kapazität <strong>de</strong>r Hash-Tabelle gibt die Anzahl <strong>de</strong>r Elemente an, die <strong>in</strong>sgesamt<br />

untergebracht wer<strong>de</strong>n können. Der La<strong>de</strong>faktor zeigt an, bei welchem Füllungsgrad<br />

die Hash-Tabelle vergrößert wer<strong>de</strong>n muß. Das Vergrößern erfolgt automatisch, falls<br />

die Anzahl <strong>de</strong>r Elemente <strong>in</strong>nerhalb <strong>de</strong>r Tabelle größer ist als das Produkt aus<br />

Kapazität und La<strong>de</strong>faktor. Seit <strong>de</strong>m JDK 1.2 darf <strong>de</strong>r La<strong>de</strong>faktor auch größer als 1<br />

se<strong>in</strong>. In diesem Fall wird die Hash-Tabelle erst dann vergrößert, wenn <strong>de</strong>r<br />

Füllungsgrad größer als 100% ist und bereits e<strong>in</strong> Teil <strong>de</strong>r Elemente <strong>in</strong> <strong>de</strong>n<br />

Überlaufbereichen untergebracht wur<strong>de</strong>.<br />

Die Klasse Hashtable ist e<strong>in</strong>e beson<strong>de</strong>re Klasse für Wörterbücher. E<strong>in</strong> Wörterbuch<br />

ist e<strong>in</strong>e Datenstruktur, die Elemente mite<strong>in</strong>an<strong>de</strong>r assoziiert. Das Wörterbuchproblem<br />

ist das Problem, wie aus <strong>de</strong>m Schlüssel möglichst schnell <strong>de</strong>r zugehörige Wert<br />

konstruiert wird. Die Lösung <strong>de</strong>s Problems ist: Der Schlüssel wird als Zahl kodiert<br />

(Hashco<strong>de</strong>) und dient <strong>in</strong> e<strong>in</strong>em Array als In<strong>de</strong>x. An e<strong>in</strong>em In<strong>de</strong>x hängen dann noch<br />

die Werte mit gleichem Hashco<strong>de</strong> als Liste an.<br />

6.1.6 Die abstrakte Klasse Dictionary<br />

Die Klasse Dictionary ist e<strong>in</strong>e abstrakte Klasse, die Metho<strong>de</strong>n anbietet, wie<br />

Objekte (also Schlüssel und Wert) mite<strong>in</strong>an<strong>de</strong>r assoziiert wer<strong>de</strong>n:<br />

public abstract Object put(Object key, Object value)<br />

// fügt <strong>de</strong>n Schlüssel key mit <strong>de</strong>m verbun<strong>de</strong>nen Wert value <strong>in</strong> das Wörterbuch<br />

// e<strong>in</strong><br />

public abstract Object get(Object key)<br />

// liefert das zu key gehören<strong>de</strong> Objekt zurück. Falls ke<strong>in</strong> Wert mit <strong>de</strong>m<br />

// Schlüssel verbun<strong>de</strong>n ist, so liefert get() e<strong>in</strong>e null. E<strong>in</strong>e null als<br />

// Schlüssel o<strong>de</strong>r Wert kann nicht e<strong>in</strong>gesetz wer<strong>de</strong>n. In put() wür<strong>de</strong> das zu<br />

// e<strong>in</strong>er NullPo<strong>in</strong>terException führen.<br />

public abstract Object remove(Object key)<br />

// entfernt e<strong>in</strong> Schlüssel/Wertepaar aus <strong>de</strong>m Wörterbuch. Zurückgegeben wird<br />

// <strong>de</strong>r assoziierte Wert.<br />

public abstract boolean isEmpty()<br />

// true, falls ke<strong>in</strong>e Werte im Wörterbuch<br />

public <strong>in</strong>t size()<br />

gibt zurück, wie viele Elemente aktuell im Wörterbuch s<strong>in</strong>d.<br />

public abstract Enumeration keys()<br />

// liefert e<strong>in</strong>e Enumeration für alle Schlüssel<br />

public abstract Enumeration elements()<br />

// liefert e<strong>in</strong>e Enumeration über alle Werte.<br />

432


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

6.1.7 Die Klasse Properties<br />

Die Properties Klasse ist e<strong>in</strong>e Erweiterung von Hashtable. E<strong>in</strong> Properties Objekt<br />

erweitert die Hashtable um die Möglichkeit, sich unter e<strong>in</strong>em wohl<strong>de</strong>f<strong>in</strong>ierten Format<br />

über e<strong>in</strong>en Strom zu la<strong>de</strong>n und zu speichern.<br />

Erzeugen. public Properties()<br />

// erzeugt e<strong>in</strong> leeres Propertes Objekt ohne Worte.<br />

public Properties(Properties p)<br />

// erzeugt e<strong>in</strong> leeres Properties Objekt mit Standard-werten aus <strong>de</strong>n<br />

// übergebenen Properties<br />

La<strong>de</strong>n e<strong>in</strong>er Properties-Liste. public void load(InputStream is)<br />

Speichern e<strong>in</strong>er Properties-Liste. public void store(OutputStream os,<br />

Str<strong>in</strong>g kennung). An <strong>de</strong>n Kopf <strong>de</strong>r Datei wird e<strong>in</strong>e kennung geschrieben (die im<br />

2. Argument angegeben ist). Die Kennung darf „null“ se<strong>in</strong>.<br />

Enumeration. public void Enumeration propertyNames()<br />

Zeichenketten suchen. Die Metho<strong>de</strong> getProperty(Str<strong>in</strong>g s).<br />

public Str<strong>in</strong>g getProperty(Str<strong>in</strong>g s)<br />

// sucht <strong>in</strong> <strong>de</strong>n Properties nach <strong>de</strong>r Zeichenkette<br />

public Str<strong>in</strong>g getProperty(Str<strong>in</strong>g key, Str<strong>in</strong>g <strong>de</strong>fault)<br />

// sucht <strong>in</strong> <strong>de</strong>n Properties nach <strong>de</strong>r Zeichenkette key. Ist dieser nicht<br />

// vorhan<strong>de</strong>n, wird <strong>de</strong>r Str<strong>in</strong>g <strong>de</strong>fault zurückgegeben<br />

Eigenschaften ausgeben. Die Metho<strong>de</strong> list() wan<strong>de</strong>rt durch die Daten und gibt sie<br />

auf e<strong>in</strong>em Pr<strong>in</strong>tWriter aus:<br />

public void list(Pr<strong>in</strong>tWriter pw)<br />

// listet die Properties auf <strong>de</strong>m Pr<strong>in</strong>tWriter aus.<br />

Bsp.: Systemeigenschaften <strong>de</strong>r <strong>Java</strong>-Umgebung<br />

import java.util.*;<br />

import java.io.*;<br />

public class SaveProp<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args [])<br />

{<br />

try {<br />

Properties p1 = System.getProperties();<br />

FileOutputStream propAusFile = new<br />

FileOutputStream("properties.txt");<br />

p1.store(propAusFile,null);<br />

propAusFile.close();<br />

Properties p2 = new Properties();<br />

FileInputStream e<strong>in</strong>g = new FileInputStream("properties.txt");<br />

p2.load(e<strong>in</strong>g);<br />

p2.list(System.out);<br />

}<br />

catch(IOException e) { System.err.pr<strong>in</strong>tln(e);}<br />

}<br />

}<br />

433


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

6.2 Collection API<br />

Die <strong>Java</strong> 2 Plattform hat <strong>Java</strong> erweitert um das Collection API. Anstatt Collection<br />

kann man auch Conta<strong>in</strong>er (Behälter) sagen. E<strong>in</strong> Conta<strong>in</strong>er ist e<strong>in</strong> Objekt, das<br />

wie<strong>de</strong>rum Objekte aufnimmt und die Verantwortung für die Elemente übernimmt. Im<br />

„util“-Paket bef<strong>in</strong><strong>de</strong>n sich sechs Schnittstellen, die grundlegen<strong>de</strong> Eigenschaften<br />

<strong>de</strong>r Conta<strong>in</strong>erklassen <strong>de</strong>f<strong>in</strong>ieren.<br />

Das <strong>in</strong> <strong>Java</strong> 1.2 enthaltene Collections Framework be<strong>in</strong>haltet im Wesentlichen drei<br />

Grundformen: Set, List und Map. Je<strong>de</strong> dieser Grudformen ist als Interface<br />

implementiert. Die Interfaces List und Set s<strong>in</strong>d direkt aus Collection abgeleitet. Es<br />

gibt auch noch e<strong>in</strong>e abstrakte Implementierung <strong>de</strong>s Interface, mit <strong>de</strong>ssen Hilfe das<br />

Erstellen eigener Collections erleichtert wird. Bei allen Collections, die das Interface<br />

Collection implementieren, kann e<strong>in</strong> Iterator zum Durchlaufen <strong>de</strong>r Elemente mit <strong>de</strong>r<br />

Metho<strong>de</strong> „iterator()“ beschafft wer<strong>de</strong>n.<br />

Zusätzlich for<strong>de</strong>rt die JDK 1.2-Spezifikation für je<strong>de</strong> Collection-Klasse zwei<br />

Konstruktoren:<br />

- E<strong>in</strong>en parameterlosen Konstruktor zum Anlegen e<strong>in</strong>er neuen Collection.<br />

- E<strong>in</strong> mit e<strong>in</strong>em e<strong>in</strong>zigen Collection-Argument ausgestatteter Konstruktor, <strong>de</strong>r e<strong>in</strong>e neue Collection anlegt und<br />

mit <strong>de</strong>n Elementen <strong>de</strong>r als Argument übergebenen Collection auffüllt.<br />

6.2.1 Die Schnittstellen Collection, Iterator, Comparator<br />

Das Interface Collection bil<strong>de</strong>t die Basis <strong>de</strong>r Collection-Klasse und –Interfaces <strong>de</strong>s<br />

JDK 1.2. Alle Behälterklassen implementieren das Collection Interface und geben<br />

<strong>de</strong>n Klassen damit e<strong>in</strong>en äußeren Rahmen.<br />

434


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Das Interface Collection<br />

><br />

Collection<br />

public void clear();<br />

// Optional: Löscht alle Elemente <strong>in</strong> <strong>de</strong>m Conta<strong>in</strong>er. Eird dies vom Conta<strong>in</strong>er nicht unterstützt,<br />

// kommt es zur UnsupportedOperationException<br />

public boolean add(Object o);<br />

// Optional: Fügt e<strong>in</strong> Objekt <strong>de</strong>m Conta<strong>in</strong>er h<strong>in</strong>zu und gibt true zurück, falls sich das Element<br />

// e<strong>in</strong>fügen läßt. Gibt false zurück, falls schon e<strong>in</strong> Objektwert vorhan<strong>de</strong>n ist und doppelte Werte<br />

// nicht erlaubt s<strong>in</strong>d.<br />

public boolean addAll(Collection c);<br />

// fügt alle Elemente <strong>de</strong>r Collection c <strong>de</strong>m Conta<strong>in</strong>er h<strong>in</strong>zu<br />

public boolean remove(Object o);<br />

// Entfernen e<strong>in</strong>er e<strong>in</strong>zelnen Instanz. Rückgabewert ist true, wenn das Element gefun<strong>de</strong>n und<br />

// entfernt wer<strong>de</strong>n konnte<br />

public boolean removeAll(Collection c);<br />

// Oprtional: Entfernt alle Objekte <strong>de</strong>r Collection c aus <strong>de</strong>m Conta<strong>in</strong>er<br />

public boolean conta<strong>in</strong>s(Object o);<br />

// liefert true, falls <strong>de</strong>r Conta<strong>in</strong>er das Element enthält<br />

// Rückgabewert ist true, falls das vorgegebene Element gefun<strong>de</strong>n wer<strong>de</strong>n konnte<br />

public boolean conta<strong>in</strong>sAll(Collection c);<br />

// liefert true, falls <strong>de</strong>r Conta<strong>in</strong>er alle Elemente <strong>de</strong>r Collection c enthält.<br />

public boolean equals(Object o);<br />

// vergleicht das angegebene Objekt mit <strong>de</strong>m Conta<strong>in</strong>er, ob die gleichen Elemente vorkommen.<br />

public boolean isEmpty();<br />

// liefert true, falls <strong>de</strong>r Conta<strong>in</strong>er ke<strong>in</strong>e Elemente enthält<br />

public <strong>in</strong>t size();<br />

// gibt die Größe <strong>de</strong>s Conta<strong>in</strong>ers zurück<br />

public boolean reta<strong>in</strong>All(Collection c);<br />

public Iterator iterator();<br />

public Object[] toArray();<br />

// gibt e<strong>in</strong> Array mit Elementen <strong>de</strong>s Conta<strong>in</strong>ers zurück<br />

public Object[] toArray(Object[] a);<br />

public <strong>in</strong>t hashCo<strong>de</strong>();<br />

// liefert <strong>de</strong>n Hashwert <strong>de</strong>s Conta<strong>in</strong>ers<br />

public Str<strong>in</strong>g toStr<strong>in</strong>g()<br />

// Rückgabewert ist die Zeichenketten-Repräsentation <strong>de</strong>r Kollektion.<br />

Abb.: Das Interface Collection<br />

Die abstrakte Basisklasse AbstractCollection implementiert die Metho<strong>de</strong>n <strong>de</strong>s<br />

Interface Collection (ohne iterator() und size()). AbstractCollection<br />

ist die Basisklasse von AbstractList und AbstractSet.<br />

435


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Das Interface Iterator<br />

><br />

Iterator<br />

public boolean hasNext();<br />

// gibt true zurück, wenn <strong>de</strong>r Iterator m<strong>in</strong><strong>de</strong>stens e<strong>in</strong> weiteres Element enthält.<br />

public Object next();<br />

// liefert das nächste Element bzw. löst e<strong>in</strong>e Ausnahme <strong>de</strong>s Typs NoSuchElementException<br />

// aus, wenn es ke<strong>in</strong>e weiteren Elemente gibt<br />

public void remove();<br />

// entfernt das Element, das <strong>de</strong>r Iterator bei next() geliefert hat.<br />

Abb.: Das Interface Iterator<br />

Bei allen Collections, die das Interface Collection implementieren, kann e<strong>in</strong> Iterator<br />

zum Durchlaufen <strong>de</strong>r Elemente mit <strong>de</strong>r Metho<strong>de</strong> „iterator()“ beschafft wer<strong>de</strong>n.<br />

Das Interface Comparator<br />

Vergleiche zwischen Objekten wer<strong>de</strong>n mit speziellen Objekten vorgenommen, <strong>de</strong>n<br />

Comparatoren. E<strong>in</strong> konkreter Comparator implementiert die folgen<strong>de</strong> Schnittstelle.<br />

><br />

java.util.Comparator<br />

public <strong>in</strong>t compare(Object o1, Object o2)<br />

// vergleicht 2 Argumente auf ihre Ordnung<br />

public boolean equals(Object arg)<br />

// testet, ob zwei Objekte bzgl. <strong>de</strong>s Comparator-Objekts gleich s<strong>in</strong>d<br />

Abb. Das Interface Comparator<br />

6.2.2 Die Behälterklassen und Schnittstellen <strong>de</strong>s Typs List<br />

Behälterklassen <strong>de</strong>s Typs List fassen e<strong>in</strong>e Menge von Elementen zusammen, auf die<br />

sequentiell o<strong>de</strong>r über In<strong>de</strong>x (-positionen) zugegriffen wer<strong>de</strong>n kann. Wie Vektoren <strong>de</strong>r<br />

Klasse Vector 309 hat das erste Element <strong>de</strong>n In<strong>de</strong>x 0 und das letzte <strong>de</strong>n In<strong>de</strong>x<br />

„size() – 1“. Es ist möglich an e<strong>in</strong>er beliebigen Stelle e<strong>in</strong> Element e<strong>in</strong>zufügen<br />

o<strong>de</strong>r zu löschen. Die weiter h<strong>in</strong>ten stehen<strong>de</strong>n Elemente wer<strong>de</strong>n dann entsprechend<br />

weiter nach rechts bzw. nach l<strong>in</strong>ks verschoben.<br />

309 seit <strong>Java</strong> 1.2 implementiert die Klasse Vector die Schnittstelle List<br />

436


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Das Interface List 310<br />

><br />

List<br />

public void add(<strong>in</strong>t <strong>in</strong><strong>de</strong>x, Object element);<br />

// E<strong>in</strong>fügen e<strong>in</strong>es Elements an <strong>de</strong>r durch In<strong>de</strong>x spezifizierten Position<br />

public boolean add(Object o);<br />

// Anhängen e<strong>in</strong>es Elements ans En<strong>de</strong> <strong>de</strong>r Liste<br />

// Rückgabewert ist true, falls die Liste durch <strong>de</strong>n Aufruf von add verän<strong>de</strong>rt wur<strong>de</strong>. Er ist false,<br />

// wenn die Liste nicht verän<strong>de</strong>rt wur<strong>de</strong>. Das kann bspw. <strong>de</strong>r Fall se<strong>in</strong>, wenn die Liste ke<strong>in</strong>e<br />

// Duplikate erlaubt und e<strong>in</strong> bereits vorhan<strong>de</strong>nes Element noch e<strong>in</strong>mal e<strong>in</strong>gefügt wer<strong>de</strong>n soll.<br />

// Konnte das Element aus e<strong>in</strong>em an<strong>de</strong>ren Grund nicht e<strong>in</strong>gefügt wer<strong>de</strong>n, wird e<strong>in</strong>e Ausnahme<br />

// <strong>de</strong>s Typs UnsupportedOperationException, ClassCastException o<strong>de</strong>r IllegalArgumentException<br />

// ausgelöst<br />

public boolean addAll(Collection c);<br />

// E<strong>in</strong>fügen e<strong>in</strong>er vollständigen Collection <strong>in</strong> die Liste. Der Rückgabewert ist true, falls die Liste<br />

// durch <strong>de</strong>n Ausfruf von add verän<strong>de</strong>r wur<strong>de</strong><br />

public boolean addAll(<strong>in</strong>t <strong>in</strong><strong>de</strong>x, Collection c)<br />

public void clear();<br />

public boolean equals(Object object);<br />

public boolean conta<strong>in</strong>s(Object element);<br />

public boolean conta<strong>in</strong>sAll(Collection collection);<br />

public Object remove(<strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

public boolean remove(Object element);<br />

public boolean removeAll(Collection c);<br />

// Alle Elemente wer<strong>de</strong>n gelöscht, die auch <strong>in</strong> <strong>de</strong>r als Argument angebenen<br />

// Collection enthalten s<strong>in</strong>d.<br />

public boolean reta<strong>in</strong>All(Collection c);<br />

// löscht alle Elemente außer <strong>de</strong>n <strong>in</strong> <strong>de</strong>r Argument-Collection enthaltenen<br />

public Object get(<strong>in</strong>t <strong>in</strong><strong>de</strong>x);<br />

public <strong>in</strong>t hashCo<strong>de</strong>();<br />

public Iterator iterator();<br />

public ListIterator listIterator():<br />

public ListIterator listIterator(<strong>in</strong>t startIn<strong>de</strong>x);<br />

public Object set(<strong>in</strong>t <strong>in</strong><strong>de</strong>x, Object element);<br />

public List subList(<strong>in</strong>t fromIn<strong>de</strong>x, <strong>in</strong>t toIn<strong>de</strong>x);<br />

public Object[] toArray();<br />

public Object[] toArray(Object[] a);<br />

Abb.: Das Interface List<br />

Auf die Elemente e<strong>in</strong>er Liste läßt sich mit e<strong>in</strong>em In<strong>de</strong>x zugreifen und nach Elementen<br />

läßt sich mit l<strong>in</strong>earem Aufwand suchen. Doppelte Elemente s<strong>in</strong>d erlaubt. Die<br />

Schnittstelle List, die <strong>in</strong> ArrayList und L<strong>in</strong>kedList e<strong>in</strong>e Implementierung f<strong>in</strong><strong>de</strong>t, erlaubt<br />

sequentiellen Zugriff auf die gespeicherten gespeicherten Elemente. Das Interface<br />

List wird im JDK von verschie<strong>de</strong>nen Klassen implementiert:<br />

AbstractList<br />

ist e<strong>in</strong>e abstrakte Basisklasse (für eigene List-Implementierungen), bei <strong>de</strong>r alle<br />

Metho<strong>de</strong>n die Ausnahme UnsupportedOperationException auslösen und diverse<br />

Metho<strong>de</strong>n abstract <strong>de</strong>klariert s<strong>in</strong>d. Die direkten Subklassen s<strong>in</strong>d<br />

310 Da ebenfalls das AWT-Paket e<strong>in</strong>e Klasse mit gleichen Namen verwen<strong>de</strong>t, muß <strong>de</strong>r voll qualifizierte Name <strong>in</strong><br />

Anwendungen benutzt wer<strong>de</strong>n.<br />

437


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

AbstractSequentialList, ArrayList und Vector.java.util.<br />

AbstractList implementiert bereits viele Metho<strong>de</strong>n für die bei<strong>de</strong>n Listen-<br />

Klassen 311 :<br />

abstract class AbstractList extends AbstractCollection implements List<br />

><br />

public void add(<strong>in</strong>t <strong>in</strong><strong>de</strong>x, Object element)<br />

// Optional: Fügt e<strong>in</strong> Objekt an <strong>de</strong>r spezifizierten stelle e<strong>in</strong><br />

public boolean add(Object o)<br />

// Optional: Fügt das Element am En<strong>de</strong> an<br />

public boolean addAll(<strong>in</strong>t <strong>in</strong><strong>de</strong>x, Collection c)<br />

// Optional: Fügt alle Elemente <strong>de</strong>r Collection e<strong>in</strong><br />

public void clear()<br />

// Optional: Löscht alle Elemente<br />

public boolean equals(Object o)<br />

// vergleicht die Liste mit <strong>de</strong>m Objekt<br />

public abstract Object get(<strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

// liefert das Element an dieser Stelle<br />

<strong>in</strong>t hashCo<strong>de</strong>()<br />

// liefert HashCo<strong>de</strong> <strong>de</strong>r Liste<br />

<strong>in</strong>t <strong>in</strong><strong>de</strong>xOf(Object o)<br />

// liefert Position <strong>de</strong>s ersten Vorkommens für o o<strong>de</strong>r –1,<br />

// wenn das Element nicht existiert.<br />

Iterator iterator()<br />

// liefert <strong>de</strong>n Iterator. Überschreibt die Metho<strong>de</strong> AbstractCollection,<br />

// obwohl es auch listIterator() für die spezielle Liste gibt. Die Metho<strong>de</strong><br />

// ruft aber listIterator() auf und gibt e<strong>in</strong> ListIterator-Objekt zurück<br />

Object remove(<strong>in</strong>t <strong>in</strong><strong>de</strong>x)<br />

// löscht e<strong>in</strong> Element an Position <strong>in</strong><strong>de</strong>x.<br />

protected void removeRange(<strong>in</strong>t fromIn<strong>de</strong>x, <strong>in</strong>t toIn<strong>de</strong>x)<br />

// löscht Teil <strong>de</strong>r Liste von fromIn<strong>de</strong>x bis toIn<strong>de</strong>x. fromIn<strong>de</strong>x wird mitgelöscht,<br />

// toIn<strong>de</strong>x nicht.<br />

public Object set(<strong>in</strong>t <strong>in</strong><strong>de</strong>x, Object element)<br />

// Optional. Ersetzt das Element an <strong>de</strong>r Stelle <strong>in</strong><strong>de</strong>x mit element.<br />

public List subList(<strong>in</strong>t fromIn<strong>de</strong>x, <strong>in</strong>t toIn<strong>de</strong>x)<br />

// liefert Teil e<strong>in</strong>er Liste fromIn<strong>de</strong>x (e<strong>in</strong>schließlich) bis toIn<strong>de</strong>x (nicht mehr dabei)<br />

Abb.: Die abstrakte Klasse AbstractList<br />

AbstractSequentialList<br />

bereitet die Klasse L<strong>in</strong>kedList darauf vor, die Elemente <strong>in</strong> e<strong>in</strong>er Liste zu<br />

verwalten und nicht wie ArrayList <strong>in</strong> e<strong>in</strong>em <strong>in</strong>ternen Array.<br />

L<strong>in</strong>kedList<br />

realisiert die doppelt verkettete, l<strong>in</strong>eare Liste und implementiert List.<br />

ArrayList<br />

implementiert die Liste als Feld von Elementen und implementiert List. Da<br />

ArrayList e<strong>in</strong> Feld ist, ist <strong>de</strong>r Zugriff auf e<strong>in</strong> spezielles Element sehr schnell. E<strong>in</strong>e<br />

L<strong>in</strong>kedList muß aufwendiger durchsucht wer<strong>de</strong>n. Die verkettete Liste ist aber<br />

<strong>de</strong>utlich im Vorteil, wenn Elemente gelöscht o<strong>de</strong>r e<strong>in</strong>gefügt wer<strong>de</strong>n.<br />

311 Der Aufruf e<strong>in</strong>er optionalen Metho<strong>de</strong>, die von <strong>de</strong>r Subklasse nicht implementiert wird, führt zur<br />

UnsupportedOperationException.<br />

438


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

><br />

Collection<br />

><br />

List<br />

L<strong>in</strong>kedList ArrayList Vector<br />

> ><br />

public L<strong>in</strong>kedList();<br />

public ArrayList();<br />

public L<strong>in</strong>kedList(Collection collection); public ArrayList(Collection collection);<br />

public ArrayList(<strong>in</strong>t anfangsKapazitaet);<br />

> ><br />

public void addFirst(Object object); protected void removeRange<br />

public void addLast(Object object);<br />

(<strong>in</strong>t fromIn<strong>de</strong>x, <strong>in</strong>t toIn<strong>de</strong>x)<br />

public Object getFirst();<br />

// löscht Teil <strong>de</strong>r Liste von<br />

public Object getLast();<br />

// fromIn<strong>de</strong>x bis toIn<strong>de</strong>x. fromIn<strong>de</strong>x wird<br />

public Object removeFirst();<br />

// mitgelöscht, toIn<strong>de</strong>x nicht.<br />

public Object removeLast();<br />

Abb. L<strong>in</strong>kedList, ArrayList, Vector<br />

Das Interface ListIterator<br />

><br />

ListIterator<br />

public boolean hasPrevious();<br />

// bestimmt, ob es vor <strong>de</strong>r aktuellen Position e<strong>in</strong> weiteres Element gibt, <strong>de</strong>r Zugriff ist mit<br />

// previous möglich<br />

public boolean hasNext();<br />

public Object next();<br />

public Object previous();<br />

public <strong>in</strong>t nextIn<strong>de</strong>x();<br />

public <strong>in</strong>t previousIn<strong>de</strong>x();<br />

public void add(Object o);<br />

// E<strong>in</strong>fügen e<strong>in</strong>es neuen Elements an <strong>de</strong>r Stelle <strong>de</strong>r Liste, die unmittelbar vor <strong>de</strong>m nächsten<br />

// Element <strong>de</strong>s Iterators liegt<br />

public void set(Object o);<br />

// erlaubt, das durch <strong>de</strong>n letzten Aufruf von next() bzw. previous() beschaffene Element zu<br />

// ersetzen<br />

public void remove();<br />

Abb.: Das Interface ListIterator<br />

ListIterator ist e<strong>in</strong>e Erweiterung von Iterator. Die Schnittstelle fügt noch<br />

Metho<strong>de</strong>n h<strong>in</strong>zu, damit an aktueller Stelle auch Elemente e<strong>in</strong>gefügt wer<strong>de</strong>n können.<br />

Mit e<strong>in</strong>em ListIterator läßt sich rückwärts laufen und auf das vorgehen<strong>de</strong><br />

Element zugreifen.<br />

439


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

6.2.3 Behälterklassen <strong>de</strong>s Typs Set<br />

E<strong>in</strong> Set ist e<strong>in</strong>e Menge, <strong>in</strong> <strong>de</strong>r ke<strong>in</strong>e doppelten E<strong>in</strong>träge vorkommen können. Set hat<br />

die gleichen Metho<strong>de</strong>n wie Collection. Standard-Implementierung für Set s<strong>in</strong>d das<br />

unsortierte HashSet (Array mit verän<strong>de</strong>rlicher Größe) und das sortierte TreeSet<br />

(B<strong>in</strong>ärbaum).<br />

Bsp.<br />

import java.util.*;<br />

public class SetBeispiel<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args [])<br />

{<br />

Set set = new HashSet();<br />

set.add("Gerhard");<br />

set.add("Thomas");<br />

set.add("Michael");<br />

set.add("Peter");<br />

set.add("Christian");<br />

set.add("Valent<strong>in</strong>a");<br />

System.out.pr<strong>in</strong>tln(set);<br />

Set sortedSet = new TreeSet(set);<br />

System.out.pr<strong>in</strong>tln(sortedSet);<br />

}<br />

}<br />

440


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

><br />

Collection<br />

><br />

Set<br />

public boolean add(Object element);<br />

public boolean addAll(Collection collection);<br />

public void clear();<br />

public boolean equals(Object object);<br />

public boolean conta<strong>in</strong>s(Object element);<br />

public boolean conta<strong>in</strong>sAll(Collection collection);<br />

public <strong>in</strong>t hashCo<strong>de</strong>();<br />

public Iterator iterator();<br />

public boolean remove(Object element);<br />

public boolean removeAll(Collection collection);<br />

public boolean reta<strong>in</strong>All(Collection collection);<br />

public <strong>in</strong>t size();<br />

public Object[] toArray();<br />

public Object[] toArray(Object[] a);<br />

HashSet ><br />

SortedSet<br />

public HashSet();<br />

public HashSet(Collection collection);<br />

public HashSet(<strong>in</strong>t anfangskapazitaet);<br />

public HashSet(<strong>in</strong>t anfangskapazitaet,<br />

float la<strong>de</strong>faktor);<br />

public Object first();<br />

public Object last();<br />

public SortedSet headSet(Object toElement);<br />

public SortedSet subSet(Object fromElement,<br />

Object toElement);<br />

public SortedSet tailSet(Object fromElement);<br />

public Comparator comparator();<br />

TreeSet 312<br />

public TreeSet()<br />

public TreeSet(Collection collection);<br />

public TreeSet(Comparator vergleich);<br />

public TreeSet(SortedSet collection);<br />

Abb.:<br />

312 implementiert die sortierte Menge mit Hilfe <strong>de</strong>r Klasse TreeMap, verwen<strong>de</strong>t e<strong>in</strong>en Red-Black-Tree als<br />

Datenstruktur<br />

441


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Die Schnittstelle Set<br />

ist e<strong>in</strong>e im mathematischen S<strong>in</strong>ne <strong>de</strong>f<strong>in</strong>ierte Menge von Objekten. Die Reihenfolge<br />

wird durch das E<strong>in</strong>fügen festgelegt. Wie von mathematischen Mengen bekannt, darf<br />

e<strong>in</strong> Set ke<strong>in</strong>e doppelten Elemente enthalten. Beson<strong>de</strong>re Beachtung muß Objekten<br />

geschenkt wer<strong>de</strong>n, die ihren Wert nachträglich än<strong>de</strong>rn. Die kann e<strong>in</strong> Set nicht<br />

kontrollieren. E<strong>in</strong>e Menge kann sich nicht selbst als Element enthalten.<br />

Zwei Klassen ergeben sich aus Set: die abstrakte Klasse AbstractSet und die<br />

konkrete Klasse HashSet.<br />

Die Schnittstelle SortedSet<br />

erweitert Set so, daß Elemente sortiert ausgelesen wer<strong>de</strong>n können. Das<br />

Sortierkriterium wird durch die Hilfsklasse Comparator gesetzt.<br />

442


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

6.2.4 Behälterklassen <strong>de</strong>s Typs Map<br />

E<strong>in</strong> Map ist e<strong>in</strong>e Menge von Elementen, auf die über Schlüssel zugegriffen wird.<br />

Je<strong>de</strong>m Schlüssel (key) ist genau e<strong>in</strong> Wert (value) zugeordnet. Standard-<br />

Implementierungem s<strong>in</strong>d HashMap, HashTable und TreeMap.<br />

Interface Map, SortedMap und implemetieren<strong>de</strong> Klassen<br />

><br />

Collection<br />

><br />

Map<br />

public void clear();<br />

public boolean conta<strong>in</strong>sKey(Object key);<br />

public boolean conta<strong>in</strong>sValue(Object value);<br />

public Set entrySet();<br />

public Object get(Object key);<br />

public boolean isEmpty();<br />

public Set keySet();<br />

public Object remove(Object key);<br />

public <strong>in</strong>t size();<br />

public Collection values();<br />

HashMap ><br />

SortedMap<br />

public HashMap();<br />

public HashMap(Map collection);<br />

public HashMap(<strong>in</strong>t anfangskapazitaet);<br />

public HashMap(<strong>in</strong>t anfangskapazitaet,<br />

<strong>in</strong>t la<strong>de</strong>faktor);<br />

public Comparator comparator();<br />

public Object firstKey();<br />

public Object lastKey();<br />

public SortedMap headMap(Object toKey);<br />

public SortedMap subMap(Object fromKey,<br />

Object toKey);<br />

public SortedMap tailMap(Object fromKey);<br />

Hashtable<br />

TreeMap<br />

public TreeMap();<br />

public TreeMap(Map collection);<br />

public TreeMap(Comparator vergleich);<br />

public TreeMap(SortedMap collection);<br />

Abb.:<br />

Die Schnittstelle Map<br />

443


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

E<strong>in</strong>e Klasse, die Map implementiert, behan<strong>de</strong>lt e<strong>in</strong>en assoziativen Speicher. Dieser<br />

verb<strong>in</strong><strong>de</strong>t e<strong>in</strong>en Schlüssel mit e<strong>in</strong>em Wert. Die Klasse Hashtable erbt von Map.<br />

Map ist für die implementieren<strong>de</strong>n Klassen AbstractMap, HashMap, Hashtable,<br />

Ren<strong>de</strong>r<strong>in</strong>gH<strong>in</strong>ts, WeakHashMap und Attributes das, was die abstrakte Klasse<br />

Dictionary für die Klasse Hashtable ist.<br />

Die Schnittstelle SortedMap<br />

E<strong>in</strong>e Map kann mit Hilfe e<strong>in</strong>es Kriteriums sortiert wer<strong>de</strong>n und nennt sich dann<br />

SortedMap. SortedMap erweitert direkt Map. Das Sortierkriterium wird mit e<strong>in</strong>em<br />

speziellen Objekt, das sich Comparator nennt, gesetzt. Damit besitzt auch <strong>de</strong>r<br />

assoziative Speicher über e<strong>in</strong>en Iterator e<strong>in</strong>e Reihenfolge. Nur die konkrete Klasse<br />

TreeMap implementiert bisher e<strong>in</strong>e SortedMap.<br />

Die abstrakte Klasse AbstractMap<br />

implementiert die Schnittstelle Map.<br />

Die konkrete Klasse HashMap<br />

implementiert e<strong>in</strong>en assoziativen Speicher, erweitert die Klasse AbstractMap und<br />

implementiert die Schnittstelle Map.<br />

Die konkrete Klasse TreeMap<br />

Erweitert AbstractMap und implementiert SortedMap. E<strong>in</strong> Objekt von TreeMap hält<br />

Elemente <strong>in</strong> e<strong>in</strong>em Baum sortiert.<br />

444


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

6.2.5 Algorithmen<br />

Die Wahl e<strong>in</strong>er geeigneten Datenstruktur ist <strong>de</strong>r erste Schritt. Im zweiten Schritt<br />

müssen die Algorithmen implementiert wer<strong>de</strong>n. Die <strong>Java</strong> Bibliothek hilft mit e<strong>in</strong>igen<br />

Standardalgorithmen weiter. Dazu zählen Funktionen zum Sortieren und Suchen <strong>in</strong><br />

Conta<strong>in</strong>ern und das Füllen von Conta<strong>in</strong>ern. Zum flexiblen E<strong>in</strong>satz dieser Funktionen<br />

haben die <strong>Java</strong>-Entwickler die Klasse Collections bereitgestellt. Collections bietet<br />

Algorithmen statischer Funktionen an, die als Parameter e<strong>in</strong> Collection Objekt<br />

erwarten. Lei<strong>de</strong>r s<strong>in</strong>d viele Algorithmen nur auf List Objekte <strong>de</strong>f<strong>in</strong>iert, z.B.<br />

public static void shuffle(List list)<br />

// würfelt die Werte e<strong>in</strong>er Liste durche<strong>in</strong>an<strong>de</strong>r<br />

Bsp.<br />

import java.util.*;<br />

public class VectorShuffle<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

Vector v = new Vector();<br />

for (<strong>in</strong>t i = 0; i < 10; i++)<br />

v.add(new Integer(i));<br />

Collections.shuffle(v);<br />

System.out.pr<strong>in</strong>tln(v);<br />

}<br />

}<br />

public static void shuffle(List list, Random rnd)<br />

// würfelt die werte <strong>de</strong>r Liste durche<strong>in</strong>an<strong>de</strong>r und benutzt dabei <strong>de</strong>n Random Generator rnd.<br />

Nur die Metho<strong>de</strong>n m<strong>in</strong>() und max() arbeiten auf allgeme<strong>in</strong>en Collection-Objekten.<br />

6.2.5.1 Datenmanipulation<br />

Daten umdrehen. Die Metho<strong>de</strong> reverse() dreht die Werte e<strong>in</strong>er Liste um. Die<br />

Laufzeit ist l<strong>in</strong>ear zu <strong>de</strong>r Anzahl <strong>de</strong>r Elemente.<br />

public static void reverse(List l)<br />

// dreht die Elemente <strong>in</strong> <strong>de</strong>r Liste um<br />

Listen füllen. Mit <strong>de</strong>r fill()-Metho<strong>de</strong> läßt sich e<strong>in</strong>e Liste <strong>in</strong> l<strong>in</strong>earer Zeit belegen.<br />

Nützlich ist dies, wenn e<strong>in</strong>e Liste mit Werten <strong>in</strong>itialisiert wer<strong>de</strong>n muß.<br />

public static void fill (List l, Object o)<br />

// füllt e<strong>in</strong>e Liste mit <strong>de</strong>m Element o<br />

Daten zwischen Listen kopieren. Die Metho<strong>de</strong> copy(List quelle, List ziel)<br />

kopiert alle Elemente von quelle <strong>in</strong> die Liste ziel und überschreibt dabei<br />

Elemente, die evtl. an dieser Stelle liegen.<br />

public static void copy(List quelle, List ziel)<br />

// kopiert Elemente von quelle nach ziel. Ist ziel zu kle<strong>in</strong>, gibt es e<strong>in</strong>e In<strong>de</strong>xOutOfBoundsException<br />

445


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

6.2.5.2 Größter und kle<strong>in</strong>ster Wert e<strong>in</strong>er Collection<br />

Die Metho<strong>de</strong>n m<strong>in</strong>() und max() suchen das größte und kle<strong>in</strong>ste Element e<strong>in</strong>er<br />

Collection. Die Laufzeit ist l<strong>in</strong>ear zur Größe <strong>de</strong>r Collection. Die Metho<strong>de</strong>n machen<br />

ke<strong>in</strong>en Unterschied, ob die Liste schon sortiert ist o<strong>de</strong>r nicht.<br />

public static Object m<strong>in</strong>(Collection c)<br />

//<br />

public static Object max(Collection c)<br />

/* Falls m<strong>in</strong>() bzw. max() auf e<strong>in</strong> Collection-Objekt angewen<strong>de</strong>t wird, erfolgt die Bestimmung <strong>de</strong>s<br />

M<strong>in</strong>imums bzw. Maximums nach <strong>de</strong>r Metho<strong>de</strong> compareTo <strong>de</strong>r Comparable Schnittstelle. Byte,<br />

Character, Double, File, Float, Long, Short, Str<strong>in</strong>g, Integer, BigInteger, ObjectStreamField, Date und<br />

Calendar haben diese Schnittstelle implementiert. Lassen sich die Daten nicht vergleichen, dann gibt<br />

es e<strong>in</strong>e ClassCastException<br />

*/<br />

public static Object m<strong>in</strong>(Collection c, Comparator vergl)<br />

//<br />

public static Object max(Collection c, Comparator vergl)<br />

6.2.5.3 Sortieren<br />

Die Collection Klasse bietet zwei sort() Metho<strong>de</strong>n an, die die Elemente e<strong>in</strong>er Liste<br />

stabil 313 sortieren. Die Metho<strong>de</strong> sort() sortiert die Elemente <strong>in</strong> ihrer natürlichen<br />

Ordnung, z.B.:<br />

- Zahlen nach <strong>de</strong>r Größe (13 < 40)<br />

- Zeichenketten alphanumerisch (Juergen < Robert < Ulli)<br />

E<strong>in</strong>e zweite überla<strong>de</strong>ne Form von sort() arbeitet mit e<strong>in</strong>em speziellen Comparator<br />

Objekt, das zwei Objekte mit <strong>de</strong>r Metho<strong>de</strong> compare() vergleicht.<br />

public static void sort(List liste)<br />

// sortiert die Liste<br />

public static void sort(List liste, Comparator c)<br />

// sortiert die Liste mit <strong>de</strong>m Comparator c<br />

Die Sortierfunktion arbeitet nur mit List-Objekten. „sort()“ gibt es aber auch <strong>in</strong> <strong>de</strong>r<br />

Klasse Arrays.<br />

Bsp.: Das folgen<strong>de</strong> Programm sortiert e<strong>in</strong>e Reihe von Zeichenketten <strong>in</strong> aufsteigen<strong>de</strong>r<br />

Folge. Es nutzt die Metho<strong>de</strong> Arrays.asList() zur Konstruktion e<strong>in</strong>er Liste aus<br />

e<strong>in</strong>em Array 314 .<br />

import java.util.*;<br />

public class CollectionsSortDemo<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

Str<strong>in</strong>g feld[] =<br />

{ "Reg<strong>in</strong>a","Angela","Michaela","Maria","Josepha",<br />

"Amalia","Vera","Valent<strong>in</strong>a","Daniela","Saida",<br />

313 Stabile Sortieralgorithmen beachten die Reihenfolge von gleichen Elementen, z.B. beim Sortieren von<br />

Nachrichten <strong>in</strong> e<strong>in</strong>em Email-Programm, zuerst nach <strong>de</strong>m Datum und anschließend nach <strong>de</strong>m Sen<strong>de</strong>r, soll die<br />

Liste <strong>in</strong>nerhalb <strong>de</strong>s Datum sortiert bleiben.<br />

314 Lei<strong>de</strong>r gibt es ke<strong>in</strong>en Konstruktor für ArrayList, <strong>de</strong>r e<strong>in</strong>en Array mit Zeichenketten zuläßt.<br />

446


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

"L<strong>in</strong>da","Elisa"<br />

};<br />

List l = Arrays.asList(feld);<br />

Collections.sort(l);<br />

System.out.pr<strong>in</strong>tln(l);<br />

}<br />

}<br />

Die <strong>Java</strong> Bibliothek bietet nicht viel zur Umwandlung von Fel<strong>de</strong>rn („Array“) <strong>in</strong><br />

dynamische Datenstrukturen. E<strong>in</strong>e Ausnahme bil<strong>de</strong>t die Hilfsklasse Arrays, die die<br />

Metho<strong>de</strong> asList() anbietet. Die Behälterklassen ArrayList und L<strong>in</strong>kedList wer<strong>de</strong>n<br />

über asList() nicht unterstützt, d.h. Über asList() wird zwar e<strong>in</strong>e <strong>in</strong>terne Klasse<br />

ArrayList benutzt, die e<strong>in</strong>e Erweiterung von AbstractList ist, aber nur das<br />

notwendigste implementiert.<br />

Sortieralgorithmus. Es han<strong>de</strong>lt sich um e<strong>in</strong>en optimierten „Merge-Sort“. Se<strong>in</strong>e<br />

Laufzeit beträgt N ⋅ log(N)<br />

.<br />

Die sort() Metho<strong>de</strong> arbeitet mit <strong>de</strong>r toArray() Funktion <strong>de</strong>r Klasse List. Damit<br />

wer<strong>de</strong>n die Elemente <strong>de</strong>r Liste <strong>in</strong> e<strong>in</strong>em Feld (Array) abgelegt. Schließlich wird die<br />

sort() Metho<strong>de</strong> <strong>de</strong>r Klasse Arrays genutzt und mit e<strong>in</strong>em ListIterator wie<strong>de</strong>r<br />

<strong>in</strong> die Liste e<strong>in</strong>gefügt.<br />

Daten <strong>in</strong> umgekehrter Reihenfolge sortieren. Das wird über e<strong>in</strong> spezielles<br />

Comparator-Objekt geregelt, das von Collections über die Metho<strong>de</strong><br />

reverseOr<strong>de</strong>r() angefor<strong>de</strong>rt wer<strong>de</strong>n kann.<br />

Bsp.:<br />

import java.util.*;<br />

public class CollectionsReverseSortDemo<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

Vector v = new Vector();<br />

for (<strong>in</strong>t i = 0; i < 10; i++)<br />

{<br />

v.add(new Double(Math.random()));<br />

}<br />

Comparator comparator = Collections.reverseOr<strong>de</strong>r();<br />

Collections.sort(v,comparator);<br />

System.out.pr<strong>in</strong>tln(v);<br />

}<br />

}<br />

E<strong>in</strong>e an<strong>de</strong>re Möglichkeit für umgekehrt sortierte Listen besteht dar<strong>in</strong>, erst die Liste<br />

mit sort() zu sortieren und anschließend mit reverse() umzudrehen.<br />

447


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

6.2.5.4 Suchen von Elementen<br />

Die Behälterklassen enthalten die Metho<strong>de</strong> conta<strong>in</strong>s(), mit <strong>de</strong>r sich Elemente<br />

suchen lassen. Für sortierte Listen gibt es e<strong>in</strong>e wesentlich schnellere Suchmetho<strong>de</strong>:<br />

b<strong>in</strong>arySearch():<br />

public static <strong>in</strong>t b<strong>in</strong>arySearch(List liste, Object key)<br />

// sucht e<strong>in</strong> Element <strong>in</strong> <strong>de</strong>r Liste. Gibt die Position zurück o<strong>de</strong>r e<strong>in</strong> Wert kle<strong>in</strong>er 0,<br />

// falls key nicht <strong>in</strong> <strong>de</strong>r Liste ist.<br />

public static <strong>in</strong>t b<strong>in</strong>arySearch(List liste, Object key, Comparator c)<br />

// Sucht e<strong>in</strong> Element mit Hilfe <strong>de</strong>s Comparator Objekts <strong>in</strong> <strong>de</strong>r Liste. Gibt die Position zurück o<strong>de</strong>r<br />

// e<strong>in</strong>en Wert kle<strong>in</strong>er als 0, falls <strong>de</strong>r key nicht <strong>in</strong> <strong>de</strong>r Liste ist.<br />

Bsp.: Das folgen<strong>de</strong> Programm sortiert (zufällig ermittelte) Daten und bestimmt Daten<br />

<strong>in</strong> Listen mit Hilfe <strong>de</strong>r b<strong>in</strong>ären Suche.<br />

import java.util.*;<br />

public class ListSort<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g [] args)<br />

{<br />

f<strong>in</strong>al <strong>in</strong>t GR = 20;<br />

// Verwen<strong>de</strong>n e<strong>in</strong>er natuerliche Ordnung<br />

List a = new ArrayList();<br />

for (<strong>in</strong>t i = 0; i < GR; i++)<br />

a.add(new VglClass((<strong>in</strong>t)(Math.random() * 100)));<br />

Collections.sort(a);<br />

Object f<strong>in</strong><strong>de</strong> = a.get(GR / 2);<br />

<strong>in</strong>t ort = Collections.b<strong>in</strong>arySearch(a,f<strong>in</strong><strong>de</strong>);<br />

System.out.pr<strong>in</strong>tln("Ort von " + f<strong>in</strong><strong>de</strong> + " = " + ort);<br />

// Verwen<strong>de</strong>n e<strong>in</strong>es Comparator<br />

List b = new ArrayList();<br />

// Bestimmt zufaellig Zeichenketten <strong>de</strong>r Laenge 4<br />

for (<strong>in</strong>t i = 0; i < GR; i++)<br />

b.add(Fel<strong>de</strong>r.randStr<strong>in</strong>g(4));<br />

// Instanz fuer <strong>de</strong>n Comparator<br />

AlphaVgl av = new AlphaVgl();<br />

// Sortieren<br />

Collections.sort(b,av);<br />

// B<strong>in</strong>aere Suche<br />

f<strong>in</strong><strong>de</strong> = b.get(GR / 2);<br />

ort = Collections.b<strong>in</strong>arySearch(b,f<strong>in</strong><strong>de</strong>,av);<br />

System.out.pr<strong>in</strong>tln(b);<br />

System.out.pr<strong>in</strong>tln("Ort von " + f<strong>in</strong><strong>de</strong> + " = " + ort);<br />

}<br />

}<br />

448


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

6.2.6 Generics<br />

6.2.6.1 Sammlungsklassen<br />

Mit <strong>Java</strong> 1.5 wur<strong>de</strong> durch E<strong>in</strong>führung <strong>de</strong>r sog. Generics das Feld <strong>de</strong>r Templatebasierten<br />

Metaprogrammierung eröffnet. Das s<strong>in</strong>nvollste E<strong>in</strong>satzfeld dieses<br />

Mechanismus bil<strong>de</strong>t die Anwendung auf die vorhan<strong>de</strong>nen Klassen <strong>de</strong>r Collection API<br />

zur Realisierung typsicherer Objektsammlungen.<br />

Allgeme<strong>in</strong>e Syntax: Nachstellung <strong>de</strong>s durch spitze W<strong>in</strong>kellammern e<strong>in</strong>geschlossenen<br />

Typnamens nach <strong>de</strong>m Namen <strong>de</strong>r so typisierten Sammlung, z.B.<br />

Def<strong>in</strong>ition e<strong>in</strong>er Liste, die ausschließlich Objekte <strong>de</strong>s Typs Str<strong>in</strong>g enthält: List<br />

Bsp. 315 :<br />

import java.util.*;<br />

import java.util.List;<br />

class ListTest<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g [] args)<br />

{<br />

List xs = new ArrayList();<br />

xs.add("Shakespeare");<br />

xs.add("Schiller");<br />

xs.add("Goethe");<br />

xs.add("Brecht");<br />

xs.add("Thomas Mann");<br />

Str<strong>in</strong>g x = xs.get(3);<br />

System.out.pr<strong>in</strong>tln(xs);<br />

}<br />

}<br />

Test:<br />

Aus Kompatiblitätsgrün<strong>de</strong>n mit bestehen<strong>de</strong>m Co<strong>de</strong> können generische Klassen auch<br />

weiter h<strong>in</strong> ohne konkrete Angabe <strong>de</strong>s Typparameters benutzt wer<strong>de</strong>n. Während <strong>de</strong>r<br />

Übersetzung wird <strong>in</strong> diesen Fällen e<strong>in</strong>e Warnung ausgegeben.<br />

import java.util.*;<br />

import java.util.List;<br />

class WarnTest<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g [] args)<br />

{<br />

List xs = new ArrayList();<br />

xs.add("Shakespeare");<br />

xs.add("Schiller");<br />

xs.add("Goethe");<br />

315 pr66100<br />

449


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

xs.add("Brecht");<br />

xs.add("Thomas Mann");<br />

System.out.pr<strong>in</strong>tln(xs);<br />

}<br />

}<br />

Konventionsgemäß erlaubt <strong>Java</strong> ausschließlich die Festlegung von Klassen als<br />

Inhaltstypen von Objektsammlungen. Primitivtypen s<strong>in</strong>d von <strong>de</strong>r Verwendung<br />

ausgeschlossen.<br />

6.2.6.2 Generische Metho<strong>de</strong>n<br />

Generische Typen s<strong>in</strong>d nicht an e<strong>in</strong>en objektorientierten Kontext gebun<strong>de</strong>n. In <strong>Java</strong><br />

verlässt man <strong>de</strong>n objektorientierten Kontext <strong>in</strong> statischen Metho<strong>de</strong>n. Statische<br />

Metho<strong>de</strong>n s<strong>in</strong>d nicht an e<strong>in</strong> Objekt gebun<strong>de</strong>n und lassen sich generisch <strong>in</strong> <strong>Java</strong><br />

<strong>de</strong>f<strong>in</strong>ieren. Hierzu ist vor <strong>de</strong>r Metho<strong>de</strong>nsignatur <strong>in</strong> spitzen Klammern e<strong>in</strong>e Liste <strong>de</strong>r<br />

für die statischen Metho<strong>de</strong>n benutzten Typvariablen anzugeben.<br />

Bsp.: Generische Metho<strong>de</strong>n <strong>de</strong>r Klasse TestGenericB<strong>in</strong>aerBaumKnoten<br />

Die folgen<strong>de</strong> Darstellung zeigt die Def<strong>in</strong>ition e<strong>in</strong>er generischen Klasse für Knoten <strong>in</strong> e<strong>in</strong>em b<strong>in</strong>ären<br />

Suchbaums mit e<strong>in</strong>er Typvariablen, die für alle Subtypen <strong>de</strong>r Schnittstelle Comparable steht.<br />

// Elementarer Knoten e<strong>in</strong>es b<strong>in</strong>aeren Baums, <strong>de</strong>r nicht ausgeglichen ist<br />

class B<strong>in</strong>aerBaumknoten<br />

{<br />

// Instanzvariable<br />

protected B<strong>in</strong>aerBaumknoten l<strong>in</strong>ks;<br />

// l<strong>in</strong>ker Teilbaum<br />

protected B<strong>in</strong>aerBaumknoten rechts; // rechter Teilbaum<br />

public T daten; // Daten<strong>in</strong>halt <strong>de</strong>r Knoten<br />

// Konstruktor<br />

public B<strong>in</strong>aerBaumknoten(T datenElement)<br />

{<br />

this(datenElement, null, null );<br />

}<br />

public B<strong>in</strong>aerBaumknoten(T datenElement,<br />

B<strong>in</strong>aerBaumknoten l,<br />

B<strong>in</strong>aerBaumknoten r)<br />

{<br />

daten = datenElement;<br />

l<strong>in</strong>ks = l;<br />

rechts = r;<br />

}<br />

public void <strong>in</strong>sert (T x)<br />

{<br />

if (x.compareTo(daten) > 0) // dann rechts<br />

{<br />

if (rechts == null) rechts = new B<strong>in</strong>aerBaumknoten(x);<br />

else rechts.<strong>in</strong>sert(x);<br />

}<br />

else // sonst l<strong>in</strong>ks<br />

{<br />

if (l<strong>in</strong>ks == null) l<strong>in</strong>ks = new B<strong>in</strong>aerBaumknoten (x);<br />

else l<strong>in</strong>ks.<strong>in</strong>sert(x);<br />

}<br />

}<br />

public B<strong>in</strong>aerBaumknoten getL<strong>in</strong>ks()<br />

{<br />

return l<strong>in</strong>ks;<br />

450


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

}<br />

public B<strong>in</strong>aerBaumknoten getRechts()<br />

{<br />

return rechts;<br />

}<br />

}<br />

Die Klasse TestGenericB<strong>in</strong>aerBaumKnoten erzeugt e<strong>in</strong>en b<strong>in</strong>ären Baum mit Hilfe <strong>de</strong>r<br />

vorliegen<strong>de</strong>n Klasse B<strong>in</strong>aerBaumknoten. Die Knoten <strong>de</strong>s b<strong>in</strong>ären Baums wer<strong>de</strong>n über zahlreiche<br />

generische Metho<strong>de</strong>n durchlaufen.<br />

public class TestGenericB<strong>in</strong>aerBaumKnoten<br />

{<br />

public static void ma<strong>in</strong> (Str<strong>in</strong>g args[])<br />

{<br />

B<strong>in</strong>aerBaumknoten baum = null;<br />

/*<br />

for (<strong>in</strong>t i = 0; i < 20; i++) // 20 Zusfallsstr<strong>in</strong>gs speichern<br />

{<br />

Str<strong>in</strong>g s = "Zufallszahl " + (<strong>in</strong>t)(Math.random() * 100);<br />

if (baum == null) baum = new B<strong>in</strong>aerBaumknoten(s);<br />

}<br />

pr<strong>in</strong>t(baum); // Sortiert wie<strong>de</strong>r ausdrucken<br />

*/<br />

for (<strong>in</strong>t i = 0; i < 10; i++)<br />

{<br />

// Erzeuge e<strong>in</strong>e Zahl zwischen 0 und 100<br />

Integer r = new Integer((<strong>in</strong>t)(Math.random()*100));<br />

if (baum == null) baum = new B<strong>in</strong>aerBaumknoten(r);<br />

else baum.<strong>in</strong>sert(r);<br />

}<br />

System.out.pr<strong>in</strong>tln("Inor<strong>de</strong>r-Durchlauf");<br />

pr<strong>in</strong>t(baum);<br />

System.out.pr<strong>in</strong>tln();<br />

System.out.pr<strong>in</strong>tln("Baumdarstellung um 90 Grad versetzt");<br />

ausgB<strong>in</strong>aerBaum(baum,0);<br />

System.out.pr<strong>in</strong>t("Kle<strong>in</strong>ster Wert: ");<br />

System.out.pr<strong>in</strong>t(((Integer)(f<strong>in</strong><strong>de</strong>M<strong>in</strong>(baum))).<strong>in</strong>tValue());<br />

System.out.pr<strong>in</strong>tln();<br />

System.out.pr<strong>in</strong>t("Groesster Wert: ");<br />

System.out.pr<strong>in</strong>t(((Integer)(f<strong>in</strong><strong>de</strong>Max(baum))).<strong>in</strong>tValue());<br />

System.out.pr<strong>in</strong>tln();<br />

}<br />

// Generische Metho<strong>de</strong>n<br />

public static <br />

void pr<strong>in</strong>t (B<strong>in</strong>aerBaumknoten


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

}<br />

}<br />

public static T f<strong>in</strong><strong>de</strong>M<strong>in</strong>(B<strong>in</strong>aerBaumknoten b)<br />

{<br />

return datenZugriff( f<strong>in</strong>dM<strong>in</strong>(b) );<br />

}<br />

public static T f<strong>in</strong><strong>de</strong>Max(B<strong>in</strong>aerBaumknoten b)<br />

{<br />

return datenZugriff( f<strong>in</strong>dMax(b) );<br />

}<br />

public static T datenZugriff(B<strong>in</strong>aerBaumknoten b)<br />

{<br />

return b == null ? null : b.daten;<br />

}<br />

public static <br />

B<strong>in</strong>aerBaumknoten f<strong>in</strong>dM<strong>in</strong>(B<strong>in</strong>aerBaumknoten b)<br />

{<br />

if (b == null) return null;<br />

else if (b.getL<strong>in</strong>ks() == null) return b;<br />

return f<strong>in</strong>dM<strong>in</strong>(b.getL<strong>in</strong>ks());<br />

}<br />

public static <br />

B<strong>in</strong>aerBaumknoten f<strong>in</strong>dMax(B<strong>in</strong>aerBaumknoten b)<br />

{<br />

if (b != null)<br />

while (b.getRechts() != null)<br />

b = b.getRechts();<br />

return b;<br />

}<br />

}<br />

6.2.6.3 Iteration<br />

In typischer Weise wird <strong>in</strong> e<strong>in</strong>em Programm über die Elemente e<strong>in</strong>es Sammlungstyps<br />

iteriert o<strong>de</strong>r über alle Elemente e<strong>in</strong>es Array (Reihung). <strong>Java</strong> kennt dafür<br />

verschie<strong>de</strong>nene Schleifenkonstrukte. Lei<strong>de</strong>r kannte <strong>Java</strong> bis zur Version 1.4 ke<strong>in</strong><br />

eigenes Schleifenkonstrukt, das bequem e<strong>in</strong>e Iteration über die Elemente e<strong>in</strong>er<br />

Sammlung (Collection) ausdrücken konnte.<br />

Bsp. 316 :<br />

import java.util.List;<br />

import java.util.ArrayList;<br />

import java.util.Iterator;<br />

class AlteIteration<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g [] args)<br />

{<br />

Str<strong>in</strong>g [] ar =<br />

{ "Brecht", "Schiller", "Goethe", "Shakespeare", "Thomas Mann" };<br />

List xs = new ArrayList();<br />

for (<strong>in</strong>t i = 0;i < ar.length; i++)<br />

{<br />

f<strong>in</strong>al Str<strong>in</strong>g s = ar[i];<br />

xs.add(s);<br />

}<br />

for (Iterator it = xs.iterator(); it.hasNext();)<br />

{<br />

f<strong>in</strong>al Str<strong>in</strong>g s = (Str<strong>in</strong>g) it.next();<br />

316 pr66100<br />

452


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

}<br />

}<br />

}<br />

System.out.pr<strong>in</strong>tln(s.toUpperCase());<br />

Mit <strong>Java</strong> 1.5 gibt es endlich die Möglichkeit, direkt zu bestimmen: "Mache für alle<br />

Elemente im nachfolgen<strong>de</strong>n Sammlungsobjekt etwas ...". Vorgesehen ist dafür e<strong>in</strong>e<br />

Kontrollstrukturanweisung 317 von folgen<strong>de</strong>r Form:<br />

for (Type i<strong>de</strong>ntifier expr) { body }<br />

Dieser Konstrukt wird so gelesen: "Für je<strong>de</strong>s i<strong>de</strong>ntifier <strong>de</strong>s Typs Type <strong>in</strong> expr<br />

führe aus". Die Kontrollstrukturanweisung gilt für alle Objekte vom Typ <strong>de</strong>r<br />

Schnittstelle Iterable, wozu auf je<strong>de</strong>m Fall alle Datenstrukturen (e<strong>in</strong>schl. Array)<br />

zählen.<br />

Bsp. 318 :<br />

import java.util.List;<br />

import java.util.ArrayList;<br />

class NeueIteration<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g [] args)<br />

{<br />

Str<strong>in</strong>g [] ar =<br />

{ "Brecht", "Schiller", "Goethe", "Shakespeare", "Thomas Mann" };<br />

List xs = new ArrayList();<br />

for (Str<strong>in</strong>g s:ar) xs.add(s);<br />

for (Str<strong>in</strong>g s:xs) System.out.pr<strong>in</strong>tln(s.toUpperCase());<br />

}<br />

}<br />

Def<strong>in</strong>ition <strong>de</strong>r Schnittstelle Iterable: public <strong>in</strong>terface Iterable<br />

{ SimpleIterator iterator();}<br />

Iterable schreibt die Existenz e<strong>in</strong>er Funktion iterator() vor, die e<strong>in</strong>en<br />

java.lang.SimpleIterator liefert. SimpleIterator ist e<strong>in</strong> Iterator 319 ohne<br />

remove():<br />

public <strong>in</strong>terface SimpleIterator<br />

{<br />

boolean hasNext();<br />

T next();<br />

}<br />

Der konkrete SimpleIterator muß nur die Metho<strong>de</strong>n hasNext() und next()<br />

implementieren, um das nächste Element <strong>in</strong> <strong>de</strong>r Aufzählung zu beschaffen und das<br />

En<strong>de</strong> anzuzeigen.<br />

java.util.Iterator() implementiert seit <strong>Java</strong> 1.5 <strong>de</strong>n SimpleIterator:<br />

public <strong>in</strong>terface Iterator extends SimpleIterator<br />

{<br />

boolean hasNext();<br />

E next();<br />

void remove();<br />

}<br />

317 vgl. 2.4.7<br />

318 pr66100<br />

319 vgl. 6.2.1<br />

453


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Das bekannte Programmierer-Idiom<br />

for (Iterator i = c.iterator(); i.hasNext)<br />

{<br />

Typ o = (Typ) i.next();<br />

}<br />

kann durch das erweiterte for erfolgreich abgekürzt wer<strong>de</strong>n:<br />

for (i : c)<br />

{<br />

Typ o = (Typ) i;<br />

}<br />

Bsp. 320 :<br />

import java.util.Iterator;<br />

import java.util.Str<strong>in</strong>gTokenizer;<br />

class WordIterable implements Iterable, Iterator<br />

{<br />

private Str<strong>in</strong>gTokenizer st;<br />

public WordIterable( Str<strong>in</strong>g s )<br />

{<br />

st = new Str<strong>in</strong>gTokenizer( s );<br />

}<br />

// Metho<strong>de</strong> vom Iterable<br />

public Iterator iterator()<br />

{<br />

return this;<br />

}<br />

// Metho<strong>de</strong>n vom Iterator<br />

public boolean hasNext()<br />

{<br />

return st.hasMoreTokens();<br />

}<br />

public Object next()<br />

{<br />

return st.nextToken();<br />

}<br />

public void remove() { }<br />

}<br />

public class WordIterableDemo<br />

{<br />

public static void ma<strong>in</strong>( Str<strong>in</strong>g args[] )<br />

{<br />

Str<strong>in</strong>g s =<br />

"Am Anfang war das Wort - am En<strong>de</strong> die Phrase. (Stanislaw Jerzy Lec)";<br />

for ( Object word : new WordIterable(s) )<br />

System.out.pr<strong>in</strong>tln( word );<br />

}<br />

}<br />

320 pr66100<br />

454


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

6.3 Die Klasse Str<strong>in</strong>gTokenizer<br />

Die Str<strong>in</strong>gTokenizer-Klasse unterstützt das Aufteilen e<strong>in</strong>es Str<strong>in</strong>gs <strong>in</strong> Teilworte bzw.<br />

Zeichen, sog. Tokens. Tokens wer<strong>de</strong>n mit Hilfe von Trennzeichen erkannt, die e<strong>in</strong><br />

Token vom an<strong>de</strong>ren trennen. E<strong>in</strong> Token ist e<strong>in</strong>e Zeichenkette, <strong>in</strong> <strong>de</strong>r ke<strong>in</strong>e<br />

Trennzeichen vorkommen. In <strong>de</strong>r Regel wer<strong>de</strong>n Whitespaces 321 als Trennzeichen<br />

verwen<strong>de</strong>t. Der Str<strong>in</strong>gTokenizer ist nicht an bestimmte Trennzeichen gebun<strong>de</strong>n, sie<br />

können vielmehr frei gewählt wer<strong>de</strong>n. Nur <strong>in</strong> <strong>de</strong>r Vore<strong>in</strong>stellung s<strong>in</strong>d Tabulator,<br />

Leerzeichen und Zeilentrenner die Delimiter.<br />

Konstruktor: public Str<strong>in</strong>gTokenizer(Str<strong>in</strong>g str)<br />

E<strong>in</strong> Str<strong>in</strong>gTokenizer mit Whitespaces als Trennzeichen wird erzeugt. Der zu<br />

untersuchen<strong>de</strong> Str<strong>in</strong>g ist str. Sollen an<strong>de</strong>re Zeichen als Trennzeichen angegeben<br />

wer<strong>de</strong>n, so müssen diese ebenfalls an <strong>de</strong>n Konstruktor übergeben wer<strong>de</strong>n:<br />

public Str<strong>in</strong>gTokenizer(Str<strong>in</strong>g str, Str<strong>in</strong>g <strong>de</strong>limiters)<br />

Metho<strong>de</strong>n:<br />

public Str<strong>in</strong>g nextToken() throws NoSuchElementException<br />

gibt das jeweils nächste Token an<br />

public boolean hasMoreTokens()<br />

gibt Auskunft darüber, ob es noch weitere Tokens gibt.<br />

public <strong>in</strong>t countTokens()<br />

gibt Auskunft darüber, wie viele Tokens <strong>in</strong> e<strong>in</strong>em Str<strong>in</strong>g vorhan<strong>de</strong>n s<strong>in</strong>d.<br />

public Str<strong>in</strong>g nextToken(Str<strong>in</strong>g neueDelimiter)<br />

bewirkt nach e<strong>in</strong>em Aufruf die Verän<strong>de</strong>rung <strong>de</strong>r Trennzeichen.<br />

Das Enumerations-Interface ist <strong>in</strong> die Str<strong>in</strong>gTokenizer-Klasse implementiert:<br />

public boolean hasMoreElements()<br />

{<br />

return hasMoreTokens();<br />

}<br />

public Object nextElement()<br />

{<br />

return nextToken();<br />

}<br />

Bsp.: Ausgabe <strong>de</strong>r Worte e<strong>in</strong>es Satzes 322<br />

import java.util.Str<strong>in</strong>gTokenizer;<br />

import java.io.*;<br />

public class Str<strong>in</strong>gTokenizerTest<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

System.out.pr<strong>in</strong>tln("\nStr<strong>in</strong>gTokenizerTest\n");<br />

System.out.pr<strong>in</strong>tln("Gib e<strong>in</strong>ige Saetze fuer <strong>de</strong>n Test an.");<br />

System.out.pr<strong>in</strong>tln("Term<strong>in</strong>iere ueber e<strong>in</strong>e leere Zeile");<br />

boolean moreInput;<br />

do {<br />

System.out.pr<strong>in</strong>t("> ");<br />

Str<strong>in</strong>g e<strong>in</strong>gabeZeile = null;<br />

BufferedRea<strong>de</strong>r e<strong>in</strong>gabe = null;<br />

e<strong>in</strong>gabe = new BufferedRea<strong>de</strong>r(new InputStreamRea<strong>de</strong>r(System.<strong>in</strong>));<br />

try {<br />

321 Zu <strong>de</strong>n Whitespaces gehören Leerzeichen, Tabulator, Zeilenvorschub und Zeilenumbruch. Wird beim<br />

Erzeugen e<strong>in</strong>es Tokenizer ke<strong>in</strong>e Trennzeichen-Zeichenkette angegeben, so wer<strong>de</strong>n Whitespaces als<br />

Trennzeichen angenommen.<br />

322 vgl. pr66100<br />

455


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

}<br />

}<br />

e<strong>in</strong>gabeZeile =e<strong>in</strong>gabe.readL<strong>in</strong>e();<br />

}<br />

catch(IOException e) { System.out.pr<strong>in</strong>tln(e); }<br />

if (e<strong>in</strong>gabeZeile.equals("")) break;<br />

Str<strong>in</strong>gTokenizer st = new Str<strong>in</strong>gTokenizer(e<strong>in</strong>gabeZeile);<br />

if (st.hasMoreTokens())<br />

{<br />

moreInput = true;<br />

Str<strong>in</strong>g wort = st.nextToken();<br />

System.out.pr<strong>in</strong>t(wort);<br />

while (st.hasMoreTokens())<br />

{<br />

System.out.pr<strong>in</strong>t(" / ");<br />

System.out.pr<strong>in</strong>t(st.nextToken());<br />

}<br />

System.out.pr<strong>in</strong>tln();<br />

}<br />

else { moreInput = false; }<br />

} while (moreInput);<br />

6.4 Die Klasse Random<br />

Konstruktoren<br />

Random()<br />

// Erzeugt e<strong>in</strong>en neuen Zufallszahlengenerator. Der Seed 323 wird auf die aktuelle Zeit gesetzt.<br />

Random(long seed)<br />

// Erzeugt e<strong>in</strong>en neuen Zufallszahlengenerator und benutzt <strong>de</strong>n Parameter als Seed.<br />

Metho<strong>de</strong>n<br />

public void setSeed(long seed)<br />

// setzt <strong>de</strong>n Zufallswert neu<br />

Die Random-Klasse kann Zufallszahlen <strong>in</strong> vier verschie<strong>de</strong>nen Datentypen erzeugen:<br />

public <strong>in</strong>t nextInt()<br />

// liefert die nächste Zufallszahl<br />

public long nextLong()<br />

// liefert die nächste Zufallszahö<br />

public float nextFloat()<br />

// liefert die nächste Zufallszahl zwischen 0.0 und 1.0<br />

public double nextDouble()<br />

erzeugt e<strong>in</strong>e Zufallszahl im double-Format zwischen 0.0 und 1.0 324 .<br />

323 Startwert für je<strong>de</strong> Zufallszahl ist e<strong>in</strong> 48 Bit Seed. Das Wort „Seed“ kommt vom englischen Wort für Samen<br />

und <strong>de</strong>utet an, daß es bei <strong>de</strong>r Generierung von Zufalsszahlen wie bei Pflanzen e<strong>in</strong>en Samen gibt, <strong>de</strong>r zu<br />

Nachkommen führt. Aus diesem Startwert ermitteln sich anschließend die an<strong>de</strong>ren Zahlen durch l<strong>in</strong>eare<br />

Kongruenzen. Dadurch s<strong>in</strong>d die Zahlen nicht wirklich zufällig, sie gehorchen e<strong>in</strong>em mathematischen Verfahren<br />

324 wird von Math.random() verwen<strong>de</strong>t.<br />

456


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

6.5 Datumsberechnungen<br />

6.5.1 Die Klassen Date, Calendar, GregorianCalendar<br />

Die Klasse Date<br />

Die Date-Klasse repräsentiert e<strong>in</strong> bestimmtes Datum bzw. e<strong>in</strong>en bestimmten<br />

Zeitpunkt. Ausgangspunkt ist <strong>de</strong>r 1. Januar 1970 0:00 h Greenwich-Zeit.<br />

Date<br />

><br />

public Date()<br />

// erzeugt e<strong>in</strong>e Datums-Angabe und <strong>in</strong>itialisiert es mit <strong>de</strong>r Zeit, die bei <strong>de</strong>r Erzeugung gelesen<br />

// wur<strong>de</strong><br />

public Date(long d)<br />

// erzeugt e<strong>in</strong> Datums-Objekt und <strong>in</strong>itialisiertes mit <strong>de</strong>r angegebenen Anzahl von Millisekun<strong>de</strong>n<br />

// seit <strong>de</strong>m 1. Januar 1970, 00:00:00 GMT<br />

><br />

public long getTime()<br />

// liefert die Anzahl <strong>de</strong>r Millisekun<strong>de</strong>n seit <strong>de</strong>m 1. Januar 1970, 00:00:00 GMT zurück<br />

public void setTime(long t)<br />

// setzt die Anzahl Millisekun<strong>de</strong>n <strong>de</strong>s Datums-Objekts neu. Die akuelle Zeit kann über<br />

// today.setTime(new java.util.Date()) gesetzt wer<strong>de</strong>n.<br />

public boolean before(Date d)<br />

public boolean after(Date d)<br />

// testet, ob das eigene Datum vor / nach <strong>de</strong>m Datum <strong>de</strong>s Parameters ist. „true“, wenn getTime() für<br />

// bei<strong>de</strong> <strong>de</strong>n gleichen Wert ergibt und <strong>de</strong>r Parameter nicht null ist.<br />

public <strong>in</strong>t compareTo(Date d)<br />

// vergleicht zwei Datum-Objekte und gibt 0 zurück, falls bei<strong>de</strong> die gleiche Zeit repräsentieren.<br />

//<br />

public <strong>in</strong>t compareTo(Object o)<br />

// Ist das übergebene Objekt vom Typ Date, dann verhält sich die Funktion wie das zuvor<br />

// angegebene compareTo(). An<strong>de</strong>renfalls wirft die Metho<strong>de</strong> e<strong>in</strong>e ClassCastException<br />

...<br />

Abb.: Die Klasse Date<br />

Bsp. 325 :<br />

import java.util.Date;<br />

class DatumundZeit<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

System.out.pr<strong>in</strong>tln(new Date() );<br />

}<br />

}<br />

325 pr65100<br />

457


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Die Klasse Calendar<br />

(abstract class java.util.Calendar implements Serializable, Cloneable)<br />

Sie wird e<strong>in</strong>gesetzt zur Konvertierung zwischen verschie<strong>de</strong>nen Datumsformaten. Die<br />

Klasse Calendar besitzt nur e<strong>in</strong>ige statisch Funktionen, z.B. getInstance(). Mit<br />

<strong>de</strong>r Klassenmetho<strong>de</strong> getInstance() bekommt man e<strong>in</strong> nutzbares Format:<br />

public static Calendar getInstance()<br />

liefert e<strong>in</strong>en Calendar <strong>in</strong> <strong>de</strong>r Ausprägung von GregorianCalendar mit <strong>de</strong>r dargestellten Zeitzone und<br />

Lokalisierung.<br />

Die Klasse GregorianCalendar<br />

Die abstrakte Klasse Calendar wird durch die Klasse GregorianCalendar<br />

implementiert. In <strong>de</strong>r gegenwärtigen Implementierung <strong>de</strong>ckt die Klasse <strong>de</strong>n Zeitraum<br />

von 4716 v. Chr. bis zum Jahre 5.000.000.<br />

GregorianCalendar<br />

><br />

public GregorianCalendar()<br />

// erzeugt e<strong>in</strong> standardmäßiges GregorianCalendar-Objekt mit <strong>de</strong>r aktuellen Zeit <strong>in</strong> <strong>de</strong>r<br />

// vore<strong>in</strong>gestellten Zeitzone und Lokalisierung<br />

public GregorianCalendar(<strong>in</strong>t jahr, <strong>in</strong>t monat, <strong>in</strong>t tag)<br />

// erzeugt e<strong>in</strong> GregorianCalendar-Objekt <strong>in</strong> <strong>de</strong>r vore<strong>in</strong>gestellten Zeitzone und Lokalisierung. Das<br />

// Datum wird durch jahr, monat (0


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Feldbezeichner M<strong>in</strong>imalwert Maximalwert Erklärung<br />

ERA 0(BC) 1(AD) Datum vor o<strong>de</strong>r nach Christi<br />

YEAR 1 5000000 Jahr<br />

MONTH 0 11 Monat<br />

DAY_OF_MONTH 1 31 Tag<br />

WEEK_OF_YEAR 1 54 Woche<br />

WEEK_OF_MONTH 1 6 Woche <strong>de</strong>s Monats<br />

DAY_OF_YEAR 1 366 Tag <strong>de</strong>s Jahres<br />

DAY_OF_WEEK 1 7 Tag <strong>de</strong>r Woche (1 = Sonntag, 7 =<br />

Samstag<br />

DAY_OF_WEEK_IN_MONTH -1 6 Tag <strong>de</strong>r Woche im Monat<br />

HOUR 0 12 Stun<strong>de</strong> von 12<br />

HOUR_OF_DAY 0 23 Stun<strong>de</strong> von 24<br />

MINUTE 0 59 M<strong>in</strong>ute<br />

SECOND 0 59 Sekun<strong>de</strong>n<br />

MILLISECOND 0 999 Millisekun<strong>de</strong>n<br />

AM_PM 0 1 Vor 12, nach 12<br />

ZONE_OFFSET -12*60*60*1000 12*60*60*1000 Zeitzonenabweichnung <strong>in</strong><br />

Millisekun<strong>de</strong>n<br />

DST_OFFSET 0 1*60*60*1000 Sommerzeitabweichnung <strong>in</strong><br />

Millisekun<strong>de</strong>n<br />

Abb.: Konstanten aus <strong>de</strong>r Klasse Calendar<br />

Bsp.: Ausgabe wichtiger Datumsfel<strong>de</strong>r 326<br />

import java.util.*;<br />

public class DateDemo<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

GregorianCalendar cal = new GregorianCalendar();<br />

cal.setTimeZone(TimeZone.getTimeZone("ECT"));<br />

cal.setTime(cal.getTime());<br />

pr<strong>in</strong>tCalendar(cal);<br />

cal.set(Calendar.DATE,12);<br />

cal.set(Calendar.MONTH,Calendar.MARCH);<br />

cal.set(Calendar.YEAR,1973);<br />

pr<strong>in</strong>tCalendar(cal);<br />

}<br />

public static void pr<strong>in</strong>tCalendar(Calendar cal)<br />

{<br />

Str<strong>in</strong>g wochenTag = (new Str<strong>in</strong>g[]{"Sonntag",<br />

"Montag","Dienstag","Mittwoch","Donnerstag",<br />

"Freitag","Samstag"})<br />

[cal.get(Calendar.DAY_OF_WEEK)];<br />

System.out.pr<strong>in</strong>tln(wochenTag + ", " +<br />

cal.get(Calendar.DATE) + "." +<br />

(cal.get(Calendar.MONTH)+1) + "." +<br />

cal.get(Calendar.YEAR) + ", " +<br />

cal.get(Calendar.HOUR_OF_DAY) + ":" +<br />

o(cal.get(Calendar.MINUTE)) + ":" +<br />

o(cal.get(Calendar.SECOND)) + " und " +<br />

cal.get(Calendar.MILLISECOND) + " ms ");<br />

System.out.pr<strong>in</strong>tln("Es ist die " +<br />

cal.get(Calendar.WEEK_OF_YEAR) + ". Woche im Jahr und ");<br />

System.out.pr<strong>in</strong>tln(cal.get(Calendar.WEEK_OF_MONTH) +<br />

". Woche im Monat\n");<br />

}<br />

public static Str<strong>in</strong>g o(<strong>in</strong>t i)<br />

326 pr65100<br />

459


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

{<br />

return (i >= 10) ? Integer.toStr<strong>in</strong>g(i) : "0" + i;<br />

}<br />

}<br />

Die Klasse TimeZone (abstract class java.util.TimeZone implements<br />

Serializable, Cloneable)<br />

Die Klasse TimeZone repräsentiert e<strong>in</strong>e Zeitzone <strong>in</strong>klusive Zeitverschiebung und<br />

benutzt dazu folgen<strong>de</strong> statische Metho<strong>de</strong>n:<br />

public static TimeZone getDefault()<br />

// gibt die Zeitzone für die Umgebung zurück<br />

public static Str<strong>in</strong>g[] getAvailableIDs()<br />

// liefert alle verfügbaren IDs<br />

public static TimeZone getTimeZone(Str<strong>in</strong>g ID)<br />

// liefert die Zeitzone für e<strong>in</strong>e gegebene ID<br />

Bsp.: Gib e<strong>in</strong>e Aufzählung aller unterstützten Zeitzonen 327 an.<br />

import java.util.*;<br />

public class AlleZonen<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

Str<strong>in</strong>g s[] = TimeZone.getAvailableIDs();<br />

Arrays.sort(s);<br />

for (<strong>in</strong>t i = 0; i < s.length; i++)<br />

System.out.pr<strong>in</strong>tln(s[i]);<br />

}<br />

}<br />

327 Unter W<strong>in</strong>dows mit <strong>de</strong>m JDK 1.3 s<strong>in</strong>d dies 321 Zeitzonen, pr65100<br />

460


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

6.5.2 Formatieren <strong>de</strong>r Datumsangaben<br />

Die Klasse DateFormat<br />

DateFormat<br />

><br />

public f<strong>in</strong>al Str<strong>in</strong>g format(Date d)<br />

// formatiert das Datum <strong>in</strong> e<strong>in</strong>en Datum / Zeit-Str<strong>in</strong>g<br />

public static f<strong>in</strong>al DateFormat getDateInstance()<br />

public static f<strong>in</strong>al DateFormat getTimeInstance()<br />

public static f<strong>in</strong>al DateFormat getDateTimeInstance()<br />

// liefert e<strong>in</strong>en Datum/Zeit-Formatierer mit <strong>de</strong>m vorgegebenen stil aus <strong>de</strong>r Standardumgebung<br />

public static f<strong>in</strong>al DateFormat getDateInstance(<strong>in</strong>t dateStil)<br />

public static f<strong>in</strong>al DateFormat getTimeInstance(<strong>in</strong>t stil)<br />

// liefert e<strong>in</strong>en Datum/Zeit-Formatierer mit <strong>de</strong>n Stil stil und <strong>de</strong>r Standardsprache<br />

public static f<strong>in</strong>al DateFormat getDateInstance(<strong>in</strong>t stil, Locale lokal 328 )<br />

public static f<strong>in</strong>al DateFormat getTimeInstance(<strong>in</strong>t stil, Locale lokal)<br />

// liefert e<strong>in</strong>en Datum/Zeit-Formatierer und <strong>de</strong>r Sprache lokal<br />

public static f<strong>in</strong>al DateFormat getDateTimeInstance(<strong>in</strong>t dateStil, <strong>in</strong>t timeStil)<br />

// gibt e<strong>in</strong>en Datum/Zeit-Formatierer für die gesetzte Sprache im angegebenen<br />

// Formatierungsstil zurück<br />

public static f<strong>in</strong>al DateFormat getDateTimeInstance(<strong>in</strong>t dateStil, <strong>in</strong>t timeStil, Locale lokal)<br />

// gibt e<strong>in</strong> Datum/Zeit-Formatierer für die Sprache lokal im angegebenen Formatierungsstil zurück<br />

public Date parse(Str<strong>in</strong>g fs) throws ParseException<br />

// „parst“ e<strong>in</strong> Datum o<strong>de</strong>r e<strong>in</strong>en Zeit-Str<strong>in</strong>g<br />

...<br />

Abb.: Die Klasse DateFormat<br />

(abstract class java.text.DateFormat extends Format implements Cloneable, Serializable<br />

Die Klasse DateFormat bietet die Metho<strong>de</strong>n getDateInstance(),<br />

getTimeInstance(), getDateTimeInstance() mit <strong>de</strong>n Paramtern<br />

DateFormat.SHORT, DateFormat.MEDIUM, DateFormat.LONG,<br />

DateFormat.FULL an, die die Zeit bzw. das Datum auf vier verschie<strong>de</strong>ne Arten<br />

formatieren:<br />

Konstante Beispiel für das Datum Beispiel für die Zeit<br />

SHORT 29.9.01 21:51<br />

MEDIUM 29.9.2001 21:51:45<br />

LONG 22. September 2001 21:53:20 GMT+02:00<br />

FULL Samstag, 22. September 2001 21:53 Uhr GMT+02:00<br />

Abb.: Konstanten aus DateFormat<br />

Da DateFormat abstrakt ist, ist erst die aus DateFormat abgeleitete Klasse<br />

SimpleDateFormat e<strong>in</strong>satzbereit.<br />

Bsp. mit Sprachen 329 : Das folgen<strong>de</strong> Programm erzeugt die Ausgabe für Deutschland<br />

und anschließend für Italien.<br />

328 Unter Locale s<strong>in</strong>d für e<strong>in</strong>ige Sprachen Konstanten vor<strong>de</strong>f<strong>in</strong>iert: ENGLISH, FRENCH, GERMAN,<br />

ITALIAN, JAPANESE, KOREAN, CHINESE, SIMPLIFIED_CHINESE, TRADITIONAL_CHINESE<br />

461


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

import java.util.*;<br />

import java.text.*;<br />

public class SimpleDateFormatierer<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

Calendar cal;<br />

cal = new GregorianCalendar(TimeZone.getTimeZone("ECT") );<br />

cal.setTime(cal.getTime());<br />

DateFormat format;<br />

format = DateFormat.getDateTimeInstance(<br />

DateFormat.FULL, DateFormat.MEDIUM);<br />

System.out.pr<strong>in</strong>tln(format.format(cal.getTime() ) );<br />

format = DateFormat.getDateTimeInstance(<br />

DateFormat.FULL, DateFormat.MEDIUM, Locale.ITALY);<br />

System.out.pr<strong>in</strong>tln(format.format(cal.getTime() ) );<br />

}<br />

}<br />

Die Klasse SimpleDateFormat<br />

Zum Formatieren e<strong>in</strong>es Datum-Objekts muß zunächst e<strong>in</strong> Objekt von<br />

SimpleDateFormat erzeugt wer<strong>de</strong>n. Dieses bekommt dann evtl.<br />

Formatierungsanweisungen (über e<strong>in</strong>e an<strong>de</strong>re Metho<strong>de</strong> o<strong>de</strong>r über e<strong>in</strong>en weiteren<br />

Konstruktor) mit und formatiert dann mit <strong>de</strong>r format() das Datum.<br />

SimpleDateFormat<br />

><br />

public SimpleDateFormat()<br />

// erzeugt e<strong>in</strong>e neues SimpleDateFormat-Objekt <strong>in</strong> <strong>de</strong>r e<strong>in</strong>gestellten Sprache<br />

public SimpleDateFormat(Str<strong>in</strong>g fs)<br />

// erzeugt e<strong>in</strong> SimpleDateFormat-Objekt mit <strong>de</strong>m vorgegebenen Formatierungsstr<strong>in</strong>g <strong>in</strong> <strong>de</strong>r<br />

// vore<strong>in</strong>gestellten Sprache<br />

public SimpleDateFormat(Str<strong>in</strong>g fs, Locale loc)<br />

// erzeugt e<strong>in</strong> SimpleDateFormat-Objekt mit <strong>de</strong>m vorgebenen Formatierungsstr<strong>in</strong>g <strong>in</strong> <strong>de</strong>r Sprache<br />

// loc.<br />

><br />

public void applyPattern(Str<strong>in</strong>g fs)<br />

// setze <strong>de</strong>n Formatieruns-Str<strong>in</strong>g<br />

public void applyLocalizedPattern(Str<strong>in</strong>g pattern)<br />

// sertzt <strong>de</strong>n Formatierungs-str<strong>in</strong>g für die gegebenen Sprache<br />

public Str<strong>in</strong>g toPattern()<br />

// liefert <strong>de</strong>n Formatierungsstr<strong>in</strong>g<br />

public Str<strong>in</strong>g toLocalizedPattern()<br />

// liefert <strong>de</strong>n übersetzten Formatierungsstr<strong>in</strong>g<br />

Abb.: Die Klasse SimpleDateFormat (class java.text.SimpleDateFormat extends<br />

DateFormat)<br />

Zur <strong>in</strong>dividuellen Anpassung <strong>de</strong>r Ausgabe kann e<strong>in</strong> Formatierungsstr<strong>in</strong>g angegeben<br />

wer<strong>de</strong>n. Diese Formatierungsanweisung wird entwe<strong>de</strong>r <strong>de</strong>m Konstruktor übergeben<br />

o<strong>de</strong>r kann nachträglich mit <strong>de</strong>r Metho<strong>de</strong> applyPattern() geän<strong>de</strong>rt wer<strong>de</strong>n.<br />

329 vgl. pr65200<br />

462


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Symbol Be<strong>de</strong>utung Präsentation Beispiel<br />

G Ära Text AD<br />

Y Jahr Nummer 1998<br />

M Monat im Jahr Nummer 7<br />

MM Monat im Jahr m. Null Nummer 07<br />

MMM Monat im Jahr (kurz) Text Sep<br />

MMMM Monat im Jahr (lang) Text September<br />

d Tag im Monat Nummer 26<br />

h Stun<strong>de</strong> (1-12) Nummer 9<br />

H Stun<strong>de</strong> am Tag (0-23) Nummer 0<br />

m M<strong>in</strong>ute <strong>de</strong>r Stun<strong>de</strong> Nummer 13<br />

s Sekun<strong>de</strong> <strong>de</strong>r M<strong>in</strong>ute Nummer 22<br />

S Millisekun<strong>de</strong> Nummer 257<br />

E Tag <strong>de</strong>r Woche (kurz) Text Mi<br />

EEEE Tag <strong>de</strong>r Woche (lang) Text Mittwoch<br />

D Tag im Jahr Nummer 304<br />

F Tag <strong>de</strong>r Woche i. Mon. Nummer 3<br />

w Woche im Jahr Nummer 13<br />

W<br />

Woche im Monat<br />

a am/pm-Text Text AM<br />

k Stun<strong>de</strong> am Tg (1-24) Nummer 24<br />

K Stun<strong>de</strong> (0-11) Nummer 0<br />

Z Zeitzone Text GMT+02:00<br />

‘ Zeichen für Text Trennzeichen<br />

‘ ' E<strong>in</strong>zelnes Hochkomma Literal ‘<br />

Abb.: Symbole im Formatstr<strong>in</strong>g zur Steuerung <strong>de</strong>r Ausgabe<br />

6.6 Formatieren mit Format-Objekten<br />

Zahlen, Datumsangaben und Text können auf verschie<strong>de</strong>ne Art und Weise formatiert<br />

wer<strong>de</strong>n. Unter <strong>Java</strong> wird das Formatierungsverhalten <strong>in</strong> e<strong>in</strong>er abstrakten Klasse<br />

Format (abstract class java.text.Format implements Serializable,<br />

Cloneable) fixiert. Format stellt die Metho<strong>de</strong>n format() und parseObject()<br />

bereit. Je<strong>de</strong> Zeichenkette, die vom Format-Objekt erzeugt wur<strong>de</strong>, ist auch mit <strong>de</strong>m<br />

Parser wie<strong>de</strong>r e<strong>in</strong>lesbar.<br />

Im JDK erweitern drei Klassen Format: DateFormat, MessageFormat und<br />

NumberFormat. Sie übernehmen die E<strong>in</strong>-/Ausgabe für das Datum, allgeme<strong>in</strong>e<br />

Botschaften (Nachrichten) und Zahlen. Je<strong>de</strong> <strong>de</strong>r Klassen implementiert die Metho<strong>de</strong>n<br />

zur Ausgabe format() und zum Erkennen parseObject(). In <strong>Java</strong> 5 realisiert die<br />

format()-Funktion e<strong>in</strong>e Ausgabe, so wie sie unter <strong>de</strong>r Programmiersprache C mit<br />

pr<strong>in</strong>tf() gesetzt wur<strong>de</strong>.<br />

463


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

6.6.1 Die Klasse Numberformat für die Ausgabe von Prozenten, Zahlen,<br />

Währungen<br />

Für die häufigsten Ausgaben als Prozentzahlen o<strong>de</strong>r als Währungszahlen gibt es die<br />

speziellen statischen Funktionen getPercentInstance(),<br />

getNumberInstance(), getIntegerInstance() und<br />

getCurrencyInstance() 330 .<br />

6.6.2 Ausgabe formatieren mit MessageFormat<br />

MessageFormat ist e<strong>in</strong>e konkrete Unterklasse <strong>de</strong>r abstrakten Klasse Format. Sie<br />

dient dazu, Nachrichten sprachunabhängig zu erzeugen.<br />

6.6.3 Dezimalzahlenformatierung mit DecimalFormat<br />

Die Klasse DecimalFormat dient zur formatierten Ausgabe von ganzen Zahlen.<br />

Dem Konstruktor wird e<strong>in</strong> Formatierungs- Str<strong>in</strong>g übergeben. Die Formatierung erfolgt<br />

unter Berücksichtigung <strong>de</strong>r e<strong>in</strong>gestellten Sprache.<br />

Bsp.: Die Klasse DecimalFormatTest 331<br />

import java.text.*;<br />

public class DecimalFormatTest<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

double d = 12345.67890;<br />

DecimalFormat df = new DecimalFormat("###,##0.00");<br />

System.out.pr<strong>in</strong>tln(df.format(d));<br />

}<br />

}<br />

Symbol Be<strong>de</strong>utung<br />

0 repräsentiert e<strong>in</strong>e Zahl<br />

# e<strong>in</strong>e Zahl. Ist an dieser Stelle ke<strong>in</strong>e Zahl angegeben, dann bleibt die Stelle leer<br />

. trennt Vor- und Nachkommastellen<br />

, gruppiert die Zahl (E<strong>in</strong>e Gruppe ist so graß wie <strong>de</strong>r Abstand von ',' zu '.'.<br />

; Trennzeichen für mehrere Formate<br />

- Standardzeichen für negatives Präfix<br />

% die Zahl wird mit 100 multipliziert und als Prozent ausgewiesen<br />

%% genau wie Prozent nur mit Promille<br />

- Nationales Währungssymbol<br />

-- Internationales Währungssymbol<br />

X alle an<strong>de</strong>re Zeichen können normal benutzt wer<strong>de</strong>n<br />

' Ausmarkieren von speziellen Symbolen (im Präfix o<strong>de</strong>r Suffix benutzt).<br />

Abb.: Formatierungsanweisungen für DecimalFormat<br />

330 jeweils <strong>in</strong> <strong>de</strong>r parmeterlosen Variante und <strong>in</strong> <strong>de</strong>r Variante mit e<strong>in</strong>em Locale-Objekt.<br />

331 vgl. pr65200<br />

464


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

6.6.4 Formatieren mit format()<br />

Die Klasse f<strong>in</strong>al class java.lang.Str<strong>in</strong>g implements CharSequence,<br />

Comparable, Serializable stellt mit <strong>de</strong>r statischen Funktion format() e<strong>in</strong>e<br />

Metho<strong>de</strong> bereit, Zeichenketten nach e<strong>in</strong>er Vorlage zu formatieren<br />

static Str<strong>in</strong>g format(Locale l, Str<strong>in</strong>g format, Object ... args)<br />

liefert e<strong>in</strong>en formatierten Str<strong>in</strong>g, <strong>de</strong>r aus <strong>de</strong>r gewünschten Sprache, <strong>de</strong>m Str<strong>in</strong>g und Argumenten<br />

hervorgeht.<br />

static Str<strong>in</strong>g format(Str<strong>in</strong>g format,Object ... args)<br />

liefert e<strong>in</strong>en formatierten Str<strong>in</strong>g, <strong>de</strong>r aus <strong>de</strong>m format-Str<strong>in</strong>g und <strong>de</strong>n Argumenten hervorgeht.<br />

Der Str<strong>in</strong>g format nennt sich Format-Str<strong>in</strong>g. Er enthält neben <strong>de</strong>m auszugeben<strong>de</strong>n<br />

Zeichen sogenannte Format-Spezifierer, die <strong>de</strong>m Formatierer darüber Auskunft<br />

geben, wie das Argument formatiert wer<strong>de</strong>n soll. "%s" steht für e<strong>in</strong>e unformatierte<br />

Ausgabe e<strong>in</strong>es Str<strong>in</strong>gs.<br />

Die Anzahl <strong>de</strong>r Format-Spezifierer ist groß und vielfältig. Intern übernehmen diese<br />

Aufgabe java.util.Formatter, die sich auch direkt verwen<strong>de</strong>n lassen.<br />

Praktischerweise ist das Formatieren und Ausgaben zu e<strong>in</strong>er neuen Funktion<br />

pr<strong>in</strong>tf() <strong>in</strong> <strong>de</strong>n Pr<strong>in</strong>tWriter und Pr<strong>in</strong>tStream 332 gewan<strong>de</strong>rt<br />

332 das System.out-Objekt ist vom Typ Pr<strong>in</strong>tStream<br />

465


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

7. E<strong>in</strong>- und Ausgabe<br />

In <strong>Java</strong> wer<strong>de</strong>n E<strong>in</strong>-, Ausgabeoperationen über sog. Datenströme 333 realisiert. Es<br />

stehen zur Behandlung <strong>de</strong>s Datenstrommo<strong>de</strong>lls zwei abstrakte Klassen zur<br />

Verfügung: InputStream und OutputStream. Die bei<strong>de</strong>n Klassen gehören zu<br />

<strong>de</strong>m Paket java.io 334 , das bei je<strong>de</strong>m Programm mit E<strong>in</strong>-/Ausgabeoperationen<br />

importiert wer<strong>de</strong>n muß.<br />

E<strong>in</strong>fache Strom-Metho<strong>de</strong>n erlauben nur das Versen<strong>de</strong>n von Bytes mit Hilfe von<br />

Datenströmen 335 . E<strong>in</strong> nicht <strong>in</strong>terpretierbarer Bytestrom kann von je<strong>de</strong>r beliebigen<br />

Quelle kommen. Quelle bzw. Ziel <strong>de</strong>s Stroms s<strong>in</strong>d völlig verschie<strong>de</strong>ne Erzeuger bzw.<br />

Verbraucher von Bytes.<br />

Allgeme<strong>in</strong>e Metho<strong>de</strong>n, die von je<strong>de</strong>r beliebigen Quelle lesen können, akzeptieren e<strong>in</strong><br />

Stromargument zur Bezeichnung <strong>de</strong>r Quelle. Allgeme<strong>in</strong>e Metho<strong>de</strong>n zum Schreiben<br />

akzeptieren e<strong>in</strong>en Strom zur Zielbestimmung. Filter haben zwei Stromargumente. Sie<br />

lesen vom ersten, verarbeiten die Daten und Schreiben <strong>in</strong>s zweite.<br />

Zum Sen<strong>de</strong>n/Empfangen verschie<strong>de</strong>ner Datentypen gibt es die Schnittstellen<br />

DataInput und DataOutput. Sie legen Metho<strong>de</strong>n zum Sen<strong>de</strong>n/Empfangen<br />

an<strong>de</strong>rer <strong>Java</strong>-Datentypen fest. Mit Hilfe <strong>de</strong>r Schnittstellen ObjectInput und<br />

ObjectOutput lassen sich ganze Objekte über e<strong>in</strong>en Strom sen<strong>de</strong>n.<br />

Alle Metho<strong>de</strong>n, die sich mit E<strong>in</strong>- und Ausgabeoperationen beschäftigen, wer<strong>de</strong>n <strong>in</strong><br />

<strong>de</strong>r Regel mit throws IOException abgesichert. Diese Subklasse von Exception<br />

enthält alle potentiellen I/O-Fehler, die bei <strong>de</strong>r Verwendung von Datenströmen<br />

auftreten können.<br />

Die I/O-Exceptions können direkt mit e<strong>in</strong>em try-catch-Block aufgefangen o<strong>de</strong>r an<br />

übergeordnete Metho<strong>de</strong>n weitergegeben wer<strong>de</strong>n.<br />

333 Der Begriff Strom kommt aus <strong>de</strong>m Betriebssystem Unix und bezieht sich auf „Pipes“. E<strong>in</strong>e Pipe ist e<strong>in</strong> nicht<br />

<strong>in</strong>terpretierbarer Strom von Bytes, <strong>de</strong>r zur Kommunikation zwischen Programmen (bzw. „gegabelten Kopien“<br />

e<strong>in</strong>es Programms) o<strong>de</strong>r zum Lesen und Schreiben von verschie<strong>de</strong>nen Geräten und Dateien benutzt wird.<br />

E<strong>in</strong> Strom ist e<strong>in</strong> Kommunikationspfad zwischen <strong>de</strong>r Quelle und <strong>de</strong>m Ziel e<strong>in</strong>es Informationsblocks.<br />

334 Die Klasse java.io enthält e<strong>in</strong>e große Anzahl von Klassen zur E<strong>in</strong>-/Ausgabe. Die meisten dieser Klassen<br />

leiten sich von InputStream bzw. OutputStream ab.<br />

335 E<strong>in</strong> Strom von Bytes kann mit e<strong>in</strong>em Wasserstrom verglichen wer<strong>de</strong>n. Wird aus e<strong>in</strong>em Strom Wasser<br />

entnommen, dann wird er als E<strong>in</strong>gabestrom benutzt. Wird <strong>in</strong> e<strong>in</strong>en Strom Wasser geschüttet, dann wird er als<br />

Ausgabestrom verwen<strong>de</strong>t. Die Verb<strong>in</strong>dung von Strömen kann mit <strong>de</strong>m Verb<strong>in</strong><strong>de</strong>n von Wasserschläuchen<br />

verglichen wer<strong>de</strong>n.<br />

467


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

DataInput<br />

ByteArrayInputStream<br />

FileInputStream<br />

BufferedInputStream<br />

DataInputStream<br />

FilterInputStream<br />

L<strong>in</strong>eNumberInputStream<br />

InputStream PipedInputStream PushbackInputStream<br />

SequenceInputStream<br />

Str<strong>in</strong>gBufferedInputStream<br />

Object<br />

RandomAccessFile<br />

DataOutput<br />

OutputStream<br />

ByteArrayOutputStream<br />

FileOutputStream<br />

FilterOutputStream<br />

PipedOutputStream<br />

BufferedOutputStream<br />

DataOutputStream<br />

Pr<strong>in</strong>tStream<br />

EOFException<br />

FileNotFoundException<br />

Throwable Exception IOException InterruptedIOException<br />

UTFDataFormatException<br />

Abb.: java.io<br />

468


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

7.1 Die abstrakten Klassen InputStream und OutputStream<br />

7.1.1 InputStream<br />

Aufgabenbeschreibung<br />

Mit dieser Klasse können Leseoperationen e<strong>in</strong>es Bytestroms verwirklicht wer<strong>de</strong>n.<br />

Woher die Bytes kommen und wie sie beför<strong>de</strong>rt wer<strong>de</strong>n, spielt ke<strong>in</strong>e Rolle. Sie<br />

müssen <strong>de</strong>m e<strong>in</strong>lesen<strong>de</strong>n Objekt nur zur Verfügung stehen. Die Aufgabe <strong>de</strong>r Klasse<br />

InputStream ist die Repräsentation von Klassen, die E<strong>in</strong>gaben aus verschie<strong>de</strong>nen<br />

Quellen produzieren.<br />

Klasse Funktion Argument für <strong>de</strong>n Konstruktor<br />

Nutzungsmöglichkeit<br />

ByteArrayInputStream<br />

E<strong>in</strong> Puffer im arbeitsspeicher Der Puffer aus <strong>de</strong>m Bytes geholt<br />

wird ald E<strong>in</strong>gabestrom benutzt. wer<strong>de</strong>n.<br />

Als Datenquelle. Verbun<strong>de</strong>n mit<br />

e<strong>in</strong>em FileInputStream-Objekt<br />

kann daraus e<strong>in</strong> nützliches<br />

Str<strong>in</strong>gBufferInputStream<br />

Konvertiert e<strong>in</strong>en Str<strong>in</strong>g <strong>in</strong> e<strong>in</strong>en<br />

InputStream<br />

469<br />

Interface gestaltet wer<strong>de</strong>n.<br />

E<strong>in</strong> Str<strong>in</strong>g. Die zugrun<strong>de</strong>liegen<strong>de</strong><br />

Implementierung benutzt e<strong>in</strong>en<br />

Str<strong>in</strong>gBuffer.<br />

Als Datenquelle. Verbun<strong>de</strong>n mit<br />

e<strong>in</strong>em FiIeInputStream-Objekt<br />

kann daraus e<strong>in</strong> nützliches<br />

Interface gestaltet wer<strong>de</strong>n.<br />

FileInputStream Dient zum Lesen aus e<strong>in</strong>er Datei E<strong>in</strong> Str<strong>in</strong>g <strong>de</strong>r <strong>de</strong>n Date<strong>in</strong>amen<br />

enthält o<strong>de</strong>r e<strong>in</strong> File- bzw.<br />

FileDescriptor-Objekt<br />

Als Datenquelle. Verbun<strong>de</strong>n mit<br />

PipedInputStream Produziert Daten für <strong>de</strong>n<br />

PipedOutputStream (implentiert<br />

das „Pipe“-Konzept.<br />

SequenceInputStream<br />

Abb.: E<strong>in</strong>gabestrom-Typen<br />

Konvertiert zwei o<strong>de</strong>r mehrere<br />

„InputStream“-Objekte <strong>in</strong> e<strong>in</strong>en<br />

e<strong>in</strong>zelnen InputStream.<br />

Quellen zu e<strong>in</strong>em E<strong>in</strong>gabestrom können se<strong>in</strong>:<br />

1. E<strong>in</strong> Array von Bytes<br />

2. E<strong>in</strong> Zeichenketten-Objekt<br />

3. E<strong>in</strong>e Datei<br />

4. E<strong>in</strong>e „Pipe“<br />

e<strong>in</strong>em FilterInputStream-Objekt<br />

kann daraus e<strong>in</strong> nützliches<br />

Interface gestaltet wer<strong>de</strong>n.<br />

PipedOutputStream.<br />

Als Datenquelle im<br />

Multithread<strong>in</strong>g. Verbun<strong>de</strong>n mit<br />

e<strong>in</strong>em FilterInputStream-Objekt<br />

kann daraus e<strong>in</strong> nützliches<br />

Interface gestaltet wer<strong>de</strong>n.<br />

Zwei InputStream-Objekte o<strong>de</strong>r<br />

e<strong>in</strong>e Enumeration für e<strong>in</strong>en<br />

Conta<strong>in</strong>er von InputStream-<br />

Objekten.<br />

Als Datenquelle. Verbun<strong>de</strong>n mit<br />

e<strong>in</strong>em FilterInputStream-Objekt<br />

kann daraus e<strong>in</strong> nützliches<br />

Interface gestaltet wer<strong>de</strong>n.


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

5. E<strong>in</strong>e Folge von Strömen, die zu e<strong>in</strong>em umfassen<strong>de</strong>n, e<strong>in</strong>zelnen Strom gesammelt wer<strong>de</strong>n können.<br />

6. An<strong>de</strong>rs beschaffene Quellen, z.B. e<strong>in</strong>e Internet-Verb<strong>in</strong>dung<br />

Die „read“-Metho<strong>de</strong>n<br />

Zum E<strong>in</strong>lesen von Datenströmen gibt es die „read“-Metho<strong>de</strong>. Die e<strong>in</strong>fachste Form<br />

ist: public abstract <strong>in</strong>t read() throws IOException. E<strong>in</strong> e<strong>in</strong>zelnes Byte<br />

wird aus <strong>de</strong>m E<strong>in</strong>gabestrom gelesen und ausgegeben. Falls <strong>de</strong>r Bytestrom das En<strong>de</strong><br />

erreicht hat, wird „-1“ 336 ausgageben. Die Metho<strong>de</strong> führt e<strong>in</strong> blockieren<strong>de</strong>s Lesen<br />

aus, d.h.: Es wird auf Daten gewartet, falls sie nicht unmittelbar zur Verfügung<br />

stehen.<br />

Die Metho<strong>de</strong> public <strong>in</strong>t read(byte[] bytes) throws IOException füllt<br />

e<strong>in</strong> Datenfeld mit Bytes, die aus e<strong>in</strong>em Strom gelesen wur<strong>de</strong>n und gibt die Anzahl<br />

Bytes zurück. Bei Bedarf (z.B. im Datenstrom s<strong>in</strong>d nicht genügend Bytes vorhan<strong>de</strong>n)<br />

wer<strong>de</strong>n weniger Bytes gelesen, als das Datenfeld aufnehmen kann.<br />

public <strong>in</strong>t read(byte[] bytes, <strong>in</strong>t offset, <strong>in</strong>t length) throws<br />

IOException füllt e<strong>in</strong> Datenfeld ab Position „offset“ mit bis zu length Bytes aus<br />

<strong>de</strong>m Strom. Es wird entwe<strong>de</strong>r die Anzahl <strong>de</strong>r gelesenen Bytes o<strong>de</strong>r –1 für das<br />

Dateien<strong>de</strong> ausgegeben.<br />

Weitere nützliche Metho<strong>de</strong>n<br />

public <strong>in</strong>t available throws IOException<br />

Allen „read“-Metho<strong>de</strong>n ist geme<strong>in</strong>sam: Sie warten auf das En<strong>de</strong> aller angefor<strong>de</strong>rten<br />

E<strong>in</strong>gaben. Es kann dabei zu längeren Blocka<strong>de</strong>n beim E<strong>in</strong>lesen von Daten kommen.<br />

Die Metho<strong>de</strong> „available“ gibt die Anzahl Bytes zurück, die ohne Blockieren<br />

gelesen wer<strong>de</strong>n kann.<br />

public long skip(long n) throws IOException<br />

Die „skip“-Metho<strong>de</strong> benutzt „read“ zum Überspr<strong>in</strong>gen und blockiert <strong>de</strong>shalb unter<br />

<strong>de</strong>n gleichen Bed<strong>in</strong>gungen wie „read“. Die gibt die Anzahl Bytes zurück, die sie<br />

übersprungen hat, o<strong>de</strong>r „-1“, falls das En<strong>de</strong> <strong>de</strong>s Stroms erreicht wur<strong>de</strong><br />

public boolean markSupported()<br />

Die Metho<strong>de</strong> gibt „true“ zurück, falls <strong>de</strong>r Sttrom Markierungen unterstützt.<br />

public void mark(<strong>in</strong>t readLimit)<br />

Die Metho<strong>de</strong> markiert die aktuelle Position im Strom für e<strong>in</strong>e spätere Rückkehr.<br />

public void reset() throws IOException<br />

Mit dieser Metho<strong>de</strong> kann <strong>de</strong>r Strom auf die Markierung zurücksetzen.<br />

public void close() throws IOException<br />

Diese Metho<strong>de</strong> schließt die Verarbeitung von e<strong>in</strong>em Strom. Die meisten Ströme<br />

wer<strong>de</strong>n automatisch bei <strong>de</strong>r Garbage Collection geschlossen.<br />

336 Rückgabe „-1“ be<strong>de</strong>utet zum Unterschied von C nicht, daß e<strong>in</strong> Fehler aufgetreten ist.<br />

470


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

7.1.2 OutputStream<br />

Aufgabenbeschreibung<br />

Der Ausgabestrom ist e<strong>in</strong> Empfänger von Daten. Man f<strong>in</strong><strong>de</strong>t Ausgabeströme fast nur<br />

<strong>in</strong> Verb<strong>in</strong>dung mit E<strong>in</strong>gabeströmen. Die Aufgabe <strong>de</strong>r Klasse OutputStream ist die<br />

Repräsentation von Klassen zur Festlegung <strong>de</strong>r Datensenke. Das kann e<strong>in</strong> „Array<br />

von Bytes“, e<strong>in</strong>e Datei o<strong>de</strong>r e<strong>in</strong>e „Pipe“ se<strong>in</strong>.<br />

Klasse Funktion Argumente für <strong>de</strong>n Konstruktur<br />

Nutzungsmöglichkeit<br />

ByteArrayOutputStream Kreiert e<strong>in</strong>en Arbeits-speicher-<br />

Puffer. Alle vom Strom<br />

gesen<strong>de</strong>ten Daten wer<strong>de</strong>n hier<br />

plaziert.<br />

Optional: zu <strong>in</strong>itialisieren<strong>de</strong><br />

Puffergröße<br />

Zum Bestimmen <strong>de</strong>s Datenziels.<br />

Verbun<strong>de</strong>n mit e<strong>in</strong>em<br />

FilterOutputStream-Objekt kann<br />

daraus e<strong>in</strong> nützliches Interface<br />

gestaltet wer<strong>de</strong>n.<br />

FileOutputStream Zum Schreiben e<strong>in</strong>er Datei E<strong>in</strong>e Zeichenkette, die <strong>de</strong>n<br />

Namen <strong>de</strong>r Datei repräsentiert ,<br />

bzw. e<strong>in</strong> File- o<strong>de</strong>r<br />

FileDescriptor-Objekt.<br />

Zum Bestimmen e<strong>in</strong>es<br />

Datenziels. Verbun<strong>de</strong>n mit<br />

e<strong>in</strong>em FileOutputStream-Objekt<br />

kann daraus e<strong>in</strong> nützliches<br />

Interface gestaltet wer<strong>de</strong>n.<br />

PipedOutputStream<br />

Abb.: Ausgabestrom-Typen<br />

Information zum Schreiben ist<br />

E<strong>in</strong>gabe für <strong>de</strong>n assoziierten<br />

PipedInputStream<br />

PipedInputStream<br />

Zum Bestimmen <strong>de</strong>s datenziels<br />

für „Multithread<strong>in</strong>g“. Verbun<strong>de</strong>n<br />

mit e<strong>in</strong>em FilterOutputStream-<br />

Objekt kann daraus e<strong>in</strong><br />

nützliches Interface gestaltet<br />

wer<strong>de</strong>n.<br />

Die „write“-Metho<strong>de</strong>n<br />

public abstract void write(<strong>in</strong>t b) throws IOException<br />

Die Metho<strong>de</strong> schreibt e<strong>in</strong> e<strong>in</strong>zelnes Byte <strong>in</strong> <strong>de</strong>n Ausgabestrom.<br />

public void write(byte[] bytes) throws IOException<br />

Die Metho<strong>de</strong> schreibt <strong>de</strong>n Inhalt <strong>de</strong>s Datenfelds bytes <strong>in</strong> <strong>de</strong>n Ausgabestrom.<br />

public void write(byte[] bytes, <strong>in</strong>t offset, <strong>in</strong>t length) throws<br />

IOException<br />

Die Metho<strong>de</strong> schreibt „length“ Bytes ab Position „offset“ aus <strong>de</strong>m Datenfeld<br />

bytes.<br />

public void flush() throws IOException<br />

Diese Metho<strong>de</strong> leert e<strong>in</strong>en Ausgabestrom<br />

public void close() throws IOException<br />

Auch Ausgabeströme sollten am En<strong>de</strong> e<strong>in</strong>er Verarbeitung geschlossen wer<strong>de</strong>n.<br />

471


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

7.2 Gefilterte Ströme<br />

Die Klassen FilterInputStream und FilterOutputStream zum Verknüpfen<br />

von Strömen.<br />

Diese Klassen ermöglichen das Verknüpfen von Strömen, ke<strong>in</strong>e neuen Metho<strong>de</strong>n.<br />

Der große Vorteil liegt <strong>in</strong> <strong>de</strong>r Verb<strong>in</strong>dung mit e<strong>in</strong>em an<strong>de</strong>ren Strom. Die<br />

Konstruktoren für <strong>de</strong>n FilterInputStream und <strong>de</strong>n FilterOutputStream<br />

haben <strong>de</strong>shalb InputStream- und OutputStream-Objekte als Parameter:<br />

protected FilterInputStream(InputStream e<strong>in</strong>)<br />

public FilterOutpuStream(OutputStream aus)<br />

Nützliche Subklassen von FilterInputStream s<strong>in</strong>d:<br />

Klasse Funktion Konstruktor-Argumente<br />

Nutzungsmöglichkeit<br />

DataInputStream<br />

Ermöglicht das E<strong>in</strong>lesen von InputStream<br />

primitiven Typen<br />

Enthält e<strong>in</strong> Interface für das<br />

lesen primitiver Typen<br />

BufferedInputStream Gepuffertes E<strong>in</strong>lesen InputStream mit optionale<br />

L<strong>in</strong>eNumberInputStream Verfolgt die zeilennummer im<br />

E<strong>in</strong>gabestrom. Die Metho<strong>de</strong><br />

getL<strong>in</strong>eNumber()<br />

und<br />

setL<strong>in</strong>eNumber(<strong>in</strong>t) können<br />

verwen<strong>de</strong>t wer<strong>de</strong>n<br />

PushbackInputStream Hat e<strong>in</strong>en Byte großen<br />

„Pushback“-Puffer, das letzte<br />

gelesene Zeichen kann <strong>in</strong> <strong>de</strong>n<br />

strom zurückgelegt wer<strong>de</strong>n.<br />

Abb.: „FilterInputStream“-Typen<br />

Nützliche Subklassen von FilterOutputStream s<strong>in</strong>d:<br />

Angabe <strong>de</strong>r Puffergröße<br />

InputStream<br />

Fügt e<strong>in</strong>e Zeilennummerierung<br />

h<strong>in</strong>zu<br />

InputStream<br />

Braucht <strong>de</strong>r <strong>Java</strong>-Compiler<br />

Klasse Funktion Konstruktor-Argumente<br />

Nutzungsmöglichkeit<br />

DataOutputStream Ausgabe primitiver Typen OutputStream<br />

Erlaubt das Schreiben primitiver<br />

Typen<br />

Pr<strong>in</strong>tStream Produziert formatierte Ausgabe. OutputStream<br />

Formatierte Ausgabe<br />

BufferedOutputStream<br />

Abb.: „FilterOutputStream“-Typen<br />

Gepufferte Ströme<br />

Gepufferte Ausgabe, <strong>de</strong>r Puffer<br />

kann mit flush() geleert wer<strong>de</strong>n<br />

OutputStream<br />

Gepufferte Ausgabe<br />

Gepufferte Ströme wer<strong>de</strong>n mit <strong>de</strong>r Klasse BufferedInputStream für die E<strong>in</strong>gabe und<br />

BufferedOutputStream für die Ausgabe realisiert:<br />

public BufferedInputStream(InputStream e<strong>in</strong>)<br />

public BufferedInputStream(InputStream e<strong>in</strong>, <strong>in</strong>t puffergroesse)<br />

public BufferedOutputStream(OutputStream aus)<br />

public BufferedOutputStream(OutputStream aus, <strong>in</strong>t puffergroesse)<br />

472


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Die Klasse BufferedInputStream implementiert die vollen Fähigkeiten <strong>de</strong>r<br />

InputStream-Metho<strong>de</strong>n, besitzt aber zusätzlich e<strong>in</strong>en gepufferten Byte-Array<br />

(Cache für weitere Leseoperationen).<br />

Die Klasse BufferedOutputStream implementiert die vollen Fähigkeiten <strong>de</strong>r<br />

OutputStream-Metho<strong>de</strong>n, besitzt aber zusätzlich e<strong>in</strong>en gepufferten Byte-Array.<br />

Die BufferedInputStream-Klasse versucht mit e<strong>in</strong>em e<strong>in</strong>zigen read-Aufruf soviel<br />

Daten wie möglich <strong>in</strong> ihre Puffer e<strong>in</strong>zulesen. Die BufferedOutputStream-Klasse<br />

ruft nur dann die write-Metho<strong>de</strong> auf, wenn ihr Puffer voll ist o<strong>de</strong>r wenn flush<br />

aufgerufen wird.<br />

Datenströme<br />

Die Filter DataInputStream und DataOutputStream s<strong>in</strong>d nützliche Filter <strong>de</strong>s<br />

java.io-Pakets. Sie ermöglichen Schreiben und Lesen primitiver Typen <strong>in</strong> <strong>Java</strong> auf<br />

masch<strong>in</strong>enunabhängige Weise.<br />

DataInputStream implementiert e<strong>in</strong>e DataInput-Schnittstelle. Diese Schnittstelle<br />

<strong>de</strong>f<strong>in</strong>iert Metho<strong>de</strong>n zum Lesen von primitiven Datentypen <strong>in</strong> <strong>Java</strong> (sowie noch e<strong>in</strong><br />

paar weitere Metho<strong>de</strong>n).<br />

DataOutputStream implementiert e<strong>in</strong>e DataOutput-Schnittstelle mit Ausgabe-<br />

Metho<strong>de</strong>n, die <strong>de</strong>n E<strong>in</strong>gabe-Metho<strong>de</strong>n <strong>de</strong>r DataInput-Schnittstelle entsprechen.<br />

Die Konstruktoren zu <strong>de</strong>n DataInputStream- und DataOutputStream-Klassen<br />

s<strong>in</strong>d Stromfilter-Konstruktoren, die <strong>de</strong>n zu filtern<strong>de</strong>n Strom als Parameter verwen<strong>de</strong>n:<br />

public DataInputStream (InputStream e<strong>in</strong>)<br />

public DataOutputStream(OutputStream aus)<br />

Die Schnittstelle DataInput. Sie enthält Metho<strong>de</strong>n zum Lesen primitiver Typen:<br />

public boolean readBoolean() throws EOFException, IOException<br />

public byte readByte() throws EOFException, IOException<br />

public <strong>in</strong>t readUnsignedByte() throws EOFException, IOException<br />

public char readChar() throws EOFException, IOException<br />

public short readShort() throws EOFException, IOException<br />

public <strong>in</strong>t readUnsignedShort() throws EOFException, IOException<br />

public <strong>in</strong>t readInt() throws EOFException, IOException<br />

public long readLong() throws EOFException, IOException<br />

public float readFloat() throws EOFException, IOException<br />

public double readDouble() throws EOFException, IOException<br />

public Str<strong>in</strong>g readUTF() throws EOFException, IOException 337 ,<br />

UTFDataFormatException<br />

Ausgeworfene Ausnahmen s<strong>in</strong>d vom Typ IOException o<strong>de</strong>r EOFException.<br />

EOFException wird ausgeworfen, wenn das En<strong>de</strong> <strong>de</strong>s Stroms erreicht wird. Die<br />

Ausnahme läßt sich überall e<strong>in</strong>setzen, wo bisher auf „-1“ überprüft wur<strong>de</strong>.<br />

Zum Lesen e<strong>in</strong>er durch Zeilenumschaltung (\r, \n) begrenzten Zeile aus e<strong>in</strong>er<br />

Textdateigibt es die Metho<strong>de</strong>: public Str<strong>in</strong>g readL<strong>in</strong>e() throws<br />

IOException. „\r, \n, \r\n“ wer<strong>de</strong>n entfernt, bevor die Zeile als Zeichenkette<br />

wie<strong>de</strong>rgegeben wird.<br />

Bsp.: E<strong>in</strong>lesen von <strong>de</strong>r Standar<strong>de</strong><strong>in</strong>gabe und Ausgabe<br />

337 UTF (Unico<strong>de</strong> Transmission Format) ist e<strong>in</strong> spezielles Format zum Kodieren von 16-Bit-Unico<strong>de</strong>-Werten<br />

473


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Mit <strong>de</strong>r Metho<strong>de</strong> public <strong>in</strong>t skipBytes(<strong>in</strong>t anzahlBytes) wird e<strong>in</strong>e Anzahl<br />

Bytes übersprungen.<br />

Die Schnittstelle DataOutput. Duch diese Schnittstelle s<strong>in</strong>d folgen<strong>de</strong> Metho<strong>de</strong>n zur<br />

Ausgabe primitiver Typen <strong>de</strong>f<strong>in</strong>iert:<br />

public void writeBoolean(boolean b) throws IOException<br />

public void writeByte(<strong>in</strong>t b) throws IOException<br />

public void writeChar(<strong>in</strong>t c) throws IOException<br />

public void writeShort(<strong>in</strong>t c) throws IOException<br />

public void writeInt(<strong>in</strong>t i) throws IOException<br />

public void writeFloat(float f) throws IOException<br />

public void writeDouble(double d) throws IOException<br />

public void writeUTF(Str<strong>in</strong>g s) throws IOException<br />

Mit <strong>de</strong>n Metho<strong>de</strong>n<br />

public void writeBytes(Str<strong>in</strong>g s) throws IOException<br />

public void writeChars(Str<strong>in</strong>g s) throws IOException<br />

kann e<strong>in</strong>e Zeichenkette als e<strong>in</strong>e Reihe von Bytes o<strong>de</strong>r Zeichen abgelegt wer<strong>de</strong>n.<br />

Die Pr<strong>in</strong>tStream-Klasse<br />

Der „System.out“-Strom mit <strong>de</strong>n Metho<strong>de</strong>n „System.out.pr<strong>in</strong>t“ bzw.<br />

„System.out.pr<strong>in</strong>tln“ ist e<strong>in</strong>e Instanz von Pr<strong>in</strong>tStream. „System.<strong>in</strong>“ ist e<strong>in</strong><br />

InputStream.<br />

Erstellen e<strong>in</strong>es „Pr<strong>in</strong>tStream“-Objekts. Das Pr<strong>in</strong>tStream-Objekt muß <strong>in</strong> e<strong>in</strong>en<br />

existieren<strong>de</strong>n Ausgabestrom angehangen wer<strong>de</strong>n. Über e<strong>in</strong>en optionalen Parameter<br />

kann automatisch die Metho<strong>de</strong> flush() aufgerufen wer<strong>de</strong>n:<br />

public Pr<strong>in</strong>tStream(OutputStream aus)<br />

public Pr<strong>in</strong>tStream(OutputStream aus, boolean autoFlush)<br />

Metho<strong>de</strong> für die Ausgabe von Objekten.<br />

public void flush()<br />

public void close()<br />

public void write(<strong>in</strong>t b)<br />

public void pr<strong>in</strong>t(Object o)<br />

public void pr<strong>in</strong>t(Str<strong>in</strong>g s)<br />

public void pr<strong>in</strong>t(boolean c)<br />

public void pr<strong>in</strong>t(char[] puffer)<br />

public void pr<strong>in</strong>t(char c)<br />

public void pr<strong>in</strong>t(<strong>in</strong>t i)<br />

public void pr<strong>in</strong>t(float f)<br />

public void pr<strong>in</strong>t(double d)<br />

public void pr<strong>in</strong>tln(Object f)<br />

public void pr<strong>in</strong>tln(Str<strong>in</strong>g s)<br />

public void pr<strong>in</strong>tln(boolean c)<br />

public void pr<strong>in</strong>tln(char[] puffer)<br />

public void pr<strong>in</strong>tln(char c)<br />

public void pr<strong>in</strong>tln(<strong>in</strong>t i)<br />

public void pr<strong>in</strong>tln(float f)<br />

public void pr<strong>in</strong>tln(double d)<br />

public void pr<strong>in</strong>tln()<br />

474


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

7.3 Die Klasse File<br />

E<strong>in</strong> File-Objekt kann sich auf e<strong>in</strong>e Datei o<strong>de</strong>r e<strong>in</strong> Verzeichnis beziehen und läßt<br />

sich auf verschie<strong>de</strong>ne Arten erstellen.<br />

public File(Str<strong>in</strong>g pfadname)<br />

erstellt e<strong>in</strong>e File-Instanz, die <strong>in</strong> pfadname angegeben wur<strong>de</strong>.<br />

public File(Str<strong>in</strong>g pfadname, Str<strong>in</strong>g date<strong>in</strong>ame)<br />

erstellt e<strong>in</strong>e File-Instanz, die sich aus <strong>de</strong>m <strong>in</strong> date<strong>in</strong>ame angegebenen Date<strong>in</strong>amen und <strong>de</strong>m <strong>in</strong><br />

pfadnamen aungegebenen Pfad zusammensetzt.<br />

public File(File verzeichnis, Str<strong>in</strong>g date<strong>in</strong>ame)<br />

erstellt e<strong>in</strong>e File-Instanz, die sich aus <strong>de</strong>m <strong>in</strong> date<strong>in</strong>ame angegebenen Date<strong>in</strong>amen und <strong>de</strong>m <strong>in</strong><br />

verzeichnis angegebenen Verzeichnis zusammensetzt.<br />

Die Klasse File (class java.io.File implements Serializable,<br />

Comparable) umfaßt Metho<strong>de</strong>n zum Bearbeiten von Dateien bzw. Verzeichnissen.<br />

Nach<strong>de</strong>m e<strong>in</strong> File-Objekt erzeugt wur<strong>de</strong>, können Metho<strong>de</strong>n zum Zugriff auf die<br />

e<strong>in</strong>zelnen Bestandteile <strong>de</strong>s Date<strong>in</strong>amens aufgerufen wer<strong>de</strong>n:<br />

public Str<strong>in</strong>g getName();<br />

// liefert <strong>de</strong>n Namen <strong>de</strong>r Datei<br />

public Str<strong>in</strong>g getPath();<br />

// liefert <strong>de</strong>n kompletten Namen (Datei o<strong>de</strong>r Verzeichnis)<br />

public Str<strong>in</strong>g getAbsolutePath();<br />

// liefert <strong>de</strong>n absoluten Pfadnamen für das File-Objekt<br />

public Str<strong>in</strong>g getParent();<br />

Die Metho<strong>de</strong>n<br />

public boolean isFile()<br />

public boolean isDirectory()<br />

überprüfen, ob e<strong>in</strong>e Datei o<strong>de</strong>r e<strong>in</strong> Verzeichnis vorliegt. Mit<br />

public boolean canRead()<br />

public boolean canWrite()<br />

wird Lese- bzw. Schreiberlaubnis überprüft. Die Metho<strong>de</strong><br />

public long lastModified()<br />

zeigt an, wann die Datei bzw. das Verzeichnis geän<strong>de</strong>rt wur<strong>de</strong>. Mit<br />

public boolean exists()<br />

wird die physikalische Existenz e<strong>in</strong>er Datei o<strong>de</strong>r e<strong>in</strong>es Verzeichnisses überprüft. Über<br />

public Str<strong>in</strong>g getName()<br />

wird <strong>de</strong>r Name e<strong>in</strong>er Datei bzw. e<strong>in</strong>es Verzeichnisses ermittelt.<br />

Wur<strong>de</strong> e<strong>in</strong> File-Objekt für e<strong>in</strong> Verzeichnis konstruiert, stehen folgen<strong>de</strong> Metho<strong>de</strong>n zur<br />

Verfügung:<br />

public Str<strong>in</strong>g[] list();<br />

liefert e<strong>in</strong>en Array von Str<strong>in</strong>gs, <strong>de</strong>r für je<strong>de</strong>n gefun<strong>de</strong>nen Verzeichnise<strong>in</strong>trag e<strong>in</strong><br />

Element enthält. Die Liste enthält <strong>de</strong>n Namen aller Dateien und Unterverzeichnisse<br />

mit Ausnahme von „.“ und „..“. Die e<strong>in</strong>fache Funktion list() liefert nur relative<br />

475


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Pfa<strong>de</strong> (Date<strong>in</strong>ame o<strong>de</strong>r Verzeichnisname). Komplette File-Objekte beschafft die<br />

Metho<strong>de</strong> listFiles), die ihre vollständige Pfadangabe kennen. Der vollständige Pfad<br />

kann dann mit getName() erfragt wer<strong>de</strong>n.<br />

In e<strong>in</strong>er Variante von list() kann die Auswahl <strong>de</strong>r Verzeichnise<strong>in</strong>träge<br />

e<strong>in</strong>geschränkt wer<strong>de</strong>n. Es muß aber dann e<strong>in</strong> Objekt übergeben wer<strong>de</strong>n, das das<br />

Interface FilenameFilter implementiert. Dieses besitzt die Metho<strong>de</strong> accept 338 ,<br />

die für je<strong>de</strong> gefun<strong>de</strong>ne Datei aufgerufen wird und entschei<strong>de</strong>t, ob sie <strong>in</strong> die Liste<br />

aufgenommen wer<strong>de</strong>n soll o<strong>de</strong>r nicht. Zusätzlich gibt es die statische Metho<strong>de</strong><br />

listRoots, mit <strong>de</strong>r e<strong>in</strong>e Liste <strong>de</strong>r Wurzeln verfügbarer Dateisysteme beschafft<br />

wer<strong>de</strong>n kann.<br />

public static File[] listRoots()<br />

liefert die verfügbaren Wurzeln <strong>de</strong>r Dateisysteme o<strong>de</strong>r null, falls diese nicht<br />

festgestellt wer<strong>de</strong>n konnten.<br />

Neben <strong>de</strong>m Zugriff auf Verzeichnise<strong>in</strong>träge gibt es Metho<strong>de</strong>n 339 für Löschen,<br />

Umbenennen von Dateien bzw. Verzeichnissen und für das Neuanlegen von<br />

Verzeichnissen.<br />

public boolean mkdir()<br />

// Anlegen <strong>de</strong>s spezifizierten Verzeichnisses<br />

public boolean mkdirs()<br />

// auch "Vater"-Verzeichnisse wer<strong>de</strong>n automatisch angelegt.<br />

public boolean renameTo(File ziel)<br />

public boolean <strong>de</strong>lete();<br />

// löscht die durch das File-Objekt bezeichnete Datei<br />

Da es bei URL-Objekten häufig vorkommt, daß e<strong>in</strong>e Datei die Basis ist, wur<strong>de</strong> ab<br />

Version 1.2 von <strong>Java</strong> die Metho<strong>de</strong> toURL() aufgenommen:<br />

public URL toURL() throws MalformedURLException<br />

// liefert e<strong>in</strong> URL-Objekt vom File-Objekt<br />

Es muß e<strong>in</strong> File-Objekt erzeugt wer<strong>de</strong>n. Anschließend erzeugt toURL() e<strong>in</strong> URL-<br />

Objekt, das das Protokoll „file“ trägt und danach e<strong>in</strong>e absolute Pfadangabe zur Datei<br />

bzw. zum Verzeichnis enthält.<br />

338 public boolean accept(File verzeichnis, Str<strong>in</strong>g dateiName)<br />

339 Alle Metho<strong>de</strong>n geben true zurück, wenn sie ihre Aufgabe erfolgreich ausführen konnten; an<strong>de</strong>renfalls geben<br />

sie false zurück<br />

476


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

7.4 Die RandomAccessFile-Klasse<br />

Für <strong>de</strong>n Zugriff auf Random-Access-Dateien stellt das Paket java.io die Klasse<br />

RandomAccessFile zur Verfügung. Es kann nur auf Dateien zugegriffen wer<strong>de</strong>n,<br />

auch das durch die „Streams“ realisierte Filter-Konzept gibt es <strong>in</strong> Random-Access-<br />

Dateien nicht.<br />

Öffnen, Neuanlegen und Schließen. Das Öffnen von Random-Access-Dateien erfolgt<br />

mit <strong>de</strong>n Konstruktoren:<br />

public RandomAccessFile(File datei, Str<strong>in</strong>g mo<strong>de</strong>) throws<br />

FileNotFoundException<br />

Bei <strong>de</strong>r Übergabe <strong>de</strong>s File-Objekts wird die durch dieses Objekt spezifizierte Datei<br />

geöffnet.<br />

public RandomAccessFile(Str<strong>in</strong>g name, Str<strong>in</strong>g mo<strong>de</strong>) throws<br />

FileNotFoundException<br />

Bei <strong>de</strong>r Übergabe <strong>de</strong>s Str<strong>in</strong>g-Parametres „name“ wird die Datei mit diesem Namen<br />

geöffnet. Der zweite Parameter mo<strong>de</strong> bestimmt die Art <strong>de</strong>s Zugriffs:<br />

- „r“: Öffnen nur zum Lesen<br />

- „rw“: Öffnen zum Schreiben und Lesen. E<strong>in</strong> re<strong>in</strong>er Schreibmodus wird nicht<br />

unterstützt.<br />

Es gibt <strong>in</strong> <strong>de</strong>r Klasse RandomAccessFile ke<strong>in</strong>e explizite Differenzierung zwischen<br />

Öffnen <strong>de</strong>r Datei und Neuanlegen. Implizit gilt: E<strong>in</strong>e Datei wird neu angelegt, wenn<br />

beim Öffnen im Modus „w“ nicht vorhan<strong>de</strong>n ist. Existiert die Datei bereits, wird sie<br />

unverän<strong>de</strong>rt geöffnet, und es gibt ke<strong>in</strong>e Möglichkeit, ihren Inhalt zu löschen o<strong>de</strong>r die<br />

Dateilänge auf e<strong>in</strong>en bestimmten Wert zusetzen.<br />

Das Schliessen erfolgt durch Aufruf <strong>de</strong>r parameterlosen Metho<strong>de</strong> close.<br />

Positionieren <strong>de</strong>s Dateizeigers. Je<strong>de</strong>r Schreib- und Lesezugriff erfolgt an <strong>de</strong>r<br />

Position, die durch <strong>de</strong>n aktuellen Inhalt <strong>de</strong>s Satzzeigers bestimmt wird und<br />

positioniert <strong>de</strong>n Zeiger um die Anzahl gelesener bzw. geschriebener Bytes weiter.<br />

Die Klasse RandomAccessFile stellt e<strong>in</strong>e Reihe von Metho<strong>de</strong>n zum Zugriff auf <strong>de</strong>n<br />

Satzzeiger zur Verfügung.<br />

Die Metho<strong>de</strong> seek. Die RandomAccessFile-Klasse besitzt alle Metho<strong>de</strong>n, die <strong>in</strong><br />

<strong>de</strong>n Schnittstellen DataInput und DataOutput verfügbar s<strong>in</strong>d. Mit public void<br />

seek(long dateiPosition) throws IOException kann man an je<strong>de</strong><br />

beliebige Position <strong>de</strong>r Datei spr<strong>in</strong>gen. Mit <strong>de</strong>r Metho<strong>de</strong> public long<br />

getFilePo<strong>in</strong>ter() throws IOException kann die <strong>de</strong>rzeitige Dateiposition 340<br />

bestimmt wer<strong>de</strong>n.<br />

Weiterh<strong>in</strong> stehen lesen<strong>de</strong> und schreiben<strong>de</strong> Zugriffsmetho<strong>de</strong>n zur Verfügung.<br />

340 Der Dateipositionswert <strong>in</strong> <strong>de</strong>n Metho<strong>de</strong>n seek und getFilePo<strong>in</strong>ter() ist die Anzahl <strong>de</strong>r Bytes vom<br />

Anfang bis zum En<strong>de</strong> <strong>de</strong>r Datei.<br />

477


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

RandomAccessFile<br />

<br />

public RandomAccessFile(File datei, Str<strong>in</strong>g zugriffsmodus) throws FileNotFoundException;<br />

// erzeugt e<strong>in</strong>en Random-Access-Datei-Strom vom File-Objekt.<br />

// Ob aus <strong>de</strong>r datei gelesen o<strong>de</strong>r geschrieben wird, bestimmt<br />

// e<strong>in</strong> Str<strong>in</strong>g, <strong>de</strong>r <strong>de</strong>n Modus angibt: „r“, „w“ o<strong>de</strong>r „rw“.<br />

public RandomAccessFile(Str<strong>in</strong>g datei, Str<strong>in</strong>g zugriffsmodus) throws FileNotFoundException;<br />

><br />

public long getFilePo<strong>in</strong>ter() throws IOException;<br />

publc void seek(long pos) throws IOException;<br />

// setzt <strong>de</strong>n Dateizeiger – bezogen auf <strong>de</strong>n Dateianfang – auf e<strong>in</strong>e<br />

// bestimmte Stelle, an <strong>de</strong>r <strong>de</strong>r nächste Lese- und Schreibzugriff<br />

// stattf<strong>in</strong><strong>de</strong>t<br />

public <strong>in</strong>t skipBytes(<strong>in</strong>t n) throws IOException;<br />

public long length() throws IOException;<br />

public f<strong>in</strong>al boolean readBoolean() throws IOException;<br />

public f<strong>in</strong>al byte readByte() throws IOException;<br />

public f<strong>in</strong>al char readChar() throws IOException;<br />

public f<strong>in</strong>al double readDouble() throws IOException;<br />

public f<strong>in</strong>al float readFloat() throws IOException;<br />

public f<strong>in</strong>al <strong>in</strong>t readInt() throws IOException;<br />

public f<strong>in</strong>al long readLong() throws IOException;<br />

public f<strong>in</strong>al short readShort() throws IOException;<br />

public f<strong>in</strong>al Str<strong>in</strong>g readUTF() throws IOException;<br />

public f<strong>in</strong>al void readFully(byte b[]) throws IOException;<br />

public f<strong>in</strong>al void readFully(byte[], <strong>in</strong>t off, <strong>in</strong>t laenge) throws IOException;<br />

public f<strong>in</strong>al Str<strong>in</strong>g readL<strong>in</strong>e() throws IOException;<br />

public f<strong>in</strong>al <strong>in</strong>t readUnsignedByte() throws IOException;<br />

public f<strong>in</strong>al <strong>in</strong>t readUnsignedShort() throws IOException;<br />

public <strong>in</strong>t read() throws IOException;<br />

public <strong>in</strong>t read(byte b[]) throws IOException;<br />

public <strong>in</strong>t read(byte b[], <strong>in</strong>t off, <strong>in</strong>t laenge) throws IOException;<br />

public f<strong>in</strong>al void writeBoolean(boolean w) throws IOException;<br />

public f<strong>in</strong>al void writeByte(<strong>in</strong>t w) throws IOException;<br />

public f<strong>in</strong>al void writeBytes(Str<strong>in</strong>g s) throws IOException;<br />

public f<strong>in</strong>al void writeChar(<strong>in</strong>t w) throws IOException;<br />

public f<strong>in</strong>al void writeChars(Str<strong>in</strong>g s) throws IOException;<br />

public f<strong>in</strong>al void writeDouble(double w) throws IOException;<br />

public f<strong>in</strong>al void writeFloat(float w) throws IOException;<br />

public f<strong>in</strong>al void writeInt(<strong>in</strong>t w) throws IOException;<br />

public f<strong>in</strong>al void writeLong(long w) throws IOException;<br />

public f<strong>in</strong>al void writeShort(<strong>in</strong>t w) throws IOException;<br />

public f<strong>in</strong>al void writeUTF(Str<strong>in</strong>g str) throws IOException;<br />

public void write(<strong>in</strong>t b) throws IOException;<br />

public void write(byte b[]) throws IOException;<br />

public void write(byte b[], <strong>in</strong>t off, <strong>in</strong>t laenge) throws IOException;<br />

Abb.: Die Klasse RandomAccessFile<br />

478


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

7.5 Die Klasse StreamTokenizer<br />

Die Klasse StreamTokenizer arbeitet im Gegensatz zum Str<strong>in</strong>gTokenizer aus<br />

<strong>de</strong>m Paket java.util auf e<strong>in</strong>em Datenstrom, genauer auf e<strong>in</strong>em Rea<strong>de</strong>r. Sie<br />

beachtet ke<strong>in</strong>e Unico<strong>de</strong>-E<strong>in</strong>gabe, son<strong>de</strong>rn nur Zeichen aus <strong>de</strong>m Bereich von \u0000<br />

bid \u00FF. Wahrend <strong>de</strong>s Parsens wer<strong>de</strong>n bestimmte Merkmale aus <strong>de</strong>m Text<br />

erkannt, so u. a. I<strong>de</strong>ntifier (etwa Schlüsselworte), Zahlen, Str<strong>in</strong>gs <strong>in</strong><br />

Anführungszeichen und verschie<strong>de</strong>ne Kommentararten (C-Stil o<strong>de</strong>r C++-Stil). Der<br />

Erkennungsvorgang wird anhand e<strong>in</strong>er Syntaxtabelle überprüft. Diese Tabelle enthält<br />

bspw. die Zeichen, die e<strong>in</strong> Schlüsselwort i<strong>de</strong>ntifizieren o<strong>de</strong>r die Zeichen, die<br />

Trennzeichen s<strong>in</strong>d. Je<strong>de</strong>s gelesene Zeichen wird dann ke<strong>in</strong>em, e<strong>in</strong>em o<strong>de</strong>r mehreren<br />

Attributen zugeordnet. Diese Attribute fallen <strong>in</strong> die Kategorie Trennzeichen,<br />

alphanumerische Zeichen, Zahlen, Hochkomma bzw. Anführungszeichen.<br />

Zur Benutzung <strong>de</strong>r Klasse wird zunächst e<strong>in</strong> StreamTokenizer-Objekt erzeugt und<br />

dann die Syntaxtabellen <strong>in</strong>itialisiert. Das Überlesen von Kommentarzeilen wird durch<br />

st.slashSlashComments(true); // Kommentar<br />

st.slashStarComments(true); /* Kommentart */<br />

gesteuert. Die erste Metho<strong>de</strong> überliest im E<strong>in</strong>gabestrom alle Zeichen bis zum Return.<br />

Die zweite Metho<strong>de</strong> überliest alles bis zum Stern / Slash.<br />

Beim Lesen <strong>de</strong>s Datenstroms mit nextToken() kann über bestimmte Flags erfragt<br />

wer<strong>de</strong>n, ob im Stream e<strong>in</strong> Wort (TT_WORD) e<strong>in</strong>e Zahl (TT_NUMBER), das En<strong>de</strong> <strong>de</strong>r<br />

Datei (TT_EOF) o<strong>de</strong>r das En<strong>de</strong> <strong>de</strong>r Zeile (TT_EOL) vorliegt. Wichtig ist<br />

eolIsSignificant(true) zu setzen, da an<strong>de</strong>rnfalls <strong>de</strong>r StreamTokenizer nie e<strong>in</strong><br />

TT_EOL f<strong>in</strong><strong>de</strong>t. Wur<strong>de</strong> e<strong>in</strong> Wort erkannt, dann wer<strong>de</strong>n alle Zeichen <strong>in</strong><br />

Kle<strong>in</strong>buchstaben konvertiert.<br />

Bsp. 341 : StreamTokenizerDemo.java<br />

import java.io.*;<br />

public class StreamTokenizerDemo<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[]) throws IOException<br />

{<br />

Str<strong>in</strong>g fn = "StreamTokenizerDemo.java";<br />

StreamTokenizer st = new StreamTokenizer(new FileRea<strong>de</strong>r(fn));<br />

st.slashSlashComments(true);<br />

st.ord<strong>in</strong>aryChar('/');<br />

st.parseNumbers();<br />

st.eolIsSignificant(true);<br />

<strong>in</strong>t tval;<br />

while ((tval = st.nextToken()) != st.TT_EOF)<br />

{<br />

if (tval == st.TT_NUMBER)<br />

System.out.pr<strong>in</strong>tln("Nummer: " + st.nval);<br />

else if (tval == st.TT_WORD)<br />

System.out.pr<strong>in</strong>tln("Wort: " + st.sval);<br />

else if (tval == st.TT_EOL)<br />

System.out.pr<strong>in</strong>tln("En<strong>de</strong> <strong>de</strong>r Zeile");<br />

else System.out.pr<strong>in</strong>tln("Zeichen: " + (char) st.ttype);<br />

}<br />

}<br />

}<br />

341 vgl. pr75100<br />

479


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

480


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

class java.io.StreamTokenizer<br />

><br />

StreamTokenizer(Rea<strong>de</strong>r r)<br />

><br />

public void resetSyntax<br />

// Re<strong>in</strong>itialisiert die Syntaxtabelle <strong>de</strong>s Tokenizers, so dass ke<strong>in</strong> Zeichen e<strong>in</strong>e So<strong>de</strong>rstellung<br />

// geniesst.<br />

public void wordChars(<strong>in</strong>t low, <strong>in</strong>t hi)<br />

// Zeichen im Bereich von low bis hi wer<strong>de</strong>n als Trennzeichen erkannt<br />

public void whitespaceChars(<strong>in</strong>t low, <strong>in</strong>t hi)<br />

// Zeichen im Bereich von low bis hi wer<strong>de</strong>n als Trennzeichen erkannt.<br />

public void ord<strong>in</strong>aryChars(<strong>in</strong>t low, <strong>in</strong>t hi)<br />

// Zeichen im Bereich von low bis hi geniessen ke<strong>in</strong>e Son<strong>de</strong>rbehandlung, wer<strong>de</strong>n als<br />

// normale zeichen behan<strong>de</strong>lt.<br />

public void ord<strong>in</strong>aryChar(<strong>in</strong>t ch)<br />

// Das Zeichen besitzt ke<strong>in</strong>e zusätzliche Funktion, ist bspw. ke<strong>in</strong> Kommentarzeichen,<br />

// trennsymbol o<strong>de</strong>r Nummernzeichen<br />

public void parseNumbers()<br />

// Nummern sollen vom Tokenizer erkannt wer<strong>de</strong>n. In <strong>de</strong>r Syntaxtabelle gelten die 12<br />

// Zeichen 0, 1, 2, 3, 4, 5, 6., 7, 8, 9, ., - als numerisch. Liegt e<strong>in</strong> Gleipunktzahl an,<br />

// so wird <strong>de</strong>r Vorkommateil <strong>in</strong> nval abgelegt und das Token ergibt im Attribut ttype<br />

// <strong>de</strong>n Wert TT_NUMBER<br />

public void commentChar(<strong>in</strong>t ch)<br />

// Gibt das Zeichen an, das e<strong>in</strong>en e<strong>in</strong>zeiligen Kommentar e<strong>in</strong>leitet<br />

public void slashStarComments(boolean flag)<br />

// Der Tokenizer soll Kommentare im C-Stil (/* .. */) erkennen o<strong>de</strong>r nicht<br />

public void slashSlashComments(boolean flag)<br />

// Der Tokenizer soll Kommentare im C++-Stil (// ..) erkennen o<strong>de</strong>r nicht<br />

public void lowerCaseMo<strong>de</strong>(boolean fl)<br />

// liegt <strong>in</strong> ttype e<strong>in</strong> Token vom Typ TT_WORD vor, so wird das automatisch <strong>in</strong><br />

// Kle<strong>in</strong>schreibweise konvertiert, falls fi gleich true ist<br />

public <strong>in</strong>t nextToken() throws IOException<br />

// liefert das nächste Token im Strom. Der Typ <strong>de</strong>s Token wird im Attribut ttype h<strong>in</strong>terlegt.<br />

// Zusätzliche Informationen bef<strong>in</strong><strong>de</strong>n sich im Attribut nval (Nummer) o<strong>de</strong>r sval (Zeichenkette).<br />

// In <strong>de</strong>r Regel wird solange geparst, bis das Token TT_EOF zurückgegeben wird.<br />

public void pushBack()<br />

// Der Aufruf von nextToken() liefert <strong>de</strong>n aktuellen wert im Attribut ttype und än<strong>de</strong>rt nval o<strong>de</strong>r sval<br />

// nicht<br />

public <strong>in</strong>t l<strong>in</strong>eno()<br />

// liefert die Zeilennummer<br />

Abb.: Die Klasse StreamTokenizer<br />

481


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

7.6 Klassen für spezielle nützliche Ströme<br />

Die L<strong>in</strong>eInputStream-Klasse<br />

Die SequenceInputStream-Klasse<br />

Die PushbackInputStream-Klasse<br />

482


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

7.7 <strong>Java</strong> 1.1 IO-Ströme<br />

7.7.1 Grundlagen<br />

Bis zur Version 1.0 <strong>de</strong>s JDK gab es nur Byte-Streams <strong>in</strong> <strong>Java</strong>. Das ergab<br />

Schwierigkeiten bei <strong>de</strong>r Umwandlung zwischen Bytes und 16 Bit langen Unico<strong>de</strong>-<br />

Zeichen, die <strong>in</strong>nerhalb von <strong>Java</strong> benutzt wer<strong>de</strong>n. Im JDK 1.1. wur<strong>de</strong>n daher<br />

„Character“-Streams auf <strong>de</strong>r Basis von 16 Bit langen Unico<strong>de</strong>-Zeichen e<strong>in</strong>geführt.<br />

Die Kompatibilität wur<strong>de</strong> durch Brückenklassen gewährleistet, die „Character“-<br />

Streams <strong>in</strong> „Byte“-Streams überführen (und umgekehrt).<br />

Quellen und Senken: „<strong>Java</strong> 1.0“-Klassen Korrespondieren<strong>de</strong> Klassen <strong>in</strong> <strong>Java</strong> 1.1<br />

InputStream<br />

Rea<strong>de</strong>r<br />

Konverter: InputStreamRea<strong>de</strong>r<br />

OutputStream<br />

Writer<br />

Konverter: OutputStreamWriter<br />

FileInputStream<br />

FileRea<strong>de</strong>r<br />

FileOutputStream<br />

FileWriter<br />

Str<strong>in</strong>gBufferInputStream<br />

Str<strong>in</strong>gRea<strong>de</strong>r<br />

Str<strong>in</strong>gWriter<br />

ByteArrayInputStream<br />

CharArrayRea<strong>de</strong>r<br />

ByteArrayOutputStream<br />

CharArrayWriter<br />

PipedInputStream<br />

PipedRea<strong>de</strong>r<br />

PipedOutputStream<br />

PipedWriter<br />

Abb. Wichtige Klassen zur E<strong>in</strong>gabe<br />

In Filterklassen kann nur e<strong>in</strong>e etwas gröbere Zuordnung von Klassen aus <strong>Java</strong> 1.0 zu<br />

<strong>Java</strong> 1.1 angegeben wer<strong>de</strong>n. So ist „BufferedOutputStream“ e<strong>in</strong>e Subklasse von<br />

FilterOutputStream, BufferedWriter ist dagegen ke<strong>in</strong>e Subklasse von<br />

FilterWriter.<br />

Filter: Klassen <strong>in</strong> <strong>Java</strong> 1.0 Korrespondieren<strong>de</strong> Klassen <strong>in</strong> <strong>Java</strong> 1.1<br />

FilterInputStream<br />

FilterRea<strong>de</strong>r<br />

FilterOutputStream<br />

FilterWriter (abstrakte Klasse ohne Subklassen)<br />

BufferedInputStream<br />

BufferedRea<strong>de</strong>r (mit readL<strong>in</strong>e)<br />

BufferedOutputStream<br />

BufferedWriter<br />

DataInputStream<br />

DataInputStream, bei readL<strong>in</strong>e() BufferedRea<strong>de</strong>r<br />

verwen<strong>de</strong>n<br />

Pr<strong>in</strong>tStream<br />

Pr<strong>in</strong>tWriter<br />

L<strong>in</strong>eNumberInputStream<br />

L<strong>in</strong>eNumberRea<strong>de</strong>r<br />

StreamTokenizer<br />

StreamTokenizer<br />

PushbackInputStream<br />

PushbackRea<strong>de</strong>r<br />

Abb. Wichtige Klassen zur Ausgabe<br />

E<strong>in</strong>ige Klassen bleiben von <strong>de</strong>r Umstellung unberührt: DataOutputStream, File,<br />

RandomAccessFile, SequenceInputStream.<br />

483


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

7.7.2 Die abstrakte Klassen Rea<strong>de</strong>r und ihre Ableitungen<br />

Basis aller sequentiellen E<strong>in</strong>gaben ist die abstrakte Klasse Rea<strong>de</strong>r, die e<strong>in</strong>e<br />

Schnittstelle für streambasierte E<strong>in</strong>gaben zur Verfügung stellt:<br />

public abstract class java.io.Rea<strong>de</strong>r<br />

{<br />

protected Rea<strong>de</strong>r();<br />

public abstract void close() throws IOException;<br />

// schliesst <strong>de</strong>n E<strong>in</strong>gabestrom<br />

public void mark(<strong>in</strong>t readAheadlimit) throws IOException;<br />

// markiert e<strong>in</strong>e bestimmte Position <strong>in</strong>nerhalb e<strong>in</strong>es E<strong>in</strong>gabestroms<br />

public boolean markSupported();<br />

// überprüft, ob Markieren unterstützt wird<br />

public <strong>in</strong>t read() throws IOException;<br />

// liest das nächste Zeichen aus <strong>de</strong>m E<strong>in</strong>gabestrom und liefert es als<br />

// "<strong>in</strong>t". Der Rückgabewert –1 kennzeichnet das En<strong>de</strong> <strong>de</strong>s E<strong>in</strong>gabestroms<br />

public <strong>in</strong>t read(char cbuf[]) throws IOException;<br />

// übernimmt e<strong>in</strong>e Reihe von Zeichen <strong>in</strong> <strong>de</strong>n als Parameter angegebenen<br />

// Array<br />

public abstract <strong>in</strong>t read(char cbuf[], <strong>in</strong>t off, <strong>in</strong>t len) throws IOException;<br />

// übernimmt e<strong>in</strong>e Reihe von zeichen <strong>in</strong> e<strong>in</strong>en Bereich <strong>de</strong>s Puffers, <strong>de</strong>r<br />

// durch Versatz (Offset) und die gewünschte Länge gekennzeichnet ist<br />

public long skip(long n) throws IOException;<br />

// überliest Zeichen im E<strong>in</strong>gabestrom<br />

public boolean ready() throws IOException;<br />

// liefert True, falls <strong>de</strong>r nächste Aufruf von read erfolgen kann, ohne<br />

// daß die E<strong>in</strong>gabe been<strong>de</strong>t ist<br />

public void reset() throws IOException;<br />

// setzt <strong>de</strong>n Lesezeiger an die markierte Stelle zurück<br />

}<br />

Von <strong>de</strong>r abstrakten Basisklasse Rea<strong>de</strong>r können ke<strong>in</strong>e Instanzen abgeleitet wer<strong>de</strong>n.<br />

Es gibt e<strong>in</strong>e Reihe aus Rea<strong>de</strong>r abgeleitete Klassen, die die Verb<strong>in</strong>dung zur<br />

konkreten E<strong>in</strong>gabe bzw. zum konkreten E<strong>in</strong>gabegerät herstellen.<br />

Klasse<br />

InputStreamRea<strong>de</strong>r<br />

FileRea<strong>de</strong>r<br />

PushbackRea<strong>de</strong>r<br />

BufferedRea<strong>de</strong>r<br />

L<strong>in</strong>eNumberRea<strong>de</strong>r<br />

Str<strong>in</strong>gRea<strong>de</strong>r<br />

CharArrayRea<strong>de</strong>r<br />

PipedRea<strong>de</strong>r<br />

Be<strong>de</strong>utung<br />

Abstrakte Klasse für alle Rea<strong>de</strong>r, die e<strong>in</strong>en Byte-<br />

Strom <strong>in</strong> e<strong>in</strong>en Character-Strom umwan<strong>de</strong>ln<br />

Konkrete Ableitung von InputStreamRea<strong>de</strong>r zum<br />

E<strong>in</strong>lesen e<strong>in</strong>er Datei<br />

E<strong>in</strong>gabefilter mit <strong>de</strong>r Möglichkeit zur Rückgabe<br />

von Zeichen<br />

Rea<strong>de</strong>r zur E<strong>in</strong>gabepufferung und zum Lesen<br />

kompletter Zeilen<br />

Ableitung von BufferedRea<strong>de</strong>r mit <strong>de</strong>r Fähigkeit<br />

zum Zählen von Zeilen<br />

Rea<strong>de</strong>r zum E<strong>in</strong>lesen von Zeichen aus e<strong>in</strong>em<br />

Str<strong>in</strong>g<br />

Rea<strong>de</strong>r zum E<strong>in</strong>lesen von Zeichen aus e<strong>in</strong>em<br />

Zeichen-Array<br />

Rea<strong>de</strong>r zum E<strong>in</strong>lesen von Zeichen aus e<strong>in</strong>em<br />

PipedWriter<br />

Abb.: Von Rea<strong>de</strong>r abgeleitete Klassen<br />

InputStreamRea<strong>de</strong>r<br />

Diese Klasse konvertiert Byte- <strong>in</strong> Character-Streams.<br />

484


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

FileRea<strong>de</strong>r<br />

Sie ermöglicht die E<strong>in</strong>gabe e<strong>in</strong>er Datei. Die Klasse FileRea<strong>de</strong>r geht direkt aus e<strong>in</strong>em<br />

InputStreamRea<strong>de</strong>r hervor.<br />

Konstruktoren. Mit ihnen ermöglicht FileRea<strong>de</strong>r das Öffnen von Dateien:<br />

public FileRea<strong>de</strong>r(Str<strong>in</strong>g dateiName) throws FileNotFoundException;<br />

Bei Übergabe <strong>de</strong>r Zeichenkette dateiName wird die Datei mit <strong>de</strong>m angegebenen<br />

Namen zum Lesen geöffnet. Falls sie nicht vorhan<strong>de</strong>n ist, kommt es zur Ausnahme<br />

<strong>de</strong>s Typs FileNotFoundException<br />

public FileRea<strong>de</strong>r(File datei) throws FileNotFoundException;<br />

erwartet e<strong>in</strong> File-Objekt zur Spezifikation e<strong>in</strong>er zu öffnen<strong>de</strong>n Datei.<br />

public FileRea<strong>de</strong>r(FileDescriptor fd);<br />

erwartet e<strong>in</strong> File-Deskriptor-Objekt, das e<strong>in</strong>e bereits geöffnete Datei angibt.<br />

Str<strong>in</strong>gRea<strong>de</strong>r<br />

Diese Klasse erlaubt das Lesen von Zeichen aus e<strong>in</strong>em Str<strong>in</strong>g.<br />

CharArrayRea<strong>de</strong>r<br />

Diese Klasse erlaubt das Lesen von Zeichen aus e<strong>in</strong>en „Zeichen-Array“<br />

BufferedRea<strong>de</strong>r<br />

Diese Klasse dient zur Pufferung von E<strong>in</strong>gaben. BufferedRea<strong>de</strong>r implementiert die<br />

vollen Fähigkeiten <strong>de</strong>r Metho<strong>de</strong>n von Rea<strong>de</strong>r. Diese Klasse verwen<strong>de</strong>t dazu<br />

gepufferte Zeichen-Arrays.<br />

Konstruktoren: public BufferedRea<strong>de</strong>r(Rea<strong>de</strong>r e<strong>in</strong>)<br />

public BufferedRea<strong>de</strong>r(Rea<strong>de</strong>r e<strong>in</strong>, <strong>in</strong>t gr)<br />

Der erste Parameter ist e<strong>in</strong> Rea<strong>de</strong>r-Objekt, auf das e<strong>in</strong> BufferedRea<strong>de</strong>r aufgesetzt<br />

wer<strong>de</strong>n soll. Der optionale Parameter gr gibt die Größe <strong>de</strong>s <strong>in</strong>ternen Puffer an. Fehlt<br />

er, so wird e<strong>in</strong>e für die meisten Situationen angemessene Standar<strong>de</strong><strong>in</strong>stellung<br />

verwen<strong>de</strong>t.<br />

Zeilenweises Lesen: public Str<strong>in</strong>g readL<strong>in</strong>e throws IOException.<br />

Rückgabewert ist e<strong>in</strong> Str<strong>in</strong>g mit <strong>de</strong>m Zeichen<strong>in</strong>halt (ohne Begrenzungszeichen) o<strong>de</strong>r<br />

„null“, falls das En<strong>de</strong> von „Stream“ erreicht wur<strong>de</strong>.<br />

L<strong>in</strong>eNumberRea<strong>de</strong>r<br />

Diese Klasse ist e<strong>in</strong>e Ableitung von BufferedRea<strong>de</strong>r, die zusätzlich noch die<br />

Anzahl <strong>de</strong>r E<strong>in</strong>gabezeichen beim E<strong>in</strong>lesen zählen kann. Mit „public <strong>in</strong>t<br />

getL<strong>in</strong>eNumber()“ wird <strong>de</strong>r aktuelle Stand <strong>de</strong>s Zeilenzählers abgefragt. Mit<br />

public void setL<strong>in</strong>eNumber(<strong>in</strong>t L<strong>in</strong>eNumber) kann <strong>de</strong>r aktuelle Stand <strong>de</strong>s<br />

Zeilenzählers verän<strong>de</strong>rt wer<strong>de</strong>n.<br />

485


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

7.7.3 Die abstrakte Klasse Writer und ihre Ableitungen<br />

Basis aller sequentiellen E<strong>in</strong>gaben ist die Klasse Writer, die e<strong>in</strong>e Schnittstelle für<br />

„stream“-basierte Ausgaben zur Verügung stellt:<br />

public abstract class java.io.Writer<br />

{<br />

protected Writer();<br />

// Oeffnen und Vorbereiten <strong>de</strong>s Ausgabestroms<br />

public abstract void close() throws IOException;<br />

// Schliessen <strong>de</strong>s Ausgabestroms<br />

public abstract void flush() throws IOException;<br />

// Leeren <strong>de</strong>r Ausgabe von e<strong>in</strong>em gepufferten Cache<br />

public void write(<strong>in</strong>t c);<br />

// Grundform mit e<strong>in</strong>em e<strong>in</strong>zigen Ausgabeparameter vom typ <strong>in</strong>t,<br />

// <strong>de</strong>r als Byte <strong>in</strong> <strong>de</strong>n Ausgabestrom geschrieben wird<br />

public void write(char e<strong>in</strong>Puffer[]) throws IOException;<br />

// Varianten von write, die e<strong>in</strong> Array von Bytes o<strong>de</strong>r e<strong>in</strong> Str<strong>in</strong>g-Objekt<br />

// erwarten und dieses durch Aufruf von write() ausgeben<br />

public abstract void write(char e<strong>in</strong>Puffer[], <strong>in</strong>t off, <strong>in</strong>t len) throws<br />

IOException;<br />

public void write(Str<strong>in</strong>g str) throws IOException;<br />

public void write(Str<strong>in</strong>g str, <strong>in</strong>t off, <strong>in</strong>t len) throws IOException;<br />

}<br />

Von <strong>de</strong>r abstrakten Basisklasse Writer können ke<strong>in</strong>e Instanzen abgeleitet wer<strong>de</strong>n.<br />

Es gibt e<strong>in</strong>e Reihe aus „Writer“ abgeleitete Klassen, die die Verb<strong>in</strong>dung zur<br />

konkreten Ausgabe(gerät) herstellen:<br />

Klasse<br />

OutputStreamWriter<br />

FileWriter<br />

FilterWriter<br />

Pr<strong>in</strong>tWriter<br />

BufferedWriter<br />

Str<strong>in</strong>gWriter<br />

CharArrayWriter<br />

PipedWriter<br />

Be<strong>de</strong>utung<br />

Abstrakte Basisklasse für alle Writer, die e<strong>in</strong>en Character-Stream <strong>in</strong><br />

e<strong>in</strong>en Byte-Stream umwan<strong>de</strong>ln<br />

Konkrete Ableitung von OutputStreamWriter zur Ausgabe e<strong>in</strong>er Datei<br />

Abstrakte Basisklasse für die Konstruktion von Ausgabefiltern<br />

Ausgabe aller Basistypen im Textformat<br />

Writer mit Ausgabepufferung<br />

Writer zur Ausgabe <strong>in</strong> e<strong>in</strong>em Str<strong>in</strong>g<br />

Writer zur Ausgabe im Zeichen-Array<br />

Writer zur Ausgabe <strong>in</strong> e<strong>in</strong>em PipedRea<strong>de</strong>r<br />

Abb.: Aus Writer abgeleitete Klassen<br />

Von <strong>de</strong>n abgeleiteten Klassen wird erwartet: Überlagerung <strong>de</strong>r Metho<strong>de</strong>n flush(),<br />

close() und write(char e<strong>in</strong>Puffer[], <strong>in</strong>t offset, <strong>in</strong>t laenge).<br />

Zun Schachteln von Ausgabe-Streams gibt es die aus Writer abgeleiteten Klassen:<br />

BufferedWriter, Pr<strong>in</strong>tWriter und FilterWriter. Falls die "write"-Metho<strong>de</strong><br />

e<strong>in</strong>es <strong>de</strong>rart geschachtelten "Writer" aufgerufen wird, gibt sie Daten nicht mehr<br />

direkt an <strong>de</strong>n <strong>in</strong>ternen "Writer", son<strong>de</strong>rn führt Filterfunktionen aus.<br />

OutputStreamWriter<br />

Diese Klasse übernimmt e<strong>in</strong>e Konvertierung zwischen Character- und Byte-Streams.<br />

486


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

FileWriter<br />

ermöglicht die Ausgabe <strong>in</strong> e<strong>in</strong>e Datei. Sie implementiert die abstrakten Eigenschaften<br />

von Writer. Die Klasse FileWriter bietet 4 Konstruktoren für das Öffnen von<br />

Dateien an:<br />

public FileWriter(Str<strong>in</strong>g dateiName) throws IOException<br />

Falls dateiName e<strong>in</strong>e bereits vorhan<strong>de</strong>ne Datei bezeichnet, wird sie geöffnet und ihr bisheriger Inhalt<br />

gelöscht, an<strong>de</strong>renfalls wird e<strong>in</strong>e neue Datei mit diesem Namen angelegt. Sukzessive Aufrufe von write<br />

schreiben dann <strong>in</strong> diese Datei.<br />

public FileWriter(Str<strong>in</strong>g dateiName, boolean append) throws IOException<br />

Wird <strong>de</strong>r Parameter append mit <strong>de</strong>m Wert true an <strong>de</strong>n Konstruktor übergeben, dann wer<strong>de</strong>n die<br />

Ausgabezeichen an die Datei angehängt, falls sie bereits existiert.<br />

public FileWriter (File datei) throws IOException<br />

public FileWriter (FileDescriptor fd)<br />

Parameter ist hier e<strong>in</strong> File-Objekt bzw. e<strong>in</strong> FileDescriptor-Objekt<br />

Die Klasse Str<strong>in</strong>gWriter<br />

Sie besitzt zwei Konstruktoren:<br />

public Str<strong>in</strong>gWriter()<br />

bestimmt e<strong>in</strong>en Str<strong>in</strong>gWriter <strong>in</strong> <strong>de</strong>r Standardgröße e<strong>in</strong>es Str<strong>in</strong>gBuffer-Objekts. Der <strong>in</strong>terne Puffer<br />

wächst automatisch, falls fortgesetzte Aufrufe von „write“ das ero<strong>de</strong>rlich machen<br />

public Str<strong>in</strong>gWriter(<strong>in</strong>t <strong>in</strong>itialGroesse)<br />

Zugriffe: Die schreiben<strong>de</strong>n Zugriffe auf die Puffer erfolgen mit <strong>de</strong>n von Writer<br />

bekannten „write“-Metho<strong>de</strong>n. Für <strong>de</strong>n Zugriff auf <strong>de</strong>n Inhalt <strong>de</strong>s Puffers gibt es die<br />

Metho<strong>de</strong>n public Str<strong>in</strong>gBuffer getBuffer() und public Str<strong>in</strong>g<br />

toStr<strong>in</strong>g().<br />

CharArrayWriter<br />

Sie schreibt Zeichen <strong>in</strong> e<strong>in</strong> Character-Array, das automatisch bei fortgesetzten<br />

Aufrufen von „write“ erweitert wird. Die Klasse besitzt zwei Konstruktoren:<br />

public CharArrayWriter()<br />

public CharArrayWriter(<strong>in</strong>t <strong>in</strong>itialGroesse)<br />

Zugriffe: Schreiben<strong>de</strong> Zugriffe erfolgen mit <strong>de</strong>n von „Writer“ bekannten „write“-<br />

Metho<strong>de</strong>n. Für <strong>de</strong>n Zugriff auf das Array gibt es die Metho<strong>de</strong>n public Str<strong>in</strong>g<br />

toStr<strong>in</strong>g() und public char[] toCharArray(). Mit <strong>de</strong>r Metho<strong>de</strong> public<br />

void reset() wird <strong>de</strong>r <strong>in</strong>terne Puffer geleert. Die Größe <strong>de</strong>s Zeichen-Array wird<br />

über public <strong>in</strong>t size() ermittelt. Durch Aufruf von public void<br />

writeTo(Writer aus) throws IOException kann <strong>de</strong>r Inhalt <strong>de</strong>s Array an<br />

e<strong>in</strong>en an<strong>de</strong>ren Writer übergeben wer<strong>de</strong>n und so mit e<strong>in</strong>em e<strong>in</strong>zigen<br />

Funktionsaufruf <strong>in</strong> e<strong>in</strong>e Datei geschrieben wer<strong>de</strong>n.<br />

BufferedWriter<br />

Die Klasse puffert „Stream“-Ausgaben über e<strong>in</strong>en <strong>in</strong>ternen Puffer, <strong>in</strong> <strong>de</strong>m die<br />

Ausgaben von write zwischengespeichert wer<strong>de</strong>n. Falls <strong>de</strong>r Puffer gefüllt ist o<strong>de</strong>r die<br />

Metho<strong>de</strong> flush aufgerufen wird, wer<strong>de</strong>n alle gepufferten Ausgaben <strong>in</strong> <strong>de</strong>n „Stream“<br />

geschrieben. Das Puffern <strong>de</strong>r Ausgabe ist immer dann nützlich, wenn die Ausgabe <strong>in</strong><br />

e<strong>in</strong>e Datei geschreiben wer<strong>de</strong>n soll. BufferedWriter besitzt zwei Konstruktoren:<br />

public BufferedWriter(Writer aus)<br />

public BufferedWriter(Writer aus, <strong>in</strong>t groesse)<br />

487


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

In bei<strong>de</strong>n Fällen wird e<strong>in</strong> existieren<strong>de</strong>r Writer übergeben, an <strong>de</strong>n alle gepufferten<br />

Ausgaben weitergereicht wer<strong>de</strong>n. Ist die Größe <strong>de</strong>s Puffers nicht explizit angegeben,<br />

legt BufferedWriter e<strong>in</strong>en Standardpuffer an.<br />

Die „write“-Metho<strong>de</strong>n von BufferedWriter entsprechen <strong>de</strong>nen <strong>de</strong>r Klasse<br />

Writer. Zusätzlich gibt es e<strong>in</strong>e Metho<strong>de</strong> newL<strong>in</strong>e mit <strong>de</strong>r e<strong>in</strong>e Zeileumschaltung <strong>in</strong><br />

<strong>de</strong>n Stream geschrieben wer<strong>de</strong>n kann.<br />

Pr<strong>in</strong>tWriter<br />

Diese Klasse stellt Ausgabemetho<strong>de</strong>n für alle primitiven Datentypen und für<br />

Objektgruppen zur Verfügung.<br />

Konstruktoren.<br />

public Pr<strong>in</strong>tWriter(Writer aus)<br />

<strong>in</strong>stanziert e<strong>in</strong> Pr<strong>in</strong>tWriter-Objekt durch Übergabe e<strong>in</strong>es Writer-Objekts, auf das die Ausgabe<br />

umgelenkt wer<strong>de</strong>n soll.<br />

public Pr<strong>in</strong>tWriter(Writer aus, boolean autoflush)<br />

Mit <strong>de</strong>m Parameter autoflush wird angegeben, ob nach <strong>de</strong>r Ausgabe „e<strong>in</strong>er Zeilenschaltung“<br />

automatisch die Metho<strong>de</strong> flush() aufgerufen wer<strong>de</strong>n soll.<br />

Ausgabe primitiver Typen (und Objekttypen): Sie wird realisiert über e<strong>in</strong>e Reihe<br />

überla<strong>de</strong>ner Metho<strong>de</strong>n mit <strong>de</strong>m Namen „pr<strong>in</strong>t“. Zusätzlich gibt es alle Metho<strong>de</strong>n <strong>in</strong><br />

<strong>de</strong>r Variante pr<strong>in</strong>tln, bei <strong>de</strong>r automatisch an das En<strong>de</strong> <strong>de</strong>r Ausgabe e<strong>in</strong><br />

Zeilenvorschub (Zeilenumschaltung) angehängt wird. Pr<strong>in</strong>tln() existiert auch<br />

parameterlos zur Ausgabe e<strong>in</strong>er e<strong>in</strong>zelnen Zeilenumschaltung.<br />

public void pr<strong>in</strong>t(boolean b)<br />

public void pr<strong>in</strong>t(char z)<br />

public void pr<strong>in</strong>t(char s[])<br />

public void pr<strong>in</strong>t(double d)<br />

public void pr<strong>in</strong>t(float f)<br />

public void pr<strong>in</strong>t(<strong>in</strong>t i)<br />

public void pr<strong>in</strong>t(long l)<br />

public void pr<strong>in</strong>t(Object obj)<br />

public void pr<strong>in</strong>t(Str<strong>in</strong>g s)<br />

488


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

7.7.4 Demonstrationsprogramm zur E<strong>in</strong>-/ Ausgabe ab <strong>Java</strong> Version 1.1<br />

Das Demonstrationsprogramm soll folgen<strong>de</strong> E<strong>in</strong>-/Ausgabe zeigen:<br />

1. Zeilenweises Lesen<br />

2. E<strong>in</strong>gabe vom Speicher<br />

3. Formatierte Speicher-E<strong>in</strong>gabe<br />

4. Zeilennummerierung und Dateiausgabe<br />

5. Speichern und Wie<strong>de</strong>rgew<strong>in</strong>nen von Daten<br />

import java.io.*;<br />

public class E<strong>in</strong>AusDemo<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g[] args)<br />

{<br />

try<br />

{<br />

// 1. Zeilenweises Lesen<br />

BufferedRea<strong>de</strong>r e<strong>in</strong>1 = new BufferedRea<strong>de</strong>r(<br />

new FileRea<strong>de</strong>r(args[0]));<br />

Str<strong>in</strong>g s1, s2 = new Str<strong>in</strong>g();<br />

while ((s1 = e<strong>in</strong>1.readL<strong>in</strong>e()) != null)<br />

s2 += s1 + '\n';<br />

e<strong>in</strong>1.close();<br />

// Lesen Standar<strong>de</strong><strong>in</strong>gabe<br />

BufferedRea<strong>de</strong>r std<strong>in</strong> = new BufferedRea<strong>de</strong>r(<br />

new InputStreamRea<strong>de</strong>r(System.<strong>in</strong>));<br />

System.out.pr<strong>in</strong>t("Gib e<strong>in</strong>e Zeile an: ");<br />

System.out.pr<strong>in</strong>tln(std<strong>in</strong>.readL<strong>in</strong>e());<br />

// 2. E<strong>in</strong>gabe vom Speicher<br />

Str<strong>in</strong>gRea<strong>de</strong>r e<strong>in</strong>2 = new Str<strong>in</strong>gRea<strong>de</strong>r(s2);<br />

<strong>in</strong>t zch;<br />

while ((zch=e<strong>in</strong>2.read()) != -1)<br />

System.out.pr<strong>in</strong>t((char) zch);<br />

// 3. Formatierte Speicher-e<strong>in</strong>gabe<br />

try<br />

{<br />

DataInputStream e<strong>in</strong>3 = new DataInputStream(<br />

new Str<strong>in</strong>gBufferInputStream(s2));<br />

while (true)<br />

System.out.pr<strong>in</strong>t((char) e<strong>in</strong>3.readByte());<br />

}<br />

catch(EOFException e)<br />

{<br />

System.out.pr<strong>in</strong>tln("En<strong>de</strong> <strong>de</strong>s Stroms");<br />

}<br />

// 4. Zeilennummerierung und Datei-Ausgabe<br />

try<br />

{<br />

L<strong>in</strong>eNumberRea<strong>de</strong>r zn = new L<strong>in</strong>eNumberRea<strong>de</strong>r(<br />

new Str<strong>in</strong>gRea<strong>de</strong>r(s2));<br />

// BufferedRea<strong>de</strong>r e<strong>in</strong>4 = new BufferedRea<strong>de</strong>r(zn);<br />

Pr<strong>in</strong>tWriter aus1 = new Pr<strong>in</strong>tWriter(<br />

new BufferedWriter(<br />

new FileWriter("E<strong>in</strong>AusDemo.aus")));<br />

while ((s1 = zn.readL<strong>in</strong>e()) != null)<br />

aus1.pr<strong>in</strong>tln("/* " + zn.getL<strong>in</strong>eNumber() + " */" + s1);<br />

// System.out.pr<strong>in</strong>tln("/* " + zn.getL<strong>in</strong>eNumber() + " */" + s1);<br />

aus1.close();<br />

}<br />

catch(EOFException e)<br />

{<br />

System.out.pr<strong>in</strong>tln("En<strong>de</strong> <strong>de</strong>s Stroms");<br />

489


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

}<br />

// 5. Speichern und Wie<strong>de</strong>rgew<strong>in</strong>nen von Daten<br />

try<br />

{<br />

DataOutputStream aus2 = new DataOutputStream(<br />

new BufferedOutputStream(<br />

new FileOutputStream("Daten.txt")));<br />

aus2.writeDouble(3.14159);<br />

aus2.writeBytes("Das war Pi");<br />

aus2.close();<br />

DataInputStream e<strong>in</strong>5 = new DataInputStream(<br />

new BufferedInputStream(<br />

new FileInputStream("Daten.txt")));<br />

BufferedRea<strong>de</strong>r e<strong>in</strong>5br = new BufferedRea<strong>de</strong>r(<br />

new InputStreamRea<strong>de</strong>r(e<strong>in</strong>5));<br />

System.out.pr<strong>in</strong>tln(e<strong>in</strong>5.readDouble());<br />

System.out.pr<strong>in</strong>tln(e<strong>in</strong>5br.readL<strong>in</strong>e());<br />

}<br />

catch(EOFException e)<br />

{<br />

System.out.pr<strong>in</strong>tln("En<strong>de</strong> <strong>de</strong>s Stroms");<br />

}<br />

}<br />

catch(FileNotFoundException e)<br />

{<br />

System.out.pr<strong>in</strong>tln("Datei nicht gefun<strong>de</strong>n: " + args[1]);<br />

}<br />

catch(IOException e)<br />

{<br />

System.out.pr<strong>in</strong>tln("IO Exception");<br />

}<br />

}<br />

}<br />

490


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

8. Serialisierung<br />

8.1 Grundlagen<br />

Serialisierung 342 ist die Fähigkeit e<strong>in</strong> Objekt, das im Hauptspeicher <strong>de</strong>r Anwendung<br />

existiert, <strong>in</strong> e<strong>in</strong> Format zu konvertieren, das es erlaubt, das Objekt <strong>in</strong> e<strong>in</strong>e Datei zu<br />

schreiben o<strong>de</strong>r über e<strong>in</strong>e Netzwerkverb<strong>in</strong>dung zu transportieren. Auch <strong>de</strong>r<br />

umgekehrte Weg gehört dazu: Rekonstruktion e<strong>in</strong>es <strong>in</strong> serialisierter Form<br />

vorliegen<strong>de</strong>n Objekts <strong>in</strong> das <strong>in</strong>terne Format <strong>de</strong>r laufen<strong>de</strong>n <strong>Java</strong>-Masch<strong>in</strong>e.<br />

8.1.1 Das Interface Serializable<br />

Serialisierbare Objekte können <strong>in</strong> Dateien gespeichert o<strong>de</strong>r über Netzwerke<br />

übertragen wer<strong>de</strong>n. Dazu müssen sie <strong>in</strong> e<strong>in</strong> Byteformat umgewan<strong>de</strong>lt wer<strong>de</strong>n<br />

können. Das Markierungs<strong>in</strong>terface Serializable kennzeichnet e<strong>in</strong>e Klasse als<br />

serialisierbar. Ähnlich wie bei Cloneable s<strong>in</strong>d dazu ke<strong>in</strong>e Metho<strong>de</strong>n zu<br />

implementieren, es müssen aber auch alle referenzieren<strong>de</strong>n Klassen serialisierbar<br />

se<strong>in</strong>. Falls dies nicht <strong>de</strong>r Fall ist, wird e<strong>in</strong>e NotSerializableException erzeugt.<br />

Voraussetzung für das Serialisieren ist die Implementierung <strong>de</strong>r Schnittstelle<br />

Serializable. Das Interface enthält ke<strong>in</strong>e Implementierung, son<strong>de</strong>rn dient nur<br />

dazu, durch die Implementierungs-Hierarchie die Fähigkeit zum Schreiben<br />

anzuzeigen.<br />

342 häufig auch mit <strong>de</strong>m Begriff Persistenz gleichgesetzt. Persistenz bezeichnet das dauerhafte Speichern von<br />

Daten auf e<strong>in</strong>em externen Datenträger, so daß sie auch nach <strong>de</strong>m Been<strong>de</strong>n <strong>de</strong>s Programms erhalten bleiben.<br />

491


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

8.1.2 Die Klasse ObjectOutputStream<br />

Zur Serialisierung e<strong>in</strong>es Objekts ( - o<strong>de</strong>r allgeme<strong>in</strong>er Daten bzw. Primitive - ) benötigt<br />

man e<strong>in</strong>en OutputStream. Am besten eignet sich e<strong>in</strong> FileOutputStream dafür,<br />

da meistens die Daten <strong>in</strong> e<strong>in</strong>er Datei 343 gesichert wer<strong>de</strong>n sollen. FileOutputStream<br />

erweitert die Klasse OutputStream. E<strong>in</strong>e Verb<strong>in</strong>dung zwischen Datei und Objekt-<br />

Strom wird durch die Klasse ObjectOutputStream (class<br />

java.io.ObjectOutputStream extends OutputStream implements<br />

DataOutput, ObjectOutput, ObjectStreamConstants) geschaffen.<br />

ObjectOutputStream implementiert das Interface ObjectOutput, z.B. die<br />

Funktion writeObject() zum Schreiben von Objekten.<br />

OutputStream<br />

{ abstract }<br />

ObjectOutputStream<br />

<br />

public ObjectOutputStream(OutputStream out) throws IOException<br />

// erzeugt e<strong>in</strong>en ObjectOutputStream, <strong>de</strong>r <strong>in</strong> <strong>de</strong>n angegebenen OutputStream<br />

// schreibt<br />

><br />

public f<strong>in</strong>al void writeObject(Object obj) throws IOException<br />

// schreibt das Objekt. Die implementieren<strong>de</strong> Klasse weiß, wie<br />

// das Objekt zu schreiben ist<br />

public void write(<strong>in</strong>t b) throws IOException<br />

// E<strong>in</strong> Bayte wird geschrieben<br />

public void write(byte b[]) throws IOException<br />

// schreibt e<strong>in</strong> Array von Bytes<br />

...<br />

public void writeBytes(Str<strong>in</strong>g daten) throws IOException;<br />

public void writeChars(Str<strong>in</strong>g daten) throws IOException;<br />

public void writeByte(<strong>in</strong>t daten) throws IOException;<br />

public void writeShort(<strong>in</strong>t daten) throws IOException;<br />

public void writeChar(<strong>in</strong>t daten) throws IOException;<br />

public void writeInt(<strong>in</strong>t daten) throws IOException;<br />

public void writeLong(long daten) throws IOException;<br />

public void writeFloat(float daten) throws IOException;<br />

public void writeDouble(double daten) throws IOException;<br />

...<br />

public void flush() throws IOException<br />

// Noch gepufferte Daten wer<strong>de</strong>n geschrieben<br />

public void close() throws IOException<br />

// Stream wird geschlossen.<br />

...<br />

Abb.: Die Klasse OblectOutputStream e<strong>in</strong>schl. e<strong>in</strong>iger Metho<strong>de</strong>n <strong>de</strong>s Interface ObjectOutput<br />

343 Der Date<strong>in</strong>ame wird häufig so gewählt, daß er mit <strong>de</strong>m Präfix „ser“ en<strong>de</strong>t.<br />

492


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Die Metho<strong>de</strong> public f<strong>in</strong>al void writeObject(Object obj) throws<br />

IOException schreibt folgen<strong>de</strong> Daten <strong>in</strong> <strong>de</strong>n OutputStream:<br />

- Die Klasse <strong>de</strong>s als Argument übergebenen Objekts<br />

- Die Signatur <strong>de</strong>r Klasse<br />

- Alle nicht statischen, nicht transienten Membervariablen <strong>de</strong>s übergebenen Objekts e<strong>in</strong>schl. <strong>de</strong>r aus<br />

Elternklassen geerbten Membervariablen.<br />

Bsp.: Serialisierung e<strong>in</strong>es „Zeit-“ Objekts. Das Zeit-Objekt ist e<strong>in</strong>e Instanz <strong>de</strong>r<br />

Klasse „Zeit“, die e<strong>in</strong>e Uhrzeit (Stun<strong>de</strong>, M<strong>in</strong>ute) kapselt.<br />

import java.io.*;<br />

public class Zeit implements Serializable<br />

{<br />

private <strong>in</strong>t stun<strong>de</strong>;<br />

private <strong>in</strong>t m<strong>in</strong>ute;<br />

public Zeit(<strong>in</strong>t stun<strong>de</strong>, <strong>in</strong>t m<strong>in</strong>ute)<br />

{<br />

this.stun<strong>de</strong> = stun<strong>de</strong>;<br />

this.m<strong>in</strong>ute = m<strong>in</strong>ute;<br />

}<br />

public Str<strong>in</strong>g toStr<strong>in</strong>g()<br />

{<br />

return stun<strong>de</strong> + ":" + m<strong>in</strong>ute;<br />

}<br />

}<br />

Mit Hilfe e<strong>in</strong>es Objekts vom Typ ObjectOutputStream kann e<strong>in</strong> Time-Objekt<br />

serialisiert wer<strong>de</strong>n.<br />

import java.io.*;<br />

import java.util.*;<br />

public class Pr81100<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

try {<br />

FileOutputStream fs = new FileOutputStream("test1.ser");<br />

ObjectOutputStream os = new ObjectOutputStream(fs);<br />

Zeit z = new Zeit(10,20);<br />

os.writeObject(z);<br />

os.close();<br />

}<br />

catch(IOException e)<br />

{ System.err.pr<strong>in</strong>tln(e.toStr<strong>in</strong>g()); }<br />

}<br />

}<br />

Nach <strong>de</strong>m Schließen <strong>de</strong>s Streams steht das serialisierte Objekt <strong>in</strong> „test1.ser“.<br />

493


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

8.1.3 Die Klasse ObjectInputStream<br />

An e<strong>in</strong>en E<strong>in</strong>gebestrom kommt man über e<strong>in</strong>en InputStream. Da die Daten häufig<br />

aus e<strong>in</strong>er Datei kommen ist dies häufig e<strong>in</strong> FileInputStream, <strong>de</strong>r mit e<strong>in</strong>em<br />

ObjectInputStream verknüpft wird. Die Metho<strong>de</strong> readObject() liest das<br />

Objekt, f<strong>in</strong><strong>de</strong>t heraus, was für e<strong>in</strong> Typ es ist und holt, falls nötig, auch noch Objekte,<br />

auf die verwiesen wird.<br />

InputStream<br />

{ abstract }<br />

ObjectInputStream<br />

<br />

public ObjectInputStream(InputStream <strong>in</strong>) throws IOException,<br />

StreamCorruptedException<br />

><br />

public f<strong>in</strong>al Object readObject() throws OptionalDataException<br />

ClassNotFoundException, IOException<br />

// Liest e<strong>in</strong> Objekt und gibt es zurück<br />

public boolean readBoolean() throws IOException<br />

public byte readByte(i) throws IOException<br />

public short readShort() throws IOException<br />

public char readChar() throws IOException<br />

public <strong>in</strong>t readInt() throws IOException<br />

public long readLong() throws IOException<br />

public float readFloat() throws IOException<br />

public double readDouble() throws IOException<br />

. ...<br />

Abb.: Die Klasse ObjectInputStream<br />

Analog zum Interface ObjectOutput gibt es hier das Interface ObjectInput<br />

(<strong>in</strong>terface java.io.ObjectInput extends DataInput)<br />

494


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

><br />

ObjectInput<br />

><br />

public Object readObject() throws ClassNotFoundException, IOException<br />

public <strong>in</strong>t read() throws IOException<br />

// liest e<strong>in</strong> byte aus <strong>de</strong>m datenstrom. Dieses ist –1, wenn das En<strong>de</strong> erreicht ist<br />

public <strong>in</strong>t read(byte b[]) throws IOException<br />

// liest e<strong>in</strong> Array <strong>in</strong> <strong>de</strong>n Puffer. Das En<strong>de</strong> wird durch –1 angezeigt<br />

public <strong>in</strong>t read(byte b[], <strong>in</strong>t off, <strong>in</strong>t laenge) throws IOException<br />

// liest e<strong>in</strong> Array von Bytes <strong>in</strong> <strong>de</strong>n Puffer b an die Stelle off genau len Bytes<br />

public long skip (long n) throws IOException<br />

// überspr<strong>in</strong>gt n Bytes im E<strong>in</strong>gabestrom.<br />

public <strong>in</strong>t available() throws IOException<br />

// Gibt die Anzahl <strong>de</strong>r Zeichen zurück, die ohne Blocka<strong>de</strong> gelesen wer<strong>de</strong>n<br />

public void close() throws IOException<br />

// schließt <strong>de</strong>n E<strong>in</strong>gabestrom<br />

...<br />

Abb.: Das Interface ObjectInput<br />

Das Deserialisieren kann mann sich etwa so vorstellen:<br />

1. Anlegen e<strong>in</strong>en neuen Objekts <strong>de</strong>s zu serialisieren<strong>de</strong>n Typs, Vorbelegen <strong>de</strong>r Membervariablen mit<br />

Defaultwerten, Aufruf <strong>de</strong>s Defaultkonstruktor <strong>de</strong>r ersten nicht serialisierbaren Superklasse.<br />

2. Lesen <strong>de</strong>r serialsierten Daten und Zuweisen <strong>de</strong>r Daten zu <strong>de</strong>n entsprechen<strong>de</strong>n Membervariablen<br />

<strong>de</strong>s angelegten Objekts.<br />

Das durch Deserialisieren erzeugte Objekt hat anschließend dieselbe Struktur und<br />

<strong>de</strong>nselben Zustand, <strong>de</strong>n das serialsierte Objekt hatte 344 . Da <strong>de</strong>r Rückgabewert von<br />

readObject() vom Typ Object ist, muß das erzeugte Objekt <strong>in</strong> <strong>de</strong>n tatsächlichen<br />

Typ (o<strong>de</strong>r e<strong>in</strong>e se<strong>in</strong>er Oberklassen) umgewan<strong>de</strong>lt wer<strong>de</strong>n.<br />

Bsp.: Deserialisieren <strong>de</strong>s im vorherigen Beispiel serialisierten und <strong>in</strong> die Datei<br />

„test1.ser“ geschriebenen Zeit-Objekts.<br />

import java.io.*;<br />

import java.util.*;<br />

public class Pr81101<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

try {<br />

FileInputStream fs = new FileInputStream("test1.ser");<br />

ObjectInputStream is = new ObjectInputStream(fs);<br />

Zeit z = (Zeit) is.readObject();<br />

System.out.pr<strong>in</strong>tln(z.toStr<strong>in</strong>g());<br />

is.close();<br />

}<br />

catch(ClassNotFoundException e)<br />

{ System.err.pr<strong>in</strong>tln(e.toStr<strong>in</strong>g()); }<br />

catch(IOException e)<br />

{ System.err.pr<strong>in</strong>tln(e.toStr<strong>in</strong>g()); }<br />

}<br />

}<br />

344 Abgesehen von <strong>de</strong>n nicht serialsierten Membervariablen <strong>de</strong>s Typs static o<strong>de</strong>r transient.<br />

495


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Beim Deserialisieren von Objekten können e<strong>in</strong>ige Fehler passieren. Damit e<strong>in</strong> Aufruf<br />

von readObject() erfolgreich ist, müssen mehrere Kriterien erfüllt se<strong>in</strong>:<br />

- das nächste Element <strong>de</strong>s E<strong>in</strong>gabestroms ist tatsächlich e<strong>in</strong> Objekt (ke<strong>in</strong> primitiver Typ).<br />

- Das Objekt muß vollständig und fehlerfrei aus <strong>de</strong>r E<strong>in</strong>gabedatei lesen lasse.<br />

- Es muß e<strong>in</strong>e Konvertierung auf <strong>de</strong>n gewünschten Typ erlauben, also entwe<strong>de</strong>r zu <strong>de</strong>rselben o<strong>de</strong>r<br />

e<strong>in</strong>er daraus abgeleiteten Klasse gehören.<br />

- Der Byteco<strong>de</strong> für die Klasse <strong>de</strong>s zu serialsieren<strong>de</strong>n Objekts muß vorhan<strong>de</strong>n se<strong>in</strong>. Er wird beim<br />

Serialisieren nicht mitgespeichert, son<strong>de</strong>rn muß <strong>de</strong>m Empfängerstrom wie üblich als kompilierter<br />

Byteco<strong>de</strong> zur Verfügung stehen.<br />

- Die Klassen<strong>in</strong>formation <strong>de</strong>s serialisierten Objekts und die im <strong>de</strong>serialisieren<strong>de</strong>n Programm als<br />

Byteco<strong>de</strong> vorhan<strong>de</strong>ne Klasse müssen zue<strong>in</strong>an<strong>de</strong>r kompatibel se<strong>in</strong>.<br />

8.2 Tiefe Objektkopien<br />

Klassen könen die Metho<strong>de</strong> clone() überschreiben und so e<strong>in</strong>e Kopie <strong>de</strong>r Werte<br />

liefern. Die Standarimplementierung ist jedoch so festgelegt, daß die Kopie flach ist.<br />

Das be<strong>de</strong>utet: Referenzen auf Objekte, die vom klonen<strong>de</strong>n Objekt ausgehen, wer<strong>de</strong>n<br />

beibehalten und diese Objekte nicht extra kopiert.<br />

E<strong>in</strong>e tiefe Kopie kann folgen<strong>de</strong>rmaßen erzeugt wer<strong>de</strong>n: Das zu klonen<strong>de</strong> Objekt ist<br />

zu serialisieren und dann wie<strong>de</strong>r auszupacken. Die zu klonen<strong>de</strong>n Objekte müssen<br />

nur das Serializable-Interface implementieren.<br />

496


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

9. Netzwerkprogrammierung<br />

Das Paket java.net enthält Klassen zur Programmierung von TCP/IP 345 -<br />

Netzwerkzugriffen. Dieses (Internet-) Protokoll stellt die Basis aller <strong>Java</strong>-Netzzugriffe<br />

dar. Darauf aufbauend gibt es weiter entwickelte Netzwerkprotokolle wie RMI für<br />

entfernte Metho<strong>de</strong>naufrufe und verteilte Anwendungen und Enterprise<br />

<strong>Java</strong>Beans für verteilte Objekte.<br />

9.1 Adressen, Ressourcen und URLs<br />

9.1.1 Die Klasse InetAddress<br />

Zur Adressierung von Rechnern im Netz wird die Klasse InetAddress <strong>de</strong>s Pakets<br />

java.net verwen<strong>de</strong>t. E<strong>in</strong>e InetAddress enthält sowohl e<strong>in</strong>e IP-Adresse 346 als<br />

auch <strong>de</strong>n symbolischen Namen <strong>de</strong>s jeweiligen Rechners 347 . Die bei<strong>de</strong>n Bestandteile<br />

können mit <strong>de</strong>n Metho<strong>de</strong>n getHostName() und getHostAddress() abgefragt<br />

wer<strong>de</strong>n. Mit Hilfe von getAddress() kann die IP-Adresse auch direkt als „byte“-<br />

Array (mit 4 Elementen) beschafft wer<strong>de</strong>n.<br />

Zur Generierung e<strong>in</strong>es InetAddress-Objekts stehen zur Verfügung:<br />

public static InetAddress getByName(Str<strong>in</strong>g host) throws<br />

UnknownHostException<br />

// erwartet e<strong>in</strong>en Str<strong>in</strong>g mit <strong>de</strong>r IP-Adtesse o<strong>de</strong>r <strong>de</strong>m Namen als Argument<br />

public static InetAddress getLocalHost() throws UnknownHostException<br />

// liefert e<strong>in</strong> InetAdress-Objekt für <strong>de</strong>n eigenen Rechner<br />

Das folgen<strong>de</strong> Programm 348 ermittelt zu e<strong>in</strong>er IP-Adresse <strong>de</strong>n symbolischen Namen<br />

<strong>de</strong>s zufehörigen Rechners bzw. zum symbolischen Namen e<strong>in</strong>e IP-Adresse,<br />

import java.net.*;<br />

public class WerB<strong>in</strong>Ich<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

if (args.length != 1)<br />

{<br />

System.err.pr<strong>in</strong>tln("Verwendung: java WerB<strong>in</strong>Ich ");<br />

System.exit(1);<br />

}<br />

try {<br />

345 Als Protokoll bezeichnet man die Menge aller Regeln, die nötig s<strong>in</strong>d, um e<strong>in</strong>en kontrollierten und<br />

e<strong>in</strong><strong>de</strong>utigen Verb<strong>in</strong>dungsaufbau, Datenaustausch und Verb<strong>in</strong>dungsabbau gewährleisten zu können. Die <strong>de</strong>rzeit<br />

<strong>in</strong> <strong>Java</strong> verfügbareb Netzwerkfähigkeiten basieren auf <strong>de</strong>n Internet-Protokollen TCP/IP (bzw. TCP/UDP)<br />

346 IP steht für Internet Protocol. Die 32-Bit-lange IP-Adresse besteht aus e<strong>in</strong>er Netzwerk-ID und e<strong>in</strong>er Host-ID.<br />

Die Host-ID gibt die Bezeichnung <strong>de</strong>s Rechners <strong>in</strong>nerhalb se<strong>in</strong>es eigenen Netzwerks an, die Netwerk-ID liefert<br />

die Bezeichnung <strong>de</strong>s Rechners.<br />

347 Anstelle <strong>de</strong>r IP-Adresse können bei Anwendungsprotokollen symbolische Namen verwen<strong>de</strong>t wer<strong>de</strong>n. Sie<br />

wer<strong>de</strong>n mit Hilfe von Namen-Servern <strong>in</strong> die zugehörige IP-Adresse übersetzt , bevor die Verb<strong>in</strong>dung aufgebaut<br />

wird. Das Doma<strong>in</strong> Name System (DNS) ordnet numerischen IP-Adressen sprechen<strong>de</strong> Namen zu.<br />

348 vgl. pr91100<br />

497


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

// Hole die angefor<strong>de</strong>rte Adresse<br />

InetAddress addr = InetAddress.getByName(args[0]);<br />

System.out.pr<strong>in</strong>tln(addr.getHostName());<br />

System.out.pr<strong>in</strong>tln(addr.getHostAddress());<br />

}<br />

catch(Exception e)<br />

{<br />

System.err.pr<strong>in</strong>tln(e.toStr<strong>in</strong>g());<br />

System.exit(1);<br />

}<br />

}<br />

}<br />

9.1.2 Die Klasse URL<br />

Im WWW wer<strong>de</strong>n Ressourcen über URLs (Universal Ressource Locator) i<strong>de</strong>ntifiziert.<br />

E<strong>in</strong>e URL besteht aus:<br />

e<strong>in</strong>em Protokollnamen, z.B. http 349 (HTML), file (lokale Dteien), ftp (Dateitransfer), rmi (Remote<br />

Method Invocation), <strong>in</strong>op(Inter ORB Protocol) o<strong>de</strong>r jdbc (<strong>Java</strong> Database Connectivity), <strong>de</strong>m e<strong>in</strong><br />

Doppelpunkt und zwei Schrägstriche folgen. Nach <strong>de</strong>m Doppel-Slash kommt die Angabe:<br />

//user 350 :password 351 @host 352 :port 353 /url-path 354 . E<strong>in</strong>ige Teile können bei e<strong>in</strong>er URL<br />

ausgelassen wer<strong>de</strong>n. So s<strong>in</strong>d „user:password@“, „password“, „port“ und „/url-path“ optional.<br />

S<strong>in</strong>d Benutzername und Paßwort gegeben, so folgt e<strong>in</strong> „At“-Zeichen @. Paßwort und Benutzername<br />

durfen nicht Doppelpunkt, At-Zeichen o<strong>de</strong>r Slash enthalten. Fehlt die Angabe <strong>de</strong>s Rechners, wird <strong>de</strong>r<br />

aktuelle Rechner (localhost) benutzt, bei Ports bekannte Standardnummern. Schließlich folgt e<strong>in</strong>e<br />

Bezeichnung <strong>de</strong>r Ressource, typischerweise unter Angabe e<strong>in</strong>es Pfads.<br />

<strong>Java</strong> implementiert das Konzept e<strong>in</strong>es Uniform Resource Locator durch e<strong>in</strong>e eigene<br />

Klasse URL, die sich im Paket java.net bef<strong>in</strong><strong>de</strong>t. Aus e<strong>in</strong>er gegebenen<br />

Zeichenreihe erstellt <strong>Java</strong> e<strong>in</strong>en geeigneten URL, <strong>de</strong>n Man zum E<strong>in</strong>richten e<strong>in</strong>er<br />

URLConnection nutzen kann. Die Verb<strong>in</strong>dung ermöglicht e<strong>in</strong>e Interaktion, die vom<br />

Protokoll <strong>de</strong>r betreffen<strong>de</strong>n Ressource <strong>de</strong>f<strong>in</strong>iert wird.<br />

Erzeugen von URL-Objekten. Am e<strong>in</strong>fachsten ist es, über e<strong>in</strong>e Str<strong>in</strong>g-Repräsentation<br />

<strong>de</strong>r URL-Adresse zu gehen, z.B.: URL fhURL = new URL("http://www.fhregensburg.<strong>de</strong>/");<br />

Diese URL wur<strong>de</strong> mit <strong>de</strong>m Konstruktor<br />

public URL(Str<strong>in</strong>g urlAddr) throws MalformedURLException<br />

erzeugt. E<strong>in</strong> an<strong>de</strong>rer Konstruktor ist<br />

349 Mit <strong>de</strong>m Hypertext Tranfer Protocol wird auf Inhalte <strong>de</strong>s Web zugegriffen. Die URL für Dienste im Web<br />

beg<strong>in</strong>nt mit http.<br />

350 Optionaler Benutzername<br />

351 optionales Paßwort, e<strong>in</strong> Paßwort ohne Benutzername kann nicht angegeben wer<strong>de</strong>n<br />

352 Auf die Angabe <strong>de</strong>s Protokolls folgt <strong>in</strong> <strong>de</strong>r Regel <strong>de</strong>r Name <strong>de</strong>r Domäne o<strong>de</strong>r die IP-Adresse <strong>de</strong>s Servers.<br />

Name und IP-Adresse s<strong>in</strong>d <strong>in</strong> <strong>de</strong>r Regel gleichwertig, da von e<strong>in</strong>em beson<strong>de</strong>ren Dienst <strong>de</strong>r Name <strong>in</strong> e<strong>in</strong>e IP-<br />

Adresse umgesetzt wird.<br />

353 E<strong>in</strong>e Verb<strong>in</strong>dung zu e<strong>in</strong>em Rechner geschieht immer durch e<strong>in</strong>e Art Tür, die Port genannt wird. Die Port-<br />

Nummer läßt <strong>de</strong>n Server die Dienste kategorisieren. Je<strong>de</strong>r Dienst bekommt e<strong>in</strong>e an<strong>de</strong>re Portnummer, damit sie<br />

sich unterschei<strong>de</strong>n lassen. Normalerweise horcht <strong>de</strong>r HTTP-Server auf Port 80.<br />

354 Auf <strong>de</strong>n Servernamen folgt die Angabe <strong>de</strong>r Datei, auf die über HTTP o<strong>de</strong>r FTP zugegriffen wer<strong>de</strong>n soll. Da<br />

sie <strong>in</strong> e<strong>in</strong>em Verzeichnis liegt, beschreibt url-path <strong>de</strong>n Weg zur Datei. Ist ke<strong>in</strong>e Datei vorhan<strong>de</strong>n und en<strong>de</strong>t die<br />

Angabe <strong>de</strong>r URL mit e<strong>in</strong>em Slash „/“, dann versucht <strong>de</strong>r Web-Server auf e<strong>in</strong>e <strong>de</strong>r Dateien <strong>in</strong><strong>de</strong>x.html bzw.<br />

<strong>in</strong><strong>de</strong>x.htm zuzugreifen.<br />

498


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

public URL(URL urlObj, Str<strong>in</strong>g … ) throws MalformedURLException<br />

// erzeugt relativ zur URL e<strong>in</strong> neues URL-Objekt.<br />

Bsp.: Zugiff auf die Homepage <strong>de</strong>r FH Regensburg<br />

import java.net.*;<br />

import java.io.*;<br />

public class OeffneURLStrom<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[])<br />

{<br />

try {<br />

Str<strong>in</strong>g s;<br />

URL fhURL = new URL("http://www.fh-regensburg.<strong>de</strong>/");<br />

BufferedRea<strong>de</strong>r e<strong>in</strong> = new BufferedRea<strong>de</strong>r(<br />

new InputStreamRea<strong>de</strong>r(fhURL.openStream()));<br />

while ((s = e<strong>in</strong>.readL<strong>in</strong>e()) != null)<br />

System.out.pr<strong>in</strong>tln(s);<br />

e<strong>in</strong>.close();<br />

}<br />

catch(MalformedURLException e)<br />

{ System.out.pr<strong>in</strong>tln("MalformedURLException: " + e); }<br />

catch(IOException e)<br />

{ System.out.pr<strong>in</strong>tln("IOException: " + e); }<br />

}<br />

}<br />

Die Klasse URL besitzt auch Konstruktoren, die die Angabe <strong>de</strong>r Komponenten von<br />

<strong>de</strong>r Adresse (also Zugriffsart, Hostname und Dateiadresse getrennt) akzeptieren:<br />

public URL(Str<strong>in</strong>g protocol, Str<strong>in</strong>g host, <strong>in</strong>t port, Str<strong>in</strong>g file) throws<br />

MalformedURLException<br />

public URL(Str<strong>in</strong>g protocol, Str<strong>in</strong>g host, Str<strong>in</strong>g file) throws<br />

MalformedURLException<br />

Je<strong>de</strong>r <strong>de</strong>r Konstruktoren wirft e<strong>in</strong>e MalformedURLException, wenn <strong>de</strong>r Parameter<br />

im Konstruktor entwe<strong>de</strong>r null ist o<strong>de</strong>r er e<strong>in</strong> unbekanntes Protokoll beschreibt.<br />

Zugriff auf Daten über e<strong>in</strong>e URL. Es gibt zwei Möglichkeiten über e<strong>in</strong>e URL bzw. über<br />

e<strong>in</strong>e URLConnection. Bei<strong>de</strong> Wege benutzen Streams. Je<strong>de</strong>s URL-Objekt besitzt die<br />

Metho<strong>de</strong> openStream(), die e<strong>in</strong>en InputStream zum Weiterverarbeiten liefert.<br />

f<strong>in</strong>al InputStream openStream() throws IOException<br />

// öffnet e<strong>in</strong>e Verb<strong>in</strong>dung zum Server und liefert e<strong>in</strong>en InputStream zurück<br />

URLConnection openConnection() throws IOException<br />

// liefert e<strong>in</strong> URLConnection-Objekt, das die Verb<strong>in</strong>dung zum entfernten<br />

// Server vertritt. openConnection() wird vom Protokoll-Handler immer<br />

// dann aufgerufen, wenn e<strong>in</strong>e neue Verb<strong>in</strong>dung geöffnet wird.<br />

Verweist die URL auf e<strong>in</strong>e Textdatei, dann erweitert man <strong>de</strong>n InputStream zu<br />

e<strong>in</strong>em BufferedRea<strong>de</strong>r, da dieser die readL<strong>in</strong>e()-Metho<strong>de</strong> besitzt.<br />

Bsp.: E<strong>in</strong>e Antwort (HTML-Seite) auf e<strong>in</strong>e Suchfrage<br />

import java.io.*;<br />

import java.net.*;<br />

public class GoogleSucher<br />

{<br />

public static void ma<strong>in</strong>(Str<strong>in</strong>g args[]) throws IOException,<br />

MalformedURLException<br />

{<br />

499


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

Str<strong>in</strong>g s = "";<br />

if (args.length == 0) s = "Stephan Meier <strong>in</strong> Regensburg";<br />

else<br />

for (<strong>in</strong>t i = 0; i < args.length; i++) s += args[i] + " ";<br />

s.trim();<br />

s = "p=" + URLEnco<strong>de</strong>r.enco<strong>de</strong>(s);<br />

// Metho<strong>de</strong>n <strong>de</strong>r Klasse URLEnco<strong>de</strong>r machen aus e<strong>in</strong>em Str<strong>in</strong>g e<strong>in</strong>e URL<br />

System.out.pr<strong>in</strong>tln(s);<br />

URL u = new URL("http://<strong>de</strong>.google.yahoo.com/b<strong>in</strong>/query_<strong>de</strong>?" + s);<br />

BufferedRea<strong>de</strong>r e<strong>in</strong> = new BufferedRea<strong>de</strong>r(<br />

new InputStreamRea<strong>de</strong>r(u.openStream()));<br />

Str<strong>in</strong>g zeile, antwort = null;<br />

while ((zeile = e<strong>in</strong>.readL<strong>in</strong>e()) != null) antwort += zeile + "\n";<br />

System.out.pr<strong>in</strong>t(antwort);<br />

}<br />

}<br />

URLs <strong>in</strong> Applets. Die Klasse Applet hat zwei Metho<strong>de</strong>n mit <strong>de</strong>nen man e<strong>in</strong>e Basis-<br />

URL erzeugen kann, ohne e<strong>in</strong>e feste Adresse im Programm anzugeben:<br />

- Die Metho<strong>de</strong> getDocumentBase() gibt e<strong>in</strong> URL-Objekt zurück, welches das Verzeichnis<br />

repräsentiert, das die Webseite mit <strong>de</strong>m Applet enthält.<br />

- Die Metho<strong>de</strong> getCo<strong>de</strong>Base() gibt e<strong>in</strong> URL-Objekt zurück, das <strong>de</strong>n Ordner repräsentiert, <strong>in</strong> <strong>de</strong>m<br />

sich die .class-Datei <strong>de</strong>r Hauptklasse <strong>de</strong>s Applet bef<strong>in</strong><strong>de</strong>t.<br />

Die Applet-Klasse bietet e<strong>in</strong>e Metho<strong>de</strong> mit <strong>de</strong>m Namen getImage() an, mit <strong>de</strong>r e<strong>in</strong><br />

Bild <strong>in</strong> e<strong>in</strong> Image-Objekt gela<strong>de</strong>n wer<strong>de</strong>n kann. Es gibt zwei Möglichkeiten, diese<br />

Metho<strong>de</strong> zu verwen<strong>de</strong>n:<br />

- Die Metho<strong>de</strong> getImage(), aufgerufen mit e<strong>in</strong>em Argument (e<strong>in</strong> Objekt vom Typ URL), lädt das Bild<br />

mit dieser URL<br />

- Die Metho<strong>de</strong> getImage(), aufgerufen mit zwei Argumenten (<strong>de</strong>r Basis-URL und e<strong>in</strong>em Str<strong>in</strong>g, <strong>de</strong>r<br />

<strong>de</strong>n relativen Pfad o<strong>de</strong>r Date<strong>in</strong>amen <strong>de</strong>s aktuellen Bilds ausgibt).<br />

Bsp.:<br />

import java.applet.Applet;<br />

import java.awt.*;<br />

import java.net.*;<br />

public class AppletURL extends Applet<br />

{<br />

Image bild;<br />

public void <strong>in</strong>it()<br />

{<br />

URL u1 = getDocumentBase(); System.out.pr<strong>in</strong>tln(u1);<br />

try { URL u2 = new URL(u1,"images/scratch1.gif");<br />

System.out.pr<strong>in</strong>tln(u2);<br />

bild = getImage(getCo<strong>de</strong>Base(),"images/B04240900.jpg");<br />

}<br />

catch (MalformedURLException e) { System.out.pr<strong>in</strong>tln(e); }<br />

}<br />

public void pa<strong>in</strong>t(Graphics schirm)<br />

{<br />

<strong>in</strong>t iBreite = bild.getWidth(this); <strong>in</strong>t iHoehe = bild.getHeight(this);<br />

schirm.drawImage(bild,10,10,iBreite/4,iHoehe/4,this);<br />

}<br />

}<br />

500


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

URL<br />

><br />

public URL(Str<strong>in</strong>g url) throws MalformedURLException<br />

public URL(Str<strong>in</strong>g protokoll, Str<strong>in</strong>g host, Str<strong>in</strong>g pfad) throws MalformedURLException<br />

public URL(Str<strong>in</strong>g protololl, Str<strong>in</strong>g host, <strong>in</strong>t portnummer, Str<strong>in</strong>g pfad)<br />

throws MalformedURLException<br />

// erzeugt e<strong>in</strong> URL-Objekt mit gegebenen Protokoll, Hostnamen, Portnummer und Datei<br />

// Ist die Portnummer –1, so wird <strong>de</strong>r Standard-Port verwen<strong>de</strong>t, z:B für WWW Port 80<br />

><br />

public f<strong>in</strong>al Object getContent() throws IOException<br />

//<br />

public f<strong>in</strong>al InputStream openStream() throws IOException<br />

// öffnet e<strong>in</strong>e Verb<strong>in</strong>dung zum Server und liefert e<strong>in</strong>en Inputstream zurück. Die Me-<br />

// tho<strong>de</strong> ist e<strong>in</strong>e Ankürzung für openConnection.getInputStream()<br />

public URLConnection openConnection() throws IOException<br />

// liefert e<strong>in</strong> URLConnection-Objekt, das die Verb<strong>in</strong>dung zum entfernten Objekt<br />

// vertritt.. openConnection() wird vom Protokoll-Handler immer dann aufgerufen,<br />

// wenn e<strong>in</strong>e neue Verb<strong>in</strong>dung geöffnet wird.<br />

public Str<strong>in</strong>g getProtocol()<br />

// liefert das Protokoll <strong>de</strong>r URL<br />

public Str<strong>in</strong>g getHost()<br />

// liefert <strong>de</strong>n Hostnamen <strong>de</strong>r URL, falls das möglich ist.<br />

// Für das Protokoll „file“ ist das e<strong>in</strong> leerer Str<strong>in</strong>g<br />

public <strong>in</strong>t getPort()<br />

// liefert die Portnummer. Ist sie nicht gesetzt, liefert die Metho<strong>de</strong> e<strong>in</strong>e –1.<br />

public Str<strong>in</strong>g getFile()<br />

// gibt <strong>de</strong>n Date<strong>in</strong>amen <strong>de</strong>r URL zurück<br />

Abb.: Die Klasse java.net.URL<br />

URL toURL() throws MalformedURLException<br />

liefert e<strong>in</strong> URL-Objekt zu e<strong>in</strong>em File-Objekt. Es muß e<strong>in</strong> File-Objekt erzeugt se<strong>in</strong>,<br />

anschließend erzeugt toURL() e<strong>in</strong> URL-Objekt (mit <strong>de</strong>m Protokoll "file" und <strong>de</strong>n<br />

absoluten Pfadangaben zur Datei bzw. zum Verzeichnis).<br />

501


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

9.1.3 Die Klasse URLConnection und abgeleitete Klassen<br />

E<strong>in</strong>e URL-Connection erhält man durch Aufruf <strong>de</strong>r Metho<strong>de</strong> openConnection()<br />

e<strong>in</strong>es URL-Objekts. Mit diesem Objekt lassen sich Inhalte <strong>de</strong>r Ressource lesen sowie<br />

Informationen über die Art <strong>de</strong>s Objekts ermitteln. Je nach Art <strong>de</strong>s Protokolls wird die<br />

abstrakte Klasse URLConnection durch verschie<strong>de</strong>n konkrete Klassen<br />

implementiert, z.B. HttpURLConnection, AppletResourceConnection,<br />

FileURLConnection o<strong>de</strong>r FtpURLConnection. Die Subklassen implementieren<br />

die Protokolle, mit <strong>de</strong>nen die Verb<strong>in</strong>dung zum Inhalt aufgebaut wird. Subklassen<br />

bedienen sich dabei <strong>de</strong>r Klasse URLStreamHandler mit <strong>de</strong>n <strong>de</strong>r eigentliche Inhalt<br />

ausgelesen wird.<br />

URLConnection<br />

{ abstract }<br />

><br />

protected URLConnection(URL url)<br />

><br />

public Object getContent() throws IOException, UnkownServiceException<br />

// liefert <strong>de</strong>n Inhalt, auf <strong>de</strong>n die URL verweist.<br />

Der Inhalt e<strong>in</strong>es URL-Objekts lässt sich mit getContent() vom Server beschaffen.<br />

Für HTML-Dateien liefert getContent() e<strong>in</strong> Objekt vom Typ<br />

sun.net.www.MeteredStream, für normale Textdateien e<strong>in</strong><br />

sun.www.content.txt.Pla<strong>in</strong>TextInputStream-Objekt. Für Texte und HTML-<br />

Seiten kann man mit Hilfe e<strong>in</strong>es InputStream-Objekts die Datei (zeilenweise) lesen.<br />

Mit<br />

Object o = u.getContent();<br />

System.out.pr<strong>in</strong>tln("Ich erhielt " + o.getClass().getName());<br />

kann festgestellt wer<strong>de</strong>n, was für e<strong>in</strong> Handler-Objekt e<strong>in</strong>e URL-Klasse für <strong>de</strong>n<br />

Datenstrom e<strong>in</strong>setzt. getContent() erkennt anhand <strong>de</strong>r Endung bzw. <strong>de</strong>n ersten<br />

Bytes <strong>de</strong>n Typ <strong>de</strong>r Datei. Dann konvertiert <strong>de</strong>r Content Handler die Bytes se<strong>in</strong>es<br />

Datenstroms <strong>in</strong> e<strong>in</strong> <strong>Java</strong> Objekt.<br />

502


<strong>Programmieren</strong> <strong>in</strong> <strong>Java</strong><br />

9.2 Kommunikation <strong>in</strong> Netzwerken<br />

Das Common GatewayInterface (CGI)<br />

CGI ist die Beschreibung e<strong>in</strong>er Schnittstelle über die externe Programme mit<br />

Informations-Servern, meistens Web-Servern, Daten austauschen. CGI-Programme<br />

können <strong>in</strong> allen möglichen Programmiersprachen verfaßt se<strong>in</strong>. Häufig s<strong>in</strong>d es Shello<strong>de</strong>r<br />

Perl-Skripte.<br />

Die CGI-Programme wer<strong>de</strong>n von e<strong>in</strong>em Browser durch e<strong>in</strong>e URL angesprochen. Der<br />

Browser baut e<strong>in</strong>e Verb<strong>in</strong>dung zum Server auf. Dieser erkennt an Hand <strong>de</strong>s Pfads<br />

<strong>de</strong>r URL, ob es sich um e<strong>in</strong>e normale Web-Seite o<strong>de</strong>r um e<strong>in</strong> Skript han<strong>de</strong>lt. Falls es<br />

e<strong>in</strong> Skript ist, dann führt <strong>de</strong>r Server das Skript aus, das e<strong>in</strong>e HTML-Datei erzeugt.<br />

Diese wird übertragen und im Browser dargestellt.<br />

Sockets 357<br />

Als Socket bezeichnet man e<strong>in</strong> streambasierte Programmierschnittstelle zur<br />

Kommunikation zweier Rechner <strong>in</strong> e<strong>in</strong>em TCP/IP-Netz. Das Übertragen von Daten<br />

über e<strong>in</strong>e Socket-Verb<strong>in</strong>dung besteht aus:<br />

1. Verb<strong>in</strong>dungsaufbau<br />

2. Lesen bzw. Schreiben <strong>de</strong>r Daten<br />

3. Verb<strong>in</strong>dungsabbau<br />

Wer<strong>de</strong>n Rechner verbun<strong>de</strong>n, so implementiert je<strong>de</strong>r Rechner e<strong>in</strong>en Socket.<br />

Derjenige, <strong>de</strong>r Daten empfängt (Client), öffnet e<strong>in</strong>e Socket-Verb<strong>in</strong>dung zum Horchen<br />

und <strong>de</strong>rjenige, <strong>de</strong>r sen<strong>de</strong>t, öffnet e<strong>in</strong>e Verb<strong>in</strong>dung zum Sen<strong>de</strong>n (Server). Damit <strong>de</strong>r<br />

Empfänger <strong>de</strong>n Sen<strong>de</strong>r auch hören kann, muß dieser durch e<strong>in</strong>e e<strong>in</strong><strong>de</strong>utige Adresse<br />

als Server ausgemacht wer<strong>de</strong>n. Er bekommt also e<strong>in</strong>e IP-Adresse im Netz und e<strong>in</strong>e<br />

ebenso e<strong>in</strong><strong>de</strong>utige Port-Adresse. Der Port ist so etwas wie e<strong>in</strong>e Zimmernummer: Die<br />

Adresse bleibt dieselbe, aber <strong>in</strong> je<strong>de</strong>m Zimmer sitzt e<strong>in</strong>er und macht se<strong>in</strong>e Aufgaben.<br />

Für je<strong>de</strong>n Dienst (Service), <strong>de</strong>n e<strong>in</strong> Server bereitstellt, gibt es e<strong>in</strong>en Port 358 . E<strong>in</strong>e<br />

Port-Nummer ist e<strong>in</strong>e 16-Bit-Zahl und <strong>in</strong> die Gruppen System und Benutzer e<strong>in</strong>geteilt.<br />

Sockets aus <strong>de</strong>r Sicht e<strong>in</strong>er Client- bzw. Server-Anwendung wer<strong>de</strong>n durch die bei<strong>de</strong>n<br />

Klassen Socket und ServerSocket repräsentiert.<br />

Bsp 359 .:<br />

1. E<strong>in</strong> e<strong>in</strong>facher Server, <strong>de</strong>r alles „nachplappert“, was <strong>de</strong>r Client sen<strong>de</strong>t.<br />

2. E<strong>in</strong> e<strong>in</strong>facher Client, <strong>de</strong>r Zeilen zum Server sen<strong>de</strong>t und e<strong>in</strong>liest<br />

357 Sockets wur<strong>de</strong>n zu Beg<strong>in</strong>n <strong>de</strong>r achtziger Jahre für die Programmiersprache C entwickelt.<br />

358 Diese Adressen wer<strong>de</strong>n von <strong>de</strong>r IANA (Internet Assigned Number Authority) vergeben. Die IANA ist <strong>de</strong>r<br />

zentrale Koord<strong>in</strong>ator für die IP-Adressen, Doma<strong>in</strong> Namen, MIME Typen und viele an<strong>de</strong>re Parameter, u.a.a.<br />

Portnummern.<br />

359 pr92100<br />

503

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!