Programmieren in Java - oth

Werbung
Programmieren in Java
Prof. Jürgen Sauer
Programmieren in Java
Skriptum zur Vorlesung im WS 2002/2003
1
Programmieren in Java
Inhaltsverzeichnis
0. ÜBERSICHT ..........................................................................................................................................................................7
0.1 ZIEL DER VORLESUNG „PROGRAMMIEREN“ ............................................................................................................... 7
0.2 JAVA -M ASCHINE UND PROGRAMMIERSYSTEM .......................................................................................................... 7
0.3 DER ERSTE VERSUCH ...................................................................................................................................................... 9
0.3.1 Das erste Programm...............................................................................................................................................9
0.3.2 Bestandteile eines Programms...........................................................................................................................15
1. EINFÜHRUNG IN DIE JAVA-PROGRAMMIERUNG..................................................................................................16
1.1 ÜBERSICHT ZUR ENTWICKLUNG DER PROGRAMMIERSPRACHE JAVA ................................................................. 16
1.2 W AS IST JAVA ? ............................................................................................................................................................. 17
1.3 EINSTIEG IN DIE JAVA -PROGRAMMIERUNG .............................................................................................................. 20
1.3.1 Die Software für die Java-Programmierung ...................................................................................................20
1.3.2 Applets und Anwendungen.................................................................................................................................22
1.3.2.1 Entwicklung von Java-Anwendungen................................................................................................................ 22
1.3.2.2 Entwicklung von Java-Applets ......................................................................................................................... 29
1.4 DIE OBJEKTORIENTIERUNG VON JAVA ..................................................................................................................... 39
1.4.1 Grundlegende Konzepte .....................................................................................................................................39
1.4.1.1 Zustand und Verhalten von Objekten, Klassen, Instanz- und Klassen-Variable bzw. -Methoden................... 39
1.4.1.2 Superklassen und Subklassen, Vererbung und Klassenhierarchie...................................................................... 50
1.4.1.3 Referenzen und Referenztypen......................................................................................................................... 55
1.4.1.4 Konvertieren von Objekten und Primitivtypen................................................................................................. 56
1.4.1.5 Manipulieren von Objekten............................................................................................................................... 57
1.4.1.6 Innere (lokale) und anonyme Klassen................................................................................................................ 61
1.4.1.7 Schnittstellen und Pakete................................................................................................................................... 62
1.4.1.8 Polymorphismus und Binden............................................................................................................................ 68
1.4.2 Klassen des Pakets java.lang.............................................................................................................................70
1.4.2.1 Die Klasse Object .............................................................................................................................................. 70
1.4.2.2 Die Class-Klasse................................................................................................................................................ 71
1.4.2.3 Die Klasse System............................................................................................................................................. 73
1.4.2.4 Die Klasse Thread ............................................................................................................................................. 75
1.4.2.5 Die Klassen String und StringBuffer.................................................................................................................. 80
1.4.2.6 Die Math-Klasse............................................................................................................................................... 84
1.4.2.7 Object-Wrapper-Klassen................................................................................................................................... 85
1.4.3 Ausnahmen und Ausnahmenbehandlung ........................................................................................................89
1.4.4 Ein-, Ausgaben......................................................................................................................................................96
1.4.4.1 Ein- und Ausgabeströme der Klasse System..................................................................................................... 96
1.4.4.2 Datenströme ...................................................................................................................................................... 97
1.4.4.3 Eingabe und Ausgabe mit Dateien ..................................................................................................................... 98
1.4.4.4 Verwalten von Dateien und Verzeichnissen durch die Klasse File.................................................................. 101
1.4.4.5 Der Dateiauswahl-Dialog................................................................................................................................. 101
2. HAUPTBESTANDTEILE DER SPRACHE.................................................................................................................. 102
2.1 TOKEN ........................................................................................................................................................................... 102
2.1.1 Schlüsselworte................................................................................................................................................... 103
2.1.2 Bezeichner und Namenskonventionen .......................................................................................................... 103
2.1.3 Literale................................................................................................................................................................ 104
2.1.4 Trennzeichen...................................................................................................................................................... 106
2.1.5 Operatoren ......................................................................................................................................................... 107
2.1.6 Kommentare, eingebettete Dokumentation.................................................................................................. 113
2.2 TYPEN ........................................................................................................................................................................... 115
2.2.1 Primitive Datentypen........................................................................................................................................ 115
2.2.2 Operationen mit primitiven Datentypen....................................................................................................... 116
2.2.3 Datenfelder (Arrays)......................................................................................................................................... 119
2.2.3.1 Deklarieren, Erstellen von Array-Objekten..................................................................................................... 119
2
Programmieren in Java
2.2.3.2 Zugriff auf Datenfeld-Elemente, Ändern von Datenfeld-Elementen ............................................................... 121
2.2.3.3 Anwendungen mit eindimensionaler Datenfeldern .......................................................................................... 122
2.2.3.4 Mehrdimensionale Datenfelder........................................................................................................................ 124
2.2.3.5 Die Klasse Arrays........................................................................................................................................... 131
2.3 A USDRÜCKE ................................................................................................................................................................. 132
2.3.1 Arithmetische Ausdrücke................................................................................................................................. 132
2.3.2 Bewertung von Ausdrücken ............................................................................................................................ 132
2.3.3 Typkonvertierungen ......................................................................................................................................... 133
2.3.4 Vergleichsoperatoren....................................................................................................................................... 136
2.3.5 Logische Ausdrücke.......................................................................................................................................... 136
2.4 A NWEISUNGEN ............................................................................................................................................................ 137
2.4.1 Blöcke und Anweisungen ................................................................................................................................ 137
2.4.2 Leere Anweisungen........................................................................................................................................... 137
2.4.3 Benannte Anweisungen.................................................................................................................................... 137
2.4.4 Deklarationen.................................................................................................................................................... 137
2.4.5 Ausdrucksanweisungen ................................................................................................................................... 138
2.4.6 Auswahlanweisungen....................................................................................................................................... 139
2.4.7 Iterationsanweisungen..................................................................................................................................... 142
2.4.8 Sprung-Anweisungen ....................................................................................................................................... 145
2.4.9 Synchronisationsanweisungen....................................................................................................................... 149
2.4.10 Schutzanweisungen ........................................................................................................................................ 149
2.4.11 Unerreichbare Anweisungen........................................................................................................................ 149
2.5 KLASSEN........................................................................................................................................................................ 150
2.6 M ETHODEN .................................................................................................................................................................. 151
2.6.1 Die Deklaration................................................................................................................................................. 151
2.6.2 Die Zugriffsspezifizierung ................................................................................................................................ 151
2.6.3 Die Methodenmodifizierer............................................................................................................................... 151
2.6.4 Rückgabewerte von Methoden ....................................................................................................................... 152
2.6.5 Methodenname und Parameterliste............................................................................................................... 152
2.6.6 Rekursion............................................................................................................................................................ 152
2.6.6.1 Rekursive Funktionen...................................................................................................................................... 152
2.6.6.2 Rekursion und Iteration................................................................................................................................... 153
3. GRAFISCHE BENUTZEROBERFLÄCHEN UND APPLETS .................................................................................. 154
3.1 EREIGNISSE IN GRAFISCHEN BENUTZEROBERFLÄCHEN........................................................................................ 154
3.1.1 Gestaltung von GUI mit Hilfe der AWT-Klassen.......................................................................................... 154
3.1.2 Ereignisbehandlung unter grafischen Benutzeroberflächen.................................................................... 158
3.1.3 Anwendung lokaler Klassen für die Ereignisbehandlung ........................................................................ 160
3.1.4 Externe und interne Darstellung von numerischen Werten ...................................................................... 163
3.1.5 Low-Level-Events .............................................................................................................................................. 164
3.1.6 Der Weg eines Ereignisses............................................................................................................................... 171
3.2 KOMMUNIKATION A NWENDER – PROGRAMM ÜBER DIALOGE BZW. M ENÜS................................................. 173
3.2.1 Dialoge................................................................................................................................................................ 173
3.2.2 Menüs .................................................................................................................................................................. 181
3.2.3 Popup-Menüs..................................................................................................................................................... 183
3.3 GRUNDLAGEN DER A PPLET -ERSTELLUNG............................................................................................................. 185
3.3.1 HTML-Grundlagen............................................................................................................................................ 185
3.3.2 Die interne Arbeitsweise eines Applets......................................................................................................... 187
3.3.3 „Multithreading“-fähige Applets .................................................................................................................. 193
3.3.4 Animation in Applets........................................................................................................................................ 196
3.3.5 Das Laden und Anzeigen von Bildern ........................................................................................................... 201
3.3.6 Die Ausgabe von Sound ................................................................................................................................... 202
3.3.7 Die Klasse JApplet............................................................................................................................................ 204
3.3.8 Das Interface AppletContext........................................................................................................................... 204
3.4 GRAFISCHE BENUTZEROBERFLÄCHE MIT OBSERVABLE-OBSERVER.................................................................. 205
3.5 SWING............................................................................................................................................................................ 208
3.6 JAVA BEANS.................................................................................................................................................................. 209
3.6.1 Wiederverwendbare Softwarekomponenten ................................................................................................ 209
3.6.2 Komponenten für Java: Beans........................................................................................................................ 209
3
Programmieren in Java
3.6.3 Enterprise Java Beans...................................................................................................................................... 210
4. GRAFIK UND BILDVERARBEITUNG........................................................................................................................ 211
4.1 A LLGEMEINE ZEICHENVORGÄNGE .......................................................................................................................... 212
4.1.1 Punkte, Linien, Kreise, Bögen, Polygone..................................................................................................... 212
4.1.2 Farbangaben ..................................................................................................................................................... 217
4.1.3 Textausgabe über den Zeichen-Modus ......................................................................................................... 219
4.1.4 Grössenangaben................................................................................................................................................ 222
4.1.5 Clipping-Operationen...................................................................................................................................... 223
4.2 BILDVERARBEITUNG................................................................................................................................................... 224
4.2.1 Klassen zur Bildverarbeitung......................................................................................................................... 224
4.2.2 Bildproduzenten und Bildkonsumenten ....................................................................................................... 227
4.2.3 Bildfilter.............................................................................................................................................................. 228
4.2.4 Kopieren von Speicher in ein Bild bzw. von Bildern in einen Speicher................................................. 229
4.2.4.1 Kopieren von Speicher in ein Bild................................................................................................................... 229
4.2.4.2 Kopieren von Bildern in einen Speicher .......................................................................................................... 230
4.3 2D JAVA ......................................................................................................................................................................... 232
4.3.1 Das Zeichnen unter Java 2D-API................................................................................................................... 233
4.3.2 Eigenschaften geometrischer Objekte........................................................................................................... 235
4.3.3 Affine Transformation....................................................................................................................................... 238
4.3.4 Farbmanagement unter 2D-Java ................................................................................................................... 238
4.3.5 2D-Bildverarbeitung ........................................................................................................................................ 238
5. AWT................................................................................................................................................................................... 239
5.1 BESTANDTEILE DES AWT......................................................................................................................................... 239
5.2 DIE AWT-KOMPONENTEN ....................................................................................................................................... 239
5.2.1 Schaltflächen (Buttons) ................................................................................................................................... 240
5.2.2 Labels .................................................................................................................................................................. 243
5.2.3 Kontrollkästchen und Optionsfelder............................................................................................................. 243
5.2.4 Auswahlmenüs................................................................................................................................................... 247
5.2.5 Listenfelder......................................................................................................................................................... 248
5.2.6 Textbereiche und Textfelder............................................................................................................................ 250
5.2.7 Schieber und Bildlaufleisten........................................................................................................................... 253
5.2.8 ScrollPane.......................................................................................................................................................... 256
5.2.9 Zeichenbereiche ................................................................................................................................................ 257
5.3 CONTAINER.................................................................................................................................................................. 260
5.3.1 Panels.................................................................................................................................................................. 260
5.3.2 Frames................................................................................................................................................................. 260
5.3.3 Menüs .................................................................................................................................................................. 261
5.3.4 Dialoge................................................................................................................................................................ 266
5.4 DIE LAYOUT -M ANAGER ............................................................................................................................................ 267
5.4.1 Layout-Regeln.................................................................................................................................................... 267
5.4.2 Die einzelnen Layout-Manager...................................................................................................................... 268
5.5 DIE EVENT -M ODELLE 1.0 UND 1.1........................................................................................................................... 276
5.5.1 Der AWT-Handler 1.0 ....................................................................................................................................... 276
5.5.2 Das Event-Handling in Java 1.1 bzw. Java 1.2 ........................................................................................... 278
5.6 BENUTZERSCHNITTSTELLEN MIT SWING.............................................................................................................. 285
6. UTILITIES ......................................................................................................................................................................... 289
6.1 KOLLEKTIONEN (COLLECTIONS)............................................................................................................................. 289
6.1.1 Durchwandern von Daten mit Iteratoren ..................................................................................................... 289
6.1.2 Die Klasse Vector.............................................................................................................................................. 290
6.1.3 Die Klasse Stack................................................................................................................................................ 292
6.1.4 Die Klasse Bitset für Bitmengen ..................................................................................................................... 295
6.1.5 Die Klasse Hashtable und assoziative Speicher ......................................................................................... 297
6.1.6 Die abstrakte Klasse Dictionary.................................................................................................................... 301
2.2.7 Die Klasse Properties....................................................................................................................................... 302
6.2 COLLECTION API........................................................................................................................................................ 304
6.2.1 Die Schnittstellen Collection, Iterator, Comparator................................................................................. 304
4
Programmieren in Java
6.2.2 Die Behälterklassen und Schnittstellen des Typs List................................................................................ 306
6.2.3 Behälterklassen des Typs Set.......................................................................................................................... 310
6.2.4 Behälterklassen des Typs Map ....................................................................................................................... 313
6.2.5 Algorithmen........................................................................................................................................................ 315
6.2.5.1 Datenmanipulation .......................................................................................................................................... 315
6.2.5.2 Größter und kleinster Wert einer Collection.................................................................................................... 316
6.2.5.3 Sortieren........................................................................................................................................................... 316
6.2.5.4 Suchen von Elementen..................................................................................................................................... 318
6.3 DIE KLASSE STRINGTOKENIZER............................................................................................................................... 319
6.4 DIE KLASSE RANDOM ................................................................................................................................................. 320
6.5 DATUMSBERECHNUNGEN .......................................................................................................................................... 321
6.5.1 Die Klassen Date, Calendar, GregorianCalendar..................................................................................... 321
6.5.2 Formatieren der Datumsangaben.................................................................................................................. 325
6.6 FORMATIEREN MIT FORMAT -OBJEKTEN ............................................................................................................... 327
7. EIN- UND AUSGABE....................................................................................................................................................... 328
7.1 DIE ABSTRAKTEN KLASSEN INPUT STREAM UND OUTPUT STREAM ................................................................ 329
7.1.1 InputStream........................................................................................................................................................ 329
7.1.2 OutputStream..................................................................................................................................................... 332
7.2 GEFILTERTE STRÖME ................................................................................................................................................ 333
7.3 DIE KLASSE FILE ......................................................................................................................................................... 337
7.4 DIE RANDOMA CCESSFILE-KLASSE ........................................................................................................................... 339
7.5 DIE KLASSE STREAMTOKENIZER............................................................................................................................. 341
7.6 KLASSEN FÜR SPEZIELLE NÜTZLICHE STRÖME .................................................................................................... 343
7.7 JAVA 1.1 IO-STRÖME .................................................................................................................................................. 344
7.7.1 Grundlagen ........................................................................................................................................................ 344
7.7.2 Die abstrakte Klassen Reader und ihre Ableitungen................................................................................. 345
7.7.3 Die abstrakte Klasse Writer und ihre Ableitungen..................................................................................... 347
7.7.4 Demonstrationsprogramm zur Ein-/ Ausgabe ab Java Version 1.1......................................................... 350
8. SERIALISIERUNG........................................................................................................................................................... 352
8.1 GRUNDLAGEN............................................................................................................................................................... 352
8.1.1 Das Interface Serializable ............................................................................................................................... 352
8.1.2 Die Klasse ObjectOutputStream..................................................................................................................... 353
8.1.3 Die Klasse ObjectInputStream........................................................................................................................ 355
8.2 TIEFE OBJEKTKOPIEN ................................................................................................................................................ 357
9. NETZWERKPROGRAMMIERUNG............................................................................................................................. 358
9.1 A DRESSEN, RESSOURCEN UND URLS........................................................................................................................ 358
9.1.1 Die Klasse InetAddress..................................................................................................................................... 358
9.1.2 Die Klasse URL.................................................................................................................................................. 359
9.1.3 Die Klasse URLConnection und abgeleitete Klassen................................................................................ 360
9.2 KOMMUNIKATION IN NETZWERKEN ...................................................................................................................... 361
5
Programmieren in Java
Literaturverzeichnis
Alexander Newman u.a.: JAVA, Referenz und Anwendungen, 1996, Haar bei München : Que
Ralph Steyer: JAVA 1.2, Kompendium, 1998, Markt & Technik, Haar bei München
Laura Lemay u. Roger Cadenhead; JAVA 2 in 21 Tagen, 1999, Markt & Technik, Haar bei
München
Guide Krüger: Go To Java 2, 1999, Addison-Wesley, München ...
David Flanagan: Java in a Nutshell, 1996, O’Reilly&Associates Inc, Bonn ...
Florian Hawlitzek: Java 2, 2000, Addison-Wesley, München ...
6
Programmieren in Java
0. Übersicht
0.1 Ziel der Vorlesung „Programmieren“
„Vermittlung grundlegender Kenntnisse, die zum Lösen von Problemen mit einer
Rechenanlage (Computer, Rechner) nötig sind.“
Die Rechenanlage ist „irgendeine“ (vom „Jumbo“ bis zum Personal Computer) und muß
- eine Eingabemöglichkeit für Ziffern, Buchstaben und Sonderzeichen besitzen (Tastatur)
- programmierbar sein
- eine Ausgabemöglichkeit haben (Bildschirm)
Zum Erwerb grundlegender Kenntnisse für das Lösen von Problemen auf einem
Universalrechner ist
- zu zeigen, wie man von der Problemstellung zum Lösungsverfahren (Algorithmus) kommt. Alle
Probleme, für die ein Lösungweg genügend präzise formuliert werden kann, sind über den Rechner
lösbar.
- zu üben: Der Umgang mit den Algorithmen. Dazu wird die Programmiersprache Java benutzt, in der
die Algorithmen formuliert werden. Der in Java formulierte Lösungsweg (Algorithmus) ist das JavaProgramm (Anwendung, Applet), der Rechner auf dem das Programm ausführbar ist, ist die JavaMaschine. Java ist eine Sprache, die die Arbeitsanweisungen eines Programmierers einfach und
anschaulich darstellen kann.
Java wurde ab 1991 bei Sun Microsystems entwickelt. Verantwortlich für Java ist
JavaSoft, eine Tochterfirma von Sun Microsystems. Java und JavaSoft1 halten
permanent die aktuellsten Informationen im Internet bereit.
0.2 Java-Maschine und Programmiersystem
Die Java-Maschine 2 nutzt die Möglichkeiten des Universalrechners.
Die Eingabemöglichkeit dient zum Einschreiben von Daten und Programmen in den
Speicher der Zentraleinheit (central processing unit, CPU). Die Zentraleinheit
verarbeitet die eingegebenen Daten nach den Anweisungen des eingelesenen
Programms, das zur Ausführung in binäre Form gebracht wurde. Das Steuerwerk
überwacht die Ausführung des Programms (Entschlüsseluung der binär dargestellten
Anweisungen). Das eigentliche Abarbeiten der Programmanweisungen (arithmetische,
logische Operatoren) in der gegebenen Reihenfolge übernimmt das Rechenwerk.
Daten, Programme, die im Hauptspeicher keinen Platz finden, sind auf externen
Speichermedien ausgelagert (Magnetband, Magnetplatte, Floppy Disc).
1 http://java.sun.com
2 Die virtuelle Maschine von Java (Java VM) ist wirklich eine Maschine, d.h. Hardware, die bestimmte
Operationscodes (Maschinenanweisungen) interpretieren kann. Es wurden bzw. werdem Chips entwickelt, die
diese Maschine in Hardware implementieren. Im Regelfall geschieht aber eine Emulation durch das Laufzeitsystem
auf der jeweiligen Zielplattform.
7
Programmieren in Java
Eingabeeinheit
Hauptspeicher
Externer Speicher
Steuerwerk
Ausgabeeinheit
Rechenwerk
Zentraleinheit
Abb. 0.2-1: Prinzipieller Aufbau eines Universalrechners
Der Universalrechner ist vielfältig anwendbar. Welche Anwendungsmöglichkeit (z.B.
Java-Maschine) gewünscht wird, gibt der Benutzer durch Systemkommandos bekannt.
Diese Kommandos werden von Systemprogrammen entschlüsselt und anschließend
interpretiert. Eines dieser Kommandos verwandelt, falls die dazu nötigen Programme
vorhanden sind, den Universalrechner in eine Java-Maschine. Für die Java-Maschine
sind danach die Java-Programme des Benutzers die Quelle aller Erkenntnisse.
Quellprogramme werden über die Tastatur eingegeben und auf externen Speicher
abgelegt. Zur Eingabe, Korrektur und Änderung stellt das System ein Programm mit
dem Namen „Editor" zur Verfügung. Das Speichern der eingegebenen bzw. geänderten
Programme auf Externspeicher übernimmt das Systemprogramm „Dateiverwaltung“.
Java-Programm
Daten
Eingabe (Tastatur)
Externer Speicher
System,
Systemprogramme
Java-Maschine
Ausgabe (Bildschirm)
8
Externer Speicher
Java-Compiler
Java-Quellprogramme
Java-Laufzeitsystem
Java-BytecodeDateien
Programmieren in Java
0.3 Der erste Versuch
0.3.1 Das erste Programm
Quelltext:
import java.lang.*;
/* ErstesProgramm ist eine Applikation, die den einfachen
Gebrauch von Zeichenketten aufzeigt */
public class ErstesProgramm extends Object
{
// Beginn der Ausfuehrung vom Programm
public static void main(String args[])
{
// Ausgabe auf das Standard-Ausgabegeraet
System.out.println(
"Das erste Programm der Vorlesung Programmieren in Java.");
}
}
Das Programm umfaßt zwei Teile: eine Klassendefinition und einen
Programmabschnittt, der durch die Methode main() bestimmt ist.
Jede Java-Anwendung besteht aus mehreren Klassen. Die Klasse, die die
Ausgangsbasis für Java-Programme ist, muß die main()-Methode benutzen: public
static void main(String args[]) { ... }.
Es bedeuten:
public ...
static ...
main() ...
Die Methode ist für andere Klassen und Objekte verfügbar
Es handelt sich um eine Klassenmethode
Die Funktion main() hat einen Parameter vom Typ Zeichenkette
(String).
Dieser
Parameter
dient
zur
Aufnahme
von
Befehlszeilenargumente.
Argumente,
die
an
Java-Programme
übergeben werden, werden zu Zeichenketten konvertiert. In JavaProgrammen ist main(), wie in C/C++-Programmen, die erste
Routine des Programms, die ausgeführt wird.
Durch die „import“-Anweisung können Entwickler Klassen verwenden, die in anderen
Dateien definiert sind. Compiler bzw. Interpreter greifen auf die Dateien zu. Über
„import“ wird bestimmt, wo diese Dateien liegen. Java importiert immer das Paket
„java.lang“, denn hier ist die Klasse Object enthalten, von der alle Java-Klassen
abgeleitet sind. Das Paket „java.lang“, die unmittelbare Ableitung von der Klasse
Object werden in Java per default bereitgestellt. Die diesbezüglichen Angaben im
Programm können entfallen, der Quelltext kann deshalb auch folgende Gestalt
annehmen:
/* ErstesProgramm ist eine Applikation, die den einfachen
Gebrauch von Zeichenketten aufzeigt */
public class ErstesProgramm
{
// Beginn der Ausfuehrung vom Programm
public static void main(String args[])
{
// Ausgabe auf das Standard-Ausgabegeraet
9
Programmieren in Java
System.out.println(
"Das erste Programm der Vorlesung Programmieren in Java.");
}
}
Token
Die Ausdrucksformen der Sprache Java werden Token genannt. In derartige für die
Sprache Java sinnvolle Einheiten muß sich der Quelltext eines Java-Programms
zerlegen lassen. Es gibt in Java fünf Arten von Token: Bezeichner oder Identifizierer,
Schlüsselworte, Literale, Operatoren, Trennzeichen.
Bezeichner, Identifizierer: Darunter versteht man die Benennungen (Namen) für Klassen,
Objekte, Variable, Methoden. Namen sind zusammengesetzt aus Unicode-Zeichen.
Java benutzt den 16-Bit-Unicode-Zeichensatz, dessen erste 256 Zeichen dem normalen
ASCII-Zeichensatz entsprechen (Byte 1 ist immer auf 0 gesetzt). Im vorliegenden
Programm sind z.B. Bezeichner: ErstesProgramm, main, System. Java
unterscheidet Groß-/ Kleinschreibung. Namen bestehen in der Regel aus
alphabetischen Zeichen und Dezimalziffern. An der ersten Stelle darf keine Zahl stehen.
Schlüsselwörter: Das sind Wörter, die ein wesentlicher Teil der Java-Sprachdefinition
sind, z.B.: public, class, void, String.
Literale: mit einem Literal können Variablen und Konstanten bestimmte Werte
zugewiesen werden. Literale können annehmen: numerische Werte (z.B. 13), boolesche
Werte (true bzw. false), Zeichen (z.B. ‘A‘) und Zeichenketten (z.B. "Das erste
Programm der Vorlesung Programmieren in Java.").
Operatoren: Das sind Zeichen bzw. Zeichenkombinationen zur Angabe einer
auszuführenden Operation mit einer oder mehreren Variablen oder Konstanten
(Operanden), z.B. + - / <<< >>>.
Variable sind Arbeitsspeicherstellen, an denen Informationen gespeichert werden
können. Zur Deklaration erhält eine Variable Namen (Bezeichner) und Typ zugeordnet.
Der Typ kennzeichnet die Art der Information, die die Variable aufnehmen kann, z.B.:
int i;
// zur Aufnahme ganzer Zahlen
String s; // zur Aufnahme von Zeichenketten
In Java unterscheidet man: elementare (primitive Typen) und benutzerdefinierte Typen
(einschl. der vom System bereitgestellten Klassen: String und Array). Es gibt acht
primitive Typen zum Speichern von ganzen Zahlen (byte, short, int, long),
Gleitpunktzahlen (float, double), Zeichen (char) und booleschen Werten
(boolean).
Sobald die Variable deklariert wurde, kann ihr über den Zuweisungsoperator („=“) ein
Wert zugewiesen werden, z.B. innerhalb der main()-Methode
public static void main(String args[])
{
String s;
// Deklaration der Variablen s zur Aufnahme von Zeichenketten
// Zuweisung
s = "Das erste Programm der Vorlesung Programmieren in Java."
// Aufruf der Methode println() der Klasse System, die sich im Paket
// java.lang befindet
System.out.println(s);
}
10
Programmieren in Java
Vorrang
1
2
3
4
5
6
7
8
9
10
11
12
13
Operator
++
Operandentyp
Arithmetisch
Assoziation
R
--
Arithmetisch
R
+, -
Arithmetisch
R
~
Integral
R
!
boolean
R
(type)
*, /, %
irgendein
arithmetisch
R
L
+, +
<<
>>
Arithmetisch
String
Integral
Integral
L
L
L
L
>>>
Integral
L
<, <=
Arithmetisch
L
>, >=
Arithmetisch
L
instanceof
==
!=
Objekt, Typ
Primitiver Typ
Primitiver Typ
L
L
L
==
Objekt
L
!=
Objekt
L
&
&
^
^
|
|
&&
||
?=
Integral
boolean
Integral
boolean
Integral
boolean
boolean
boolean
boolean, irgendein,
irgendein
Variable, irgendein
Variable, irgendein
L
L
L
L
L
L
L
L
R
=
*=, /=,
+=, -=
<<=, >>=,
>>>=,
&=, ^=,
|=
Abb. 0.3-1: Java-Operatoren
11
R
R
Operation
Pre- oder Post-Inkrement
(unär)
Pre- oder Post-Dekrement
(unär)
Unäres Plus, unäres
Minus
Bitweises Komplement
(unär)
Logisches Komplement
(unär)
cast
Multiplikation, Division,
Rest
Addition, Subtraktion
Verkettung
Links-Shift
Rechts-Shift mit
Vorzeichen
Rechts-Shift mit NullenNachziehen
Kleiner als, kleiner als
oder gleich
Größer als, größer als
oder gleich
Objekt?, Instanz?
Gleich (identische Werte)
Ungleich (verschiedene
Werte)
Gleich (Referenz auf das
gleiche Objekt)
Ungleich (Referenz auf
verschiedene Objekte)
Bitweises Und
Logisches Und
Bitweises Oder
Logisches Oder (exklusiv)
Bitweises Oder
Logisches Oder (inklusiv)
Konditionelles Und
Konditionelles Oder
Konditioneller (ternärer)
Operator
Zuweisung
Zuweisung mit Operation
Programmieren in Java
System.out.println(s); ist eine Anweisung. Anweisungen stehen für eine
einzelne Aktion, die in einem Java-Programm ausgeführt wird. Jede Anweisung wird mit
einem Strichpunkt „;“ abgeschlossen. Einige Anweisungen erzeugen einen Wert, wie
das bspw. beim Addieren zweier Zahlen der Fall ist. Derartige Anweisungen werden als
Ausdrücke bezeichnet.
Ein Ausdruck ist eine Anweisung, die als Ergebnis einen (Rückgabe-) Wert produziert.
Dieser Wert kann zur späteren Verwendung im Programm gespeichert, direkt in einer
anderen Anweisung verwendet oder überhaupt nicht beachtet werden. Ausdrücke
können Konstanten, Variablen, Operatoren beinhalten. Ausdrücke sind vielfach mit
unären und binären Operatoren verknüpft.
Trennzeichen: Das sind Symbole und Zusammenfassungen von Quellcode: ( ) { }
[ ] ;. ,.
„(“: wird sowohl zum Öffnen einer Parameterliste für eine Methode als auch zur
Festlegung der Priorität für Operationen in einem Ausdruck benutzt.
„)“: wird sowohl zum Schließen einer Parameterliste für eine Methode als auch zur
Festlegung der Priorität für Operationen in einem Ausdruck benutzt.
Bsp.: public static void main(String args[])
„{“: wird zu Beginn eines Blocks mit Anweisungen oder einer Initialisierungsliste
gesetzt.
„}“:wird an das Ende eines Blocks mit Anweisungen oder einer Initialisierungsliste
gesetzt.
Bsp.: Methoden werden in Java durch einen Anweisungsblock bestimmt. Ein Anweisungsblock besteht
in der Regel aus einer Reihe von Anweisungen, die von geschweiften Klammern umschlossen sind.
public static void main(String args[])
{
String s;
s = "Das erste Programm der Vorlesung Programmieren in Java.";
System.out.println(s);
}
„[“: steht für eine Ausdruck, der als Index für ein Datenfeld (Array) steht.
„]“: folgt einem Ausdruck, der als Index für ein Datenfeld steht.
Bsp.: String args[] = {"Juergen", "Hubert", "Josef", "Liesel", "Christian"};
definiert einen Array mit 5 Komponenten, die alle den gleichen Typ besitzen.
Datenfelder (Arrays) dienen zum Speichern von Elementen, die alle denselben Typ aufweisen.
Jedem Element wird innerhalb des Datenfelds ein eigener Speicherplatz zugewiesen. Dieser
Speicherplatz ist für den leichten Zugriff durchnumeriert.
[0]
[1]
[2]
[3]
[4]
"Juergen"
"Hubert"
"Josef"
"Liesel"
"Christian"
Auf den Wert einer Komponenten kann über den Array-Namen (z.B. args), gefolgt von einem Index in
eckigen Klammern (z.B. [0]) zugegriffen werden. Mit
args[0] = "Roland";
kann dem ersten Element des Array args ein neuer Wert zugewiesen werden.
„;“: dient zum Beenden einer Anweisung.
„,“: wird häufig als Begrenzer (z.B. in einer Parameterliste) benutzt.
12
Programmieren in Java
„.“: wird als Dezimalpunkt als auch zum Trennen solcher Dinge wie Paketnamen von
Klassennamen oder Variablennamen benutzt (z.B. System.out.println(s);).
Standardmäßig haben Klassen Zugriff auf die Klassen im Paket „java.lang“. Zur
Bezugnahme auf eine Klasse, die sich nicht in java.lang befindet, sind alle Pakete,
in denen sich die Klasse befindet, anzugeben, z.B.
java.awt.Color // bezieht sich auf die Klasse Color im Paket awt,
// das sich im Paket java befindet.
Leerraumzeichen: Sie können in beliebiger Anzahl und an jedem Ort zwischen Token
(die eine Funktion besitzen) zur übersichtlichen Gestaltung des Quellcodes plaziert
werden. Solche Zeichen sind bspw.: Space, Tab, Zeilenende, Formularvorschub
Kommentar: Er wird vom Compiler ignoriert. Man unterscheidet den Kommentar bis
zum Zeilenende „//“ und den eingebetteten Kommentar „/* ... */“, z.B.:
/* ErstesProgramm ist eine Applikation, die den einfachen
Gebrauch von Zeichenketten aufzeigt */
Übersetzung und Ausführung.
Befindet sich der Quelltext zum Programm in einer Datei mit dem Namen
ErstesProgramm.java, dann kann dieses Programm durch Aufruf des JavaCompilers javac in ablauffähigen Bytecode übersetzt werden. Das geschieht über das
folgende Systemkommando:
javac ErstesProgramm.java
Den Byte-Code, den der Java-Compiler in der Datei ErstesProgramm.class
hinterlegt hat, kann über das Kommando
java ErstesProgramm
zur Ausführung gebracht werden.
13
Programmieren in Java
Konsole:
Arbeitsspeicher:
javac ErstesProgramm.java
Externspeicher
ErstesProgramm.java
ErstesProgramm.class
java ErstesProgramm
ErstesProgramm.class
Java (-Interpreter)
"Das erste Programm der Vorlesung Programmieren in Java"
Abb. 0.3-2: Ablaufplan ErstesProgramm
14
Programmieren in Java
0.3.2 Bestandteile eines Programms
Wesentliche Programmelemente in Java sind:
- Anweisungen
Anweisungen3 gehören zu den elementaren ausführbaren Programmelementen. Eine Anweisung kann
eine Deklaration enthalten, einen Ausdruck4 auswerten oder den Programmablauf5 (Auswahl-,
Iterations-, Sprung-Anweisungen und return-, throw-Anweisung) steuern.
- Blöcke
Ein Block6 ist eine Zusammenstellung von Anweisungen, die nacheinander ausgeführt werden. Ein
Block kann eigene Variable definieren, die nur innerhalb des Blocks sichtbar sind. Sie werden beim
Aufruf des Blocks angelegt und beim Verlassen des Blocks zerstört. Innerhalb eines Blocks sind nur
die lokalen Variablen des Blocks und die lokalen Variablen des umgebenden Blocks bzw. der
umgebenden Methode sichtbar. Nach außen stellt sich der Block als eine einzige Anweisung dar.
- Methoden
Methoden7 unterscheiden sich von Blöcken folgendermaßen:
-- Sie haben einen Namen und können von verschiedenen Stellen des Programms aufgerufen
werden.
-- Sie sind parametrisierbar
-- Sie können einen Rückgabewert besitzen.
Methoden werden in Java immer lokal zu einer Klasse definiert.
- Klassen
Sie8 enthalten Variablen zur Beschreibung des Zustands von Objekten und Methoden zur
Beschreibung des Verhaltens von Objekten.
- Schnittstellen
Eine Schnittstelle (Interface) ist eine Sammlung von Methoden, die einen Namen besitzen, aber nicht
implementiert sind. Ein Klasse kann beliebig viele Schnittstellen implementieren. Dadurch wird die
Klasse zur Implementierung der Methode gezwungen, deren Namen von der Schnittstelle definiert
wurden. Falls zwei unterschiedliche Klassen, dieselbe Schnittstelle implementieren, können beide auf
Aufrufe der Methode, die in der Schnittstelle definiert sind, reagieren. Allerdings kann die Reaktion auf
diese Methodenaufrufe bei einzelnen Klassen total unterschiedlich sein.
- Pakete
Ein Paket ist eine Sammlung von Klassen. Jede Klasse in Java gehört zu einem Paket. Pakete
ermöglichen, daß Sammlungen von Klassen bei Bedarf verfügbar sind. Die Klassenbibliotheken
befinden sich in einem Paket mit dem Namen „java“. Dieses Paket beinhaltet Pakete, die spezielle
Bestandteile der Sprache Java, z.B. Dateieingabe und Datenausgabe, Multimedia, etc. definieren.
Standardmäßig haben Klassen der Anwender nur Zugrifff auf Klassen im Paket „java.lang“ (StandardFeature). Klassen irgendeines anderen Pakets müssen importiert werden.
- Applikationen (Anwendungen)
Anwendungen (Applikationen) bilden die eigenständigen Programme. Sie benötigen zur Ausführung
keinen Browser, sondern nur den Java-Interpreter und die .class-Dateien der verwendeten Klassen.
- Applets
Applets sind ebenfalls lauffähige Java-Programme. Sie werden aus einer HTML-Seite aufgerufen und
benötigen zur Ausführung einen Web-Browser (oder ein Werkzeug wie den Appletviewer). Applets
müssen von der Klasse Applet abgeleitet und nach den Regeln dieser Klasse aufgebaut sein. Zum
Starten des Programms erzeugt der Browser eine Instanz der abgeleiteten Klasse und ruft eine Reihe
vordefinierter Callback-Methoden9 auf.
3 vgl. 2.4
4 vgl. 2.4.5
5 vgl. 2.4.6
6 vgl. 2.4.1
7 vgl. 2.6
8 vgl. 2.5
9 CallBack-Methoden sind von der abgeleiteteten Klasse zur Verfügung gestellte Methoden, die vom Browser
bzw. Appletviewer aufgerufen werden
15
Programmieren in Java
1. Einführung in die Java-Programmierung
1.1 Übersicht zur Entwicklung der Programmiersprache
Java
Java ist in den Entwicklungslaboren der amerikanischen Firma Sun Microsystems10
entstanden. Man entschied sich bei Sun zur Realisierung eines im Jahre 1990
begonnenen Projekts11 für eine neue Programmiersprache, da bisher entwickelte
Programme mit vorliegenden Programmiersprachen zu große Schwächen zeigten. Der
erste Versuch war nur bedingt erfolgreich. Lediglich der damals im Internet verbreitete
Mosaic-Browser12 wurde zu einer Zielplattform der neuen Programmiersprache 13, die
Ende 1994 für das Internet umgearbeitet wurde und über das Netz frei und umsonst
verteilt wurde.
1995 wurde die neue Programmiersprache mit dem Namen Java 14 der InternetÖffentlichkeit in Kombination mit einem Browser, HotJava, präsentiert. HotJava war
die erste komplexe und vollständig in Java geschriebene Anwendung, der erste Javafähige Browser und damit die Präsentationsform für die ersten Java-Applets. Außerdem
war dieser Browser eine wesentliche Ergänzung des ersten Java-Entwicklungstools von
Sun – das Java Develelopment Kit (JDK 1.0). Ein kommerzielles Produkt, der Java
Workshop15, wurde kurz nach der Präsentation von JDK 1.0 bereitgestellt.
10 Sun ist eine der führenden Hersteller von Workstations
11 Entwicklung eines vollkommen neuen, plattformunabhängigen Betriebssystems für den „Consumerbereich der
allgemeinen Elektronik (Telefone, Videorecorder, Waschmaschinen, Kaffemaschinen; eigentlich alle elektrischen
Maschinen, die Daten benötigen)
12 der erste WWW-Browser mit einer grafischen Benutzeroberfläche. WWW steht für World Wide Web und ist
inzwischen die wichtigste Stütze im Internet. Das WWW ist im wesentlichen durch sog. Hypertexte aufgebaut, die
mit der Sprache HTML entwickelt wurden und werden. Ein Hypertext ist im wesentlichen ein ASCII-Text, der
durch maskierte Wörter (sog. Hyperlinks) zu weiteren Seiten führt. Hypertext ist eigentlich nur ein Text mit
Verweisen auf andere Texte. Der Verweis auf den weiterführenden Text kann aktiviert werden (z.B. durch
Mausklick), und es wird zu dem gewünschten Text verzweigt.
Das Hypertext Transfer Protocol (HTTP) dient zur Übertragung von Informationen aus dem WWW. HTTP ist ein
objektorientiertes Protokoll (TCP/IP-Programm) zur einfachen Übertragung von Hypertext-Dokumenten zwischen
Client und Server. Client-Programme, die HTTP benutzen, werden (in der Regel) als Web-Browser, ServerProgramme als Web-Server bezeichnet. Der Browser schickt an den Server die Aufforderung eine bestimmte
HTML-Seite zu übertragen. Falls er in dieser Seite weitere Verweise (z.B. auf Bilder) entdeckt, schickt er weitere
Übertragungswünsche hinterher. Das Besorgen der gewünschten Dokumente erfolgt über ein einheitliches
Adressierungsschema, dem Uniform Resource Locater (URL), durch den Internet-Standort und die Art der zu
übertragenden Information identifiziert werden.
13 Dem WWW mit dem bis zu diesem Zeitpunkt realisierten Stand der HTML fehlten: dreidimensionale
Darstellung der Objekte, eine bewegte Animation und eine Möglichkeit zur vernünftigen Interaktion mit dem
Anwender. Deshalb waren hier die Multimedia- und Interaktionseigenschaften der neuen Programmiersprache
besonders erfolgreich.
14 verantwortlich für Java ist die Firma JavaSoft – eine Tochterfirma von Sun Microsystems. Sun bzw. JavaSoft
halten im Internet permanent die aktuellste Information von Java bereit. Einige der Informationen findet man
bereits auf der Einstiegseite von Sun (http://java.sun.com), andere Informationen bekommt man von der
Neuigkeitenseite (http://java.sun.com/nav/new/index.html)
15 mit Test- und Debug-Möglichkeiten, einem Referenzcompiler, einer integrierten Entwicklungsumgebung mit
Editor, Browser, Project-, Portfolio- und Build-Manager, Debugger, Project-Tester und Online-Hilfe. Der
Workshop geht mit der Version 2.0 inzwischen in eine neue Phase zur Unterstützung des neuen Java.
16
Programmieren in Java
Natürlich gab es im JDK noch diverse Kinderkrankheiten. Im zweiten Quartal 1997
folgte deshalb nach einigen Zwischenversionen die Version 1.1 des JDK. Parallel zur
1.1.x-Version gab es auch einen neuen Hot-Java-Browser zur Unterstützung der neuen
1.1-API-Funktionen.
Java 1.2 ist die neueste Version. In Verbindung mit dem JDK 1.2 wurde der Begriff
Java 216 eingeführt. Inzwischen gibt es schon die Version 1.3, die nur wenige neue
Programmierschnittstellen bietet, dafür aber eine komfortablere und schnellere
Laufzeitumgebung.
Seit Version 1.2 ist Java in verschiedenen Ausgaben (Editionen) erhältlich. Sun hat drei
verschiedene Editionen mit unterschiedlicher Ausrichtung definiert:
1. Standard-Edition für den Desktop Client oder PC
2. Enterprise-Edition für Application Server
3. Micro-Edition für Kleingeräte.
Am weitesten entwickelt ist die Standard-Edition.
Neben dem JDK gibt es zahlreiche kommerzielle Entwicklungstools für JavaProgrammierer, z. B.: Symantec Visual Café, Borland JBuilder, SuperCade, National
Intelligence Roaster, SunSoft Java Workshop.
1.2 Was ist Java?
Java ist17 eine einfache, objektorientierte, dezentrale, interpretierte, stabil laufende,
sichere, architekturneutrale, portierbare und dynamische Sprache, die
Hochleistungsgeschwindigkeitsanwendungen und Multithreading unterstützt.
- Java ist einfach, obwohl es sehr nahe an der ziemlich komplizierten C/C++-Syntax entworfen wurde.
Die komplexen Teile von C/C++ wurden jedoch aus Java ausgeschlossen. In Java gibt es keine Zeiger
(Pointer) und auch keine Zeiger-Arithmetik. Strings und Arrays sind echte Objekte. Die
Speicherverwaltung erfolgt weitgehend automatisch.
Die Ausdrücke in Java 18 entsprechen aber weitgehend denen von C/C++. Java besitzt eine „if“Anweisung, eine „while“-, „do“- und „for“-Schleife und eine „switch“-Anweisung. Es gibt die von C
bekannten „break“- und „continue“-Anweisungen (in normaler und mit einem „Label“ versehenen
Form)19.
- Java ist klein. Eine der ursprünglichen Ziele von Java war die Erleichterung der Software-Entwicklung für
kleine Rechner.
- Java ist objektorientiert. Es gehört zu einer Familie von Sprachen, die Daten als Objekte definieren und
Methoden zur Bearbeitung dieser Objekte verwenden. Das objektorientierte Konzept von Java hat viel
von C++ geerbt, aber auch Konzepte anderer objektorientierter Sprachen wurden übernommen. Wie die
meisten objektorientierten Sprachen umfaßt Java eine
umfangreiche Klassenbibliothek, die
grundlegende Datentypen, Systemein- und Systemausgabe und andere Hilfsmittel (utilities) bietet. Die
grundlegenden Klassen sind Teil des JDK, das darüber hinaus noch Klassen besitzt, die Funktionen im
Zusammenhang mit in Netzwerken üblichen Internet-Protokollen und Benutzeroberflächen unterstützen.
Da diese Klassenbibliotheken in Java geschrieben sind, sind sie, wie alle Java-Anwendungen, auf alle
Plattformen portierbar.
16 Die exakte Bezeichnung für das JDK 1.2 ist Java 2 JDK v1.2. Nähere Information enthält die Webseite:
http://java.sun.com/products/jdk/1.2/java2.html
17 Offizielle Definition von Sun Microsystems
18 vgl. 2.3
19 vgl. 2.4.
17
Programmieren in Java
- Java ist dezentral und erfüllt eine wesentliche Eigenschaft von Client/Server-Anwendungen. Die
Fähigkeit der Verteilung von Informationen für die Berechnung der Daten. „Dezentral“ beschreibt die
Beziehung von Systemobjekten: Es ist gleichgültig, ob die Objekte sich auf lokalen oder entfernten
Systemen befinden. Objekte können mit Java-Programmen über URLs 20 vom gesamten Web genauso
wie auf dem lokalen Rechner geöffnet und bearbeitet werden. Wichtige Teile der Anwendung bzw. der
Daten können lokal vorhanden sein, andere werden bei Bedarf geladen.
- Java ist interpretiert. Ein gewisser Teil des Java-Codes (ca. 20 %) werden vom Container, dem Browser
interpretiert. Der Java-Quellcode wird mit dem Java-Compiler in Bytecode (architekturneutrales ObjectCode-Format) kompiliert. Bytecode ist nicht lauffähig, bis er von der Java-Laufzeitumgebung21
interpretiert wird.
Java-Compiler (Pentium)
Java-Interpreter (Pentium)
Java-Code
Java-Compiler (SPARC)
Java-Interpreter (SPARC)
Abb. 1.2-1:
- Java ist stabil, d.h. zuverlässig. Die Stabilität einer Programmiersprache zeigt sich darin, daß während
der Kompilierungsphase der gößte Teil der Datenüberprüfung ausgeführt werden kann. Java ist stark
typisiert. Damit können Fehler früh gefunden werden. Ein weiteres Stabilitätskriterium von JavaProgrammen ist die eingebaute Begrenzung der Zugriffsmöglichkeiten auf den Speicherbereich des
20 Rechner sind über IP-Nummern bzw. über Alias-Namen (Domain-Name-System, DNS) im Internet eindeutig
adressiert. Innerhalb des Internet müssen aber auch alle Daten und Programme über unverwechselbare InternetAdressen bestimmt sein. Der Name dieser Internet-Adressen für konkrete Adressanfragen an Dokumente im
Internet lautet URL und steht für Uniform Resource Locator („einheitliches Adressierungsschema für
Objekte“ im Internet). „Einheitlich“ deshalb, weil mit einer URL sowohl die verschiedenen Dienste (WWW, FTP,
Gopher usw.) als auch Rechner oder Dokumente beschrieben werden können. Der Begriff „Objekt“ steht bspw. für
Datei, Text, Videos, Sounds usw., also für ziemlich alles, was sich im Netz befindet. Die exakte Schreibweise einer
URL ist je nach Dienstprotokoll etwas unterschiedlich, sieht jedoch in der Regel so aus:
Dienstprotokoll://host.domain:port/pfad/datei
Dienstprotokoll ist bspw.: http, ftp, gopher, news, mailto, wais. Danach folgen fast immer Doppelpunkt und zwei
Slashes (Ausnahme: mailto).
Mit hosts.domain wird ein Rechner im Internet adressiert. Dabei kann die IP-Nummer des Rechner angegeben
werden (unüblich). Häufiger nimmt man dafür den DNS-Namen (in der Form
host.{localDomain}.SecondLevelDomain.TopLevelDomain.
Auf einem Internet-Rechner kann unter einem Namen eine ganze Reihe von verschiedenen Diensten parallel
betrieben werden (z.B. FTP-Server, HTTP-Server). Zum Erreichen des gewünschten Dienstes auf dem
ausgewählten Rechner benötigt man den sog. Port. Ports sind numerische Werte (zwischen 0 und 1023). In der
Regel hat jeder Internet-Dienst einen Default-Wert, der immer verwendet wird, wenn kein Port explizit angegeben
ist. Der Port für einen HTTP-Server ist immer „80“, ein FTP-Server hat immer den Port „21“.
Das genaue Objekt verbirgt sich hinter der Angabe /pfad/datei.
21 Normalerweise durch einen Java-Browser
18
Programmieren in Java
Rechners 22. Hinzu kommt auch noch die anschließende Sicherheitsüberprüfung durch den „Linker“.
Der Linker ist ein Teil der Laufzeitumgebung, das die im System eingehenden Daten überprüft.
- Java gilt als sicher. In dieser Hinsicht muß sich Java allerdings noch bewähren23.
- Java ist auf verschieden Systemen mit unterschiedlichen Prozessoren und Betriebssystemarchitekturen lauffähig (architekturneutral). Der komplette Java-Bytecode kann auf jedem Prozessor
ausgeführt werden, der einen javafähigen Browser (bspw. die virtuelle Maschine von Java 24) unterstützt.
Plattformunabhängiger Binärcode wird allerdings nicht erzeugt, sondern Java-Bytecode wird während
der Laufzeit in systemeigenen Maschinencode übertragen (interpretiert).
- Java unterstützt „Multithreading“. „Multthreading bedeutet: Mehrere Aufgaben oder Prozesse können
gleichzeitig ausgeführt werden. Nicht die quasi gleichzeitige Ausführung mehrerer Programme
(Multitasking), sondern die gleichzeitige, parallele Ausführung von einzelnen Programmschritten (oder
zusammenhängenden Prozessen) ist „Multithreading“. Das kann bedeuten: Innerhalb eines Programms
können mehrere Dinge gleichzeitig geschehen, mehrere Faden / Threads eines Programms können
gleichzeitig verfolgt und abgearbeitet werden.
22 Ungeprüfte Zugriffe auf Speicherbereiche des Rechners ermöglicht C/C++ (Zeigerarithmetik, implizite
Deklaration)
23 Verschiedene Sicherheitslücken wurden aufgedeckt und wurden inzwischen beseitigt.
24 Die virtuelle Maschine wird auch als Java Interpreter oder Java Environment (Java-Laufzeitumgebung)
bezeichnet
19
Programmieren in Java
1.3 Einstieg in die Java-Programmierung
1.3.1 Die Software für die Java-Programmierung
Das JAVA Development Kit (JDK) enthält die Entwicklungsumgebung zum Schreiben
von Java Programmen. Das JDK ist für Sun-SPARC-Systeme mit Solaris 2.2 (oder
höher) sowie für Windows NT und Windows 95 über das Internet25 erhältlich.
Nach der Installation hat das Java-Verzeichnis26 folgenden Inhalt27:
Abb. 1.3-1: Inhalt des Java-Verzeichnisses
Innerhalb des Java-Verzeichnisses befinden sich Unterverzeichnisse28 mit folgendem
Inhalt:
Verzeichnis
\bin
\lib
\include
\demo
\src
Inhalt
In diesem Verzeichnis befinden sich die JDK-Programme
In diesem Verzeichnis befinden sich defaultmäßig die Standard-Java-Klassen des
JDK 29
In diesem Verzeichnis befinden sich diverse Header-Dateien für die gemeinsame
Verwendung von Java und C/C++
Das Demo-Verzeichnis einthält Beispielprogramme
Falls „Java-Source“ im InstallShield ausgewählt wurde, wird dieses Verzeichnis mit
angelegt. Darin befinden sich die entkomprimierten Java-Dateien, die sonst nur im
gepackten Zustand (Datei SRC.ZIP) vorhanden sind.
25 Das jeweils aktuelle JDK (aber auch ältere Versionen) können von den Sun-Microsystem-Webseiten bzw. der
JavaSoft-Homepage geladen werden (http://www.sun.com bzw. http://www.javasoft.com/ ). Genutzt werden kann
auch die JavaSoft-WWW-Download-Seite für das jeweilige JDK (http://java.sun.com.products/jdk/x.x , die
Angabe x.x ist durch die gewünschte Versionsnummer zu ersetzen). Auch eine FTP-Download, z.B. vom Sun-FTPServer (ftp.javasoft.com) ist möglich.
26 Erzeugt von der Entpackungsroutine des JDK
27 Das Verzeichnis „meinepr“ dient zur Aufnahme von Projekten und zählt nicht standardmäßig zum Inhalt eines
Java-Verzeichnisses
28 unter Windows
29 Im wesentlichen ist das die Datei classes.zip
20
Programmieren in Java
Es ist sinnvoll, die in der JDK-Umgebung verfügbaren Werkzeuge von allen
Verzeichnissen aus zugänglich zu machen. Für Windows-NT und Windows-95Anwender wird das Verzeichnis mit den Werkzeugen in der Pfadangabe der Datei
„autoexec.bat“ eingetragen, z.B.:
PATH c:\;c:\windows;c:\windows\command;c:\jdk1.1.6\bin;c:\jdk1.1.6\include;
Die CLASSPATH-Umgebungsvariable 30
„autoexec.bat“ so vorliegen:
sollte
unter
Windows
in
der
Datei
set classpath=c:\jdk1.1.6\lib\classes.zip;
Im JDK 1.2 wird die CLASSPATH-Umgebungsvariable nicht mehr benötigt. Ist
allerdings eine CLASSPATH-Variable gesetzt, so wird sie auch verwendet.
Nach der Installation des JDK 1.2 bzw. JDK 1.3 muß das Verzeichnis
\jdk1.3\bin in den Suchpfad ausführbarer Dateien eingetragen werden. Das kann
direkt in der autoexec.bat durch Modifikation der PATH-Anweisung, mit Hilfe einer
Batch-Datei oder direkt erledigt werden, z.B.:
set PATH=d:\jdk1.3\bin;%PATH%
30 Umgebungsvariable sind Einstellungen, mit denen die HotJava- und Java-Interpreter-Umgebungen des
Systems spezifiziert werden. Sie werden z.B. unter Windows im allg. auf Befehlszeilenebene oder in der
„autoexec.bat“ mit der Anweisung „SET [Umgebungsvariable]= ....“ gesetzt.
CLASSPATH ist die wichtigste der Umgebungsvariablen von Java. Mit dieser Umgebungsvariablen wird
bestimmt, woher die Systemklassen importiert werden.
21
Programmieren in Java
1.3.2 Applets und Anwendungen
Java Programme werden in zwei Hauptanwendungs-Gruppen gegliedert: Applets und
Anwendungen.
Applets sind Java-Programme, die über das WWW heruntergeladen und von einem
Web-Browser auf dem Rechner des Anwenders ausgeführt werden. Applets können nur
auf einem javafähigen Browser ausgeführt werden bzw. mit einem Tool des JDK, dem
Appletviewer, gesichtet werden.
Java-Anwendungen sind allgemeine, in der Java-Sprache geschriebene Programme. Zum Ausführen von Java-Anwendungen ist kein Browser nötig.
1.3.2.1 Entwicklung von Java-Anwendungen
1. Aufgabe: Erstelle eine Anwendung, die den Text „Willkommen in der JavaWelt“ ausgibt.
Lösungsschritte:
1) Erstelle die folgende Datei mit dem Namen „Willkommen.java“ mit Hilfe eines
Dateiaufbereiters (Editor):
class Willkommen
{
public static void main(String args[])
{
System.out.println("Willkommen in der Java-Welt!");
}
}
Das Programm umfaßt zwei Teile: eine Klassendefinition und ein Programmabschnitt,
der unter main() angegeben ist.
2) Speichern der Datei mit dem Namen „Willkommen.java“ unter einem beliebigen
Verzeichnis31.
3) Aufruf des Java-Übersetzers über die folgende Befehlszeileneingabe:
javac [optionen] dateiname.
optionen .... bestimmen das Verhalten vom Compiler
dateiname ... Name der Datei mit dem Java-Quellcode. "javac" fordert, daß
der Quelltext in Dateien steht, deren Dateiname die Extension ".java"
besitzt.
„javac“ kompiliert den Quellcode in Java-Bytecode und speichert seine Ausgabe in
einer Datei mit dem Namen „dateiname.class“. Standardmäßig werden .classDateien im gleichen Verzeichnis wie die .java-Quelldatei erzeugt32. Im vorliegenden
Fall ist der Aufruf: javac Willkommen.java.
4) Ausführen der Bytecode-Datei „Willkommen.class“ mit dem Java-Interpreter
java: „java Willkommen“. Der im JDK enthaltene Interpreter heißt „java“. Falls alles
31 Vgl. pr13210
32 Mit der Option "-d" im javac-Aufruf kann die .class-Datei an einem anderen Ort gespeichert werden.
22
Programmieren in Java
richtig gelaufen ist, erscheint in der letzten (Ausgabe-) Zeile: Willkommen in der
Java-Welt!.
2. Aufgabe: Erstelle eine Anwendung, die den Text „Herzlich Willkommen
Juergen Hubert Josef Liesel“ ausgibt. Die angebenen Namen
sollen auf Befehlszeilenebene als Parameter eingeben werden.
Lösungsschritte:
1) Erstellen einer Quelle (Datei), die folgenden Quellcode33 enthält:
class WillkommensGruss
{
public static void main(String args[])
{
System.out.print("Herzlich Willkommen ");
System.out.print(args[0]);
}
}
2) Speichern der Datei, Übersetzen und Aufruf des Programm mit dem Parameter
Juergen führt zu: „Herzlich Willkommen Juergen“.
Argumente auf Befehlszeilenebene werden in den Zeichenketten-Array „String
args[]“ aufgenommen. „args“ ist der Name des Zeichenketten-Arrays, das die
Argumentenliste enthält und dem Programm immer zur Verfügung steht. Hier wurde nur
das erste Argument der Liste (args[0])ausgegeben. Sollen alle Argumente, die auf
der Befehlszeilenebene eingegeben wurden, ausgegeben werden, dann ist die Liste
komponentenweise mit einer Zählschleife abzuarbeiten. Die Zählschleife (for-Schleife)
sieht in Java so aus:
for (Initialisierung; Test; Inkrement)
{
// Anweisungen
}
Initialisierung ist ein Ausdruck, der den Beginn der Zählschleife einleitet, z.B.
Initialisierung eines Schleifenindex (z.B. int i = 0;). Die Variablen, die in diesem
Teil der Schleife deklariert werden, sind lokal (in Bezug auf die Schleife). Das bedeutet:
Sie gehören zur Schleife und existieren nicht mehr nach der vollständigen Ausführung
der Schleife. Man kann in diesem Bereich mehr als eine Variable initialisieren (durch
Angabe mehrerer durch Kommas getrennte Ausdrücke, z.B. int i = 0, int j
= 10).
Test ist ein Ausdruck, der nach jeder Iteration der Schleife ausgeführt wird. Der Test
muß ein boolescher Ausdruck oder eine Funktion sein, die einen booleschen Wert
zurückgibt (true oder false). Ergibt der Test true, wird die Schleife ausgeführt. Sobald er
false ergibt, wird die Schleifenausführung angehalten.
Inkrement ist ein beliebiger Audruck oder Funktionsaufruf. Üblicherweise wird er
verwendet, um den Wert des Schleifenindex näher an den Endwert zu bringen und damit
für Beendigung der Schleife zu sorgen. Wie im Initialisierungsbereich kann im
Inkrement-Bereich mehr als ein Ausdruck untergebracht sein, falls die einzelnen
Ausdrücke mit Kommas voneinander getrennt sind.
33 Vgl. pr13210
23
Programmieren in Java
Auf den Wert eines Elements in einem Array wird über den Namen des Array, gefolgt
von einem Index in eckigen Klammern zugegriffen (z.B. args[i]). Array-Indizes
beginnen mit 0. Alle Array-Indizes werden geprüft, um sicher zu stellen, daß sie sich
innerhalb der Grenzen des Array befinden, wie sie bei der Erzeugung des Arrays
festgelegt wurden. Die Länge des Array kann im Programm mit der Instanzvariablen
length getestet werden (z.B. args.length).
3) Erweitern der Quellcode-Datei um eine Zählschleife, die die Argumentenliste
abarbeitet:
class WillkommensGruss
{
public static void main(String args[])
{
int i; // Lokale Variable
System.out.print("Herzlich Willkommen ");
for (i=0; i < args.length;i++)
{
System.out.print(args[i] + " ");
}
}
}
„args.length“ bestimmt die Länge der Argumentenliste.
4) Speichern der Datei, Übersetzen und Aufruf des Programm mit Parametern führt zu:
„Herzlich Willkommen Juergen Liesel Vera Christian Hubert“.
3. Aufgabe: Die auf Befehlszeilenebene eingegebenen Namen sollen sortiert werden.
So soll die Eingabe der Befehlszeile „java WillkommensGruss
Juergen Hubert Josef Liesel Christian“ zu folgender
Ausgabe führen: „Herzlich Willkommen Christian Hubert
Josef Juergen Liesel“.
Lösungsschritte:
1) Gesucht ist ein Algorithmus, der die über die Befehlszeile eingegebenen Namen
sortiert. Ein einfacher Sortieralgorithmus ist unter dem Namen „Bubble-Sort“
bekannt. Man vergleicht dabei zunächst den ersten unter args[0] abgelegten Namen
gegen alle weiteren im Datenfeld args abgelegten Namen.
args
[0]
[1]
[2]
[3]
[4]
"Juergen"
"Hubert"
"Josef"
"Liesel"
"Christian"
Nach 4 Vergleichen hat das Datenfeld args folgende Gestalt angenommen:
args
[0]
[1]
[2]
[3]
[4]
"Christian"
"Juergen"
"Josef"
"Liesel"
"Hubert"
24
Programmieren in Java
Die erste Position (args[0]) ist im Datenfeld damit schon richtig eingeordnet. Danach
muß jetzt der Name an der 2. Position ([1]) mit den übrigen Namen des Datenfelds
verglichen werden. Nach 3 Vergleichen zeigt sich folgendes Bild:
args
[0]
[1]
[2]
[3]
[4]
"Christian"
"Hubert"
"Juergen"
"Liesel"
"Josef"
„Christian“ und „Hubert“ sind jetzt richtig eingeordnet. Nun muß der Name an der dritten
Position mit allen noch verbliebenen Namen auf Position 4 und 5 noch verglichen
werden. Das Resultat der Vergleiche zeigt:
args
[0]
[1]
[2]
[3]
[4]
"Christian"
"Hubert"
"Josef"
"Liesel"
"Juergen"
Der nächste Durchgang führt dann zum sortierten Datenfeld:
args
[0]
[1]
[2]
[3]
[4]
"Christian"
"Hubert"
"Josef"
"Juergen"
"Liesel"
2) Der soeben beschriebene Sortieralgorithmus muß in Java-Programmcode
abgebildet werden. Es ist leicht erkennbar, daß hier zwei verschachtelte for-Schleifen
die Sortierung erreichen können.
for (i = 0; i < args.length; i++)
{
for (j = i + 1; j < args.length; j++)
{
if (args[i].compareTo(args[j]) > 0)
{ // Tauschen
String temp = args[i]; // lokale Variable
args[i] = args[j];
args[j] = temp;
}
}
}
Zeichenketten werden über die Methode „compareTo“ der Klasse String verglichen.
Generell stehen die Vergleichsoperatoren (== != < <= > >=) nur für das
Vergleichen von Zahlen zur Verfügung.
Die Vergleichsbedingung wird durch das Schlüsselwort if erzeugt:
25
Programmieren in Java
if (Ausdruck)
{
// Anweisung(en)
}
else {
// Anweisung(en)
}
Eine if-Bedingung verwendet einen boolschen Ausdruck für die Entscheidung, ob eine
Anweisung ausgeführt werden soll. Die Anweisung wird ausgeführt, wenn der Ausdruck
den Wert true zurückliefert. Falls gewünscht wird, daß eine bestimmte Anweisung bzw.
Anweisungen ausgeführt werden, wenn der boolsche Ausdruck false zurückliefert, ist
dieser Anweisungsblock durch das Schlüsselwort else einzuleiten.
3) Das vollständige Programm umfaßt folgenden Quelltext:
class WillkommensGruss
{
public static void main(String args[])
{
int i, j; // Lokale Variable
System.out.print("Herzlich Willkommen ");
for (i = 0; i < args.length; i++)
{
for (j = i + 1; j < args.length; j++)
{
if (args[i].compareTo(args[j]) > 0)
{
String temp = args[i];
args[i] = args[j];
args[j] = temp;
}
}
}
for (i = 0; i < args.length; i++)
{
System.out.print(args[i] + " ");
}
System.out.println();
}
}
4) Speichern der Datei, Übersetzen und Aufruf des Programms mit Parametern führt zu:
„Herzlich Willkommen Christian Hubert Josef Juergen Liesel“.
4. Aufgabe: Die Argumentenliste args soll auf folgende Weise angezeigt werden:
args[0]
args[1]
args[2]
args[3]
args[4]
=
=
=
=
=
"Christian"
"Hubert"
"Josef"
"Juergen"
"Liesel"
Lösungsschritte:
1) System.out.println() bzw. System.out.print() erwarten ein einziges
Argument innerhalb der Klammern. Sollen, wie hier gewünscht, mehrere Variablen, vom
Typ String oder Zeichenkettenliterale, Argument für println() sein, dann können
diese Elemente mit dem Verkettungsoperator „+“ zu einem einzigen String oder
Zeichenkettenliteral verknüpft werden. Der Umgang mit dem Verkettungsoperator ist in
26
Programmieren in Java
Java einfach, da er alle Variablentypen und Objektwerte wie Strings behandelt. Sobald
ein Teil einer Verkettung ein String oder ein String-Literal ist, werden alle Operatoren
wie Strings behandelt, z.B.: System.out.println("1 + 2 = " + 3);.
2) Die Ausgabe kann über die folgende Anweisungen so erfolgen
for (i = 0; i < args.length; i++)
{
System.out.println("args[" + i + "]
}
System.out.println();
= \"" + args[i] + "\"");
Die Zeichen für doppelte Anführungszeichen(") und Backslash(\) müssen durch die
Verwendung von sog. Escape-Sequenzen (\" und \\) dargestellt werden. Die beiden
Anführungszeichen, die das Literal umschliessen, müssen in derselben Zeile des
Quellcodes stehen. Eine neue Zeile kann innerhalb eines Literals durch die EscapeSequenz \n erreicht werden.
5. Aufgabe: Zwei Gleitpunktzahlen sollen über die Befehlszeile eingelesen werden.
Anschließend sollen mit diesen beiden Zahlen alle zugelassenen, binären
arithmetischen Operationen für Gleitpunktzahlen (des primitiven Datentyps
float) und unäre Inkrement- bzw. Dekrement-Operationen ausgeführt
werden.
Lösungsschritte:
1) Die Übergabe der beiden Zahlen über die Befehlsargumentenliste erfordert die
Konvertierung des Typs String in einen Typ der Klasse Float. Das konvertierte
Datum kann anschließend einer Variablen des primitiven Typs float zugewiesen
werden.
float x = Float.valueOf(args[0]).floatValue();
float y = Float.valueOf(args[1]).floatValue();
2) Für ganze Zahlen und Gleitpunktzahlen sind Additions- (+), Subtraktions- (-),
Multiplikations- (*) und Divisionsopertoren (/) definiert. Es gibt außerdem für unäre
Arithmetik noch Inkrement- und Dekrementoperatoren zur Manipulation des Werts von
Variablen.
3) Der Quelltext zur Programmlösung ist dann:
public class FloatDemo
{
public static void main(String args[])
{
// Konvertieren
float x = Float.valueOf(args[0]).floatValue();
float y = Float.valueOf(args[1]).floatValue();
// Ausgabe der ueber die Befehlszeile eingegebenen Zahlen
System.out.println("x = " + x);
System.out.println("y = " + y);
// Binaere Arithmetik mit den Operatoren + - * /
float z;
z = x + y;
System.out.println("z = x + y = " + z);
z = x - y;
System.out.println("z = x - y = " + z);
27
Programmieren in Java
z = x * y;
System.out.println("z = x * y = " + z);
z = x / y;
System.out.println("z = x / y = " + z);
// Unaere Arithmetik mit Inkrement- / Dekrementoperator
x++;
System.out.println("Nach x++: x = " + x);
y--;
System.out.println("Nach y--: y = " + y);
z = x++;
System.out.println("Nach z = x++: z = " + z + ", x = " +
z = ++x;
System.out.println("Nach z = ++x: z = " + z + ", x = " +
System.out.println("x = " + x);
System.out.println("y = " + y);
z = ++x + y--;
System.out.println("nach z = ++x + y--: z = " + z + ", x
+ x + " y = " + y);
System.out.println("x = " + x);
System.out.println("y = " + y);
z = x + y * ++y;
System.out.println("nach z = x + y * ++y: z = " + z + ",
+ x + " y = " + y);
z = (float) (1.0f / 0.0f);
System.out.println("z = (float) (1.0f / 0.0f) = " + z);
x);
x);
= "
x = "
}
}
4) Der Aufruf java FloatDemo 17 4 führt dann zu folgenden Ausgabe:
x = 17.0
y = 4.0
z = x + y = 21.0
z = x - y = 13.0
z = x * y = 68.0
z = x / y = 4.25
Nach x++: x = 18.0
Nach y--: y = 3.0
Nach z = x++: z = 18.0, x = 19.0
Nach z = ++x: x = 20.0, x = 20.0
x = 20.0
y = 3.0
Nach z = ++x + y--: z = 23.0, x = 21.0 y = 2.0
x = 21.0
y = 2.0
Nach z = x + y * ++y: z = 27.0, x = 21.0 y = 3.0
z = (float) (1.0f / 0.0f) = Infinity
Das zuletzt angegebenen Resultat zeigt die Zuordnung "Infinity" falls eine Zahl
ungleich Null durch Null geteilt wird. 1.0f und 0.0f sind Gleitpunkt-Literale. Da
"Infinity" als sehr groß interpretiert (Typ double) wird, muß hier in den Typ float
konvertiert werden.
Die Anwendung zeigt außerdem die Anwendungsweise der Inkrement- und
Dekrementoperatoren in Präfix- und Postfix-Schreibweise. Steht der Operator vor der
Variablen (z.B. ++x), dann wird er angewendet, bevor der Wert der Variablen in eine
Anweisung eingesetzt wird. Im umgekehrten Fall (z.B. x++) wird die Variable zuerst in
einer Anweisung benutzt und dann erhöht.
28
Programmieren in Java
6. Aufgabe: In der 5. Aufgabe führt der Aufruf java FloatDemo zu einem Fehler.
Dieser Fehler soll aufgefangen werden.
Lösungsschritte:
1)
Java
verfügt
zur
Behandlung
von
Fehlern
eine
spezielle
Ausnahmenbehandlungsroutine (exception handling) den „try-catch“-Block. Im
„try“-Block wird der normale Ablauf behandelt. Im „catch“-Block erfolgt die
Behandlung der Ausnahmen. Die Verzweigung in den „catch“-Block erfolgt beim
Auftreten einer Ausnahme automatisch, der „try“-Block bleibt dabei unberührt.
2) Der Quelltext nimmt nach dem Einfügen eines „try-catch“-Blocks folgende Gestalt
an:
public class FloatDemo
{
public static void main(String args[])
{
try {
// Konvertieren
................
}
catch(Exception e)
{
System.out.println("Fehler bei der Eingabe: java FloatDemo a b");
System.out.println(e.getMessage());
}
}
}
1.3.2.2 Entwicklung von Java-Applets
Die Entwicklung eines Applets unterscheidet sich von einer Anwendung, weil JavaApplets in einer Web-Seite mit anderen Seitenelementen zusammen ausgeführt
werden. Zur Ausführung eines Applets ist es nötig, das Applet in einem HTMLDokument34 einzubetten. In diesem HTML-Dokument werden dem javafähigen Browser
die Informationen mitgeteilt, die er zur Ausführung des Applets benötigt. Der Browser
lädt die Klassendatei und führt das Applet automatisch aus.
Aus der Sicht des Programmierers ist das Applet eine Klasse, die von der AppletKlasse abgeleitet wird.
1. Aufgabe: Erstelle ein Applet, das in einem Fenster den Text „Herzlich
Willkommen in der Java-Welt“ ausgibt
Lösungsschritte:
1) Erstelle die folgende Datei mit dem Namen „WillkommenApplet.java“ mit Hilfe
eines Dateiaufbereiters (Editor):
/* Das erste Java-Applet */
34 eine entsprechende Referenz innerhalb einer HTML-Seite mit einem speziellen Tag, dem <APPLET>-Tag
erledigt die Einbettung in den Browser
29
Programmieren in Java
import java.awt.Graphics;
public class WillkommenApplet extends java.applet.Applet
{
public void paint (Graphics g)
{
g.drawString("Herzlich willkommen in der Java Welt!",5,25);
}
}
Durch die „import“-Anweisung können Entwickler Klassen verwenden, die in anderen
Dateien definiert sind. Compiler bzw. Interpreter greifen auf die class-Dateien zu. Über
„import“ wird bestimmt, wo diese Dateien liegen. Java importiert immer das Paket
„java.lang“, denn hier ist die Klasse Object enthalten, von der alle Java-Klassen
abgeleitet sind. Die Graphics-Klasse enthält Methoden zum Zeichnen von
Textzeichen und Zeichenketten. Mit der „drawstring“-Methode der GraphicsKlasse können Textzeichen auf den Bildschirm gemalt werden.
Die folgende Abbildung zeigt die Modellierung 35 des vorliegenden Quellcodes:
WillkommenApplet
paint()
g.drawString
(“Herzlich Willkommen in der Java-Welt!“,5,25)
Abb. 1.3-2: Klassendiagramm zu WillkommenApplet
Die Klasse WillkommenApplet läßt sich grafisch als rechteckiges Symbol
darstellen. Die paint()-Methode wird ohne formale Parameter beschrieben, ihre
Implementierung wird durch die beigefügte Notiz gezeigt.
Die unmittelbare Superklasse wird im Quelltext direkt in der „extends“-Klausel
angegeben. „extends java.applet.Applet“ bestimmt das die angegebene
Applet-Klasse von der Applet-Klasse des Abstract Windowing Toolkit (AWT) abgeleitet
ist. Der andere Teil der Klassendefinition enthält das Schlüsselwort „public“ mit der
Bedeutung: Die Klasse ist nach dem Laden für das gesamte Java-System verfügbar.
Applets müssen „public“ deklariert werden.
Die folgende Abbildung zeigt die Beziehungen der Klasse WillkommenApplet zu
ihren unmittelbaren Nachbarn:
35 Die Modellierung erfolgt nach den Regeln der Unified Modelling Language (UML). Die UML ist eine grafische,
standardisierte Sprache zum Spezifizieren, Konstruieren, Visualisieren und Dokumentieren.
30
Programmieren in Java
Applet
WillkommenApplet
paint()
Graphics
Abb. 1.3-3: Die unmittelbare Umgebung von WillkommenApplet
Die gerichtete Linie mit der unausgefüllten Pfeilspitze von WillkommenApplet zu Applet
repräsentiert eine Generalisierung, d.h.: WillkommenApplet ist eine Unterklasse von
Applet. Der gestrichelte Pfeil repräsentiert eine Abhängigkeitbeziehung:
WillkommenApplet verwendet Graphics.
Ein eigenes, vom Benutzer erstelltes Applet überschreibt gewöhnlich Methoden, die in
der Superklasse Applet definiert sind. Diese Methoden übernehmen Aufgaben zur
Initialisierung des Applet vor der Ausführung (public void start()), zur Reaktion
auf Mauseingaben, zum Anhalten des Applet (public void stop()) und zu
Aufräumungsarbeiten (public void destroy()), wenn das Applet beendet wird.
Eine dieser Methoden ist paint(), die sich um die Anzeige des Applet in einer
Webseite kümmert. Die paint()-Methode besitzt ein einziges Argument, eine Instanz
der Klasse Graphics. Die Klasse Graphics stellt Verhaltensweisen zur Darstellung
von Schriften, Farben, zum Zeichnen von Linien36, von Ellipsen bzw. Kreisen37, von
Rechtecken38 und anderen Formen zur Verfügung. In der paint()-Methode wurde
hier der String "Herzlich Willkommen in der Java Welt!" bei den (x,y)Koordinaten (5,25) ausgegeben. Der Ursprung des Koordinatensystems liegt in der
linken oberen Ecke des Darstellungsbereichs. Der String wird in einer defaultmäßig
festgelegten Schrift und Farbe angezeigt.
Die Untersuchung der Java-Bibliotheken zu Applet und Graphics zeigt: Die beiden
Klassen sind Teil einer größeren Hierarchie. Verfolgt man die von Applet erweiterten
und implementierten Klassen, dann kann man das folgende Klassendiagramm erhalten:
36 public void drawLine(int x1, int y1, int x2, int y2); (x1,y1) bestimmt den
Anfangspunkt, (x2,y2) bestimmt den Endpunkt der Linie.
37 public void drawOval(int x, int y, int width, int height); (x,y) gibt die
Koordinaten der oberen linken Ecke des die Ellipse umschreibenden Rechtecks mit der Höhe height und der
Breite width am
38 public void drawRect(int x, int y, int width, int height); bestimmt die obere
linke Ecke des Rechtecks, (width, height) legen Breite und Höhe des Rechtecks fest.
31
Programmieren in Java
Object
Component
ImageObserver
Container
Panel
Applet
WillkommenApplet
Abb. 1.3-4: Vererbungshierarchie von WillkommenApplet
Die Beziehung zwischen ImageObserver und Component ist eine Schnittstelle.
ImageObserver wird von Component implementiert.
WillkommenApplet arbeitet mit den Klassen Applet und Graphics unmittelbar
zusammen. Diese beiden Klassen bilden lediglich einen kleinen Ausschnitt aus der
Bibliothek mit vordefinierten Java-Klassen. Die Verwaltung dieser Klassen und
Schnittstellen organisiert Java in mehreren verschiedenen Paketen. Das Wurzelpaket in
der Java-Umgebung heißt java. In dieses Paket sind mehrere weitere Pakete
geschachtelt, die wiederum andere Pakete, Schnittstellen und Klassen enthalten.
Object existiert im Paket lang, Panel, Container, Component existieren im
Paket awt, und die Klasse Applet im Paket applet. Die Schnittstelle
ImageObserver existiert im Paket image, das liegt wiederum im Paket awt
(qualifizierter Name: java.awt.ImageObeserver). Die Paketstruktur 39 kann in
einem Klassendiagramm visualisiert werden:
java
WillkommenApplet
applet
awt
lang
Abb. 1.3-5: Paketstruktur im WillkommenApplet
39 Pakete werden in der UML als Akten mit Reitern dargestellt. Die gestrichelten Pfeile repräsentieren die
Abhängigkeiten zwischen den Paketen.
32
Programmieren in Java
2) Speichern der Datei mit dem Namen „WillkommenApplet.java“ unter einem
beliebigen Verzeichnis.
3) Aufruf des Java-Übersetzers über die folgende Befehlszeileneingabe: „javac
WillkommenApplet.java“. Der Compiler gibt bei erfolgreicher Übersetzung keine
Rückmeldung. Zwei Dateien müssen nach erfolgreicher Übersetzung vorliegen:
„WillkommenApplet.java“ und „WillkommenApplet.class“.
4) Erstellen der folgenden HTML-Datei „WillkommenApplet.html“, anschließend
Speichern dieser Datei.
<HTML>
<HEAD>
<TITLE>Seid gegruesst!</TITLE>
</HEAD>
<BODY>
<P>Mein Java Applet sagt:<BR>
<APPLET CODE="WillkommenApplet.class" WIDTH=200 HEIGHT=50>
</APPLET>
</BODY>
</HTML>
Der Bezugspunkt in HTML-Dateien auf ein Applet erfolgt mit dem <Applet>-Tag. Das
Code-Attribut dient zur Angabe von dem Namen der Klasse, die das Applet enthält. Die
Attribute WIDTH und HEIGHT dienen zum Bestimmen der Größe des Applets. Der
Browser benutzt diese Werte zur Zuteilung des Raums, der für das Applet auf der Seite
freigehalten werden muß. Hier wurde eine Box mit einer Breite von 200 und einer Höhe
von 50 Pixeln definiert.
WillkommenApplet ist als Applet implementiert. Es kann nie isoliert stehen, sondern ist
normalerweise Teil einer Webseite.
WillkommenApplet.java
WillkommenApplet.class
WillkommenApplet.html
Abb. 1.3-6: Die Komponenten von WillkommenApplet
5) Ausführung des Applet mit einem javafähigen Web-Browser bzw. dem
Appletviewer, z.B. mit dem Aufruf „appletviewer WillkommenApplet.html“. Das
Resultat müßte so aussehen:
33
Programmieren in Java
Abb. 1.3-7: Darstellung des Fensters mit dem Appletviewer
In einem Browser würde zusätzlich der Text rund um das Applet („Mein
Applet sagt:“) gezeigt werden.
Java
2. Aufgabe: Verändern von Schrift und Farbe für den Text „Herzlich Willkommen
in der Java-Welt“.
Lösungsschritte:
1) Erweitere die Datei mit dem Namen „WillkommenApplet.java“ mit Hilfe eines
Dateiaufbereites (Editor).
Die erste Erweiterung soll die Schrift verändern, in der der Text ausgegeben wird. Es
wird ein Objekt der Klasse java.awt.Font über folgende Anweisung erzeugt: Font f =
new Font("TimesRoman",Font.BOLD,12);
Objekte der Klasse Font dienen zum Bereitstellen verschiedener Schriftarten für die
Methode drawString() und repräsentieren den Namen, den Stil und die Größe einer
Schrift. Mit einem spezifischen Objekt der Klasse Font kann man eine Schrift aufrufen,
die sich von der standardmäßig in Applets benutzten Schrift unterscheidet. Dem FontObjekt wird hier die Schrift „TimesRoman“, fett in „12-Punkt“ Größe zugewiesen.
Das neue Objekt wird anschließend der Instanzvariablen f zugewiesen und ist so der
Methode paint() zugänglich:
public void paint (Graphics g)
{
// Mitteilung: Die Schrift zur Anzeige von Text befindet sich in der
// Instanzvariablen f
g.setFont(f);
// Mitteilung: Die Farbe fuer die Ausgabe ist gelb
g.setColor(Color.yellow);
// Die glebe Farbe dient zum Fuellen eines Rahmens fuer den Text, der
// aus einem Rechteck mit abgerundeten Ecken besteht
g.fillRoundRect(0,0,225,30,10,10);
// Mitteilung: die Farbe fuer Textausgaben ist
// eine Instanz der Klasse Color fuer die Farbe rot
g.setColor(Color.red);
// Mitteilung: Text wird in festgelegter Schrift und Farbe bei den
// (x,y)-Koordinaten (5,25) ausgegeben.
g.drawString("Herzlich Willkommen in der Java Welt!",5,25);
}
Die Klassen Font und Color werden über die folgenden import-Anweisungen
bereitgestellt:
import java.awt.Graphics;
import java.awt.Font;
import java.awt.Color;
34
Programmieren in Java
Dafür kann man auch „import java.awt.*;“1 schreiben.
2) Kompiliere die Datei „WillkommenApplet.java“ in eine „.class“-Datei.
3)
Ausführung
des
Applet,
z.B.
über
den
Aufruf
appletviewer
WillkommenApplet.html.
3. Aufgabe: Erweitere das Applet der 1. Aufgabe um einen Dokumentationskommentar
Java kennt verschiedene Kommentarzeichen
- für die interne Dokumentation: // und die Kombination /* ... */
- für die externe Dokumentation: //** ... */
Grundsätzlich steht der Dokumentationskommentar
Konstruktoren,) Methoden, z.B.41:
vor
Klassen,
(Interfaces,
/* Das erste Java Applet */
import java.awt.Graphics;
/** Dieses Applet demonstriert den Grundaufbau von
*** Applets.
*** @author Juergen Sauer
*** @version WS 2001/2002
*** @see WillkommenApplet01
*/
public class WillkommenApplet01 extends java.applet.Applet
{
/** Die Methode paint ist von Component geerbt
*** Sie wird aufgerufen, wenn das Applet neu
*** gezeichnet werden muß.
*** @param g Eine Referenz eines Grafikobjekts
*/
public void paint (Graphics g)
{
g.drawString("Herzlich Willkommen in der Java Welt!",5,25);
}
}
Das Programm, das einen derartigen Quellcode zu einer HTML-Dokumentation
verarbeitet, heißt javadoc.exe und ist im Java-SDK von Sun enthalten.
Aufrufsyntax: javadoc [flags] [klasse|package]
Für „flags“ kann * oder folgende Schalter angegeben werden:
-version
-author
-sourcepath
-classpath
-doctype
-noindex
-notree
@version berücksichtigen
@author berücksichtigen
Suchpfad für Klassen
Synonym für sourcepath
Ausgabetyp (Standard: html)
Kein Verzeichnis aller Konstruktoren und Methoden erzeugen
Keine Java-Klassenhierarchie erzeugen
40 Diese Anweisung stellt alle Klassen des Pakets java.awt zur Verfügung
41 pr13210
35
Programmieren in Java
Falls das vorliegende Beispiel mit javadoc –author –version –notree –
noindex WillkommenApplet01.java aufgerufen wird, erzeugt das Programm
eine Datei WillkommenApplet01.html, die Auswertungen der Java-API zeigt und
den Kommentar, z. B.:
Abb.: Ausschnitt zum über javadoc erzeugten Dokument
4. Aufgabe: Überwachen des Lebenszyklus eines Applet.
Ein Applet führt keine Aktionen aus eigener Initiative aus, sondern empfängt Ereignisse
vom System und liefert Ergebnisse zurück. Beim Start eines Applet ruft der
Webbrowser die Methode init() des Appletobjekts auf. Beim Beenden eines
Applets wird die Methode destroy() aufgerufen. init() und destroy() werden
im Lebenszyklus eines Applet genau einmal ausgeführt, nämlich beim Erzeugen bzw.
beim Beenden eines Objekts. Zusätzlich sind zwei weitere Methoden start() und
stop() vorgesehen, die an verschiedenen Stellen zur Aktivierung aufgerufen werden
36
Programmieren in Java
können. Wählt ein Anwender bei gestartetem Applet z.B. im Browser eine andere
Internetseite an, so ruft der Browser die stop()-Routine auf und hält das Applet an, bis
der Anwender wieder auf die Appletseite zurückkehrt. Dann ruft der Browser die
start()-Routine zum ordnungsgemäßen Fortsetzen des Applet auf.
import java.awt.*;
import java.applet.*;
public class AppletDemo extends Applet
{
// Instanzvariable
String s;
int
initialisierungen = 0;
int
startpunkte
= 0;
int
haltepunkte
= 0;
// Methoden
public void init() { initialisierungen++; }
public void start() { startpunkte++; }
public void stop() { haltepunkte++; }
public void paint(Graphics g)
{
// Zur Ausgabe einer geeigneten Nachricht über das Verhalten
// des Applet wird der String s mit Hilfe des Opertors + aus
// Teilstrings zusammengesetzt. Ganze Zahlen werden dabei in Zei// chenketten konvertiert.
s = "Initialisierungen: " + initialisierungen +
", Startpunkte: " + startpunkte +
", Haltepunkte: " + haltepunkte;
g.drawString(s,10,10);
}
}
Browser
öffnet
<<new>>
Webseite
:Applet
init()
start()
paint()
verlässt
stop()
Browser
schliesst
destroy()
Browser
Abb.: Lebenszyklus eines Applets
37
Programmieren in Java
5. Aufgabe: Entwickle aus der vorliegenden Aufgabe eine allgemeine AppletSchablone, die als Vorlage für das Erstellen von Applets dienen kann.
Das folgende Gerüst beschreibt ein Muster für alle Applets:
//
//
//
//
//
//
Name der Klasse:
Beschreibung:
Import der Pakete
import java.lang.*;
import java.applet.*;
import java.awt.*;
// Top-Level-Klassen-Deklaration bzw. Definition des Applets
public Klassenname extends java.applet.Applet
{
// Variablen-Deklarationen bzw. Definitionen
// ...
// Eigene Methoden
// ...
// Methoden, die ueberschrieben werden
//
public void init()
{
// ...
}
public void start()
{
// ...
}
public void stop()
{
// ...
}
public void destroy()
{
// ...
}
// Optional: die Ausgabemethode
public void paint(Graphics g)
{
// ..
}
}
Ein Applet erbt Methoden und Variablen der Applet-Klasse. Die Applet-Klasse erbt
wiederum von einer Reihe anderer Klassen. Eine Klasse, die java.applet.Applet
erweitert,
kann
Variablen
und
Methoden
aus
java.lang.Object,
java.awt.Component, java.awt.Container sowie java.awt.Panel
verwenden.
38
Programmieren in Java
1.4 Die Objektorientierung von Java
1.4.1 Grundlegende Konzepte
1.4.1.1 Zustand und Verhalten von Objekten, Klassen, Instanz- und Klassen-Variable bzw. Methoden
Die Abbildung von Zustand bzw. Verhalten in Instanzvariable bzw. Instanzmethoden
Objekte sind die Schlüssel zum Verständnis der objektorientierten Technologie. Objekte
sind Gegenstände des täglichen Lebens: der Schreibtisch, das Skript, die Vorlesung.
All diese Objekte der realen Welt haben Zustand und Verhalten. Auch SoftwareObjekte haben Zustand und Verhalten. Der Zustand wird in Variablen festgehalten, das
Verhalten der Objekte beschreiben Methoden. Die Variablen bilden den Kern der
Objekte. Methoden schirmen den Objektkern von anderen Objekten des Programms ab
(Kapselung). Software-Objekte kommunizieren und verkehren über Nachrichen
(Botschaften) miteinander. Das sendende Objekt schickt dem Zielobjekt eine
Aufforderung, eine bestimmte Methode auszuführen. Das Zielobjekt versteht (hoffentlich)
die Aufforderung und reagiert mit der zugehörigen Methode. Die genaue formale
Schreibweise solcher Botschaften in objektorientierten Sprachen ist im Detail
verschieden,
jedoch
wird
meistens
folgende
Form
verwendet:
Empfänger.Methodenname(Argument).
„Argument“
ist
in
dem
Botschaftsausdruck ein Übergabeparameter für die Methode.
In der realen Welt existieren häufig Objekte der gleichen Art. Sie werden über einen
Prototyp, eine Klasse, zusammengefaßt. Eine Klassendefinition in Java wird durch das
Schlüsselwort „class“ eingeleitet. Anschließend folgt innerhalb von geschweiften
Klammern eine beliebige Anzahl an Variablen- und Methodendefinitionen. Zum Anlegen
eines Objekts einer Klasse (Instanziierung 42) muß eine Variable vom Typ der Klasse
deklariert und mit Hilfe des new-Operators ein neu erzeugtes Objekt zugewiesen
werden. Das Speicher-Management in Java erfolgt automatisch. Während das
Erzeugen von Objekten immer einen expliziten Aufruf des new-Operators erfordert43,
erfolgt die Rückgabe von nicht mehr benötigtem Speicher automatisch44.
Das Schreiben eines Programms besteht damit aus Entwurf und Zusammenstellung von
Klassen. Klassenbibliotheken (Sammlung von Klassen) stellen Lösungen für
grundlegende Programmieraufgaben bereit.
Zustand, Aussehen und andere Qualitäten eines Objekts (Attribute) werden durch
Variable definiert. Da jede Instanz einer Klasse verschiedene Werte für ihre Variablen
haben kann, spricht man von Instanzvariablen. Zusätzlich gibt es noch Klassenvariable,
die die Klasse selbst und alle ihre Instanzen betreffen. Werte von Klassenvariablen
werden direkt in der Klasse gespeichert. Der Zustand wird in Variablen festgehalten
und zeigt den momentanen Stand der Objektstruktur an, d.h. die in den einzelnen
42 Eine Instanz einer Klasse ist ein (tatsächliches) Objekt (konkrete Darstellung)
43 Ausnahmen: String-, Array-Literale
44 Ein Garbage-Collector (niedrigpriorisierte Hintergrundprozeß) sucht in regelmäßigen Abständen nach nicht
mehr referenzierten Objekten und gibt den durch sie belegten Speicher an das Laufzeitsystem zurück
39
Programmieren in Java
Bestandteilen des Objekts enthaltenen Informationen und Daten. Abhängig vom
Detaillierungsgrad kann die Notation für eine Variable den Namen, den Datentyp und
den voreingestellten Wert zeigen:
Sichtbarkeit Typ Name = voreingestellter_Wert;
Sichtbarkeit: öffentlich (public), geschützt (protected) oder privat (private)
Typ: Datentyp
Name: eine nach bestimmten Regeln45 gebildete Zeichenkette
Nach der Initialisierung haben alle Variablen des Objekts zunächst Standardwerte
(voreingestellte Werte). Der Zugriff auf sie erfolgt mit Hilfe der Punktnotation:
Objekt.Variable.
Zur Bezugnahme auf das aktuelle Objekt dient das Schlüsselwort this. Es kann an
jeder beliebigen Stelle angegeben werden, an der das Objekt erscheinen kann, z.B. in
einer Punktnotation zum Verweis auf Instanzvariablen des Objekts oder als Argument für
eine Methode oder als Ausgabewert der aktuellen Methoden. In vielen Fällen kann das
Schlüsselwort this entfallen. Das hängt davon ab, ob es Variablen mit gleichem
Namen im lokalen Bereich gibt.
Zur Definition des Verhaltens von Objekten dienen Methoden. Methoden sind
Funktionen, die innerhalb von Klassen definiert werden und auf Klasseninstanzen
angewandt werden. Methoden wirken sich aber nicht nur auf ein Objekt aus. Objekte
kommunizieren auch miteinander durch Methoden. Eine Klasse oder ein Objekt kann
Methoden einer anderen Klasse oder eines anderen Objekts aufrufen, um Änderungen
in der Umgebung mitzuteilen oder ein Objekt aufzufordern, seinen Zustand zu ändern.
Instanzmethoden (Operationen, Services) werden auf eine Instanz angewandt,
Klassenmethoden beziehen sich auf eine Klasse. Klassenmethoden können nur mit
Klassenvariablen arbeiten.
Die Beschreibung der Operationen (Nachrichten, Methoden) erfolgt nach dem
folgenden Schema:
Sichbarkeit Rückgabetypausdruck Name(Parameterliste)
Sichtbarkeit: öffentlich (public), geschützt (protected), privat (private)46
Rückgabetypausdruck: Jede Methode ist typisiert. Der Typ einer Methode bestimt den Typ des
Rückgabewerts. Dieser kann von einem beliebigen primitiven Typ47, einem Objekttyp oder vom Typ void
sein. Methoden vom Typ void haben keinen Rückgabewert und dürfen nicht in Ausdrücken verwendet
werden. Hat eine Methode einen Rückgabewert, dann kann sie mit der „return“- Anweisung48 einen Wert
an den Aufrufer zurückgeben.
Parameterliste enthält optional Argumente und hat folgende Struktur: Datentyp
variablenname, ..... Die Anzahl der Parameter ist beliebig und kann Null sein.
In Java wird jede selbstdefinierte Klasse mit Hilfe des Operators new instanziert. Mit
Ausnahme von Zeichenketten (Strings) und Datenfeldern (Arrays), bei denen der
Compiler auch Literale zur Objekterzeugung bereitstellt, gilt dies für alle vordefinierten
Klassen der Java-Bibliothek.
45 vgl. 2.1.2 Bezeichner und Namenskonventionen
46 vgl. 2.6.2
47 vgl. 2.2
48 vgl. 2.6.4
40
Programmieren in Java
Der Aufruf einer Methode erfolgt ähnlich der Verwendung einer Instanzvariablen in
„Punktnotation“. Zur Unterscheidung von einem Variablenzugriff müssen zusätzlich die
Parameter in Klammern angegeben werden, selbst wenn die Parameter-Liste leer ist.
Ein praktisches Beispiel
1. Erstellen der Klasse Rechentafel zum Rechnen mit ganzen Zahlen
Die grundlegende Klassendefinition ist: class Rechentafel{ }
Das ist eine Java-Klasse (in einfachster Form), selbstverständlich passiert hier noch
nicht viel. Damit etwas Aktion stattfinden kann, müssen Zustand und Verhalten diverser
Rechentafel-Objekte festgehalten werden können.
Der Zustand wird in Variablen gespeichert. In Rechentafel-Objekten gibt es zwei
Operanden zur Aufnahme ganzzahliger Werte für die Berechnung und eine Variable zur
Aufnahme des Resultats nach der Berechnung. Operanden und Resultat sind in der
Klasse Rechentafel Instanzvariable, da jedes Rechentafel-Objekt dafür verschiedene
Werte haben kann. Folgende Instanzvariable (mit Datentyp int) werden in den durch „{
}“markierten Klassenkörper eingetragen:
// Instanzvariable
int ersterOperand = 0;
int zweiterOperand = 0;
Da keine spezifische Angabe zur Sichtbarkeit vorliegen, haben Objekte auf die
Variablen Zugriff, die aus Klassen innerhalb des Klassenverzeichnisses gebildet
werden.
Rechentafel-Objekte sollten „Rechnen“ können. Dieses Verhalten bedeutet: Die
vorliegenden Instanzvariablen sind durch Instanzmethoden zu ergänzen, z.B.:
// Operationen
int addiere()
{
return ersterOperand + zweiterOperand;
}
int subtrahiere()
{
return ersterOperand - zweiterOperand;
}
int multipliziere()
{
return ersterOperand * zweiterOperand;
}
int dividiere()
{
// ganzzahlige Division
return ersterOperand / zweiterOperand;
}
Die Klasse ist damit für Rechentafel-Objekte vollständig bereitgestellt. Allerdings wird
erst nach dem Hinzufügen der main()-Methode aus dieser Klasse eine JavaAnwendung 1:
41
Programmieren in Java
2. Die Klasse Rechentafeltest zum Überprüfen der Klasse Rechentafel
Die Klasse Rechentafeltest ist durch die Anwendung der Klasse Rechentafel bestimmt.
Sie enthält die main()-Methode. Im Mittelpunkt der Anwendung steht die Anweisung:
Rechentafel einRechenobjekt = new Rechentafel();
Sie erzeugt eine Instanz der Klasse Rechentafel und speichert eine Referenz darauf
in
der
Variablen
„einRechenobjekt“.
In
jeder
objektorientierten
Programmiersprache lassen sich spezielle Methoden definieren, die bei der
Initialisierung eines Objekts aufgerufen werden. In Java werden Konstruktoren als
Methoden ohne Rückgabewert definiert, die den Namen der Klasse erhalten, zu der sie
gehören. Falls eine Klasse keinen expliziten Konstruktor besitzt, wird ein
parameterloser „default“-Konstruktor aufgerufen (, der hier zusammen mit dem Operator
new verwendet wird).
import java.lang.*;
class Rechentafeltest extends Object
{
// Zur Ausführung mit dem Java-Interpreter wird eine main()-Methode
// benoetigt
public static void main(String argv[])
{
// Erzeugen einer Instanz der Klasse Rechentafel
Rechentafel einRechenobjekt = new Rechentafel();
// Setzen der Instanzvariablen
einRechenobjekt.ersterOperand = 3;
einRechenobjekt.zweiterOperand = 2;
// Test mit einem Rechenobjekt der Klasse Rechentafel
System.out.println("Test mit einem Rechentafel-Objekt");
// Aufruf der Methoden und Ausgabe des jeweiligen Resultats
System.out.print("Addition ");
System.out.println("von " + einRechenobjekt.ersterOperand + " und "
+ einRechenobjekt.zweiterOperand + " ist "
+ einRechenobjekt.addiere());
System.out.print("Subtraktion ");
System.out.println("von " + einRechenobjekt.ersterOperand + " und "
+ einRechenobjekt.zweiterOperand + " ist "
+ einRechenobjekt.subtrahiere());
System.out.print("Multiplikation ");
System.out.println("von " + einRechenobjekt.ersterOperand + " und "
+ einRechenobjekt.zweiterOperand + " ist "
+ einRechenobjekt.multipliziere());
System.out.print("Division ");
System.out.println("von " + einRechenobjekt.ersterOperand + " und "
+ einRechenobjekt.zweiterOperand + " ist "
+ einRechenobjekt.dividiere());
}
}
49 vgl. pr14101
42
Programmieren in Java
Einfache Typen51 und Referenztypen
Einfache Typen
Jedes Java-Programm besteht aus einer Sammlung von Klassen. Der vollständige
Code von Java wird in Klassen eingeteilt. Es gibt davon nur eine Ausnahme: Boolesche
Operatoren, Zahlen und andere einfache Typen52 sind in Java erst einmal keine
Objekte. Java hat für alle einfachen Typen sog. Wrapper-Klassen implementiert. Ein
Wrapper-Klasse ist eine spezielle Klasse, die eine Objektschnittstelle für die in Java
verschiedenen primitiven Typen darstellt. Über Wrapper-Objekte können alle einfachen
Typen wie Klassen behandelt werden. Java besitzt acht primitive Datentypen53:
- vier Ganzzahltypen mit unterschiedlichen Wertebereichen
- zwei Gleitpunktzahlentypen mit unterschiedlichen Wertebereichen nach IEEE Standard of Binary Floating Point
Arithmetic.
- einen Zeichentyp
Primitiver Typ
boolean
char
byte
short
int
long
float
double
void
Größe
1-Bit
16-Bit
8-Bit
16-Bit
32-Bit
64-Bit
32-Bit
64-Bit
-
Minimum
Unicode 0
-128
-215
-231
-263
IEEE 754
IEEE 754
-
Maximum
Unicode 216-1
+128
+215-1
+231-1
+263-1
IEEE 754
IEEE 754
-
Wrapper-Klasse
Boolean
Character
Byte
Short
Integer
Long
Float
Double
Void
Den primitiven Typen sind „Wrapper“-Klassen zugeordnet, die ein nicht primitives
Objekt auf dem „Heap“ zur Darstellung des primitiven Typs erzeugen, z.B.:
char zeichen = ‘x‘;
Character zeichenDarstellung = new Character(zeichen)
bzw.
Character zeichenDarstellung = new Character(‘x‘);
Referenztypen
Neben den primitiven Typen gibt es die Referenztypen. Dazu gehören: Objekte der
benutzerdefinierten und aus vom System bereitgestellten Klassen, der Klassen String
und Array (Datenfeld). Weiterhin gibt es die vordefinierte Konstante „null“, die eine
leere Referenz bezeichnet.
„String“ und „Array“ weisen einige Besonderheiten aus:
- Für Strings und Arrays kennt der Compiler Literale, die einen expliziten Aufruf des Operator new
überflüssig machen
- Arrays sind klassenlose Objekte. Sie können ausschließlich vom Compiler erzeugt werden, besitzen
aber keine explizite Klassendefinition. Sie werden dennoch vom Laufzeitsystem wie normale Objeklte
behandelt.
51 vgl. 2.2.1
52 vgl. 2.2.1
53 vgl. 2.2.1
43
Programmieren in Java
- Die Klasse String ist zwar im JDK vorhanden. Der Compiler hat aber Kenntnis über den inneren Aufbau
von Strings und generiert bei Anzeigeoperationen Code, der auf Methoden der Klassen String und
StringBuffer zugreift.
Konstruktoren
Eine „Constructor“-Methode bestimmt, wie ein Objekt initialisiert wird. Konstruktoren
haben immer den gleichen Namen wie die Klasse und besitzen keine „return“Anweisung. Java erledigt beim Aufruf eines Konstruktors folgende Aufgaben:
- Speicherzuweisung für das Objekt
- Initialisieung der Instanzvariablen des Objekts auf ihre Anfangswerte oder einen Default-Wert (0 bei
Zahlen, „null“ bei Objekten, „false“ bei booleschen Operatoren.
- Aufruf der Konstruktor-Methode der Klasse
Gewöhnlich stellt man „explizit“ einen „default“-Konstruktor zur Verfügung. Dieser
parameterlose Konstruktor überlagert den implizit bereitgestellten „default“Konstruktor. Er wird dann bei allen parameterlosen Instanzierungen verwendet.
Konstruktoren können aber auch – wie normale Dateien – Parameter übergeben
bekommen, z.B.54:
public Rechentafel(int ersterOperand, int zweiterOperand)
{
super(); // Aufruf des Default-Konstruktors der Superklasse
this.ersterOperand = ersterOperand;
this.zweiterOperand = zweiterOperand;
}
super() bestimmt einen Aufruf des „default“-Konstruktors der eindeutig
bestimmten „Superklasse“. „super(...)“ darf nur als erste Anweisung eines
Konstruktors auftreten. Generell bezeichnet das reservierte Wort „super“, die nach
„extends“ benannte Superklasse.
Häufig sind die Parameter Anfangswerte für Instanzvariablen und haben oft den gleichen
Namen wie die entsprechenden Instanzvariablen. In diesen Fällen löst die Verwendung
von bsp. this.ersterOperand
=
ersterOperand;
derartige
Namenskonflikte auf.
Bei „this“ handelt es sich um einen Zeiger, der beim Anlegen eines Objekts
automatisch generiert wird. „this“ ist eine Referenzvariable, die auf das aktuelle
Objekt zeigt und zum Ansprechen der eigenen Methoden und Instanzvariablen dient. Der
„this“-Zeiger ist auch explizit verfügbar und kann wie eine ganz normale
Objektvariable verwendet werden. Er wird als versteckter Parameter an jede nicht
statische Methode übergeben.
Konstruktoren können in Java verkettet aufgerufen werden, d.h. sie können sich
gegenseitig aufrufen. Der aufrufende Konstruktor wird dabei als normale Methode
angesehen, die über this einen weiteren Konstruktor der aktuellen Klasse aufrufen
kann, z.B.:
public Rechentafel()
{
this(0,1);
}
54 pr14102
44
Programmieren in Java
„Freundliche“ Klassen und „freundliche“ Methoden
„Rechentafel“ ist eine „freundliche“ Klasse. Der voreingestellte „Defaultstatus“ einer
Klasse ist immer „freundlich“ und wird dann verwendet, wenn keine Angaben über die
Sichtbarkeit (Spezifizierer, Modifizierer) am Beginn der Klassendefinition vorliegen.
Die freundliche Grundeinstellung aller Klassen bedeutet: Diese Klasse kann von
anderen Klassen nur innerhalb desselben Pakets benutzt werden. Das Paket-Konzept
von Java faßt mehrere Klassen zu einem Paket über die Anweisung „package“
zusammen. Durch die Anweisung „import“ werden einzelne Pakete dann in einem
Programm verfügbar gemacht. Klassen, die ohne „package“-Anweisung definiert
werden, werden vom Compiler in ein Standardpaket gestellt. Die „.java“- und
„.class“-Dateien dieses Pakets befinden sich im aktuellen Verzeichnis oder im
darunterliegenden Verzeichnis.
Mit dem Voranstellen von „public“ vor die Klassendeklaration wird eine Klasse als
„öffentlich“ deklariert.Dies bedeutet: Alle Objekte haben Zugriff auf „public“-Klassen
(nicht nur die des eigenen Pakets).
Der voreingestellte Defaultstaus einer Methode ist immer freundlich und wird immer
dann verwendet, wenn keine explizite Angabe zur Sichtbarkeit am Anfang der
Methodendeklaration vorliegt. Die freundliche Grundeinstellung aller Methoden
bedeutet: Die Methoden können sowohl innerhalb der Klasse als auch innerhalb des
zugehörigen Pakets benutzt werden.
Zugriffsrechte auf Klassen, Variable und Methoden
Es gibt in Java insgesamt 4 Zugriffsrechte:
private
Ohne Schlüsselwort
protected
public
Zugriff nur innerhalb einer Klasse
Zugriff innerhalb eines Pakets
Zugriff innerhalb eines Pakets oder von Subklassen in einem anderen Paket
Zugriff von überall
Mit Voranstellen des Schlüsselworts „public“ können alle Klassen, Variablen /
Konstanten und Methoden für einen beliebigen Zugriff eingerichtet werden. Eine
derartige Möglichkeit, die in etwa der Zugriffsmöglichkeit globaler Variablen in
konventionellen Programmiersprachen entspricht, ist insbesondere bei komplexen
Programm-Systemen gefährlich. Es ist nicht sichergestellt, daß zu jedem Zeitpunkt die
Werte der Instanzvariablen von Objekten bestimmte Bedingungen erfüllen. Möglich ist
bspw. die Anweisung
einRechenobjekt.zweiterOperand = 0;
in der Klasse „Rechentafeltest“. Mit Sicherheit führt diese Anweisung bei der Ausführung
einer Division auf einen Divisionsfehler.
Abhilfe verspricht hier das Prinzip der Datenkapselung (data hiding, data
encapsulation). Instanzvariable werden als „private“ erklärt, d.h.: Zugriff auf diese
Instanzvariable nur innerhalb der Klasse. Von außerhalb kann nur indirekt über das
Aufrufen von Methoden, die als „public“ erklärt sind, auf die Instanzvariablen
zugegriffen werden. Deshalb sollte auch prinzipiell für jede Instanzvariable eine
45
Programmieren in Java
entsprechende „get“- und eine „set“-Methode („hole“ bzw. „setze“) zur Verfügung
gestellt werden, die jeweils „public“ erklärt werden.
Bsp.: Die Klasse „Rechentafel“ nimmt dann folgenden Gestalt an:
class Rechentafel extends Object
{
// Instanzvariable
private int ersterOperand = 0;
private int zweiterOperand = 0;
// Konstruktoren
public Rechentafel()
{
this(0,1);
}
public Rechentafel(int ersterOperand, int zweiterOperand)
{
super();
this.ersterOperand = ersterOperand;
this.zweiterOperand = zweiterOperand;
}
// Operationen
public int holersterOperand()
{
return ersterOperand;
}
public void setzersterOperand(int ersterOperand)
{
this.ersterOperand = ersterOperand;
}
public int holzweiterOperand()
{
return zweiterOperand;
}
public void setzweiterOperand(int zweiterOperand)
{
this.zweiterOperand = zweiterOperand;
}
public int addiere()
{
return ersterOperand + zweiterOperand;
}
public int subtrahiere()
{
return ersterOperand - zweiterOperand;
}
public int multipliziere()
{
return ersterOperand * zweiterOperand;
}
public int dividiere()
{
// ganzzahlige Division
if (zweiterOperand == 0)
{
System.out.println("Fehler: Division durch Null");
System.out.println("Divisor wird auf 1 gesetzt!");
zweiterOperand = 1;
}
46
Programmieren in Java
return ersterOperand / zweiterOperand;
}
}
Das Beispiel zeigt folgende Empfehlungen für die Vergabemöglichkeit von Zugriffsrechten:
Klassen
Instanzvariable
Instanzkonstanten
Instanzmethoden
public
private
public
public, falls ein Zugriff von außen erforderlich und sinnvoll ist.
private, falls es sich um klaseninterne Hilfsmethoden handelt.
Analoge Überlegungen gelten auch für Klassenvariable und -methoden.
Klassenvariable und Klassenmethoden
Die Klasse „Rechentafel" 55 wurde zusätzlich erweitert um
// Klassenvariable
static int anzRechenobjekte = 0;
Das reservierte Wort static macht Variable und Methoden (wie bspw. main()) zu
Klassenvariablen bzw. Klassenmethoden.
Klassenvariable werden in der Klasse definiert und gespeichert. Deshalb wirken sich
ihre Werte auf die Klasse und all ihre Instanzen aus. Jede Instanz hat Zugang zu der
Klassenvariablen, jedoch gibt es für alle Instanzen dieser Variablen nur einen Wert.
Durch Änderung des Werts ändern sich die Werte aller Instanzen der betreffenden
Klasse.
Die folgende Erweiterung des Konstruktors der Klasse „Rechentafel“ berücksichtigt das
spezielle Verhalten von Klassenvariablen:
public Rechentafel(int ersterOperand, int zweiterOperand)
{
super();
this.ersterOperand = ersterOperand;
this.zweiterOperand = zweiterOperand;
nummer = ++anzRechenobjekte;
}
„nummer“ ist eine Instanzvariable, „anzRechenObjekte“ ist die Klassenvariable.
Das reservierte Wort final macht Variablen zu Größen, denen nur einmal bei der
Deklaration ein Wert zugewiesen werden kann, d.h. sie werden dadurch zu Konstanten,
z.B.: public static final double PI = 3.1415926535897932846;
Klassenmethoden wirken sich wie Klassenvariable auf die ganze Klasse, nicht auf
einzelne Instanzen aus. Klassenmethoden sind nützlich zum Zusammenfassen
allgemeiner Methoden an einer Stelle der Klasse. So umfaßt die Math-Klasse
55 pr14103
47
Programmieren in Java
zahlreiche mathematische Funktionen in der Form von Klassenmethoden. Es gibt keine
Instanzen der Klasse Math.
Auch rekurvive Programme benutzen Klassenmethoden, z.B.:
// Berechnung der Fakultaet
public static long fakultaet(int n)
{
if (n < 0)
{
return -1;
}
else if (n == 0)
{
return 1;
}
else
{
return n * fakultaet(n-1);
}
}
Der Aufruf einer Klassenmethode kann im Rahmen der Punktnotation aber auch direkt
erfolgen, z.B.:
long resultat = fakultaet(i);
bzw.
long resultat = Rechentafel.fakultaet(i);
unter der Voraussetzung: Die Klassenmethode „fakultaet“
„Rechentafel“.
befindet
sich
in
der
Klasse
Lokale Variable und Konstanten
Lokale Variable werden innerhalb von Methodendefinitionen deklariert und können nur
dort verwendet werden. Es gibt auch auf Blöcke beschränkte lokale Variablen. Die
Deklaration einer lokalen Variablen gilt in Java als ausführbare Anweisung. Sie darf
überall dort erfolgen, wo eine Anweisung verwendet werden darf. Die Sichtbarkeit einer
lokalen Variablen erstreckt sich von der Deklaration bis zum Ende des umschließenden
Blocks.
Lokale Variablen existieren nur solange im Speicher, wie die Methode oder der Block
existiert. Lokale Variable müssen unbedingt ein Wert zugewiesen bekommen, bevor sie
benutzt werden können. Instanz- und Klassenvariable haben einen typspezifischen
Defaultwert. Auf lokale Variable kann direkt und nicht über die Punktnotation zugegriffen
werden. Lokale Variable können auch nicht als Konstanten56 gesetzt werden.
Beim Bezug einer Variablen in einer Methodendefinition sucht Java zuerst eine
Definition dieser Variablen im aktuellen Bereich, dann durchsucht es die äußeren
Bereiche bis zur Definition der aktuellen Methode. Ist die gesuchte Größe keine lokale
Variable, sucht Java nach einer Definition dieser Variablen als Instanzvariable in der
aktuellen Klasse und zum Schluß in der Superklasse.
Konstanten sind Speicherbereiche mit Werten, die sich nie ändern. In Java können
solche Konstanten ausschließlich für Instanz- und Klassenvariable erstellt werden.
56 Das bedeutet: das Schlüsselwort final ist bei lokalen Variablen nicht erlaubt
48
Programmieren in Java
Konstante bestehen aus einer Variablendeklaration, der das Schlüsselwort final
vorangestellt ist, und ein Anfangswert zugewiesen ist, z.B.: final int LINKS = 0;
Überladen von Methoden
In Java ist es erlaubt, Methoden zu überladen, d.h. innerhalb einer Klasse zwei
unterschiedliche Methoden mit denselben Namen zu definieren. Der Compiler
unterscheidet die verschiedenen Varianten anhand Anzahl und Typisierung der
Parameter. Es ist nicht erlaubt, zwei Methoden mit exakt demselben Namen und
identischer Parameterliste zu definieren. Es werden auch zwei Methoden, die sich nur
durch den Typ ihres Rückgabewerts unterscheiden als gleich angesehen.
Der Compiler kann die Namen in allen drei Fällen unterscheiden, denn er arbeitet mit
der Signatur der Methode. Darunter versteht man ihren internen Namen. Dieser setzt
sich aus dem nach außen sichtbaren Namen und zusätzlich kodierter Information über
Reihenfolge und die Typen der formalen Parameter zusammen.
Überladen kann bedeuten, daß bei Namensgleichheit von Methoden in „Superklasse“
und abgeleiteter Klasse die Methode der abgeleitetem Klasse, die der Superklasse
überdeckt. Es wird aber vorausgesetzt, daß sich die Parameter signifikant
unterscheiden, sonst handelt es sich bei der Konstellation Superklasse – Subklasse um
den Vorgang des Überschreibens. Soll die Originalmethode aufgerufen werden, dann
wird das Schlüsselwort „super“ benutzt. Damit wird der Methodenaufruf in der
Hierarchie nach oben weitergegeben.
Überschreiben einer Oberklassen-Methode: Zum Überschreiben einer Methode wird
eine Methode erstellt, die den gleichen Namen, Ausgabetyp und die gleiche
Parameterliste wie eine Methode der Superklasse besitzt. Da Java die erste
Methodendefinition ausführt, die es findet und die in Namen, Ausgabetyp und
Parameterliste übereinstimmt, wird die ursprüngliche Methodendefinition dadurch
verborgen.
Konstruktor-Methoden können „technisch“ nicht überschrieben werden. Da sie den
gleichen Namen wie die aktuelle Klasse haben, werden Konstruktoren nicht vererbt,
sondern immer neu erstellt. Wird ein Konstruktor einer bestimmten Klasse aufgerufen,
wird gleichzeitig auch der Konstruktor aller Superklassen aktiviert, so daß alle Teile
einer Klasse initialisiert werden. Ein spezielles Überschreiben kann über
super(arg1, arg2, ...) ermöglicht werden.
Die Methode toString() ist eine in Java häufig verwendete Methode.
„toString()“ wird als weitere Instanzmethode der Klasse Rechentafel definiert und
überschreibt die Methode gleichen Namens, die in der Oberklasse Object definiert
ist, z.B.57:
public String toString()
{
return " Rechenobjekt# " + nummer + " Erster Operand: "
+ ersterOperand + " Zweiter Operand: " + zweiterOperand;
}
}
57 pr14103
49
Programmieren in Java
1.4.1.2 Superklassen und Subklassen, Vererbung und Klassenhierarchie
Definitionen
Klassen können sich auf andere Klassen beziehen. Ausgangspunkt ist eine
Superklasse, von der Subklassen (Unter-) abgeleitet sein können. Jede Subklasse erbt
den Zustand (über die Variablen-Deklarationen) und das Verhalten der Superklasse.
Darüber hinaus können Subklassen eigene Variable und Methoden hinzufügen.
Superklassen, Subklassen bilden eine mehrstufige Hierarchie. In Java sind alle Klassen
Ableitungen einer einzigen obersten Klasse – der Klasse Object. Sie stellt die
allgemeinste Klasse in der Hierarchie dar und legt die Verhaltensweisen und Attribute,
die an alle Klassen in der Java-Klassenbibliothek vererbt werden, fest.
Vererbung bedeutet, daß alle Klassen in eine strikte Hierarchie eingeordnet sind und
etwas von übergeordneten Klassen erben. Was kann von übergeordneten Klassen
vererbt werden? Beim Erstellen einer neuen Instanz erhält man ein Exemplar jeder
Variablen, die in der aktuellen Klasse definiert ist. Zusätzlich wird ein Exemplar für jede
Variable bereitgestellt, die sich in den Superklassen der aktuellen Klasse befindet. Bei
der Methodenauswahl haben neue Objekte Zugang zu allen Methodennamen ihrer
Klasse und deren Superklasse. Methodennamen werden dynamisch beim Aufruf einer
Methode gewählt. Das bedeutet: Java prüft zuerst die Klasse des Objekts auf Definition
der betreffenden Methode. Ist sie nicht in der Klasse des Objekts definiert, sucht Java in
der Superklasse dieser Klasse usw. aufwärts in der Hierarchie bis die Definition der
Methode gefunden wird.
Ist in einer Subklasse eine Methode mit gleichem Namen, gleicher Anzahl und gleichem
Typ der Argumente wie in der Superklasse definiert, dann wird die Methodendefinition,
die zuerst (von unten nach oben in der Hierarchie) gefunden wird, ausgeführt. Methoden
der abgeleiteten Klasse überdecken die Methoden der Superklasse (Overriding beim
Überschreiben / Überdefinieren).
Zum Ableiten einer Klasse aus einer bestehenden Klasse ist im Kopf der Klasse mit
Hilfe des Schlüsselworts „extends“ ein Verweis auf die Basisklasse anzugeben.
Dadurch wird bewirkt: Die abgeleitete Klasse erbt alle Eigenschaften der Basisklasse,
d.h. alle Variablen und alle Methoden. Durch Hinzufügen neuer Elemente oder
Überladen der vorhandenen kann die Funktionalität der abgeleiteten Klasse erweitert
werden.
Die Vererbung einer Klasse kann beliebig tief geschachtelt werden. Eine abgeleitete
Klasse erbt dabei jeweils die Eigenschaften der unmittelbaren „Superklasse“, die
ihrerseits die Eigenschaften ihrer unmittelbaren „Superklasse“ erbt.
Superklassen können abstrakte Klassen mit generischem Verhalten sein. Von einer
abstrakten Klasse wird nie eine direkte Instanz benötigt. Sie dient nur zu
Verweiszwecken. Abstrakte Klassen dürfen keine Implementierung einer Methode
enthalten und sind damit auch nicht instanziierbar. Java charakterisiert abstrakte
Klassen mit dem Schlüsselwort abstract. Abstrakte Klassen werden zum Aufbau
einer Klassenhierarchie verwendet, z.B. für die Zusammenfassung von zwei oder mehr
Klassen.
Auch eine abstrakte Methode ist zunächst einmal durch das reservierte Wort
"abstract" erklärt. Eine abstrakte Methode besteht nur aus dem Methodenkopf,
anstelle des Methodenrumpfs (der Methodendefinition) steht nur das "Semikolon", z.B.
public abstract String toString();.
50
Programmieren in Java
Enthält eine Klasse mindestens eine abstrakte Methode, so wird automatisch die
gesamte Klasse zu einer abstrakten Klasse. Abstrakte Klassen enthalten keine
Konstruktoren. Sie können zwar Konstruktoren aufnehmen, allerdings führt jeder explizite
Versuch zur Erzeugung eines Objekts einer abstrakten Klasse zu einer Fehlermeldung.
Abstrakte Methoden stellen eine Schnittstellenbeschreibung dar, die der Programmierer
einer Subklasse zu definieren hat. Ein konkrete Subklasse einer abstrakten Klasse muß
alle abstrakten Methoden der Superklasse(n) implementieren.
Beispiel: Eine Klassenhierarchie für "geometrische" Objekte 58
Das folgende Klassendiagramm zeigt die hierarchische Struktur von Klassen, die
"geometrische Objekte" beschreiben. All diesen geometrischen Objekten ist
gemeinsam: Sie werden durch einen Bezugspunkt (x,y) fixiert. DieGemeinsamkeiten
sind in der abstrakten Klasse "Geomobjekt" zusammengefaßt.
GeomObjekt
private double x;
private double y;
public double holeX();
public void setzeX(double x);
public double holeY();
public void setzeY(double y);
Kreis
Rechteck
private double r;
private double breite;
private double hoehe;
public double holeR();
public void setzeR(double r);
public double umfang();
public double flaeche();
public String toString();
public double holeBreite();
public void setzeBreite(double breite)
public double holeHoehe(double hoehe);
public void setzeHoehe(double hoehe);
public double umfang();
public double flaeche();
public String toString();
Ausgangspunkt ist die abstrakte Klasse "GeomObjekt". Dort ist der allen Objekten
zugeordnete Bezugspunkt fixiert. Subklassen (z.B. Kreis und Rechteck) erben diesen
Bezugspunkt und die zum Zugriff auf ihn bereitgestellte Methoden. Außerdem führen die
Subklassen die nötigen Spezialisierungen auf das jeweilige geometrische Objekt durch.
58 vgl. pr14131
51
Programmieren in Java
import java.lang.*;
public abstract class GeomObjekt extends Object
{
// Instanzvariable
private double x, y;
// Bezugspunkt geom. Objekte
// Instanzmethoden
public double holeX()
{
return x;
}
public void setzeX(double x)
{
this.x = x;
}
public double holeY()
{
return y;
}
public void setzeY(double y)
{
this.y = y;
}
// Abstrakte Methoden
public abstract String toString();
public abstract double umfang();
public abstract double flaeche();
}
import java.lang.*;
public class Kreis extends GeomObjekt
{
// Instanzvariable
private double r;
// Radius
// Konstruktoren
public Kreis()
{
this(0.0,0.0,1.0);
}
public Kreis(double r)
{
this(0.0,0.0,r);
}
public Kreis(double x, double y, double r)
{
super();
super.setzeX(x);
super.setzeY(y);
this.setzeR(r);
}
// Instanzmethoden
public double holeR()
{
return r;
}
public void setzeR(double r)
{
this.r = (r >= 0.0 ? r : -r);
52
Programmieren in Java
}
public double umfang()
{
double u = 2 * r * Math.PI;
return u;
}
public double flaeche()
{
return r * r * Math.PI;
}
public String toString()
{
return "Kreis: (" + holeX() + ", " + holeY() +
") " + "r = " + r;
}
}
import java.lang.*;
public class Rechteck extends GeomObjekt
{
// Instanzvariable
private double breite, hoehe;
// Konstruktoren
public Rechteck()
{
this(0.0,0.0,1.0,1.0);
}
public Rechteck(double breite, double hoehe)
{
this(0.0,0.0,breite,hoehe);
}
public Rechteck(double x, double y, double breite, double hoehe)
{
super();
super.setzeX(x);
super.setzeY(y);
this.setzeBreite(breite);
this.setzeHoehe(hoehe);
}
// Instanzmethoden
public double holeBreite()
{
return breite;
}
public void setzeBreite(double breite)
{
this.breite = ( breite >= 0 ? breite : -breite);
}
public double holeHoehe()
{
return hoehe;
}
public void setzeHoehe(double hoehe)
{
this.hoehe = ( hoehe >= 0 ? hoehe : -hoehe);
}
public double umfang()
{
53
Programmieren in Java
return 2 * breite + 2 * hoehe;
}
public double flaeche()
{
return breite * hoehe;
}
public String toString()
{
return "Rechteck: (" + holeX() + ", " + holeY()
+ ") " + "breite = " + breite +
" hoehe = " + hoehe;
}
}
Die Klasse "Test" überprüft die vorliegende Klassenhierarchie auf Funktionsfähigkeit:
import java.lang.*;
public class Test extends Object
{
public static void main(String args[])
{
// Erzeuge einen Kreis
Kreis k1 = new Kreis();
System.out.println(k1.toString());
double umfang;
umfang = k1.umfang();
System.out.println("Umfang = " + umfang);
System.out.println("Flaeche = " + k1.flaeche());
// Erzeuge einen anderen Kreises
Kreis k2 = new Kreis(1.0,1.0,2.0);
System.out.println(k2.toString());
umfang = k2.umfang();
System.out.println("Umfang = " + umfang);
System.out.println("Flaeche = " + k2.flaeche());
// Erzeuge einen dritten Kreis
Kreis k3 = new Kreis(-2.0);
System.out.println(k3.toString());
umfang = k3.umfang();
System.out.println("Umfang = " + umfang);
System.out.println("Flaeche = " + k3.flaeche());
// Erzeugen eines Rechteck
Rechteck r1 = new Rechteck(2.0,2.0);
System.out.println(r1.toString());
umfang = r1.umfang();
System.out.println("Umfang = " + umfang);
System.out.println("Flaeche = " + r1.flaeche());
// Erzeugen eines weiteren Rechtecks
Rechteck r2 = new Rechteck(2.0,2.0,2.0,3.0);
System.out.println(r2.toString());
umfang = r2.umfang();
System.out.println("Umfang = " + umfang);
System.out.println("Flaeche = " + r2.flaeche());
}
}
54
Programmieren in Java
1.4.1.3 Referenzen und Referenztypen
Referenzen
Java erlaubt die Definition von Klassen, aus denen Objekte erzeugt werden können.
Objekte können als Referenztypen behandelt werden, die von Variablen angelegt und
verwendet werden.
Beim Zuweisen von Variablen für Objekte bzw. beim Weiterreichen von Objekten als
Argumente an Methoden werden Referenzen auf diese Objekte festgelegt.
Bsp.: Referenzen auf Objekte in der Klasse Rechentafel bzw. Rechentafeltest
System.out.println("Referenzen auf Objekte");
// Referenzen auf Objekte
Rechentafel rechenObj1 = new Rechentafel();
rechenObj1.ersterOperand = 200;
rechenObj1.zweiterOperand = 300;
Rechentafel rechenObj3 = new Rechentafel(300,200);
System.out.println("Operanden von Instanz 1: " +
rechenObj1.ersterOperand + ", " + rechenObj1.zweiterOperand);
System.out.println("Operanden von Instanz 2 vor Zuweisung: " +
rechenObj3.ersterOperand + ", " + rechenObj3.zweiterOperand);
rechenObj3 = rechenObj1;
System.out.println("Operanden von Instanz 2 nach Zuweisung: " +
rechenObj3.ersterOperand + ", " + rechenObj3.zweiterOperand);
// Test auf Gleichheit
if (rechenObj1 == rechenObj3)
{
System.out.println("Die beiden Objekte sind tatsaechlich gleich");
}
Der Test mit „==“59 bestätigt: Es handelt sich jetzt (nach der Zuweisung) um das gleiche
Objekt, d.h.: die gleiche Referenz.
Die „null“-Referenz. In Java gibt es das spezielle Literal null. Es zeigt an, daß eine
Referenz auf kein Objekt verweist. Der Wert ist nur für Referenzen vorgesehen und kann
nicht in einen primitiven Typ umgewandelt werden. Die „null“-Referenz ist typenlos, d.h.:
Sie kann jedem Objekt zugewiesen werden, z.B.:
Point p = null;
String s = null;
Da sich hinter „null“ kein Objekt verbirgt, kann darüber auch kein Objekt aufgerufen
werden.
Java verfügt über ein automatisches Speichermanagement. Deshalb braucht der JavaProgrammierer sich nicht um die Rückgabe von Speicher zu kümmern, der von
Referenzvariablen belegt ist. Ein mit niederer Priorität im Hintergrund arbeitender
„Garbage Collector“ sucht ständig nach Objekten, die nicht mehr referenziert werden,
um den belegten Speicher freizugeben.
Finalizer-Methoden. Sie werden aufgerufen, kurz bevor das Objekt im Papierkob landet
und sein Speicher freigegeben wird. Zum Erstellen einer Finalizer-Methode dient der
folgende Eintrag in der Klassendefinition void finalize(){ ... }. Im Körper
59 Der Operator „==“ überprüft die Gleichheit der Referenzen.
55
Programmieren in Java
dieser Methode können alle möglichen Reinigungsprozeduren stehen, die das Objekt
ausführen soll. Der Aufruf der Methode finalize() bewirkt nicht unmittelbar die
Ablage im Papierkorb. Nur durch das Entfernen aller Referenzen auf das Objekt wird
das Objekt zum Löschen markiert.
Referenztypen
Java kennt zwei Arten von Typen: einfache Typen und Referenztypen. Einfache Typen
sind: boolean, byte, short, int, long, char, float und double. Klassen und
Arrays sind Referenztypen. Ein Referenzwert enthält eine Referenz auf die Daten eines
Objekts bzw. Arrays. Referenztypen müssen explizit mit Hilfe des Operators new
erzeugt werden.
1.4.1.4 Konvertieren von Objekten und Primitivtypen
„Casting“ ist ein Mechanismus zum Konvertieren des Werts eines Objekts oder eines
primitiven Typs in einen anderen Typ. „Casting“ ergibt ein neues Objekt oder einen
neuen Wert und wirkt sich nicht auf das ursprüngliche Objekt bzw. den ursprünglichen
Wert aus.
Konvertieren von Primitivtypen
Ist der angestrebte Typ „größer“ als der zu konvertierende Typ, ist häufig kein explizites
„Casting“ nötig. Ein „Byte“ oder ein Zeichen kann automatisch z.B. als „int“, ein „int“
als „long“, ein „int“ als „float“ behandelt werden. Es gehen beim Konvertieren des
Werts keine Informationen verloren, weil der größere Typ mehr Genauigkeit bietet als
der kleinere.
Ist der angestrebte Typ „kleiner“ als der zu konvertierende Typ, ist „Casting“ nötig.
Explizites Casting sieht so aus: (typname) wert
„typname“ ist der Name des Typs, auf den konvertiert wird, „wert“ ist ein Ausdruck,
der den zu konvertierenden Wert ergibt, z.B.: „(int) x/y;“. Dieser Ausdruck teilt x
durch y und wandelt dann das Ergebnis in „int“ um. Da Casting eine höhere Priorität
als Arithmetik hat, müssen die Klammern angegeben werden.
Konvertieren von Objekten
Mit einer Einschränkung können auch Klasseninstanzen in Instanzen anderer Klassen
konvertiert werden. Die betroffenen Klassen müssen durch Vererbung miteinander
verbunden sein, d.h.: Ein Objekt kann nur in eine Instanz der Sub- oder Superklassen
konvertiert werden, nicht aber in eine beliebige Klasse.
Durch Konvertieren eines Objekts in eine Instanz der Superklasse gehen Informationen,
die die Subklasse bereitgestellt hat, verloren. Spezifisches Casting ist erforderlich:
(klassename) objekt.
„klassename“ ist der Name der Klasse, in die das Objekt konvertiert werden soll.
„objekt“ ist eine Referenz auf das zu konvertierende Objekt.
56
Programmieren in Java
Konvertieren von Primitivtypen in Objekte und umgekehrt
Primitive Typen und Objekte sind in Java zwei völlig verschiedene Dinge. Konvertierung
und automatisches Casting sind nicht möglich. Allerdings enthält das Paket „java.lang“
mehrere Subklassen, die je einem primitiven Datentyp entsprechen: Integer, Float,
Boolean usw. Mit den in diesen Klassen definierten Klassenmethoden kann mit Hilfe
von new
für alle Primitivtypen eine „Art Objekt“ erstellt werden, z.B.: „Integer
intObjekt = new Integer(13);“. Mit intValue() wird ein primitiver „int“Wert aus einem Integer-Objekt extrahiert, z.B.: int
ganzeZahl
=
intObjekt.intValue();
1.4.1.5 Manipulieren von Objekten
Vergleichen von Objekten
Die relationalen Operatoren funktionieren in der Regel nur mit Primitivtypen, nicht mit
Objekten. Die Ausnahme zu dieser Regel bilden die Operatoren „==“ und „!=“. Die
Operatoren können für Objekte benutzt werden und prüfen, ob sich zwei Operanden auf
genau das gleiche Objekt beziehen.
Java besitzt kein Konzept für das Überladen (Overloading) von Operatoren. Zum
Vergleichen von Instanzen eigendefinierter Klassen müssen Sondermethoden in die
Klasse einbezogen werden.
Bsp.: Gleichheitstest mit Objekten der „String“-Klasse60.
Die String-Klasse definiert die Methode equals(), die jeden Zeichen der
Zeichenkette prüft und „true“ ausgibt, falls die beiden Zeichenketten den
gleichen Wert haben. Nach dem Operator „==“ sind die beiden „String“-Objekte
nicht gleich, weil sie zwar den gleichen Inhalt haben, aber nicht die gleichen
Objekte sind.
// Besonderheiten im Zusammenhang mit String-Objekten
final class StringManipulationen
{
public static void main(String args[])
{
String a = "test";
String b = "test";
String c = new String("test");
String d = new String("test");
// Díe folgenden Anweisungen geben wahr oder falsch
// an die Konsole aus
System.out.println("a == b " + (a==b));
System.out.println("c == d " + (c==d));
System.out.println("a == c " + (a==c));
System.out.println("a == \"test\" " + (a=="test"));
System.out.println("c == \"test\" " + (c=="test"));
System.out.println();
60 pr14110
57
//
//
//
//
//
true
false
false
true
false
Programmieren in Java
System.out.println("a.equals(b) " + a.equals(b));
System.out.println("c.equals(d) " + c.equals(d));
System.out.println("a.equals(c) " + a.equals(c));
System.out.println("a.equals(\"test\") " + a.equals("test"));
System.out.println("c.equals(\"test\") " + c.equals("test"));
//
//
//
//
//
true
true
true
true
true
}
}
Kopieren von Objekten
Die clone()-Methode erzeugt ein Duplikat als Kopie eines Objekts. Damit ein Objekt
geklont werden kann, muß es die „Cloneable“-Schnittstelle unterstützen. Die
Cloneable-Schnittstelle im Paket java.lang hat selbst keine Methoden, sie dient
lediglich als Indikator dafür, daß ein Objekt geklont werden kann. Das Format für die
clone()-Methode
ist:
protected
Object
clone()
throws
CloneNotSupportedException
Bsp.: Die Klasse Rechentafel erhält eine clone()-Methode zum Klonen von
Rechentafel-Objekten61.
public class Rechentafel implements Cloneable
{
// Instanzvariable
private int ersterOperand = 0;
private int zweiterOperand = 0;
private int nummer;
// Identifikationsnummer
// Klassenvariable
static int anzRechenobjekte = 0;
// Konstruktoren
Rechentafel()
{
this(0,1);
}
Rechentafel(int ersterOperand, int zweiterOperand)
{
super();
this.ersterOperand = ersterOperand;
this.zweiterOperand = zweiterOperand;
nummer = ++anzRechenobjekte;
}
// Kopierkonstruktor
Rechentafel(Rechentafel rechenObjekt)
{
this(rechenObjekt.ersterOperand,rechenObjekt.zweiterOperand);
}
// Operationen
public int holersterOperand()
{
return ersterOperand;
}
public void setzersterOperand(int ersterOperand)
{
this.ersterOperand = ersterOperand;
}
public int holzweiterOperand()
61 pr14202
58
Programmieren in Java
{
return zweiterOperand;
}
public void setzweiterOperand(int zweiterOperand)
{
this.zweiterOperand = zweiterOperand;
}
public int addiere()
{
return ersterOperand + zweiterOperand;
}
public int subtrahiere()
{
return ersterOperand - zweiterOperand;
}
public int multipliziere()
{
return ersterOperand * zweiterOperand;
}
public int dividiere()
{
// ganzzahlige Division
try {
/* Innerhalb des try-Blocks werden diejenigen
kritischen Aktionen durchgefuehrt, die
Ausnahmen erzeugen koennen
*/
return (ersterOperand / zweiterOperand);
}
catch(java.lang.ArithmeticException a)
{
System.out.println(" Fehler: Division durch Null");
System.out.println("Divisor wird auf 1 gesetzt!");
zweiterOperand = 1;
return (ersterOperand / zweiterOperand);
}
}
public String toString()
{
return "Rechenobjekt# " + nummer + " Erster Operand: "
+ ersterOperand + " Zweiter Operand: " + zweiterOperand;
}
public Object clone()
{
Object o = null;
try {
o = super.clone();
}
catch(CloneNotSupportedException e)
{
System.out.println("Objekt kann nicht geklont werden");
}
return o;
}
}
Diese Methode kann dann über ein in der Klasse Rechentafeltest instanziertes
Objekt der Klasse Rechentafel aufgerufen werden:
59
Programmieren in Java
import java.lang.*;
class Rechentafeltest extends Object
{
public static void main(String argv[])
{
// Erzeugen Instanz der Klasse Rechentafel
Rechentafel erstesRechenobjekt = new Rechentafel(3,2);
System.out.println("Eine Instanz der Klasse " +
erstesRechenobjekt.getClass().getName() +
" wurde erzeugt.");
System.out.println(erstesRechenobjekt.toString());
// Klonen eines Rechenobjekts
System.out.println("Klonen des Objekts");
Rechentafel zweitesRechenobjekt = (Rechentafel)
erstesRechenobjekt.clone();
System.out.println(zweitesRechenobjekt.toString());
if (erstesRechenobjekt == zweitesRechenobjekt)
System.out.println("Erstes Objekt == Zweites Objekt");
else
System.out.println("Erstes Objekt != Zweites Objekt");
// Manipulation
System.out.println(
"Aenderungen am Original bleiben unberuecksichtigt");
erstesRechenobjekt.setzersterOperand(7);
erstesRechenobjekt.setzweiterOperand(5);
System.out.println(erstesRechenobjekt.toString());
System.out.println(zweitesRechenobjekt.toString());
// Zuweisung
System.out.println("Zuweisen des Objekts");
Rechentafel drittesRechenobjekt;
drittesRechenobjekt = erstesRechenobjekt;
System.out.println(drittesRechenobjekt.toString());
if (erstesRechenobjekt == drittesRechenobjekt)
System.out.println("Erstes Objekt == Drittes Objekt");
else
System.out.println("Erstes Objekt != Drittes Objekt");
// Manipulieren
System.out.println("Aenderungen beziehen sich auch auf die Kopie");
erstesRechenobjekt.setzersterOperand(2);
erstesRechenobjekt.setzweiterOperand(3);
System.out.println(erstesRechenobjekt.toString());
System.out.println(drittesRechenobjekt.toString());
// Kopierkonstruktor
System.out.println("Arbeiten mit einem Kopierkonstruktor");
Rechentafel viertesRechenobjekt = new Rechentafel(erstesRechenobjekt);
System.out.println(viertesRechenobjekt.toString());
}
}
Der Test zeigt: Das geklonte Objekt enthält die gleichen Werte.
Bestimmen der Klasse eines Objekts
Die Klasse Class stellt über die Methode getName() den Namen einer Klasse zur
Verfügung: public String getName()
60
Programmieren in Java
Bsp.: Der folgende Aufruf
Rechentafel einRechenobjekt = new
Rechentafel(3,2);
System.out.print("Eine Instanz der Klasse ");
einRechenobjekt.zeigeKlasse();
betimmt den Namen der Klasse von „einRechenobjekt“. Die Methode „zeigeKlasse“
enthält die Methode getName() der Klasse Class. Das Objekt, dessen
Klassenzugehörigkeit bestimmt werden soll ruft die Methode getClass() der Klasse
Object auf. Das Ergebnis dieser Methode ist ein Class-Objekt, das die Methode
getName() kennt. „getName()“ gibt die Zeichenkette aus.
void zeigeKlasse()
{
System.out.print(this.getClass().getName());
}
Ein anderen Test bietet der Operator „instanceof“. Er hat zwei Operanden: Ein
Objekt links und den Namen der Klasse rechts. Der Ausdruck gibt true oder false
aus, je nach dem, ob das Objekt eine Instanz der benannten Klasse ist, z.B.:
if (!(anderesObj instanceof Rechentafel)) return false;
der Operator instanceof kann auch als Schnittstelle benutzt werden. Falls ein Objekt
eine Schnittstelle implementiert, gibt instanceof mit dem Schnittstellennamen auf
der rechten Seite true aus.
1.4.1.6 Innere (lokale) und anonyme Klassen
Bisher gabb es nur Klassen, die entweder in Paketen organisiert waren oder sich in
einem Verzeichnis befanden. Diese Form von Klassen heißen „Top-Level“-Klassen.
Klassen können aber auch in andere Klassen aufgenommen werden. Das nennt sich
dann „innere Klassen“.
class Aussen
{
...
class Innen
{
...
}
...
}
Lokale Klassen
Seit JDK 1.1 kann man innerhalb einer bestehenden Klasse z.B. X eine neue Klasse
z.B. Y definieren62. Diese Klasse ist nur innerhalb von X sichtbar. Objekte von Y können
damit nur aus X erzeugt werden. Y kann aber auf alle Instanzmerkmale von X zugreifen.
62 Im JDK wird das Konzept als Inner Classes bezeichnet
61
Programmieren in Java
Bei der Instanzierung wird (neben einem impliziten this-Zeiger) ein weiterer Verweis
auf die erzeugende Instanz der umschließenden Klasse übergeben, der es ermöglicht,
auf sie zuzugreifen.
Die Anwendung lokaler Klassen für die Ereignisbehandlung besteht darin, mit ihrer Hilfe
die benötigten EventListener zu implementieren. Dazu wird das GUI-Objekt, das einen
Event-Handler benötigt, eine lokale Klasse definiert und aus einer passenden AdapterKlasse abgeleitet. Es braucht nicht mehr das gesamte Interface implementiert zu
werden, ( denn die Methodenrümpfe werden ja aus der Adapterklasse geerbt,) sondern
lediglich die tatsächlich benötigetn Methoden.
Anonyme Klassen
Eine Variante der lokalen Klassen sind anonyme Klassen. Sie werden ebenfalls lokal zu
einer anderen Klasse erzeugt, kommen aber ohne Klassennamen aus. Dazu werden sie
bei der Übergabe eines Objekts an eine Methode oder als Rückgabewert einer
Methode innerhalb einer einzigen Anweisung definiert und instanziert. Damit eine
anonyme Klasse überhaupt irgendeiner sinnvollen Aufgabe zugeführt werden kann, muß
sie aus einer anderen Klasse abgeleitet sein oder ein bestehendes Interface
implementieren.
Adapterklassen
Eine Adapterklasse implementiert ein vorgegebenes Interface mit leeren
Methodenrümpfen. Adapterklassen können verwendet werden, wenn aus einem
Interface lediglich ein Teil der Methoden benötigt wird, der Rest aber uninteressant ist. In
diesem Fall leitet man eine neue Klasse aus der Adapterklasse ab, anstatt das
zugehörige Interface zu implementieren und überlagert die benötigten Methoden. Alle
übrigen Methoden des Interfaces werden von der Basisklasse zur Verfügung gestellt.
Zu jedem der Low-Level-Ereignisempfänger stellt java.awt.event eine passende
Adapterklasse zur Verfügung.
1.4.1.7 Schnittstellen und Pakete
Schnittstellen
Definition
Eine Schnittstelle ist in Java eine Sammlung von Methodennamen ohne konkrete
Definition. Klassen haben die Möglichkeit zur Definition eines Objekts, Schnittstellen
können lediglich ein paar abstrakte Methoden und Konstanten (finale Daten)
definieren63.
Schnittstellen bilden in Java den Ersatz für Mehrfachvererbung. Eine Klasse kann
demnach nur eine Superklasse, jedoch dafür mehrere Schnittstellen haben.
63 Angaben zur Implementierung sind nicht möglich
62
Programmieren in Java
Ein Schnittstelle („Interface“) ist eine besondere Form der Klasse, die ausschließlich
abstrakte Methoden und Konstanten enthält. Anstelle von class dient zur Definition
eines „Interface“ das Schlüsselwort „interface“.
"Interfaces" werden formal wie Klassen definiert, z.B.:
public interface meineSchnittstelle
{
// Abstrakte Methoden
}
bzw.
public interface meineSpezialschnittstelle extends meineSchnittstelle
{
// Abstrakte Methoden
}
"Interfaces" können benutzt werden, indem sie in einer Klasse implementiert werden,
z.B.:
public class MeineKlasse extends Object implements meineSchnittstelle
{
/* Normale Klassendefinition + Methoden aus meineSchnittstelle */
}
Eine Klasse kann mehrere Schnittstellen implementieren, z.B.
public class MeineKlasse extends Object
implements meineSchnittstelle1, meineSchnittstelle2
{
/* Normale Klassendefinition + Methoden aus meinerSchnittstelle1
und meinerSchnittstelle2 */
}
Verwendung
Bei der Vererbung von Klassen spricht man von Ableitung, bei Interfaces nennt man es
Implementierung. Durch Implementieren einer Schnittstelle verpflichtet sich die Klasse,
alle Methoden, die im Interface definiert sind, zu implementieren. Die Implementierung
eines Interface wird durch das Schlüsselwort „implements“ bei der Klassendefinition
angezeigt.
Bsp.: Natürliche Ordnungsbeziehungen sind in Java über das Interface Comparable
eingefangen:
/**************** Comparable.java *****************************
/** Das Interface deklariert eine Methode anhand der sich das
* das aufgerufene Objekt mit dem uebergebenen vergleicht.
* Fuer jeden vom Objekt abgeleiteten Datentyp muss eine solche
* Vergleichsklasse implementiert werden.
* Die Methode erzeugt eine Fehlermeldung, wenn "a" ein Objekt
* einer anderen Klasse als dieses Objekt ist.
*
* int compareTo(Comparable a)
*
liefert
0, wenn this == 0
63
Programmieren in Java
*
liefert < 0, wenn this < a
*
liefert > 0, wenn this > a
*
*/
public interface Comparable
{
public int compareTo(Comparable a);
}
Zahlreiche von Java bereitgestellte Referenztypen (Klassen) haben dieses Interface
implementiert. Deshalb sortiert die nachfolgenden Sortierroutine (Sort) String- und
Zahlen-Objekte, wie der Aufrufe aus SortTest beweisen.
public class Sort
{
public void sort(Comparable x[])
{
bubbleSort(x);
}
public void bubbleSort(Comparable x[])
{
for (int i = 0; i < x.length; i++)
{
for (int j = i + 1; j < x.length; j++)
{
if (x[i].compareTo(x[j]) > 0)
{
Comparable temp = x[i];
x[i] = x[j];
x[j] = temp;
}
}
}
}
}
public class SortTest
{
public static void main(String args[])
{
String sa[] = {
"Juergen","Christian","Bernd","Werner","Uwe",
"Erich","Kurt","Karl","Emil","Ernst"
};
// Ausgabe des noch nicht sortierten x
System.out.println("Vor dem Sortieren:");
for (int i = 0; i < sa.length; i++)
{
System.out.println("sa["+i+"] = " + sa[i]);
}
// Aufruf der Sortieroutine
Sort s = new Sort();
s.sort(sa);
// Gib das sortierte Feld x aus
System.out.println();
System.out.println("Nach dem Sortieren:");
for (int i = 0; i < sa.length; i++)
64
Programmieren in Java
{
System.out.println("sa["+i+"] = " + sa[i]);
}
System.out.println();
int za[] = { 50, 70, 80, 10, 20, 30, 1, 2, 3, 99, 12, 11, 13};
Integer o[] = new Integer[za.length];
for (int i = 0; i < za.length; i++)
{
o[i] = new Integer(za[i]);
}
// Ausgabe des noch nicht sortierten za
System.out.println("Vor dem Sortieren:");
for (int i = 0; i < za.length; i++)
{
System.out.println("za["+i+"] = " + za[i]);
}
// Aufruf der Sortieroutine
Sort zs = new Sort();
zs.sort(o);
// Gib das sortierte Feld x aus
for (int i = 0; i < za.length; i++)
{
za[i] = ((Integer) o[i]).intValue();;
}
System.out.println();
System.out.println("Nach dem Sortieren:");
for (int i = 0; i < za.length; i++)
{
System.out.println("za["+i+"] = " + za[i]);
}
}
}
Konstanten. Neben abstrakten Methoden können Interfaces auch Konstanten
(Variablen mit dem Attributen static und final) enthalten. Wenn eine Klasse ein
solches Interface implementiert, erbt es gleichzeitig auch alle seine Konstanten. Ein
Interface kann ausschließlich Konstanten enthalten.
Pakete
Definition:
Pakete sind in Java Zusammenfassungen von Klassen und Schnittstellen. Sie
entsprechen in Java den Bibliotheken anderer Programmiersprachen. Pakete
ermöglichen, daß modulare Klassengruppen nur verfügbar sind, wenn sie gebraucht
werden. Potentielle Konflikte zwischen Klassenamen in unterschiedlichen
Klassengruppen können dadurch vermieden werden.
Für
Methoden
und
Variablen
innerhalb
von
Paketen
besteht
eine
Zugriffsschutzschablone. Jedes Paket ist gegenüber anderen, nicht zum Paket
zählenden Klassen abgeschirmt. Klassen, Methoden oder Variablen sind nur sichtbar
für Klassen im gleichen Paket. Klassen, die ohne „package“ Anweisung definiert sind,
werden vom Compiler in ein „Standardpaket“ gestellt. Voraussetzung dafür ist: Die
„.java“- und „.class“-Dateien dieses Pakets befinden sich im aktuellen Verzeichnis (oder
in einen darunter liegenden Klassenverzeichnis).
65
Programmieren in Java
Die Java-Klassenbibliothek64 von Java 1.0 enthält folgende Pakete:
- java.lang: Klassen, die unmittelbar zur Sprache gehören. Das Paket umfaßt u.a.
die Klassen Object, String, System, außerdem die Sonderklassen für die
Primitivtypen (Integer, Character, Float, etc.)
Object
Aus dieser Klasse leiten sich alle weiteren Klassen ab. Ohne explizite Angabe der Klasse,
die eine neue Klasse erweitern soll, erweitert die neue Klasse die Object-Klasse. Die Klasse
Object ist die Basis-Klasse jeder anderen Klasse in Java. Sie definiert Methoden, die von
allen Klasse in Java unterstützt werden.
Class
Für jede in Java definierte Klasse gibt es eine Instanz von Class, die diese Klasse
beschreibt
String
Enthält Methoden zur Manipulation von Java-Zeichenketten
StringBuffer
Dient zum Erstellen von Java-Zeichenketten
Thread
Stellt einen Ausführungs-Thread in einem Java-Programm dar. Jedes Programm kann
mehrere Threads laufen lassen
ThreadGroup
Ermöglicht die Verknüpfung von Threads untereinander. Einige Thread-Operationen
können nur von Threads aus der gleichen ThreadGroup ausgeführt werden.
Throwable
Ist die Basisklasse für Ausnahmen. Jedes Objekt, das mit der "catch"-Anweisung
gefangen oder mit der "throw"-Anweisung verworfen wird, muß eine Subklasse von
Throwable sein.
System
Stellt spezielle Utilities auf Systemebene zur Verfügung
Runtime
Enthält eine Vielzahl gleicher Funktionen wie System, behandelt aber auch das Laufen
externer Programme
Process
Stellt ein externes Programm dar, das von einem Runtime-Objekt gestartet wurde.
Math
Stellt eine Reihe mathematischer Funktionen zur verfügung
Number
Ist die Basisklasse für Double,Float, Integer und Long (Objeckt-Wrapper)
Character
Ist ein Objekt-Wrapper für den datentyp char und enthält eine Reihe nützlicher
zeichenorientierter Operationen
Boolean
Ist ein Objekt-Wrapper für den Datentyp boolean
ClassLoader
Ermöglicht der Laufzeit-Umgebung von Java neue Klassen hinzuzufügen
SecurityManager Legt die Sicherheits-Restriktionen der aktuellen Laufzeitumgebung fest. Viele Java-Klassen
benutzen den Security-Manager zur Sicherstellung, daß eine Operation auch tatsächlich
genehmigt ist.
Compiler
Ermöglicht, falls vorhanden, den Zugriff auf den "just-in-time" Compiler
Abb.: Klassen des java.lang-Pakets
Zusätzlich enthält das java.lang-Paket noch zwei Schnittstellen:
Cloneable
Runnable
Muß von einem anderen Objekt implementiert werden, das dann geklont oder kopiert
werden kann
Wird zusammen mit der Thread-Klasse benutzt, um die aufgerufene Methode zu
definieren, wenn ein Thread gestartet wird.
Abb.: Schnittstellen im java.lang-Paket
-
java.util
-
java.io: Klassen zum Lesen und Schreiben von Datenströmen und zum
Handhaben von Dateien.
64 Die Klassenbibliothek des JDK befindet sich in einem Paket mit dem namen „java“.
66
Programmieren in Java
-
java.net: Klassen zur Netzunterstützung, z.B. socket und URL (eine Klasse zum
Darstellen von Referenzen auf Dokumente im World Wide Web).
-
java.awt (Abstract Windowing Toolkit): Klassen zum Implementieren einer
grafischen Benutzeroberfläche. Das Paket enthält auch eine Klasse für Grafik
(java.awt.Graphics) und Bildverarbeitung (java.awt.Image).
-
java.applet: Klassen zum Implementieren von Applets, z.B. die Klasse Applet.
Die Java Version 1.1 hat die Klassenbibliothek umfassend erweitert65:
Paket
Java.applet
Java.awt
Java.awt.datatranfer
Java.awt.event
Java.awt.image
Java.beans
Java.io
Java.lang
Java.lang.reflect
Java.math
Java.net
Java.rmi
Java.rmi.dgc
Java.rmi.registry
Java.rmi.server
Java.security
Java.security.aci
Java.security.interfaces
Java.sql
Java.util
Java.util.zip
Abb.: Klassenbibliothek der Java-Version 1.1
Bedeutung
Applets
Abstract Window Toolkit
ClipBoard-Funktionalität (Copy / Paste)
AWT Event-handling
Bildanzeige
Java Beans
Ein- und Ausgabe, Streams
Elementare Sprachunterstützung
Introspektion, besserer Zugriff auf Klassen durch
Debugger und Inspektoren
Netzzugriffe
Remote Method Invocation, Zugriff auf Objekte in
anderen virtuellen Maschinen
RMI Distributed Garbage Collection
Verwaltet Datenbank, die RMI-Verbindungen
koordiniert
RMI-Server
Sicherheit durch digitale Signaturen, Schlüssel
Access Control Lists
Digital Signature Algorithm (DAS-Klassen)
Datenbankzugriff (JDBC)
Diverse Utilities, Datenstrukturen
JAR-Files, Kompression, Prüfsummen
Verwendung
Jede Klasse ist Bestandteil eines Pakets. Der vollständige Name einer Klasse besteht
aus dem Namen des Pakets, danach kommt der ein Punkt, gefolgt von dem
eigentlichen Namen der Klasse.
Zur Verwendung einer Klasse muß angegeben werden, in welchem Paket sie liegt. Hier
gibt es zwei unterschiedliche Möglichkeiten:
-
Die Klasse wird über ihren vollen (qualifizieren) Namen angesprochen, z.B.
java.util.Date d = new java.util.Date();
Am Anfang des Klassenprogramms werden die gewünschten Klassen mit Hilfe der importAnweisung eingebunden, z.B.:
import java util.*;
......
65 Zusätzlich 15 weitere Packages, etwa 500 Klassen und Schnittstellen
67
Programmieren in Java
-
-
Date d = new Date();
Die import-Anweisung gibt es in unterschiedlichen Ausprägungen:
Mit "import paket.Klasse" wird genau eine Klasse importiert, alle anderen Klassen des
Pakets bleiben verborgen
Mit "import paket.*"66 können alle Klassen des angegebenen Pakets auf einmal importiert
werden.
Standardmäßig haben Java-Klassen Zugang zu den in java.lang befindlichen
Klassen. Klassen aus anderen Paketen müssen explizit über den Paketnamen
einbezogen oder in die Quelldatei importiert werden.
1.4.1.8 Polymorphismus und Binden
Polymorphismus
Definition
Polymorphismus ist die Eigenschaft von Objekten, mehreren Klassen (und nicht nur
genau einer Klasse) anzugehören, d.h. je nach Zusammenhang in unterschiedlicher
Gestalt (polymorph) aufzutreten. Java ist polymorph.
Bsp.67: Das folgende Programm Poly ist eine Anwendung zu der bereits
bekannten Klassenhierarchie für geometrische Objekte. Es behandelt
Erscheinungsformen des Polymorphismus.
import java.lang.*;
public class Poly extends Object
{
public static void main(String args[])
{
Kreis k = new Kreis(0.0,1.0,2.0);
Rechteck r = new Rechteck(0.0,1.0,2.0,2.0);
// GeomObjekt geom = new GeomObjekt();
/* unzulaessig, da GeomObjekt abstrakt ist */
Object o = new Object();
// Folgende Zuweisungen sind zulaessig
GeomObjekt geom1 = k;
GeomObjekt geom2 = new Kreis(1.0,1.0,1.0);
GeomObjekt geom3 = r;
GeomObjekt geom4 = new Rechteck(1.0,1.0,1.0,4.0);
o = k;
// Aufruf der Methoden toString() und umfang()
// ueber geom1
System.out.println(geom1.toString());
System.out.println("Umfang: " + geom1.umfang());
/* geom1 ist ein GeomObjekt. GeomObjekt enthaelt eine
(abstrakte) Definition von toString() und von umfang().
Zur Laufzeit ist der Wert von geom1 eine Referenz auf
66 type import on demand, d.h.: Die Klasse wird erst dann in dem angegebenen Paket gesucht, wenn das
Programm sie wirklich benötigt.
67 Vgl. pr14181
68
Programmieren in Java
ein Objekt der Klasse Kreis, daher werden die im Kreis
definierten (allgemeiner: die in Kreis definierten
bzw. ererbten) Methoden toString() und umfang() ausgefuehrt.
*/
// Aufruf der Methoden toString() und umfang() ueber o
// System.out.println(o.toString());
/* o ist ein Object. Object enthaelt eine Definition von
toString(), daher akzeptiert der Java-Compiler
(zur Uebersetzungszeit) o.toString().
*/
// System.out.println("Umfang: " + o.umfang());
/* o ist ein Object. Object enthaelt oder erbt keine
Definition von umfang(), daher akzeptiert der JavaCompiler (zur Uebersetzungszeit) o.umfang() nicht.
*/
// System.out.println("Umfang: " + (Rechteck) o.umfang());
/* (Rechteck) o.umfang() akzeptiert der Java-Compiler
(zur Uebesersetzungszeit) aus dem gleichen Grund wie
zuvor nicht.
Die Schreibweise (Rechteck) o.umfang() bedeutet auch:
Der Rueckgabewert von o.umfang() soll als RechteckReferenz interpretiert werden.
*/
System.out.println("Umfang: " + ((Rechteck) o).umfang());
/* o ist ein Object, (Rechteck o) bedeutet: o soll als
Rechteck interprtiert werden. Fuer Rechteck-Objekte
ist umfang() erkaert, der Java-Compiler akzeptiert
(zur Uebersetzungszeit) ((Rechteck o).umfang().
Zur Laufzeit wird die in Recteck definierte
(allgemeiner: die in Recheck definierte bzw. ererbte)
Methode umfang() ausgefuehrt.
*/
}
}
Binden
Das Schema einer Botschaft wird aus "Empfänger Methode Argument" gebildet.
Darüber soll eine Nachricht die physikalische Adresse eines Objekts im Speicher
finden. Trotz Polymorphismus und Vererbung ist der Methodenname (oft. Selektor
genannt) nur eine Verknüpfung zum Programmcode einer Methode. Vor der Ausführung
einer Methode muß daher eine genaue Zuordnung zwischen dem Selektor und der
physikalischen Adresse des tatsächlichen Programmcodes erfolgen (Binden oder
Linken).
In der objektorientierten Programmierung unterscheidet man: frühes und spätes Binden:
Frühes Binden: Ein Compiler ordnet schon zum Zeitpunkt der Übersetzung des Programms die
tatsächliche, physikalische Adresse der Methode dem Methodenaufruf zu.
Spätes Binden: Hier wir erst zur Laufzeit des Programms die tatsächliche Verknüpfung zwischen
Selektor und Code hergestellt. Die richtige Verbindung übernimmt das Laufzeitprogramm der
Programmiersprache.
Java unterstützt das Konzept des „Late Binding“.
69
Programmieren in Java
1.4.2 Klassen des Pakets java.lang
1.4.2.1 Die Klasse Object
Die Object-Klasse ist die Basisklasse für jede andere Klasse in Java. Sie definiert
Methoden, die von allen Klassen in Java unterstützt werden.
Test auf Gleichheit von Objekten: public boolean equals(Object obj)
Darüber kann ermittelt werden, ob zwei Objekte gleich sind. Subklassen überschreiben
diese Methode zur Durchführung eines inhaltlichen Vergleichs. In jeder Klasse ist die
Methode auch gut aufgehoben, denn nur ein Objekt weiß, wann es mit einem anderen
gleich ist. Ob zwei Referenzen auf das gleiche Objekt zeigen, läßt sich durch den
Vergleichsoperator == feststellen.
Zeichenkettendarstellung von Objekten: public String toString()
Darüber kann ein Objekt an einen Ausgabestrom übergeben werden.
Berechnung von Hashcode: public int hashCode() berechnet den numerischen
Wert, der als Index zur Speicherung eines Objekts in einer Hashtabelle verwendet
werden kann.
Hashsodes werden zur Speicherung von Elementen in Hashtabellen. Das sind
Datenstrukturen, die eine effizienten Zugriff auf ihre Elemente erlauben.
Klonen
von
Objekten:
protected
Object
clone()
throws
CloneNotSupportedException, OutOfMemoryError erzeugt ein exaktes
Duplikat eines Objekts. Ein Objekt kann nur geklont werden, falls es die CloneableSchnittstelle unterstützt.
Fragen nach dem Namen der Objektklasse: public final Class getClass()
Benachrichtungsmechanismus. Hierfür gibt es die Methoden: public final void
notify() bzw. public final void notifyAll() und public final
void wait() throws InterruptedException bzw. public final void
wait(long timeout) throws InterruptedException. Threads68 können
miteinander kommunizieren und gegenseitig auf sich warten. Object definiert fünf
Versionen von wait() und notify() zur Synchronisation von Threads.
Aufräumen: protected void finalize() throws Throwable. Eine
spezielle Methode finalize() wird immer dann aufgerufen, wenn der Garbage
Collector das Objekt entfernen möchte. Objekte sollten diese Funktion überschreiben,
wenn sie bspw. noch Dateien schliessen müssen.
Objektidentifikation: public String toString(). Jedes Objekt sollte sich durch
die Methode toString() mit einer Zeichenkette identifizieren. Die Zeichenkette gibt
die Inhalt der Attribute an. Neue Objekte sollten diese Methode überschreiben. Ist dies
nicht der Fall, gelangt das zugehörige Programm zur Standardimplementierung in
Object.
68 vgl. 1.4.2.4
70
Programmieren in Java
1.4.2.2 Die Class-Klasse
Diese Klasse stellt Methoden zur Abfrage von Eigenschaften von Klassen zur Verfügung
und erlaubt es, Klassen dynamisch zu laden und Instanzen dynamisch zu erzeugen. In
Java hat jede Klasse eine entsprechende Instanz von Class. In Java liegen Klassen
selbst als Objekt vor. Neben normalen Klassen werden auch Interfaces über ClassObjekte abgelegt. Für ein bestimmtes Objekt kann man auf mehreren Wegen das
zugehörige Class-Objekt in Erfahrung bringen. Für ein bestimmtes Objekt kann man auf
mehreren Wegen das zugehörige Class-Objekt in Erfahrung bringen:
- Ist ein Exemplar der Klasse verfügbar, dann ruft man die Methode getClass() von
java.lang.Object auf. Man erhält die zugehörige Klasse.
- Ist die Klasse bekannt, besteht aber zusätzlich Interesse an den Vorfahren, dann kann man das mit
getSuperClass() herausfinden
- Ist die Klasse nicht zur Compilezeit aber zur Laufzeit bekannt, dann hilft die Klassenmethode
forName() weiter.
Ein Klassenobjekt kann eine Schnittstelle, eine Klasse, einen primitiven Datentyp oder
auch ein Array beschreiben.
Class
public static Class forName(String className) throws ClassNotFoundException
// liefert das Klassen-Objekt mit dem vollqualifizierten Namen der Klasse oder der Schnittstelle
// className. Das Metaobjekt wird, falls es nicht von der VM eingebunden ist, gesucht und
// geladen. Der Rückgabewert ist nicht null, falls es nicht gefunden werden konnte, sondern eine
// ClassNotFoundException.
public String getName()
// liefert den voll qualifizierten Namen der Klasse, der Schittstelle, des Arrays oder des
// primitiven Datentyps des Class-Objekts
public Class getSuperclass()
// liefert die Superklasse des Klassen-Objekts.
public boolean isInterface()
// liefert true, falls das Class-Objekt ein Interface beschreibt
public boolean isArray()
// liefert true, falls das Class-Objekt einen Array beschreibt
public boolean isPrimitive()
// testet, ob das Interface einen primitiven Datentyp beschreibt
public int getModifiers()
// liefert die Modifizierer für eine Klasse oder ein Interface
public Field[] getFields() throws SecurityException
// liefert ein Feld mit Field-Objekten69.
public Method[] getMethods() throws SecurityException
// gibt ein Array von Method-Objekten70 zurück, die alle öffentlichen Methoden der Klasse/
// Schnittstelle beschreiben
...
Abb.: Die Klasse Class
69 Objekten der Klasse java.lang.reflect.Field
70 Objekte der Klasse java.lang.reflect.Method
71
Programmieren in Java
Class besitzt Methoden, die hauptsächlich zur Beschreibung der Schnittstellen einer
Klasse und zur Einordnung in die Klassenhierarchie dienen. Diese abstrakte und
dynamische Dokumentation von Klassen wird als Reflection bezeichnet71.
Das folgende Programmstück72 testet Objekte systematisch durch:
import java.util.*;
class CheckClassTyp
{
public static void main(String args[])
{
Class observer
= Observer.class;
Class observable = Observable.class;
Class array = (new int[2][3][4]).getClass();
Class primitive = Integer.TYPE;
checkClassType(observer);
checkClassType(observable);
checkClassType(array);
checkClassType(primitive);
}
static void checkClassType(Class c)
{
String name = c.getName();
if (c.isArray())
System.out.println(name + " ist ein Array.");
else if (c.isPrimitive())
System.out.println(name + " ist ein primitiver Datentyp.");
else if (c.isInterface())
System.out.println(name + " ist ein Schnittstelle.");
else
System.out.println(name + " ist eine Klasse.");
}
}
Liegt der Name der Klasse als Class-Objekt vor, kann man zur Laufzeit den voll
qualifizierten Namen über die Methode getName() ausgeben.
import java.util.Date;
class BspName
{
public static void main(String [] args)
{
Date d = new Date();
Class c = d.getClass();
String s = c.getName();
System.out.println(s);
System.out.println((new int[2][3][4]).getClass().getName());
}
}
Arrays werden über getName() mit führenden „[“ kodiert. Jede Klammer beschreibt
die Tiefe des Arrays. Nach der Klammer folgt in kodierter Form, welchen Typ das Array
speichert. So liefert System.out.println((new
int
[2]
[3]
[4]
).
getClass().getName());den String „[[[I“. Nimmt das Array Objekte auf, wird dies mit
71 Die Klasse java.lang.Class ist Basis für Methoden, die Objekte vom Typ Constructor, Method, Field usw.
zurückliefern. Die zugehörigen Klassen sind im Package java.lang.reflect definiert.
72 vgl. pr14220
72
Programmieren in Java
einem
„LKlassenname;“
kodiert.
So
ergibt
(new
Object[3]).getClass().getName() den String „[Ljava.lang.Object;“.
1.4.2.3 Die Klasse System
Die Klasse System besteht nur aus Klassenvariablen und –methoden und kann nicht
instanziiert werden. Sie stellt die Verbindung zum Betriebssystem her und erlaubt bspw.
den Zugriff auf die Standardein- / Standardausgabe.
System
public static final InputStream in
public static final PrintStream out
public static final PrintStream err
public static void setIn(Inputstream in)
public static void setOut(PrintStream out)
public static void setErr(PrintStream err)
public static Securitymanager getSecurityManager()
public static void setSecurityManager(SecurityManager s)
public static long currentTimeMillis()
public static Properties getProperties()
public static void setProperties(Properties props)
public static String getProperty(String key)
public static void exit(int status)
public static void gc()
public static void load(String filename)
Abb.: Die Klasse System
System-Properties
Properties sind Listen von Eigenschaften, die Programmen vom Java-Laufzeitsystem
zur Verfügung gestellt werden. Jede Eigenschaft besitzt einen Name, unter dem auf sie
zugegriffen werden kann.
Property
java.version
java.vendor
java.vendor.url
java.home
java.class.version
java.class.path
os.name
os.arch
os.version
file.seperator
path.seperator
line.seperator
user.name
user.home
user.dir
Bedeutung
Java-Versionsnummer
Herstellerspez. Zeichenkette
URL (Internet-Link)
Installationsverzeichnis
Versionsnummer der Java-Klassenbibliothek
Aktueller Klassenpfad
Name des Betriebssystems
Betriebssystem-Architektur
Versionsnummer des Betriebssystems
Trennzeichen für die Bestandteile des Pfadnamens
Trennzeichen für die Laufwerksangabe eines Pfadnamens
Zeichenkette für Zeilenumschaltung
Name des angemeldeten Benutzers
Home-Verzeichnis
Aktuelles Arbeitsverzeichnis
73
Programmieren in Java
Für den Zugriff auf diese Eigenschaften steht die Klasse Properties aus dem Paket
java.util zur Verfügung. Sie erzeugt Property-Listen. Die Klasse Properties ist
eine Ableitung der Klasse HashTable und stellt darüber eine Tabelle mit
Schlüssel/Wertepaaren zur Verfügung.
Für den Zugriff auf einzelne Properties reicht meistens die Klassenmethode der Klasse
System aus:
public static String getProperty(String key)
liefert die Eigenschaft zu dem key in Form einer Zeichenkette. Falls keine Eigenschaft unter diesem
Namen (key) gefunden wird, wird „null“ zurückgegeben.
Die Methode
public static Properties getProperties();
liefert das komplette Properties-Objekt mit den System-Properties.
Bsp.73: Ausgabe einer Liste aller System-Properties auf dem Bildschirm
import java.lang.*;
import java.util.*;
class EigenschaftsTest extends Object
{
public static void main(String argv[])
{
System.out.println(new Date());
// System-Eigenschaften
Properties p = System.getProperties();
p.list(System.out);
System.out.println("Speicher-Verwendung:");
Runtime rt = Runtime.getRuntime();
System.out.println("Totaler Speicher = " + rt.totalMemory() +
" Freier Speicher = " + rt.freeMemory());
}
}
in, err und out
out ist eine statische Variable vom Typ PrintStream, die beim Starten des System
so initialisiert wird, daß ihre Ausgabe auf die Standardausgabe geleitet wird.
Analog zu out gibt es err und in. Dabei dient err zur Ausgabe von Fehlermeldungen,
in ist ein Standardeingabekanal, der zur Eingabe von der Tastatur verwendet wird.
Mit Hilfe von
public static void setIn(InputStream in);
public static void setOut(PrintStream out);
public static void setErr(PrintStream err);
kann die Standardein- und Standardausgabe aus dem Programm heraus umgeleitet
werden.
73 pr14231
74
Programmieren in Java
public static void exit(int status)
Mit System.exit(status) wird das laufende Programm beendet. „status“ dient
als Fehleranzeige und wird als Exitcode an den Aufrufer des Programms
zurückgegeben. Gemäß Konvention zeigt ein Wert größer oder gleich 1 einen Fehler
während der Programmausführung an, 0 signalisiert ein fehlerfreies Programmende.
public static void gc()
führt einen expliziten Aufruf des Garbage Collector durch. Dieser sucht nach
freiem Speicher und gibt ihn dann an das Laufzeitsystem zurück. Normalerweise ist ein
aufruf dieser Methode nicht erforderlich, denn der Garbage Collector läuft als niedrig
priorisierter Thread im Hintergrund.
public static long currentTimeMillis()
liefert die Anzahl der Millisekunden, die zum Zeitpunkt des Aufrufs seit Mitternacht des
1.1.1970 vergangen sind. Ob tatsächlich eine Auflösung von einer Millisekunde erreicht
wird, ist von der Java-Implementierung abhängig. In PC-basierten Systemen orientiert
sie sich meistens an der Auflösung des „System-Timer“.
1.4.2.4 Die Klasse Thread
Threads werden in Java durch die Klasse Thread und das Interface Runnable
implementiert. In beiden Fällen wird der Thread-Körper (, also der parallel
auszuführende Code) in Form der zu überlagernden Methode run bereitgestellt. Die
Kommunikation kann durch Zugriff auf die Instanz- oder Klassenvariablen oder durch
Aufruf beliebiger Methoden, die innerhalb von run sichtbar sind, erfolgen. Ein Thread
stellt einen einzigen Ablauf von Ausführungen in einem Java-Programm dar.
Die Klasse Thread ist Bestandteil des Pakets java.lang und steht damit allen
Anwendungen zur Verfügung. Über die Klasse Thread hat Java das (Betriebssystem-)
Konzept der Nebenläufigkeit implementiert. Mit Nebenläufigkeit bezeichnet man die
Fähigkeit eines Systems zwei oder mehr Vorgänge gleichzeitig oder quasi gleichzeitig
ausführen zu lassen. Ein Thread ist ein eigenständiges Programmfragment, das
parallel zu einem anderen Thread laufen kann.
75
Programmieren in Java
<< interface >>
Runnable
public abstract void run()
Thread
public static final int MIN_PRIORITY
public static final int NORM_PRIORITY
public static final int MAX_PRIORITY
<< Konstruktor >>
public Thread()
public Thread(Runnable target)
<< Methoden >>
public static Thread currentThread()
public static void yield()
public static void sleep(long millis) throws InterruptedException
public void start()
public void run()
public final void stop()
//deprecated
public final void stop(Throwable obj)
//deprecated
public void interrupt()
public static boolean interrupted()
public boolean isInterrupted()
public void destroy()
public final boolean isAlive()
public final void suspend()
//deprecated
public final void resume()
//deprecated
public final void setPriority(int newPriority)
public final int getPriority()
...
public String toString()
Abb.: Thread-Klassen
Methoden der Klasse Thread
Zum Erzeugen eines Thread muß eine eigene Klasse aus der Klasse Thread
abgeleitet, und die Methode run überlagert werden. Mit Hilfe des Aufrufs der Methode
start wird der Thread gestartet, die weitere Ausführung wird an die Methode run
übertragen. start wird nach dem Start beendet, der Aufrufer kann parallel zum neu
erzeugten Thread fortfahren.
Bsp.: Einfacher Thread, der in einer Endlosschleife einen Thread hochzählt.
class MeinThread extends Thread
{
public void run()
{
int i = 0;
76
Programmieren in Java
while (true)
{
// Endlosschleife, Abbruch durch Druecken von Strg+C
System.out.println(i++);
}
}
public static void main(String args[])
{
// Erzeugen einer Instanz
MeinThread t = new MeinThread();
// Start vom erzeugten Thread
t.start();
// Impliziter Aufruf der Methode run()
}
}
Der (quasi) parallel auszuführende Code wird durch die überlagerte Methode run
bereitgestellt. Die Kommunikation kann durch Zugriff auf Instanz- und Klassenvariablen
oder durch Aufruf beliebiger Methoden erfolgen, die innerhalb von run sichtbar sind.
Eine Java-Applikation wird immer dann beendet, wenn der letzte Thread beendet
wurde(, der kein Hintergrund-Thread (Dämon) ist). Da ein einfaches Programm nur
einen einzigen Vordergrund-Thread besitzt, (nämlich den, in dem main() läuft74,) wird
es beendet, wenn main() beendet wird. Das Beispielprogramm erzeugt einen
zusätzlichen Vordergrund-Thread und kann erst dann beendet werden, wenn dieser
Thread beendet wurde.
Weitere Methoden
public static void sleep(long millis) throws InterruptedException
sorgt dafür, daß der aktuelle Prozeß für die (in Millisekunden) angegebene Zeit
unterbrochen wird.
public static void sleep(long millis, int nanos) throws
InterruptedException
Die erzielbare Genauigkeit der Unterbrechungs-Wartezeit wird durch HardwareRestriktionen der Maschine begrenzt
public final boolean isAlive()
gibt true zurück, wenn der aktuelle Thread gestartet, aber noch nicht beendet wurde.
Beendet wird ein Thread, wenn das Ende der run-Methode erreicht ist, oder wenn die
Methode stop aufgerufen wurde.
Bsp.: Beenden des Thread nach 2 Sekunden durch Aufruf von stop
class MeinThread1 extends Thread
{
public void run()
{
int i = 0;
while (true)
{
// Endlosschleife, Abbruch durch Druecken von Strg+C
System.out.println(i++);
74 Beim Starten eines Java-Programms wird automatisch ein Thread für die Ausführung des Hauptprogramms
angelegt.
77
Programmieren in Java
}
}
public static void main(String args[])
{
// Erzeugen einer Instanz
MeinThread1 t = new MeinThread1();
// Start vom erzeugten Thread
t.start();
// Impliziter Aufruf der Methode run()
try {
Thread.sleep(2000);
}
catch(InterruptedException e)
{ }
t.stop();
}
}
public final void join() throws InterruptedException
Mit Hilfe dieser Methode kann gezielt auf die Beendigung eines Thread gewartet
werden. So etwas ist sinnvoll, wenn zeitaufwendige Operationen vor dem Aufruf einer
Folgeoperation noch beendet werden müssen. Der zeitaufwendigsten Operation ist ein
eigener Thread zugeordnet.
Unterbrechen eines Thread: Mit Hilfe der Methoden suspend bzw. resume ist es
möglich einen Thread vorübergehend zu unterbrechen bzw. fortzusetzen. Durch
suspend wird die Ausführung unterbrochen, durch resume wird der Thread
(genauer: die Methode run) an der Stelle fortgesetzt, an der die Unterbrechung
erfolgte. Sowohl suspend als auch resume sind aber deprecated und sollten nicht mehr
verwendet werden.
Das Interface Runnable
Nicht immer ist es möglich, eine Klasse, die als Thread laufen soll, von Thread
abzuleiten. Ein Thread kann deshalb auch über Implementierung des Interface
Runnable erzeugt werden. Runnable enthält nur eine einzige Deklaration nämlich
die der Methode run:
public interface Runnable
{
public abstract void run();
}
Implementierung von Runnable: Jede Klasse, deren Instanzen als Thread laufen, muß
das Interface Runnable implementieren75. Eine nicht von Thread abgeleitete Instanz
kann nach folgenden Implementierungsschritten als Thread laufen:
- Erzeugen eines neuen Thread-Objekts
- Übergabe des Objekts, das parallel ausgeführt werden soll, an den Konstruktor
- Aufruf der Methode start() des Thread-Objekts
Über start() startet das Thread-Objekt die run-Metode des übergebenen Objekts
(das sie durch die Übergabe an den Konstruktor kennt). Da dieses Objekt das Interface
75 sogar die Klasse Thread selbst
78
Programmieren in Java
Runnable implementiert, ist garantiert, daß eine geeignete Methode run zur
Verfügung steht.
Synchronisation nebenläufiger Prozesse
Zur Synchronisation nebenläufiger Prozesse hat Java das Konzept des Monitors
implementiert. Ein Monitor ist die Kapselung eines kritischen Bereichs (also eines
Programmteils, der nur von jeweils einem Prozeß zur aktuellen Zeit durchlaufen werden
darf) mit Hilfe einer automatisch gesetzten Sperre. Die Sperre wird beim Eintritt in den
Monitor gesetzt und beim Verlassen wieder zurückgenommen. Ist sie beim Eintritt in
den Monitor bereits von einem anderen Prozeß gesetzt, dann muß der aktuelle Prozeß
warten, bis der Konkurent die Sperre freigegeben und den Monitor verlassen hat.
Das Monitor-Konzept wird mit Hilfe des Schlüsselworts synchronized realisiert.
Durch synchronized kann entweder eine komplette Methode oder ein Block
innerhalb einer Methode geschützt werden. Der Eintritt in den so deklarierten Monitor
wird als Sperre der this-Pointer verwendet, anderenfalls ist die Objektvariable explizit
anzugeben.
Neben dem Monitorkonzept stehen mit den Methoden wait() und notify() der
Klasse Object noch weitere Synchronisationsprimitive zur Verfügung. Zusätzlich zu
der bereits erwähnten Sperre, die einem Objekt zugeordnet ist, besitzt ein Objekt eine
Warteliste. Dabei handelt es sich um eine (möglicherweise leere) Menge von Threads,
die von einem Scheduler unterbrochen wurden und auf Ereignisse warten, um
fortgesetzt werden zu können.
wait() und notify() dürfen nur aufgerufen werden, wenn das Objekt bereits
gesperrt ist, also nur innerhalb eines synchronized-Blocks. Ein Aufruf von wait()
nimmt die bereits gewährten Sperren (temporär) zurück und stellt dem Prozeß, der den
Aufruf von wait() verursachte, in die Warteliste des Objekts. Dadurch wird er
unterbrochen. Ein Aufruf von notify() entfernt einen (beliebigen) Prozeß aus der
Warteliste des Objekts, stellt die (temporär) aufgehobenen Sperren wieder her und führt
ihn dem normalen Scheduling zu. „wait()“ und „notify()“ sind damit für elementare
Synchronisationsaufgaben geeignet, bei denen es weniger auf die Kommunikation als
auf die Steuerung zeitlicher Abläufe ankommt
Verwaltung von Threads
Threads besitzen administrative Eigenschaften, die sich in zwei Gruppen einteilen
lassen:
- Priorität und Name
- Thread-Gruppen
Priorität, Name. Jeder Thread hat einen eindeutigen Namen. Ohne explizite Vorgabe
erfolgt die Vergabe des Namens automatisch in der Form “Thread-“+n , wobei n ein
int ist. Mit public void setName(String name) kann einem Thread ein
Name zugewiesen werden. Mit public String getName() kann der Name eines
Thread abgefragt werden. Neben dem Namen besitzt ein Thread noch eine Priorität.
Die Priorität steuert der Scheduler in der Weise, daß bei Vorhandensein mehrerer
Threads diejenigen mit höherer Priorität vor denen mit niedrigerer Priorität ausgeführt
79
Programmieren in Java
werden. Beim Anlegen eines neuen Thread bekommt dieser die Priorität des aktuellen
Thread. Mit public void setPriority(int neuePrioritaet) kann die
neue Priorität gesetzt und mit public int getPriority() abgefragt werden. Als
Parameter kann an setPriority() ein Wert übergeben werden, der zwischen den
Konstanten Thread.MIN_PRIORITY und Thread.MAX_PRIORITY liegt. Java
bietet 10 Prioritätsstufen zwischen 1 (Thread.MIN_PRIORITY) und 10
(Thread.MAX_PRIORITY) an. Der Normalwert wird durch die Konstante
Thread.NORM_PRIORITY festgelegt76.
Thread-Gruppen. Sie verwalten Informationen für eine ganze Gruppe und bieten
Methoden an, die auf alle Threads einer Gruppe angewendet werden können. In der
Klassenbibliothek gibt es dafür eine eigende ThreadGroup.
1.4.2.5 Die Klassen String und StringBuffer
Ein String ist eine Sammluung von Zeichen, die im Speicher geordnet abgelegt werden.
Die Zeichen setzen sich aus einem Zeichensatz zusammen, der in Java dem UnicodeStandard entspricht. In Java ist eine Symbiose zwischen String als Objekt und String als
eingebautem Datentyp vorgenommen worden. Möglich ist eine Zuweisung von StringLiteralen an Objekte und die Verknüpfung mehrerer Strings zu einem.
Die Klasse String
Die Klasse String repräsentiert Zeichenketten, die sich nicht ändern.
Konstruktoren:
String()
String(String wert)
String(char[] wert)
Erzeugt ein leeres String-Objekt
Erzeugt einen neuen String durch Duplizierung eines bereits
vorhandenen.
Erzeugt einen neuen String aus einem vorhandenen Zeichen-Array.
Zeichenextraktion
char charAt(int index)
String substring(int
anfang, int ende)
String trim()
Liefert das Zeichen an Position index. Dabei hat das erste
Element eines String den Wert 0 und das letzte den Index
„length()-1“. Falls der String kürzer als „index + 1“ ist, wird eine
Ausnahme des Typs „IndexOutOfBoundsException“ erzeugt
Liefert den Teilstring, der an Position anfang beginnt und an
Position ende endet. Wie bei allen Zugriffen über einen
numerischen Index beginnt hier das Zählen bei 0. „ende“ verweist
auf das erste Zeichen hinter dem zu extrahierenden Teilstring.
Der Rückgabewert ist also die Zeichenkette, die von
Indexposition anfang bis zur Indexpostion „ende –1“ reicht
Liefert den String, der entsteht, wenn auf beiden Seiten der
Zeichenkette jeweils alle zusammenhängenden Leerzeichen
entfernt werden. Dabei werden alle Zeichen, die einen Code
76 im aktuellen JDK 1, 5, 10
80
Programmieren in Java
kleiner 32 haben, als Leerzeichen angesehen. „trim“ entfernt
immer Leerzeichen auf beiden Seiten. Da die Klasse String als
final definiert wurde, gibt es keine Möglichkeit, entsprechende
Methoden nachzurüsten, z.B. für das rechtsbündige oder
linksbündige Entfernen von Leerzeichen.
Länge der Zeichenkette: int length() liefert die aktuelle Länge des String-Objekts.
Ist der Rückgabewert 0, dann ist der String leer. Wird ein Wert „n“ größer 0
zurückgegeben, dann enthält der String „n“ Zeichen, die an den Indexpositionen 0 bis „n1“ liegen.
Vergleichen von Zeichenketten:
boolean equals(Object
einObjekt)
boolean
startsWith(String s)
boolean endsWith(String
s)
int compareTo(String s)
boolean regionMatches(int
toffset, String other, int
ooffset, int len)
Liefert true, wenn das aktuelle Objekt und einObjekt
identisch sind. „equals“ testet auf inhaltlliche Gleichheit und
nicht darauf, ob beide Strings dasselbe Objekt referenzieren.
Neben „equals“ gibt es die Methode equalsIgnoreCase, die evtl.
vorhandene Unterschiede in der Groß- und Kleinschreibung
beider Zeichenketten ignoriert.
Testet, ob das String-Objekt mit der Zeichenkette s beginnt. Ist
dies der Fall, dann gibt die Methode „true“ zurück, anderenfalls
„false“.
Führt einen lexikalischen Vergleich der beiden Strings durch.
Bei einem lexikalischen Vergleich werden die Zeichen
paarweise von links nach rechts verglichen. Tritt ein Unterschied
auf oder ist einer der beiden Strings beendet, wird das Ergebnis
ermittelt. Ist das aktuelle String-Objekt kleiner als s, wird ein
negativer Wert zurückgegeben. Ist er größer, wird ein positiver
Wert zurückgegeben. Bei Gleichheit liefert die Methode den
Rückgabewert 0.
Testet, ob 2 String-Abschnitte gleich sind.
Suchen in Zeichenketten
int indexOf(String s)
int IndexOf(String s, int
vonIndex)
int lastIndexOf(String s)
Sucht das erste Vorkommen der Zeichenkette s
innerhalb des String-Objekts. Wird s gefunden, liefert
die Methode den Index des ersten übereinstimmenden
Zeichens zurück, andernfalls wird –1 zurückgegeben.
Die Methode gibt es auch in einer anderen Version, die
einen Parameter vom Typ char akzeptiert. In diesem
Fall sucht sie nach dem Auftreten des ersten
angegebenen Zeichens.
Diese Methode arbeitet wie die vorige, beginnt mit der
Suche aber erst ab Position vonIndex. Wird s
beginnend ab dieser Position gefunden, liefert die
Methode den Index des ersten übereinstimmenden
Zeichens, andernfalls –1.
Eine Variante dieser Methode erwartet anstelle eines
String-Parameters ein Argument vom Typ char.
Sucht nach dem letzten Vorkommen des Teilstrings s
im aktuellen String-Objekt- Wird „s“ gefunden, liefert die
Methode den Index des ersten übereinstimmenden
Zeichens, andernfalls –1. Eine Variante dieser Methode
erwartet ein Argument des Typs char.
81
Programmieren in Java
Ersetzen von Zeichenketten
String toLowerCase()
String toUpperCase()
String replace(char
altesZeichen, char
neuesZeichen)
Liefert den String zurück, der entsteht, wenn alle Zeichen in
Kleinbuchstaben umgewandelt werden. Besitzt der String keine
umwandelbaren
Zeichen,
dann
wird
der
Original-string
zurückgegeben.
Liefert den String zurück, der entsteht, wenn alle Zeichen in
Großbuchstaben umgewandelt werden. Besitzt der String keine
umwandelbaren Zeichen, wird der Original-String zurückgegeben.
Diese Methode führt eine zeichenweise Konvertierung des aktuellen
String-Objekts durch. Jedes Auftreten von altesZeichen wird
durch neuesZeichen ersetzt. Es gibt in Java keine Methode, die
das Ersetzen von Teilstrings durch andere Teilstrings (von
möglicherweise unterschiedlicher Länge) ermöglicht. „replace“ ist
das einzige Mittel für Ersetzungen.
Konvertierungsfunktionen:
static
static
static
static
static
static
String
String
String
String
String
String
valueOf(boolean b)
valueOf(char z)
valueOf(int i)
valueOf(long l)
valueOf(float f)
valueOf(double d)
Die „valueOf“-Methoden sind als Klassenmethoden implementiert und können ohne
String-Objekt aufgerufen werden. Da sie in der Regel zur Erzeugung von Strings
verwendet werden, ist das sinnvoll. Ein Aufruf von „valueOf“ wandelt ein primitives
Objekt mit Hilfe der Methode „toString()“, die von der zugehörigen WrapperKlassen bereitgestellt wird, in eine Zeichenkette um.
Weitere Eigenschaften: Die Klasse String ist final. Von einer String-Klasse können
keine neuen Klassen abgeleitet werden.
String-Objekte sind nicht dynamisch. Es werden durch die Klasse String keine
dynamischen Zeichenketten implementiert. Nach der Initialisierung eines String bleibt
dessen Länge konstant, z.B.:
String s = "Hallo Welt";
s = substring(0,5);
Die „substring“-Methode erzeugt eine Kopie, die mit dem gewünschten Inhalt gefüllt
wird. Diese gibt sie an den Aufrufer zurück, der das Ergebnis erneut s zuweist und damit
die Originalinstanz für den Garbage Collector freigibt.
Bsp.: Anwendung von Methoden der String-Klasse
public class PruefeString
{
public static void main(String args[])
{
String str = "Java ist die beste Programmiersprache, " +
"die Studenten der Informatik erlernen sollten.";
System.out.println("Der String ist: " + str);
System.out.println("Laenge des String: " + str.length());
System.out.println("Das Zeichen an Position 7: " + str.charAt(7));
System.out.println("Der Substring von 24 bis 31: " +
82
Programmieren in Java
str.substring(24,31));
System.out.println("Der Index zum Zeichen I: " + str.indexOf('I'));
System.out.println("Der Index zum Anfang von "
+ "Substring \"beste\": " + str.indexOf("beste"));
System.out.println("Der String in Großbuchstaben: " + str.toUpperCase());
}
}
Die Klasse StringBuffer
Die Klasse StringBuffer dient zur Implementierung veränderlicher Zeichenketten.
Die Methoden der Klasse StringBuffer dienen hauptsächlich dem Anhängen und
Einfügen von Texten. Dabei wird der benötigte Speicherplatz automatisch der Größe
angepaßt.
Konstruktoren:
StringBuffer()
StringBuffer(String s)
Erzeugt einen leeren StringBuffer
Erzeugt ein neues StringBuffer-Objekt, das eine Kopie der Zeichenkette
s ist.
Einfügen von Elementen
StringBuffer append(String s)
StringBuffer insert(int offset, String s)
Hängt String s an das Ende des StringBuffer-Objekts.
Zurückgegeben wird das verlängerte StringBuffer-Objekt.
Zusätzlich gibt es die Methode append in Varianten für
das Anhängen aller Arten von primitiven Typen. Anstelle
eines String-Objekts wird hier der entsprechende
primitive Typ übergeben, in einen String konvertiert und
an das Ende des Objekts angehängt.
Fügt den String s an die Position offset in den
aktuellen StringBuffer ein. Zurückgegeben wuird das
verlängerte StringBuffer-Objekt. Auch diese Methode gibt
es für primitive Typen. Der anstelle eines String
übergebene Wert wird zunächst in einen String
konvertiert und dann an die gewünschte Stelle eingefügt.
Verändern von Elementen: „void setCharAt(int index, char c) throws
IndexOutOfBoundsException“. Das an Position index stehende Zeichen wird
durch c ersetzt. Falls StringBuffer zu kurz ist, löst die Methode eine Ausnahme
des Typs IndexOutOfBoundsException aus.
Länge eines String-Objekts: int length() liefert die Länge des Objekts (Anzahl der
Zeichen, die zum Zeitpunkt des Aufrufs in dem StringBuffer enthalten sind).
Konvertieren in einen String: String toString(). Nachdem die Konstruktion eines
StringBuffer-Objekts abgeschlossen ist, kann es mit Hilfe dieser Methode in einen
String verwandelt werden. Die Methode legt dabei keine Kopie des StringBufferObjekts an, sondern liefert einen Zeiger auf den internen Zeichenpuffer. Erst wenn der
StringBuffer erneut verändert werden soll, wird tatsächlich eine Kopie erzeugt.
83
Programmieren in Java
1.4.2.6 Die Math-Klasse
Die Math-Klasse enthält nützliche numerische Konstanten und Funktionen:
1. Die Funktionen „min“ und „max“.
2. Der absolute Betrag
3. Zufallszahlen
Die Klasse Math unterstützt die „random“-Methode: public static double random(). Die
„random“-Methode gibt eine Zahl zwischen 0.0 und 1.0 aus.
4. Runden
Runden einer Zahl vom Typ float: public static int round(float a). Gerundet wird auf die
nächste ganze Zahl (z.B. 5.4 auf 5, 5.5. auf 6)
Runden einer Zahl vom Typ double: public static long round(double a)
Abrunden: public static double floor(double a). Math.floor rundet immer ab, z.B. ergibt
Math.floor(4.99) die Zahl 4.0.
Aufrunden: public static double ceil(double a). ceil runder immer auf, z.B.:
Math.ceil(4.01) ergibt 5.0.
Runden auf die nächste ganze Zahl: public static double rint(double a).
5. Exponenten und Logarithmen
x
Bestimmen des Expoentialwerts e : public static double exp(double x)
Bestimmen des natürlichen algorithmus ln( x) : public static double log(double x)
6. Trigonometrische Funktionen
public static double sin(double winkel)
public static double cos(double winkel)
Der Wert von winkel wird im Bogenmaß angegeben.
7. Potenzen, Quadratwurzel
y
Bestimmen vom Wert der Potenz x : public static double pow(double x, double y)
Bestimmen der Quadratwurzel von x: public static double sqrt(double x)
8. Mathematische Konstanten
public static final double E;
public static final double PI;
84
Programmieren in Java
1.4.2.7 Object-Wrapper-Klassen
Die Object-Wrapper-Klassen sind Wrapper für primitive Typen. Zusätzlich besitzen sie
Methoden zur Umwandlung von Zeichenketten in die verschiedenen Datentypen.
Boolean
Character
public static final Boolean TRUE
public static final Boolean FALSE
public static final char MIN_VALUE
public staic final char MAX_VALUE
public boolean booleanValue()
public static Boolean valueOf(String s)
public static boolean getBoolean(String name)
public char charValue()
public Character(char value)
public int hashCode()
public boolean equals(Object obj)
public String toString()
public static boolean isLowerCase(char ch)
public static boolean isUpperCase(char ch)
public static boolean isDigit(char ch)
public static boolean isLetter(char ch)
public static boolean isLetterOrDigit(char ch)
public static boolean isJavaLetter(char ch)
public static boolean isJavaLetterOrDigit(char c)
public static boolean isSpace(char ch)
public static boolean isTitleCase(char ch)
public static char toLowerCase(char ch)
public static char toUpperCase(char ch)
public static int digit(char ch, int radix)
public static int getNumericValue(char ch)
Number
{ abstract }
public abstract int intValue()
public abstract long longValue()
public abstract float floatValue()
public abstract double doubleValue()
public byte byteValue()
public short shortValue()
Byte
Short
Integer
Long
Abb.: Wrapper-Klassen
85
Float
Double
Programmieren in Java
Die Klasse Character
Konstruktor:
Ermitteln vom „char“-Wert: public char charValue()
Klassifizieren von Zeichen:
Methode
isDigit
isLetter
isLetterOrDigit
isLowerCase
isUpperCase
isJavaLetter
isJavaLetterOrDigit
isSpace
isTitleCase
Beschreibung
Eine numerische Zahl zwischen 0 und 9
Ein Buchstabe des Alphabets
Ein alphabetischer Buchstabe oder eine numerische Zahl
Ein großgeschriebener alphabetischer Buchstabe
Ein Buchstabe, ‚$‘ oder ‚_‘
Ein Buchstabe, eine Zahl, $ oder _
Eine Leerstelle, eine neue Zeile, ein Return, ein Tab oder ein
Formularvorschub
Spezielle zweibuchstabige groß- und kleingeschriebene Buchstaben
Jede dieser Klassifikationsmerkmale gibt „true“ zurück, wenn das betreffende Zeichen
zur Klassifikation gehört.
Groß- und kleingeschriebene Variante eines Buchstabens:
public static char toUppercase(char zeichen)
public static char toLowerCase(char zeichen)
Konvertierungsmethoden
Zeichenketten in Zahlen:
zur
Umwandlung
von
Zahlen
in
Zeichenketten
und
public static int digit(char zeichen, int radix)
gibt den numerischen Wert eines Zeichens der angegebenen Zahlendarstellung
(„radix“)77 wieder. Falls ein Zeichen keinem Wert in der angegebenen
Zahlendarstellung entspricht, wird „-1“ zurückgegeben.
public static char forDigit(int digit, int radix)
Konvertiert einen numerischen Wert in ein Zeichen, das diesen Wert in einer
bestimmten Zahlenbasis darstellt, z.B. Character.forDigit(6,8) ergibt 6 und
Character.forDigit(12,16) ergibt "c“.
Die Klasse Boolean
Diese Klasse ist der Objekt-Wrapper für den primitiven Typ boolean.
Konstruktoren:
public Boolean(boolean wert)
public Boolean(String str)
Beschaffung eines booleschen Werts, der in einem Objekt abgelegt ist:
public boolean booleanValue()
Objekt-Wrapper-Varianten von true und false
public final static Boolean FALSE;
public final static Boolean TRUE;
77 z.B. 10 für dezimal, 8 für oktal, 16 für hexadezimal. Zahlenbasis kann Werte von 2 (Character.MIN_RADIX) bir
32 (Character.MAX_RADIX) umfassen.
86
Programmieren in Java
Die Klasse Number
Die Objekt-Wrapper der Typen int, long, float und double sind Subklassen der
abstrakten Klasse Number. Vier Methoden sind für die Konvertierung einer Zahl in
einen bestimmten primitiven Typ zuständig:
public
public
public
public
abstract
abstract
abstract
abstract
int intValue();
long longValue();
float floatValue();
double doubleValue()
1. Die Klasse Integer
Konstruktoren:
public Integer(int wert)
public Integer(String s) throws NumberFormatException
Die Zahlenbasis (Radix) ist 10. Falls die Zeichenkette nicht numerische Zeichen enthält,
wird die Ausnahme NumberFormatException ausgeworfen.
Umwandeln Zeichenkette in ganze Zahlen:
public
public
public
public
static
static
static
static
int parseInt(String s)
int parseInt(String s,
Integer valueOf(String
Integer valueOf(String
throws NumberFormatException
int radix) throws NumberFormatException
s) throws NumberFormatException
s, int radix) throws NumberFormatException
Der Unterschied zwischen parseInt und valueOf liegt darin: parseInt gibt ein
„int“ zurück, valueOf ein Integer.
Konvertieren einer ganzen Zahl in eine Zeichenkette:
public static String toString(int i)
public static String toString(int i, int radix)
Die beiden Methoden dürfen nicht mit der Instanz-Methode toString verwechselt
werden, die für alle Subklassen von Objekt definiert ist.
Die Konstanten Integer.Min_VALUE und Integer.MAX_VALUE:
public final static int MIN_VALUE
public final static int MAX_VALUE
2. Die Klasse Long
Sie ist weitgehend mit der Integer-Klasse identisch. Allerdings dient sie als Wrapper für
long-Werte.
3. Die Klasse Float
Sie enthält einen Objekt-Wrapper für den Datentyp float.
Konstruktoren:
public Float(float wert)
public Float(double wert)
public Float(String s) throws NumberFormatException
Konvertierung von bzw. zu Zeichenketten:
public static Float valueOf(String s) throws NumberFormatException
public static String toString(float f)
87
Programmieren in Java
Überprüfen auf „Unendlich“ bzw. auf „keine Zahl“:
public static boolean isInfinite(float f)
public static boolean isNaN(float f)
// Instanz-Variante
public boolean isNaN()
// Instanz-Variante
Konstanten:
public
public
public
public
final
final
final
final
static
static
static
static
float
float
float
float
MIN_VALUE
MAX_VALUE
NEGATIVE_INFINITY
NaN
4. Die Klasse Double
Sie hat dieselbe Funktionalität wie die Float-Klasse.
88
Programmieren in Java
1.4.3 Ausnahmen und Ausnahmenbehandlung
Ausnahmen
Eine Ausnahme (Exception) ist eine unerwünschte Erscheinung. Eine derartige
Situation (z.B. Division durch Null) ist in einem Programm gegebenenfalls durch eine
Fehlerbehandlungsroutine aufzufangen.
Es gibt unterschiedliche Möglichkeiten zur Fehlerbehandlung:
- die individuelle programmierte Fehlerbehandlung, z.B.:
public int dividiere()
{
// ganzzahlige Division
if (zweiterOperand == 0)
{
System.out.println("Fehler: Division durch Null");
System.out.println("Divisor wird auf 1 gesetzt!");
zweiterOperand = 1;
}
return ersterOperand / zweiterOperand;
}
- die von Java bereitgestellten Fehlerbehandlungen
Ausnahmen sind in Java Objekte, die erzeugt werden, wenn eine Ausnahmebedingung
vorliegt. Diese Objekte werden von der Methode an das aufrufende Programm
zurückgegeben und müssen behandelt werden. Das Behandeln von Ausnahmen wird
als Auffangen bezeichnet. Das Auffangen wird durch Plazieren von Anweisungen, die
Ausnahmen erzeugen können, in eine try-catch Block erreicht, z.B .78:
public int dividiere()
{
// ganzzahlige Division
try {
/* Innerhalb des try-Blocks werden diejenigen
kritischen Aktionen durchgefuehrt, die
Ausnahmen erzeugen koennen
*/
return (ersterOperand / zweiterOperand);
}
catch(java.lang.ArithmeticException a)
{
System.out.println(" Fehler: Division durch Null");
System.out.println("Divisor wird auf 1 gesetzt!");
zweiterOperand = 1;
return (ersterOperand / zweiterOperand);
}
}
In der catch-Klausel wird die Art der aufzufangenden Ausnahme definiert. Dort ist ein
formaler Parameter angegeben, der beim Auftreten der Ausnahme ein Fehlerobjekt
übernehmen soll. Fehlerobjekte sind Instanzen der Klasse Throwable (oder eine ihrer
78 pr14103
89
Programmieren in Java
Unterklassen). Sie werden vom Aufrufer der Ausnahme erzeugt und als Parameter an
die catch-Klausel übergeben. Das Fehlerobjekt enthält Informationen über die Art der
aufgetretenen Ausnahme. Es kann dort eine der zahlreichen Standardausnahmen von
Java stehen oder auch selbstdefinierte Ausnahmen. Das Ausnahme- / Fehlerobjekt
besitzt einige nützliche Methoden, z.B. public String getMessage()79. Diese
Methode gibt Fehlermeldungen zurück.
public void printStackTrace(). Diese Methode druckt einen Auszug aus
dem Laufzeit-Stack.
Am Ende eines try-Blocks können beliebig viele „catch“-Klauseln stehen, so daß
unterschiedliche Arten von Ausnahmen behandelt werden können.
Mit Hilfe der finally-Klausel (letzter Bestandteil einer try-catch-Anweisung) kann
ein Programmfragment definiert werden, das immer ausgeführt wird, wenn die
zugehörige try-Klausel betreten wurde.
Bsp.: Rückgabe von „Resourcen“ in der finally-Klausel
In dem folgenden Programm stellt die finally-Klausel sicher, daß der Schalter
tatsächlich nach dem Ende der Bearbeitung im try-catch-Block ausgeschaltet ist.
class Schalter
{
boolean zustand = false;
boolean lies() { return zustand; }
void an() { zustand = true; }
void aus() { zustand = false; }
}
public class AnAusSchalter
{
static Schalter schalter = new Schalter();
public static void main(String args[])
{
try
{
schalter.an();
// hier kann sich Quellcode befinden, der
// Ausnahmen ausloest
}
catch(NullPointerException a)
{
System.out.println("NullPointerException");
}
catch(IllegalArgumentException a)
{
System.out.println("IllegalArgumentException");
}
finally
{
schalter.aus();
}
}
}
79 Sie ist in der Klasse Throwable definiert und daher allen Exception-Objekten zugänglich.
90
Programmieren in Java
Globales Exception-Handling
Java realisiert das globale Handling von Ausnahmen über die optionale Erweiterung
einer Methodendeklaration mit der „throws“-Klausel, z.B.:
public class MeineAusnahmenKlasse
{
public void eineAusnahmenMethode() throws MeineAusnahme
{
.........
}
}
Hier wird dem Compiler mitgeteilt, daß der vorliegende Code eine Ausnahme
(„MeineAusnahme“) erzeugen könnte.
Falls in der mit „throws“ gekennzeichneten Methode eine Ausnahmesituation auftritt,
dann wird die aufrufende Methode nach einer Ausnahmebehandlung durchsucht. Enthält
die aufrufende Methode eine Ausnahmebehandlungsmethode, wird mit dieser die
Ausnahme bearbeitet. Ist keine Routine vorhanden, wird deren aufrufende Methode
durchsucht und so fort. Die Ausnahme wird von der Hierarchieebene, wo sie aufgetreten
ist, jeweils eine Hierarchieebene weiter nach oben gereicht. Wird die Ausnahme
nirgends aufgefangen, dann bricht der Java-Interpreter normalerweise die Ausführung
des Programms ab.
Das Auslösen einer Ausnahme wird im Java-Sprachgebrauch als „throwing“ bezeichnet,
das Behandeln einer Ausnahme wird „catching“ genannt.
Das Grundprinzip des Exception-Mechanismus:
- Ein Laufzeitfehler oder eine vom Entwickler gewollte Bedingung löst eine Ausnahme aus.
- Diese kann entweder vom Programmteil, in dem sie angelegt wurde, behandelt werden, oder sie kann
weitergegeben werden.
- Wird die Ausnahme weitergegeben, so hat der Empfänger erneut die Möglichkeit sie entweder zu behandeln
oder selbst weiterzugeben.
- Wird die Ausnahme von keinem Programmteil behandelt, so führt sie zum Abbruch eines Programms, und zur
Ausgabe einer Fehlermeldung.
Auswertungen, die Ausnahmen auslösen, können entweder direkt in eine try-Klausel
mit optionalen catch-Anweisungen (Behandlung vor Ort) eingefaßt oder über die
throws-Klausel an den übergeordneten Programmblock weitergeleitet werden. Falls
weder die „Behandlung vor Ort“ noch eine throws-Klausel verwendet wird, liefert der
Compiler bereits bei der Übersetzung eine Fehlermeldung. Eine Ausnahme bildet die
Klasse RuntimeException einschl. ihrer Subklassen. Für sie ist weder die
Fehlerbehandlung noch Weiterleitung mit der throws-Klausel notwendig.
Die Fehlerklassen von Java
Zur Behandlung von Fehlern und Ausnahmen besitzt die Klasse Throwable zwei
Subklassen: die Klassen Exception und Error. In beiden Ausnahmeklassen sind
die wichtigsten Ausnahmen und Fehler der Java-Laufzeitbibliothek bereits enthalten.
Ausnahmen der Klasse Error und ihrer Klasse RuntimeError (sowie
RuntimeException) müssen nicht extra abgefangen werden. „Errors“ sind
91
Programmieren in Java
systembedingt, „Exceptions“ dagegen programmbedingt und müssen behandelt
werden. Es gibt fünf Typen von Ausnahmen, die in einer throws-Klausel aufgelistet
werden müssen:
ClassNotFoundException
IllegalAccessException
InstantiationException
InterruptedException
NoSuchMethodException
Hinzu kommen verschiedene weitere Ausnahmen aus den Java-Paketen. So gibt es
spezielle Fehlerklassen für die Ein- und Ausgabe, die Netzwerkkommunikation oder
den Zugriff auf Datenfelder. Teilweise sind sie der Klasse „Error“ (oder ihrer
Subklasse RuntimeError) zugeordnet und müssen nicht extra abgefangen und
dokumentiert werden. Sind Ausnahmen der Klasse Exception (Ausnahme:
RuntimeException) zugeordnet, dann müssen sie behandelt werden (z.B. alle
Ausnahmen von java.io).
Die Klasse RuntimeException ist die Superklasse für die Behandlung aller
Laufzeitfehler, die behandelt werden können aber nicht müssen (die Entscheidung liegt
beim Programmierer).
UnknownError
VirtualMachineError
OutOfMemoryError
InternalError
Error
AWTError
LinkageError
Object
Throwable
ClassNotFoundException
Exception
RuntimeException
EOFException
IOException
FileNotFoundException
InterruptedException
UTFDataFormatException
Abb.: Fehlerklassenhierarchie
Auslösen von Ausnahmen
Java verfügt in seinen Standardbibliotheken über zahlreiche vorgefertigte Ausnahmen
für fast alle Standardsitiuationen (z.B. Dateioperationen auf der Basis der IOException). Selbstdefinierte Ausnahmen benötigen eine Subklasse von Exception. Das
92
Programmieren in Java
Auslösen dieser selbstdefinierten Ausnahmen erfolgt über die throw-Anweisung: throw
AusnahmeObjekt.
Die Behandlung selbstausgelöseter Ausnahmen erfolgt nach den üblichen Regeln:
- Suche nach einem Ausnahmen-Handler in den umgebenden Blöcken
- Bei erfolgloser Suche Weitergabe des Fehlers an den Aufrufer.
Wird die Ausnahme nicht innerhalb derselben Methode behandelt, dann ist sie mit Hilfe
der "throws"-Klausel zu deklarieren und „weiter oben“ in der Aufrufkette zu behandeln.
Bsp.:
public class AusnahmeMethoden
{
public static void main(String args[])
{
try
{
throw new Exception("Hier ist meine Ausnahme");
}
catch (Exception a)
{
System.out.println("Eingefangene Ausnahme");
System.out.println("a.getMessage(): " + a.getMessage());
System.out.println("a.toString(): " + a.toString());
System.out.println("a.printStackTrace(): ");
a.printStackTrace();
}
}
}
/* Ausgabe:
Eingefangene Ausnahme
a.getMessage(): Hier ist meine Ausnahme
a.toString(): java.lang.Exception: Hier ist meine Ausnahme
a.printStackTrace():
java.lang.Exception: Hier ist meine Ausnahme
at AusnahmeMethoden.main(AusnahmeMethoden.java)
*/
Die throw-Anweisung besitzt Merkmale einer Sprunganweisung. Sie unterbricht das
Programm und verzweigt unmittelbar zur umgebenden catch-Klausel. Gibt es keine
catch-Klausel, wird der Fehler weitergegeben. Tritt die Ausnahme innerhalb einer
try-catch-Anweisung mit einer finally-Klausel auf, wird diese noch vor der
Weitergabe ausgeführt.
Auch das „Wiederauswerfen“ einer Ausnahme ist möglich, insbesondere dann, falls
eine Ausnahme eingefangen wurde, sie aber in einem größeren Zusammenhang
behandelt werden soll
catch (Exception a)
{
System.out.println("Eine Ausnahme wurde eingefangen");
throw a;
}
93
Programmieren in Java
Eigene, d.h. benutzerdefinierte Ausnahmen können direkt von der Klasse Exception
abgeleitet werden, z.B.80:
class MeineAusnahme extends Exception
{
private int i;
public MeineAusnahme() {}
public MeineAusnahme(String nachricht)
{
super(nachricht);
}
public MeineAusnahme(String nachricht, int x)
{
super(nachricht);
i = x;
}
public int wert() { return i; }
}
public class AuswurfMeineAusnahme
{
public static void f() throws MeineAusnahme
{
System.out.println("Auswurf von MeineAusnahme aus f()");
throw new MeineAusnahme();
}
public static void g() throws MeineAusnahme
{
System.out.println("Auswurf von MeineAusnahme aus g()");
throw new MeineAusnahme("Ursprung in g()");
}
public static void h() throws MeineAusnahme
{
System.out.println("Auswurf von MeineAusnahme aus h()");
throw new MeineAusnahme("Ursprung in h()",13);
}
public static void main(String args[])
{
try {
f();
}
catch (MeineAusnahme a)
{
a.printStackTrace();
}
try { g(); }
catch (MeineAusnahme a)
{
a.printStackTrace();
}
try { h(); }
catch (MeineAusnahme a)
{
a.printStackTrace();
System.out.println("a.wert() = " + a.wert());
}
}
}
80 Vgl. pr14140
94
Programmieren in Java
/* Ausgabe:
Auswurf vom MeineAusnahme aus f()
MeineAusnahme
at java.lang.Throwable.<init>(Compiled Code)
at java.lang.Exception.<init>(Compiled Code)
at MeineAusnahme.<init>(AuswurfMeineAusnahme.java:4)
at AuswurMeineAusnahme.f(Vererbung.java:21)
at AuswurfmeineAusnahme.main(Vererbung.java:36)
Auswurf von MeineAusnahme aus g()
MeineAusnahme: Ursprung in g()
at java.lang.Throwable.<init>(Compiled Code)
at java.lang.Exception.<init>(Compiled Code)
at MeineAusnahme.<init>(AuswurfMeineAusnahme.java:7)
at AuswurfMeineAusnahme.g(AuswurfMeineAusnahme.java:26)
at AuswurfMeineAusnahme.main(AuswurfMeineAusnahme.java:43)
Auswurf von MeineAusnahme aus h()
MeineAusnahme: Ursprung in h()
at java.lang.Throwable.<init>(Compiled Code)
at java.lang.Exception.<init>(Compiled Code)
at MeineAusnahme.<init>(AuswurfMeineAusnahme.java:11)
at AuswurfMeineAusnahme.g(AuswurfMeineAusnahme.java:31)
at AuswurfMeineAusnahme.main(AuswurfMeineAusnahme.java:50)
a.wert() = 13
*/
95
Programmieren in Java
1.4.4 Ein-, Ausgaben
1.4.4.1 Ein- und Ausgabeströme der Klasse System
Die Klasse System enthält drei Datenströme:
public static InputStream81 in
public static PrintStream82 out
public static PrintStream err
// Standardeingabe
// Standardausgabe
// Standardfehlerausgabe
Diese Datenströme dienen zum Lesen bzw. Schreiben in das Fenster von dem die
Anwendung gestartet wurde. Die Verwendung des Stroms System.in innerhalb von
Applets ist aber nicht sinnvoll, weil die verschieden Browser diesen Datenstrom
unterschiedlich behandeln. Die Ströme System.out und System.err sendet
Netscape an das Fenster der Java-Konsole, der Appletviewer gibt sie in das Fenster
weiter, aus dem der Appletviewer gestartet wurde,
Einfache Ausgaben auf das Standard-Ausgabegerät
Mit Hilfe des Kommandos „System.out.println“, das als einziges Argument eine
Zeichenkette erwartet, können einfache Ausgaben auf den Bildschirm geschrieben
werden. Mit Hilfe des Plus-Operators „+“ können Zeichenketten und numerische
Argumente verknüpft werden, so daß man neben Text auch Zahlen ausgeben kann.
Einfache Eingaben vom Standard-Eingabegerät
Leider ist es etwas komplizierter Daten zeichenweise von der Tastatur zu lesen. Es
steht zwar ein vordefinierter Eingabestrom System.in zur Verfügung, der aber nicht
die eingelesenen Zeichen primitiver Typen konvertieren kann. Statt dessen muß eine
Instanz der Klasse „InputStreamReader“83 und daraus ein „BufferedReader“
erzeugt werden. Dieser kann zum Lesen einer zeilenweisen Eingabe mit Umwandlung
des Ergebnisses in einen primitiven Typ verwendet werden, z.B.84: "Einlesen von zwei
ganzen Zahlen".
import java.io.*;
public class EingZweiZahlen
{
// Einlesen von zwei ganzen Zahlen
public static void main(String args[]) throws IOException
{
int a, b, c;
BufferedReader ein = new BufferedReader(new
InputStreamReader(System.in));
81 Mit der Klasse InputStream können Leseoperationen eines Bytestroms verwirklicht werden, vgl. 7.1.1
82 Die Klasse PrintStream bezieht sich auf die I/O von Java 1.0
83 Das grundlegende Konzept für Ein- und Ausgabe sind Datenströme, die im Paket „java.io“ versammelt
sind.
84 Vgl. pr14150
96
Programmieren in Java
System.out.print("Bitte a eingeben: ");
a = Integer.parseInt(ein.readLine());
System.out.print("Bitte b eingeben: ");
b = Integer.parseInt(ein.readLine());
c = a + b;
System.out.println("a + b = " + c);
}
}
1.4.4.2 Datenströme
Das grundlegende Modell für Ein- und Ausgabe sind Datenströme (CharacterStreams), die im Paket "java.io" versammelt sind. Das Datenstrom-Modell geht
davon aus, daß
- Programme Zeichenfolgen aus einem Datenstrom lesen bzw. in einen Datenstrom schreiben
- ein Datenstrom unabhängig von den Fähigkeiten des jeweiligen I/O-Geräts85 organisiert werden muß.
Streams können auch verkettet werden und verschachtelt werden. Das Schachteln
erlaubt die Konstruktion von Filtern, die bei der Ein-/Ausgabe bestimmte
Zusatzfunktionen übernehmen, z.B. das Puffern von Zeichen, das Zählen von Zeichen
oder die Interpretation binärer Dateien.
Alle Klassen zur Datenein- bzw. Datenausgabe befinden sich im Paket java.io und
sind von den abstrakten Klassen Reader bzw. Writer86 abgeleitet. Allgemeine
Fehlanzeige im Paket java.io ist die Ausnahme IOException, die deshalb auch
von einer Vielzahl von Methoden ausgelöst werden kann und dabei sehr
unterschiedliche Bedeutung annehmen kann.
Bsp.: Das folgende Programm liest eine Zeichenfolge aus einem Eingabestrom und
schreibt die Zeichenfolge in einen Ausgabestrom87
import java.io.*;
public class EchoEinAus extends Object
{
public static void main(String args[])
{
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader
ein = new BufferedReader(isr);
String eingabeZeile = null;
OutputStreamWriter osw = new OutputStreamWriter(System.out);
PrintWriter aus
= new PrintWriter(System.out);
while (true)
{
aus.print("Gib eine Zeichenkette ein: ");
aus.flush();
try {
eingabeZeile = ein.readLine();
}
catch (IOException e)
{
85 Hauptspeicher, Festplatte, Drucker, Netzwerk
86 vgl. 7.6
87 vgl. pr14150
97
Programmieren in Java
aus.println(e.toString());
System.exit(0);
}
if (eingabeZeile == null)
{
aus.println();
aus.flush();
break;
}
aus.println(eingabeZeile);
}
}
}
Für die Eingabe wurde herangezogen:
- InputStream in der Klasse System aus dem Paket java.lang
- die abstrakte Klasse InputStream des Pakets "java.io"
- die Klasse InputStreamReader des Pakets "java.io"
- die Methode "String readLine()" mit der Klasse BufferedReader des Pakets java.io
Für die Ausgabe kann ebenso verfahren werden. Man zieht in diesem Zusammenhang
zur Ausgabe heran:
- die Klasse PrintStream (Subklasse der abstrakten Klasse OutputStream)
- die Klasse BufferedWriter (Subklasse von Writer). Es gibt aber kein Gegenstück zu "String
readLine()".
- die Klasse PrintWriter (Subklasse von Writer) für println(...)
1.4.4.3 Eingabe und Ausgabe mit Dateien
Zur Ein-, Ausgabe von bzw. aus Dateien stehen die aus den Klassen Reader bzw.
Writer abgeleiteten Klassen FileReader bzw. FileWriter zur Verfügung.
Eingabe mit Dateien
Bsp.: Einlesen einer Eingabe-Datei
import java.io.*;
public class EingabeDatei extends Object
{
private BufferedReader ein;
EingabeDatei(String dName) throws Exception
{
try {
ein = new BufferedReader(new FileReader(dName));
}
catch (FileNotFoundException a)
{
// Der Konstruktor von FileReader war nicht erfolgreich
System.out.println("Kann nicht geoeffnet werden: " + dName);
throw a;
98
Programmieren in Java
}
catch (Exception a)
{
// Alle anderen Ausnahmen schliessen die Datei
try {
ein.close();
}
catch (IOException a1)
{
System.out.println("ein.close() nicht erfolgreich");
}
throw a;
}
finally { /* nicht geeignet fuer close() */ }
}
String holeZeile() throws IOException
{
String s;
try {
s = ein.readLine();
}
catch (IOException a)
{
System.out.println("readLine() nicht erfolgreich");
s = "Fehlanzeige";
}
return s;
}
void bereinigen()
{
// Freigabe der Syntax-Resourcen
try {
ein.close();
}
catch(IOException a2)
{
System.out.println("ein.close() nicht erfolgreich");
}
}
}
Das folgende Programm bearbeitet die Eingabedatei.
import java.io.*;
public class BearbeiteEingabeDatei extends Object
{
public static void main(String args[])
{
try {
// Erzeugen einer Instanz
EingabeDatei ein =
new EingabeDatei("BearbeiteEingabeDatei.java");
String s;
int i = 1;
while ((s = ein.holeZeile()) != null)
System.out.println("" + i++ + ": " + s);
ein.bereinigen();
}
catch (Exception a)
99
Programmieren in Java
{
System.out.println("Eingefangen in main(), a.printStackTrace()");
a.printStackTrace();
}
}
}
Das Beispiel zeigt: Eingabeströme können verschachtelt werden. Folgende Klassen
stehen dafür zur Verfügung (, die im Konstruktor einen Reader erwarten):
BufferedReader,
LineNumberReader,
FilterReader
und
PushbackReader.
Ein BufferedReader puffert Daten in einem 8192 Bytes großen Zwischenspeicher.
Dadurch müssen weniger Zugriffe auf den Datenträger gemacht werden. Zusätzlich stellt
BufferedReader die Methode public String readLine() zur Verfügung, die
eine komplette Textzeile einliest und als String an den Aufrufer zurückgibt. „null“ wird
zurückgegeben, wenn der Stream am ende ist.
Ein LineReader liest die Eingabezeilen und zählt gleichzeitig die Zeilen, die gelesen
wurden. Mit public
int
getLineNumber() bzw. public
void
setLineNumber(int zeilenNummer) läßt sich auf die die Zeilennummern
zugreifen.
Ausgabe mit Dateien
Die Ausgabe in Dateien wird unterstützt durch die Klasse FileWriter.
Umlenken von Standardeingabe, Standardausgabe und Standardfehlerausgabe
In Java 1.1 ermöglichen die folgenden Methoden das Umlenken:
- setIn(InputStream)
- setOut(PrintStream)
- setErr(PrintStream)
Bsp.: Demonstration der Umlenkung von Standardeingabe und Standardausgabe in
Java 1.1 88
import java.io.*;
class Umlenken
{
public static void main(String args[])
{
try
{
BufferedInputStream ein =
new BufferedInputStream(new FileInputStream("Umlenken.java"));
// produziert "deprecation"-Nachrichten, System.setOut() und
// System.setErr() erfordern ein PrintStream-Objekt als Argument
PrintStream aus = new PrintStream(
new BufferedOutputStream(
new FileOutputStream("test.aus")));
88 vgl. pr14150
100
Programmieren in Java
System.setIn(ein); System.setOut(aus); System.setErr(aus);
BufferedReader br =
new BufferedReader(new InputStreamReader(System.in));
String s;
while ((s = br.readLine()) != null)
System.out.println(s);
aus.close();
}
catch(IOException a)
{
a.printStackTrace();
}
}
}
1.4.4.4 Verwalten von Dateien und Verzeichnissen durch die Klasse File
1.4.4.5 Der Dateiauswahl-Dialog
101
Programmieren in Java
2. Hauptbestandteile der Sprache
2.1 Token
Token bedeuten in Java dasselbe wie Worte und Zeichensetzung für die menschliche
Sprache. Wenn Java den Quellcode mit dem Java-Compiler (javac) kompiliert, findet
eine Zerlegung des Quellcodes in kleine Bestandteile, Symbole oder Token genannt,
statt. Token müssen sich in sinnvolle Einheiten und gültige Arten einordnen lassen. Das
übernimmt der sog. Parser des javac, der Quelltext in Bytecode übersetzt und
zusätzlich noch Leerzeichen und Kommentare (aus dem Quelltext) entfernt.
Aus Token setzen sich die Ausdrucksformen von Java zusammen. Es gibt in Java fünf
Arten von Token: Bezeichner oder Identifizierer, Schlüsselwörter, Literale, Operatoren,
Trennzeichen.
Token
Schlüsselworte
Bezeichner, Identifiziere
Literal
Trennzeichen
Operatoren
Leeraum89
Kommentar
Beschreibung
Alle Wörter, die ein wesentlicher Teil der
Java-Sprachdefinition sind
Namen für Klassen,Objekte, Variablen,
Konstanten, Methoden, etc.; Namen sind
zusammengesetzt aus Unicode-Zeichen.
An erster Stelle eines Bezeichners darf
keine Zahl stehen.
Mit einem Literal können Variablen und
Konstanten bestimmte Werte zugewiesen
werden. Die können sämtliche in der JavaSprachdefinition erlaubte Arten von Werten
(numerische Werte, boolesche Werte,
Zeichen, Zeichenketten) sein
Symbol zum Anzeigen für Trennungen und
Zusammenfassungen von Code
Zeichen bzw. Zeichenkombinationen zur
Angabe einer auszuführenden Operation
mit einer oder mehreren Variablen oder
Konstanten
Zeichen, die in beliebiger Anzahl und an
jedem Ort zwischen Token mit Funktion
zur übersichtlichen Gestaltung des
Quellcodes plaziert werden können
Wird vom Compiler ignoriert. Eine
Besonderheit ist der „javadoc“-Kommentar,
der
vom
Java-Dokumentations-Tool
ausgewertet wird.
Bsp.
Public, class, static, void, String,
else, if, this, etc.
“Hallo Java“, 13, false, true
(){}[];.,
+ - * / , >>> <<<
Space,
Tab,
Formularvorschub
Zeilenende,
// .. Kommentar bis zum Zeilenende
/* Eingebetter
Kommentar*/
//* javadoc-Kommentar */
Abb.: Die Java-Token
Java benutzt den 16-Bit-Unicode-Zeichensatz. Die Unicode-Spezifikation ist sehr
umfangreich, die ersten 256 Zeichen entsprechen aber dem normalen ASCIIZeichensatz (Byte 1 ist immer auf 0 gesetzt). Der Java-Compiler erwartet Unicode90.
89 Technisch gesehen ist ein Leeraum kein Token
90 Alle Literale (d.h. alle vorkommenden Zeichen (Zahlen, Buchstaben, Sonderzeichen, etc.) bestehen aus
Unicode.
102
Programmieren in Java
Bei der Kompilierung wird der Quellcode automatisch in eine Folge von UnicodeZeichen transformiert. Eine ASCII-Kodierung wird einfach mit Voranstellen der
Zeichenfolge „\u00“ und folgender Hexadezimalzahl in das passende Unicode-Zeichen
übersetzt. Dies definiert in Java eine Escape-Sequenz, mit der alle Unicode-Zeichen
verschlüsselt werden können. Durch die „Escape-Sequenz-Darstellung“ des Quelltextes
in Java ist die Verwendung von Umlauten und anderen Sonderzeichen in Bezeichnern
möglich.
Zwei Bezeichner gelten in Java als identisch, wenn ihre Unicode-Darstellung
übereinstimmend ist. Deshalb muß auch in Java unbedingt zwischen Groß- und
Kleinschreibung unterschieden werden.
2.1.1 Schlüsselworte
Es gibt bestimmte Buchstabenkombinationen, die in Java eine besondere Bedeutung
haben. Diese Buchstabenfolgen werden in Java Schlüsselworte genannt
abstract
catch
do
finally
if
interface
outer
return
this
var
boolean
char
double
float
implements
long
package
short
throw
void
break
class
else
for
import
native
private
static
throws
volatile
byte
const
extends
future
inner
new
protected
super
transient
while
case
continue
false
generic
instanceof
null
public
switch
true
cast
default
final
goto
int
operator
rest
synchronized
try
Abb.: Reservierte Java Schlüsselworte
2.1.2 Bezeichner und Namenskonventionen
Bezeichner bzw. Identifizierer (identifier) sind Worte, die vom Programmierer gewählt,
zu Token werden und die Variablen, Konstanten, Klassen, Objekte, Beschriftungen und
Methoden darstellen. Bezeichner dürfen nicht mit Java-Schlüsselworten identisch sein.
Es gibt in Java einige Regeln zur Namensvergabe, die unbedingt eingehalten werden
müssen:
-
Bezeichner bestehen aus Unicode-Buchstaben und Zahlen in unbeschränkter Länge.
Das erste Zeichen eines Bezeichners muß ein Buchstabe, der Unterstrich „_“ oder das Zeichen „$“ sein. Alle
folgenden Zeichen sind entweder Buchstaben oder Zahlen.
Zwei Token gelten nur dann als derselbe Bezeichner, wenn sie dieselbe Länge haben, und jedes Zeichen im
ersten Token genau mit dem korrespondierenden Zeichen des zweiten Token übereinstimmt, d.h. den
vollkommen identischen Unicode-Wert hat. Java unterscheidet Groß- und Kleinschreibung.
Es hat sich eingebürgert, einige Namenskonventionen in Java einzuhalten:
-
Man sollte möglichst „sprechende“ Bezeichner wählen (Ausnahme sind Schleifen, wo für Zählvariablen
meistens nur ein Buchstabe (z.B. i, j) verwendet wird.
Konstanten (Elemente mit dem „Modifizierer“ „final“) sollten vollständig groß geschrieben werden.
103
Programmieren in Java
-
-
Identifizierer von Klassen sollten mit einem Großbuchstaben beginnen und anschließend klein geschrieben
werden. Wenn sich ein Bezeichner aus mehreren Wörtern zusammensetzt, dürfen diese nicht getrennt
werden. Die jeweiligen Anfangsbuchstaben innerhalb des gesamten Bezeichners werden jedoch groß
geschrieben.
Die Identifizierer von Variablen, Methoden und Elementen beginnen mit Kleinbuchstaben und werden auch
anschließend klein geschrieben. Wenn ein Bezeichner sich aus mehreren Wörtern zusammensetzt, dürfen
diese nicht getrennt werden. Die jeweiligen Anfangsbuchstaben der folgenden Wörter können jedoch
innerhalb des Gesamtbezeichners groß geschrieben werden.
2.1.3 Literale
Literale sind Token, die für zu speichernde Werte der Datentypen byte, short, int,
long, float, double, boolean und char stehen. Literale werden auch zur
Darstellung von Werten benutzt, die in Zeichenketten gespeichert werden.
Es gibt: Boolesche Literale, Zeichenliterale, Zeichenkettenliterale, Gleitpunktliterale,
ganzzahlige Literale.
1. Ganzzahlige Literale
Die Datentypen „int“ und „long“ können dezimal, hexadezimal und oktal beschrieben
werden. Die Voreinstellung ist dezimal und gilt immer dann, wenn Werte ohne weitere
Änderung dargestellt werden. Hexadezimale Darstellung beginnt immer mit der
Sequenz 0x oder 0X91. Oktale Darstellungen beginnen mit einer führenden Null.
Negativen Ganzzahlen wird ein Minuszeichen vorangestellt. Ganzzahlige Literale haben
gemäß Voreinstellung den Typ „int“. Durch Anhängen vom „l“ bzw. „L“ kann man
jedoch explizit den Typ „long“ wählen.
2. Gleitpunktliterale
Gleitpunktliterale bestehen aus mehreren Teilen. Sie erscheinen in folgender
Reihenfolge:
Teil
Ganzzahliger Teil
Dezimalpunkt
Gebrochener Teil
Exponent
Typensuffix
Nötig?
Nicht, wenn der gebrochene Teil
vorhanden ist
Nicht, wenn ein Exponent vorhanden ist.
Muß vorliegen, wenn es einen
Dezimalpunkt gibt
Darf nicht vorhanden sein, wenn es
keinen Dezimalpunkt gibt. Muß
vorhanden sein, wenn es keinen
ganzzahligen Teil gibt
Nur, wenn es keinen Dezimalpunkt gibt
Nein. Bei Abwesenheit eines
Typensuffix wird angenommen, daß es
sich um eine Zahl doppelter Genauigkeit
handelt.
Beispiele
0, 1, 2, ... , 9, 131140
0, 1, 1311, 41421, 9944, 718281828
E23, E-19, E6, e+307
f, F, d, D
91 Die Zahlen 0 bis 15 werden durch die Buchstaben „A“ bis „F“ bzw. „a“ bis „f“ dargestellt (Groß- oder
Kleinschreibung ist hier nicht relevant.
104
Programmieren in Java
Bsp.: Typensuffix für Ganzzahlen bzw. Gleitpunktzahlen
class Literale
{
char c = 0xffff;
// max. hex. Wert fuer char
byte b = 0x7f;
// max. hex. Wert fuer byte
short s = 0x7fff;
// max. hex. Wert fuer short
int i1 = 0x2f;
// hexadezimal in Kleinbuchstaben
int i2 = 0X2F;
// hexadezimal in Grossbuchstaben
// hexadeimale und oktale Werte mit long
long n1 = 200L;
// "long"-Suffix
long n2 = 200l;
// "long"-suffix
long n3 = 200;
// float- und double-Literale
float f1 = 1;
float f2 = 1F;
// "float"-Suffix
float f3 = 1f;
// "float"-Suffix
float f4 = 1e-45f;
// wissenschaftl. Schreibweise
float f5 = 1e+9f;
// "float"-Suffix
double d1 = 1d;
// "double"-Suffix
double d2 = 1D;
// "double"-Suffix;
double d3 = 47e47d;
// wissenschaftl. Schreibweise
}
3. Boolesche Literale
Es gibt zwei Boolesche Literale: true und false. Es gibt keinen Nullwert und kein
numerisches Äquivalent.
4. Zeichenliterale
Sie werden in einzelne, hochgestellte Anführungszeichen gesetzt. Als einzelne Zeichen
gelten alle druckbaren Zeichen mit Ausnahme des Bindestrichs „-“ und des Backslash
„\“. Bei Escape-Zeichenliteralen folgen den Anführungszeichen der Backslash und dem
Backslash eine der folgenden Zeichen:
- b, t, n, f, r, <<, ‚ oder \
- eine Serie von Oktalziffern (dreistellig)92
- ein „u“ gefolgt von einer vierstelligen Serie von Hexadezimalziffern, die für ein nicht zeilenbeendendes
Unicode-Zeichen stehen. Die vier Stellen der hexadezimalen Unicode-Darstellung (\u000 bis \uFFF)
stehen für 65535 mögliche Kodierungen.
Escape-Literal
‘\b‘
‘\t‘
‘\n‘
‘\f‘
‘\r‘
‘\“
Unicode-Steuersequenz
\u0008
\u0009
\u000a
\u000c
\u000d
\u0022
Oktal-Sequenz
\010
\011
\012
\014
\015
\042
Bedeutung
Backspace
Tab
Neue Zeile
Formularvorschub
Return
Doppeltes Anführungszeichen
92 Die als oktale Escape-Literale bezeichneten Zeichenliterale können zur Darstellung aller Unicode-Werte von
„\u0000“ bis „\u00ff“ benutzt werden. Bei oktaler Darstellung ist diese Darstellung auf „\000“ bis auf „\u377“
begrenzt.
105
Programmieren in Java
‘\‘
‘\\‘
\u0027
\u005c
\047
\134
Einfaches Anführungszeichen
Backslash
Abb.: Escape-Literale
5. Zeichenkettenliterale
Zeichenkettenliterale sind aus mehreren Zeichenliteralen zusammengesetzte Ketten
(Strings). Bei Zeichenkettenliteralen werden null oder mehr Zeichen in Anführungszeichen (“)dargestellt. Java erzeugt Zeichenketten als Instanz der Klasse String.
Damit stehen alle Methoden der Klasse String zur Manipulation einer Zeichenkette
zur Verfügung.
Zeichenkettenliterale stehen zwischen zwei Anführungszeichen (“) und können
Steuerzeichen wie Tabulatoren, Zeilenvorschübe, nichtdruckbare Unicode-Zeichen und
druckbare Unicode-Spezialzeichen enthalten. Es kann sich auch um EscapeSequenzen handeln. Beide Anführungszeichen müssen in derselben Zeile des
Quellcodes stehen.
Jedes String-Literal ist eine Referenz auf ein Objekt der Klasse String. Falls der
Compiler beim Übergeben des Quelltextes ein string-Literal findet, erzeut er ein neues
String-Objekt und verwendet es anstelle des Literals.
In Java ist der Operator + auch auf Strings definiert. Auf zwei String-Objekte angewendet, liefert er die Verkettung beider Objekte.
2.1.4 Trennzeichen
Trennnzeichen sind Token, die aus einem einzigen Zeichen bestehen und andere Token
trennen. Java kennt 9 Trennzeichen:
(
)
{
}
[
]
;
,
.
Wird sowohl zum Öffnen einer Parameterliste für eine Methode als auch zur Festlegung eines
Vorrangs für Operationen in einem Ausdruck benutzt.
Wird sowohl zum Schließen einer Parameterliste für eine Methode als auch zur Festlegung eines
Vorrangs für Operationen in einem Ausdruck benutzt
Wird zu Beginn eines Blocks mit Anweisungen oder einer Initialisierungsliste gesetzt
Wird an das Ende eines Blocks mit Anweisungen oder einer Initialisierungsliste gesetzt
Steht vor einem Ausdruck, der als Index für ein Datenfeld (Array) steht
Folgt einem Ausdruck, der als Index für ein Datenfeld dient
Dient sowohl zum Beenden einer Ausdrucksanweisung als auch zum Trennen der Teile einer forAnweisung.
wird in vielen Zusammenhängen als Begrenzer verwendet
Wird sowohl als Dezimalpunkt als auch zum Trennen solcher Dinge wie Paketnamen von
Klassennamen oder Variablennamen benutzt.
Abb.: Die Java-Trennzeichen
106
Programmieren in Java
2.1.5 Operatoren
Operatoren geben an, welche Operation mit einem oder mehreren gegebenen
Operanden durchgeführt werden soll. Es gibt in Java 37 Zeichensequenzen, die Token
darstellen, welche als Operatoren benutzt werden.
Arithmetische Operatoren
Arithmetische Operatoren benutzen nur zwei Operanden. Diese sind entweder
ganzzahlige Werte oder Gleitpunktzahlen. Als Rückgabe einer arithmetischen Operation
erhält man einen neuen Wert, dessen Datentyp sich folgendermaßen ergibt:
- Zwei ganzzahlige Datentypen (byte, short, int oder long) als Operanden ergeben immer einen
ganzzahligen Datentyp als Ergebnis. Dabei kann als Datentyp int oder long entstehen, byte und
short sind nicht möglich. Der Datentyp long entsteht nur, wenn einer der beiden Operanden bereits
vom Datentyp long war oder das Ergebnis von der Größe her nur als long dargestellt werden kann.
- Zwei Gleitpunktzahlentypen als Operanden ergeben immer einen Gleitpunktzahlentyp als Ergebnis.
Die Anzahl der Stellen des Ergebnisses ist immer das Maximum der Stellenanzahl der beiden
Operanden.
- Falls die Operanden ein ganzzahliger Typ und eine Gleitpunktzahlentyp sind, dann ist das Ergebnis
immer ein Gleitpunktzahlentyp.
Operator
+
*
/
%
Bedeutung
Additionsoperator
Subtraktionsoperator
Multiplikationsoperator
Divisionsoperator
Modulo-Operator
Beispiel
13 + 11
13 - 11
13 * 11
13 / 11
13 % 11
Abb.: Die arithmetischen Java-Operatoren
Einstellige arithmetische Operatoren
Es gibt zwei einstellige (d.h. mit nur einem Operanden) arithmetische Operatoren in
Java:
- Einstellige arithmetische Negierung: - Das Gegenteil der arithmetischen Negierung: +
Arithmetische Zuweisungsoperatoren
Neben dem direkten Zuweisungsoperator gibt es die arithmetischen
Zuweisungsoperatoren. Diese sind eigentlich nur eine Abkürzung für arithmetische
Operationen mit ganzen Zahlen und Gleitpunktzahlen. Das Ergebnis einer Zuweisung
über einen arithmetischen Zuweisungsoperator steht immer auf der linken Seite.
Operator
+=
-=
*=
/=
Bedeutung
Additions- und Zuweisungsoperator
Subtraktions- und Zuweisungsoperator
Multiplikations- und Zuweisungsoperator
Divisions- und Zuweisungsoperator
107
Programmieren in Java
%=
=
Modulo- und Zuweisungsoperator
Direkter Zuweisungsoperator
Abb.: Die arithmetischen Zuweisungsoperatoren
Inkrement- / Dekrement-Operatoren
Inkrement- und Dekrement-Operatoren sind einstellige Operatoren und werden nur in
Verbindung mit einem ganzzahligen Wert oder einer Gleitpunktzahl benutzt.
Der Inkrement-Operator (++) erhöht den Wert des Operanden um 1. Steht der
Operator vor dem Operanden, erfolgt die Erhöhung des Werts, bevor der Wert dem
Operanden zugewiesen wird. Wenn er hinter dem Operanden steht, erfolgt die
Erhöhung, nachdem der Wert bereits zugewiesen wurde.
Der Dekrement-Operator (--) erniedrigt den Wert des Operanden um 1. Steht der
Operator vor dem Operanden, erfolgt die Erniedrigung des Werts, bevor der Wert dem
Operanden zugewiesen wird. Wenn er hinter dem Operanden steht, erfolgt die
Erniedrigung, nachdem der Wert bereits zugewiesen wurde.
Bitweise arithmetische Operatoren
Bitweise Arithmetik wird im wesentlichen zum Setzen und Testen einzelner Bits und
Kombinationen einzelner Bits innerhalb einer Variablen benutzt.
Operator
&
|
^
~
Beschreibung
Bitweiser AND-Operator
Bitweiser OR-Operator
Bitweiser XOR-Operator
Bitweiser Komplement-Operator
Abb.: Bitoperatoren in Java
Bitweise Verschiebungsoperatoren
Bitweise Verschiebungsoperatoren verschieben die Bits in einer ganzen Zahl.
Operator
<<
>>
>>>
Beschreibung
Operator für bitweise Verschiebung nach links
Operator für bitweise Verschiebung nach rechts
Operator für binäre Verschiebung nach rechts mit Füllnullen
Abb.: Die bitweisen Verschiebungsoperatoren
108
Programmieren in Java
Bsp.: Bitweises Manipulieren93
import java.util.*;
public class BitManipulation
{
public static void main(String[] args)
{
Random rand = new Random();
int i = rand.nextInt();
int j = rand.nextInt();
pBinInt("-1",-1);
pBinInt("+1",+1);
int maxpos = 2147483647;
pBinInt("maxpos", maxpos);
int maxneg = -2147483648;
pBinInt("maxneg",maxneg);
pBinInt("i",i);
pBinInt("~i",~i);
pBinInt("-i",-i);
pBinInt("j",j);
pBinInt("i & j",i & j);
pBinInt("i | j",i | j);
pBinInt("i ^ j",i ^ j);
pBinInt("i << 5",i << 5);
pBinInt("i >> 5",i >> 5);
pBinInt("(~i) >> 5",(~i) >> 5);
pBinInt("i >>> 5",i >>> 5);
pBinInt("(~i) >>> 5", (~i) >>> 5);
long l = rand.nextLong();
long m = rand.nextLong();
pBinLong("-1L",-1L);
pBinLong("+1L",+1L);
long ll = 9223372036854775807L;
pBinLong("maxpos", ll);
long lln = -9223372036854775807L;
pBinLong("maxneg",lln);
pBinLong("l",l);
pBinLong("~l",~l);
pBinLong("-l",-l);
pBinLong("m",m);
pBinLong("l & m",l & m);
pBinLong("l | m",l | m);
pBinLong("l ^ m",l ^ m);
pBinLong("l << 5",l << 5);
pBinLong("l >> 5",l >> 5);
pBinLong("(~l) >> 5",(~l) >> 5);
pBinLong("l >>> 5",l >>> 5);
pBinLong("(~l) >>> 5", (~l) >>> 5);
}
static void pBinInt(String s, int i)
{
System.out.println(s + ", int: " + i + ", binaer: ");
System.out.print("
");
for (int j = 31; j >= 0; j--)
if (((1 << j) & i) != 0)
System.out.print("1");
else
93 pr21502
109
Programmieren in Java
System.out.print("0");
System.out.println();
}
static void pBinLong(String s, long l)
{
System.out.println(s + ", long: " + l + ", binaer: ");
System.out.print("
");
for (int j = 63; j >= 0; j--)
if (((1L << j) & l) != 0)
System.out.print("1");
else
System.out.print("0");
System.out.println();
}
}
Die Ausgabe zu dieser Anwendung zeigt für den Bereich „int“ jeweils die interne
Zahlendarstellung:
-1, int: -1, binaer:
11111111111111111111111111111111
+1, int: 1, binaer:
00000000000000000000000000000001
maxpos, int: 2147483647, binaer:
01111111111111111111111111111111
maxneg, int: -2147483648, binaer:
10000000000000000000000000000000
i, int: -1465644750, binaer:
10101000101001000000100100110010
~i, int: 1465644749, binaer:
01010111010110111111011011001101
-i, int: 1465644750, binaer:
01010111010110111111011011001110
j, int: -1273964081, binaer:
10110100000100001101100111001111
i & j, int: -1610610430, binaer:
10100000000000000000100100000010
i | j, int: -1128998401, binaer:
10111100101101001101100111111111
i ^ j, int: 481612029, binaer:
00011100101101001101000011111101
i << 5, int: 344008256, binaer:
00010100100000010010011001000000
i >> 5, int: -45801399, binaer:
11111101010001010010000001001001
(~i) >> 5, int: 45801398, binaer:
00000010101110101101111110110110
i >>> 5, int: 88416329, binaer:
00000101010001010010000001001001
(~i) >>> 5, int: 45801398, binaer:
00000010101110101101111110110110
110
Programmieren in Java
Bitweise Zuweisungsoperatoren
Bitweise Zuweisungsoperatoren verwenden einen Wert, führen eine entsprechende
bitweise Operation mit dem zweiten Operanden durch und legen das Ergebnis als Inhalt
des ersten Operanden ab.
Operator
Beschreibung
&=
Bitweiser AND-Zuweisungsoperator
|=
Bitweiser OR-Zuweisungsoperator
^=
Bitweiser XOR-Zuweisungsoperator
<<=
Zuweisungsoperator für die bitweise Verschiebung nach links
>>=
Zuweisungsoperator für die bitweise Verschiebung nach rechts
>>>=
Zuweisungsoperator für die bitweise Verschiebung nach rechts mit Füllnullen
Abb.: Die bitweisen Zuweisungsoperatoren
Vergleichsoperatoren
Vergleichsoperatoren haben zwei Operanden und vergleichen diese (zwei Ganzzahlen
oder zwei Gleitpunktzahlen) 1. Als Rückgabewert der Operation entsteht ein boolescher
Wert (true oder false).
Operator
==
!=
<
>
<=
>=
Bedeutung
Gleichheitsoperator
Ungleichheitsoperator
Kleiner-als-Operator
Größer-als-Operator
Kleiner-als-oder-gleich-Operator
Größer-als-oder-gleich-Operator
Abb.: Die Java-Vergleichsoperatoren
Die relationalen Operatoren „==“ und „!=“ arbeiten mit allen Objekten. Ihre Anwendung
zeigt häufig ein „verwirrendes Ergebnis“, z.B.95:
public class GleichheitsTest
{
public static void main(String[] args)
{
Integer i1 = new Integer(13);
Integer i2 = new Integer(13);
System.out.println(i1 == i2); // false
System.out.println(i1 != i2); // true
}
}
Der Inhalt der Objekte ist zwar gleich, die Referenzen auf die Objekte sind nicht gleich.
Falls der aktuelle Infalt auf Gleichheit überprüft werden soll, steht die Methode equals()96
bereit, z.B.:
94 Die Werte zweier Variablen vom Typ char können ebenfalls mit den Vergleichsoperatoren verglichen werden.
Es werden die Variablen vom Typ char bei der Verwendung eines Vergleichsoperators wie ganze 16-Bit-Zahlen
entsprechend ihrer Unicode-Codierung behandelt.
95 Vgl. pr21500
111
Programmieren in Java
public class EqualsMethode
{
public static void main(String[] args)
{
Integer i1 = new Integer(13);
Integer i2 = new Integer(13);
System.out.println(i1.equals(i2));
}
}
// true
Logische Vergleichsoperatoren
Die logischen Vergleichsoperatoren werden nur auf boolesche Operatoren angewandt
und erzeugen nur boolesche Ergebnisse.
Operator
&&
||
!
Beschreibung
Logischer AND-Operator
Logischer OR-Operator
Logischer NOT-Operator
Abb.: Logische Vergleichsoperatoren
96 Viele in der Java-Bibliorhek bereitgestellte Klassen implementieren die Methode equals()
112
Programmieren in Java
2.1.6 Kommentare, eingebettete Dokumentation
Es gibt drei Arten von Kommentaren
- Einzeilige Kommentare beginnen mit // und enden mit dem Ende der Zeile
- Mehrzeilige Kommentare (Blockkommentare) beginnen mit /* und enden mit */. Sie können sich über
mehrere Zeilen erstrecken.. Blockkommentare dürfen nicht geschachtelt sein.
- Dokumentationskommentare beginnen mit /** und enden mit */. Sie können sich ebenfalls über mehrere
Zeilen erstrecken und sind spezielle Kommentare, die für das javadoc-System benutzt werden.
Javadoc dient zum Erzeugen von API-Dokumentationen aus anderem Java-Quellcode.
Das JDK verfügt über das Dokumentations-Tool javadoc, das auf der Basis von
speziellen Kommando-Tags innerhalb einer Java-Quelltextdatei eine HTML-Datei als
API-Dokumentation der angegebenen Datei97 oder des Pakets erzeugt.
Tag
@see [Klassenname]
@see
[Klassenname]#[Methodenname]
@param [Parametername]
[Beschreibung]
@version [Text]
@author [Name]
@return [Beschreibung]
@exception [Klassenname]
@deprecated [Ausdruck]
@since [Text]
Beschreibung
Dieses tag erzeugt einen Verweis in der HTML-Datei zu der Klasse,
die von Klassenname spezifiziert wird. Dabei kann der Klassenname
auch eine vollständige Pfadangabe sein
Dieses Tag erzeugt einen Verweis („See also“-Link) in der HTMLDatei zu der Methode (innerhalb der mit Klassenname spezifizierten
Klasse), die von Methodenname spezifiziert wird
Dieses Tag dient zur Dokumentation der Parameter. Parametername
ist selbsterklärend, und die Beschreibung spezifizeirt den
Parameter. Geht die Beschreibung über eine Zeile hinaus, so wird
dies in der nächsten Zeile fortgeführt.
Mit diesem Tag kann die Version des Programms spezifiziert
werden.
Diese Tag fügt den Namen des Autors in die HTML-Datei ein
Mit diesem Tag kann der wert beschrieben werden, der von einer
Methode zurückgegeben wird
Dieses Tag erzeugt einen Link auf Ausnahmen, die von der Klasse,
die durch Klassenname spezifiziert ist, erzeugt werden.
Neu seit Version 1.1. Das Tag markiert eine Klasse, ein Interface,
ein Feld oder eine Methode als nicht verwendbar in weiteren
Anwendungen´. Bereits existierender Code wird trotz dieser
kennzeichnung weiter kompiliert und laufen, aber der Compiler wird
eine Warnung generieren,
Spezifiziert, wann das Release erstellt wurde.
Abb.: Die javadoc-Tags
Die Syntax von javadoc:
javadoc [Optionen] [Dateiname]
javadoc [Klassenname] [Paketname]
Falls ein Paketname angegeben wurde, dann dokumentiert javadoc alle JavaQuelldateien innerhalb des aktuellen Verzeichnisses und anschließend das
dazugehörige Paketverzeichnis98 relatin zum CLASSPATH. Für jede Klasse wird ein
eigenes HTML-Dokument erzeugt und für die Klassen innerhalb eines Pakets ein
HTML-Index generiert.
97 vgl. 1.3.2.2, 3. Aufgabe
98 als Paketname zu verstehen, nicht als physikalisches Verzeichnis
113
Programmieren in Java
Option
-version
-classpath
[Verzeichnis]
-v
-verbose
-d [Verzeichnis]
-public
-protected
-private
-package
-Jflag
-notree
-doencoding [Name]
-sourcepath [Pfad]
Beschreibung
Diese Option veranlaßt javadoc, die version des JDK anzuzeigen.
Diese Angabe korrespondiert wie bei den anderen JDK-Komponenten mit der
Umgebungsvariablen CLASSPATH und teilt in diesem Fall javadoc mit, in welchen
Verzeichnissen nach den Quelltext-Dateien zu suchen ist. Die Verzeichnisse
werden in der Unix-Syntax durch Doppelpunkte getrennt, Windows benutzt das
Semikolon. Die angegebenen Pfade werden in der Reihenfolge ihres Auftretens
durchsucht.
Identisch mit der Option –y
Mit dieser Option wird javadoc gezeigt, in welchem Verzeichnis sich das HTMLDokument zur Dokumentation nach der Generierung befinden soll. Normalerweise
wird das aktuelle Verzeichnis verwendet.
Zeigt nur die öffentlichen elemente an
Zeigt die geschützten und die öffentlichen Klassen und Methoden an
Zeigt alle Klassen und Methoden an
Zeigt die Packages, die geschützten und öffentlichen Klassen und Methoden an
Gibt ein Flag direkt an das Runtime-System weiter
Wenn diese Option gesetzt ist, wird keine Klassenhierarchie generiert.
Gibt den Suchpfad für zu dokumentierende Quelldateien an. Beeinflußt das laden
von Klassendateien nicht. Wird die Option nicht verwendet, dokumentiert javadoc
defaultmäßig nur die im aktuellen verzeichnis vorhandenen Klassen und
Packages im Tree-Bereich der Dokumentation. Sollen Klassen und Packages in
anderen verzeichnissen mit dokumentiert werden, müssen diese hier angegeben
werden.
Abb.: Die javadoc-Optionen
114
Programmieren in Java
2.2 Typen
Ein Typ gibt in einer Computersprache an, wie etwas (z.B. eine Variable) im Speicher
des Rechners dargestellt wird. In Java gibt es vier verschiedene Arten von Typen:
„primitive Datentypen“, Datenfelder (Arrays) , Klassen und Schnittstellen
2.2.1 Primitive Datentypen
Java besitzt 8 primitive Datentypen.
Bezeichnung
byte
Typ
8 Bits
short
16 Bits
int
32 Bits
long
64 Bits
float
32 Bits
double
64 Bits
char
16 Bits
boolean
1 Bit
Beschreibung
Kleinster Wertebereich. Mit Vorzeichen. Wird zum Darstellen von
Ganzzahlwerten (ganzzahliges Zweierkomplement) von (-2 hoch 7 = 128) bis (+2 hoch 7 = 128) verwendet.
Wertebereich mit Vorzeichen. Wird zum Darstellen von Ganzzahlwerten
(ganzzahliges Zweierkomplement) von (-2 hoch 15 = -32167) bis (+2
hoch 15 - 1 = 32.167) verwendet.
Standardwertebereich. Wird zum Darstellen von Ganzzahlwerten
(ganzzahliges Zweierkomplement) von (-2 hoch 31 = -2.147.483.648) bis
(+2 hoch 31 = 2.147.483.647) verwendet.
Größter Wertebereich. Wird zum Darstellen von Ganzzahlwerten
(ganzzahliges Zweierkomplement) von (-2 hoch 63) bis (+2 hoch 63)
verwendet.
Kürzester Wertebereich mit Vorzeichen zur Darstellung von
Gleitpunktzahlenwerten. Dies entspricht Gleitpunktzahlen mit einfacher
Genauigkeit, die den IEEE-754-1985-Standard benutzen. Es existiert
ein Literal zur Darstellung von plus/minus unendlich sowie der Wert NaN
(Not a Number) zur Darstellung von nicht definierten Ergebnissen
Größter Wertebereich mit Vorzeichen zur Darstellung von
Gleitpunktzahlenwerten. Der Wertebereich liegt zwischen +/-10 hoch
317. Auch diese Gleitpunktzahlen benutzen den IEEE-754-1985Standard. Es existiert wie beim Typ float ein Literal zur Darstellung von
plus/minus unendlich sowie der Wert NaN (Not a Number) zur
Darstellung von nicht definierten Ergebnissen
Darstellung eines Zeichens des Unicode-Zeichensatzes. Zur Darstellung
von alphanumerischen Zeichen wird dieselbe Kodierung wie beim ASCIIZeichensatz verwendet, aber das höchste Bit ist auf 0 gesetzt. Der
Datentyp ist als einziger primitiver Java-Datentyp vorzeichenlos. Der
Maximalwert ist \uFFFF.
Einer char-Variablen kann, da sie 2 Bytes lang ist, eine Ganzzahl
zwischen 0 und 65535 ohne Konvertierung zugewiesen werden. Die
Umkehrung – also die Zuweisung von char-Zeichen an andere
Datentypen – funktioniert nur für int problemlos. Für andere
Datentypen ist keine direkte Zuweisung möglich.
Der
boolschesche Datentyp umfaßt die Werte: true oder false
(Defaultwert). Logische Vergleiche sind in Java vom Typ boolean. Auch
boolesche Variablen sind dem Datentyp boolean zugeordnet. Werte
vom Typ boolean sind zu allen anderen Datentypen inkompatibel und
lassen sich nicht durch Casting in andere Typen überführen.
Abb.: Primitive Datentypen der Java-Sprache
115
Programmieren in Java
2.2.2 Operationen mit primitiven Datentypen
Operationen mit booleschen Variablen
Operation
=
Name
Zuweisung
==
Gleichheit
!=
Ungleichheit
!
&
|
^
Logisches NOT
AND
OR
XOR
&&
Logisches AND
||
Logisches OR
?:
if-then-else
Bedeutung
Einer booleschen Variable wird der Wert true oder false
zugewiesen
Durchführung eines Vergleichs innerhalb einer Kontroll-struktur.
Rückgabe ist true, wenn beide boolesche Operanden
denselben Wert (true oder false) haben. Anderenfalls wird
false zurückgegeben.
Durchführung eines Vergleichs innerhalb einer Kontroll-struktur.
Rückgabe ist true, wenn beide boolesche Operanden
unterschiedliche Werte (true oder false) haben. Anderenfalls
wird false zurückgeben.
Falls der Operand false ist, wird true zurückgegeben
Rückgabewert ist true, falls beide Operanden true sind
Rückgabewert ist false, falls beide Operanden false sind.
Rückgabewert ist true, falls genau ein Operand true ist
(exklusives Oder)
Rückgabe von true nur dann, wenn die beiden Operanden true
sind.
Rückgabe von false nur dann, wenn beide Operanden false
sind.
Diese Operation benötigt einen booleschen Ausdruck vor dem
Fragezeichen. Falls er true ist, wird der Wert vor dem
Doppelpunkt zurückgegeben, ansonsten der Wert hinter dem
Doppelpunkt.
Abb.: Operationen mit booleschen Variablen
Operationen mit Zeichenvariablen
Zeichenvariablen können Operanden in jeder ganzzahligen Operation sein und werden
wie ganze 16-Bit-Zahlen ohne Vorzeichen behandelt.
Operation
=
==
Name
!=
Ungleichheit
<,<=,>,>=
+,-
Relational
Vorzeichen
+,-,*,/
Binäre Arithmetik
+=,-=,*=,/=
Zuweisung
Bedeutung
Einer Zeichenvariablen wird ein Wert zugewiesen
Durchführung eines Vergleichs innerhalb einer Kontroll-struktur.
Rückgabe ist true, wenn beide Operanden denselben Wert
(Unicode-Werte stimmen überein) haben. Anderenfalls wird
false zurückgegeben.
Durchführung eines Vergleichs innerhalb einer Kontroll-struktur.
Rückgabe ist true, wenn beide boolesche Operanden
unterschiedliche Werte ( Bezogen auf die Unicode-Darstellung)
haben. Anderenfalls wird false zurückgeben
Operatoren zum Vergleich innerhalb einer Kontrollstruktur.
Vorzeichenoperatoren bei einem Operanden
Die Zeichenvariablen gehen in die Berechnung mit ihren
Unicode-Werten ein.
Additions-,
Subtraktions-,
Multiplikations-,
DivisionsZuweisungen
116
Programmieren in Java
++,-<<,>>,>>>
<<=,>>=,
>>>=
~
&
|
^
&=,|=,^=
Binäre Arithmetik
Inkrement- und Dekrement-Operatoren für den Unicode-Wert
von Zeichenvariablen
Verschiebung
Bitweise Verschiebeoperatoren: Operatoren für bitweises
Verschieben nach links, für bitweises Verschieben nach rechts
und für das bitweise Verschieben nach rechts mit Füllnullen.
Verschiebung
und Bitweise Verschiebungs- und Zuweisungsoperatoren (nach
Zuweisung
links, nach rechts und nach rechts mit Füllnullen
Bitweises NOT
Einstellige bitweise Komplementbildung. Wenn ein Zeichen
komplementiert wird, dann werden alle seine Bits invertiert.
Bitweises AND
Falls AND mit 2 Zeichenvariablen benutzt wird und das
Ergebnis in einem dritten Zeichen abgelegt wird, dann hat das
resultierende Zeichen nur für die Bits den Eintrag 1, wenn alle
beiden Operanden an der gleichen Stelle Bits mit dem Wert 1
hatten
Bitweises OR
Falls OR mit 2 Zeichenvariablen benutzt wird und das Ergebnis
in einem dritten Zeichen abgelegt wird, dann hat das
resultierende Zeichen nur für die Bits den Eintrag 1, wenn einer
der Operanden an dieser Position eine 1 hatte
Bitweises exklusives Falls XOR mit 2 Zeichenvariablen benutzt wird und das Resultat
OR
in einem 3. Zeichen abgelegt wird, dann hat das resultierende
Zeichen nur für die Bits den Eintrag 1, wenn das zugehörige Bit
in genau einem der beiden Operanden gesetzt ist.
Bitweise Zuweisung
Bitweise
AND-,
OR-,
exklusive
OR
(XOR)und
Zuweisungsoperatoren
Abb.: Operationen mit Zeichenvariablen
Operationen mit Gleitpunktzahlen
Operation
=,+=,-=,/=
==
Name
Zuweisung
Gleichheit
!=
Ungleichheit
<,<=,>,>=
+,+,-,*,/
+=,-=,*=,/=
Relational
Vorzeichen
Binäre Arithmetik
Zuweisung
++,--
Binäre Arithmetik
Bedeutung
Einer Gleitpunktzahl wird ein Wert zugewiesen
Durchführung eines Vergleichs innerhalb einer Kontroll-struktur.
Rückgabe ist true, wenn beide Operanden denselben Wert
haben. Anderenfalls wird false zurückgegeben
Durchführung eines Vergleichs innerhalb einer Kontroll-struktur.
Rückgabe ist true, wenn beide Operanden unterschiedliche
Werte haben. Anderenfalls wird false zurückgeben
Operatoren zum Vergleich innerhalb einer Kontrollstruktur
Vorzeichenoperatoren bei einem Operanden
Additions-, Subtraktions-, Multiplikations-, Divisions-Operatoren
Additions-,
Subtraktions-,
Multiplikations-,
DivisionsZuweisungen
Inkrement- und Dekrementoperatoren für den Wert der
Variablen.
Abb.: Operationen mit den Typen float und double
Java erzeugt keine Ausnahmen bei der Benutzung der Gleitpunktarithmetik. Ein
Überlauf99 oder das Teilen aller möglichen Zahlen (außer „Null durch Null“) führt zur
Ausgabe von positiven bzw. negativen „unendlichen“ Werten. Das Teilen von „Null durch
99 d.h.: Größeres Ergebnis von einer Operation als durch den Wertebereich des jeweiligen Typs ausgedrückt
werden kann.
117
Programmieren in Java
Null“ ergibt den Wert „NaN“ (keine Zahl). Ein Unterlauf100 gibt einen speziellen Wert aus:
„positiv oder negativ Null“. Dieser Wert kann mit Vergleichsoperatoren ausgewertet
werden (bewirkt false).
Operationen mit ganzzahligen Variablen (bzw. ganzzahligen Ausdrücken)
Operation
=,+=,-=,/=
==
!=
<,<=,>,>=
+,+,-,*,/
+=,-=,*=,/=
++,-<<,>>,>>>
<<=,>>=,
>>>=
~
&
|
^
&=,|=,^=
Name
Zuweisung
Gleichheit
Bedeutung
Einer ganzzahligen Variablen wird ein Wert zugewiesen
Durchführung eines Vergleichs innerhalb einer Kontroll-struktur.
Rückgabe ist true, wenn beide Operanden denselben Wert
haben. Anderenfalls wird false zurückgegeben
Ungleichheit
Durchführung eines Vergleichs innerhalb einer Kontroll-struktur.
Rückgabe ist true, wenn beide Operanden unterschiedliche
Werte haben. Anderenfalls wird false zurückgeben
Relational
Weitere
Operatoren
zum
Vergleich
innerhalb
einer
Kontrollstruktur
Vorzeichen
Vorzeichenoperatoren bei einem Operanden
Binäre Arithmetik
Additions-, Subtraktions-, Multiplikations-, Divisions-Operatoren
Zuweisung
Additions-,
Subtraktions-,
Multiplikations-,
DivisionsZuweisungen
Binäre Arithmetik
Additions-, Subtraktions-, Multiplikations-, Divisions-Operatoren
Bitweise Verschiebeoperatoren: Operatoren für bitweises
Verschieben nach links, für bitweises Verschieben nach rechts
und für das bitweise Verschieben nach rechts mit Füllnullen
Bitweise Verschiebungs- und Zuweisungsoperatoren (nach
links, nach rechts und nach rechts mit Füllnullen
Bitweises NOT
Einstellige bitweise Operation
Bitweises AND
Falls AND mit 2 Ganzzahlen benutzt wird und das Ergebnis in
einem dritten Ganzzahl abgelegt wird, dann hat die
resultierende Ganzzahl nur für die Bits den Eintrag 1, wenn alle
beiden Operanden an der gleichen Stelle Bits mit dem Wert 1
hatten
Bitweises OR
Falls OR mit 2 Ganzzahlen benutzt wird und das Ergebnis in
einer dritten Ganzzahl abgelegt wird, dann hat die resultierende
Ganzzahl nur für die Bits den Eintrag 1, wenn einer der
Operanden an dieser Position eine 1 hatte
Biweises
exklusives Falls XOR mit 2 Ganzzahlen benutzt wird und das Resultat in
OR
einer 3. Ganzzahl abgelegt wird, dann hat die resultierende
Ganzzahl nur für die Bits den Eintrag 1, wenn das zugehörige
Bit in genau einem der beiden Operanden gesetzt ist.
Bitweise Zuweisung
Inkrement- und Dekrementoperatoren für den Wert der Variablen
Abb.: Operationen mit ganzzahligen Variablen
Ganzzahlige Variable werden in Java als „Zweierkomplement“-Zahlen mit Vorzeichen
verwendet.
100 d.h.: kleineres Ergebnis (- außer Null -) von einer Operation als durch den Wertebereich des jeweiligen Typs
ausgedrückt werden kann.
118
Programmieren in Java
2.2.3 Datenfelder (Arrays)
Ein Datenfeld (Array) ist eine Ansammlung von Objekten eines bestimmten Typs1, die
über einen laufenden Index adressierbar sind. Gegenüber „normalen Objekten“ haben
Arrays zwei wesentliche Einschränkungen:
1. Arrays haben keine Konstruktoren. Statt dessen wird der Operator new mit spezieller Syntax
aufgerufen.
2. Es können keine Subklassen eines Datenfelds definiert werden.
Datenfelder gehören zu den Referenzvariablen1. Arrays können jeden Wertetyp
(primitiver Typ oder Objekt) enthalten, jedoch können in einem Array keine
unterschiedlichen Typen gespeichert werden.
Arrays in Java sind Objekte und unterscheiden sich von Datenfeldern in anderen
Programmiersprachen durch folgende Merkmale:
- Array-Variable sind Referenzen
- Arrays besitzen Methoden und Instanz-Variable
- Arrays werden zur Laufzeit erzeugt
2.2.3.1 Deklarieren, Erstellen von Array-Objekten
In Java umfaßt das Erstellen eines Datenfelds drei Schritte:
1. Deklaration einer Variablen zur Aufnahme eines Datenfelds.
Array-Variable zeigen den Objekttyp an, den das Datenfeld aufnimmt und den namen
des Arrays, gefolgt von leeren eckigen Klammern „[]“, z.B: int x[]. Alternativ dazu
kann eine Array-Variable auch so (Klammern nach dem Typ) festgelegt sein, z.B.
int[] x.
2. Erstellen eines neuen Array-Objekts und Zuweisen einer Array-Variablen.
Das kann erfolgen
- mit new, z.B. String[] namen = new String[10];
Hier wird ein neues Datenfeld von Strings mit 10 Elementen erstellt. Beim Erstellen
eines Array-Objekts mit new ist anzugeben, wieviele Elemente das Array aufnehmen
soll. Alle Elemente des Array werden automatisch initialisiert (0 für numerische
Datenfelder, false für boolesche, ‘\0‘ für Zeichen-Arrays und „null“ für alles
andere.
Bsp.: Erzeugen von Datenfeldern mit „new“
a) Aufnahme von Werten primitiver Typen
import java.util.*;
public class FeldPrim
{
static Random rand = new Random();
static int zRand(int mod)
{
101 Es kann sich dabei um primitive Variablentypen (byte, char, short, int, long, float, double, boolean), aber auch
um andere Datenfelder (verschachtelte Arrays) oder Objekte handeln.
102 Es gibt drei Arten von Referenzvariablen: Klassen, Schnittstellen und Datenfelder
119
Programmieren in Java
return Math.abs(rand.nextInt()) % mod;
}
public static void main(String[] args)
{
int[] a;
// Die Groesse des Array wird zur Laufzeit
// zufaellig bestimmt
a = new int[zRand(20)];
ausgabe("Laenge von a = " + a.length);
for (int i = 0; i < a.length; i++)
ausgabe("a[" + i + "] = " + a[i]);
}
static void ausgabe(String s)
{
System.out.println(s);
}
}
b) Aufnahme von Objekten
Ein Datenfeld, dessen Komponenten keine primitiven Typen aufnehmen
sollen, muß immer mit „new“ gefüllt werden.
import java.util.*;
public class FeldObj
{
static Random rand = new Random();
static int zRand(int mod)
{
return Math.abs(rand.nextInt()) % mod;
}
public static void main(String[] args)
{
Integer[] a;
// Die Groesse des Array wird zur Laufzeit
// zufaellig bestimmt
a = new Integer[zRand(20)];
ausgabe("Laenge von a = " + a.length);
for (int i = 0; i < a.length; i++)
{
a[i] = new Integer(zRand(500));
ausgabe("a[" + i + "] = " + a[i]);
}
}
static void ausgabe(String s)
{
System.out.println(s);
}
}
c) Variable Argumente
Das zweite Format in der Feldinitialisierung bestimmt eine bequeme
syntaktische Form für den „Aufruf von Methoden“, die den gleichen Effekt
besitzt wie die „Argumentenliste in C“. Über eine solche Argumentenliste kann
eine unbekannte Menge von Argumenten eines unbekannten Typs behandelt
werden. Da alle Klassen von der Klasse Object abstammen, kann eine
Methode mit einem Datenfeld als Argument verwendet werden, das aus
Objekten vom Typ Object besteht, z.B.
120
Programmieren in Java
class A
{
int i;
}
public class VarArgs
{
static void f(Object[] x)
{
for (int i = 0; i < x.length; i++)
System.out.println(x[i]);
}
public static void main(String[] args)
{
f(new Object[] {
new Integer(40), new VarArgs(),
new Float(3.14), new Double(11.11)
});
f(new Object[] { "eins", "zwei", "drei" });
f(new Object[] { new A(), new A(), new A() });
}
}
- durch direktes Initialisieren des Array-Inhalts, z.B.: String[] namen =
{"juergen", "bernd", "liesel", "dieter", "hans", "vera",
"christian", "theo", "emil", "karl"};
Alle Elemente der in der geschweiften Klammer stehenden Elemente müssen vom
gleichen Typ sein
3. Speichern von Elementen im Array
Zur Speicherung eines Werts in einem Array, wird der Array-Ausdruck „subscript“
benutzt, z.B.: x[subscript] . „subscript“ ist die Stelle (Position) für den Zugriff
auf eine Datenfeld-Element. Das Array-Subskript muß vom Typ int sein. Wegen der
vom Compiler vorgenommenen automatischen Typenkonvertierungen sind auch
short, byte, char zulässig. Indexausdrücke werden vom Laufzeitsystem auf
Einhaltung der Array-Grenzen überprüft.
2.2.3.2 Zugriff auf Datenfeld-Elemente, Ändern von Datenfeld-Elementen
Datenfeld-Subskripte beginnen mit 0. Alle Array-Subskripte werden beim Kompilieren
geprüft, ob sie sich innerhalb der Grenzem des Array befinden (Größer als 0, kleiner als
die Länge des Datenfelds).
String ort[] = new String[10];
ort[10] = "Dinkesbuehl";
ist falsch (Fehler beim Kompilieren). Das in „ort“ gespeicherte Array hat nur 10 ab 0
numerierte Elemente.
Wird das Array-Subskript zur Laufzeit berechnet und resultiert daraus ein Wert
außerhalb der Array-Grenzen, dann erzeugt der Java-Interpreter einen Fehler103. Die
103 Er weist auf eine „Ausnahme“ hin.
121
Programmieren in Java
Länge eines Datenfelds kann mit der Instanzvariablen length getestet werden, z.B.:
int laenge = ort.length;.
Zum Zuweisen eines Werts wird hinter dem Zugriffsausdruck die Zuweisungsanweisung
gestellt. Falls einem Array-Element ein Wert zugewiesen wird, dann wird auf das
betreffende Objekt eine Referenz erzeugt. Datenfelder mit primitiven Typen (z.B. int,
float) kopieren die Werte von einem Element in ein anderes.
2.2.3.3 Anwendungen mit eindimensionaler Datenfeldern
1. Sammeln
Datenfelder sind die wohl nützlichsten Objekte in Java, mit denen Objekte in leicht
zugänglichen Listen gesammelt werden können.
2. Suchen
a) sequentielle Suche
Aufgabenstellung: Gegeben ist eine ganze Zahl (Schlüssel schl) und ein Feld (Array) mit ganzzahligen
Werten, die in diesem Array beliebig verteilt sind. Such die Indexposition, für die gilt „a[i] == schl“.
Ist die Suche ergebnislos, dann gib „Nicht Gefunden“ zurück.
b) binäre Suche
Aufgabenstellung: Gegeben ist eine ganze Zahl (Schlüssel schl) und ein Array a mit ganzzahligen
Werten, die in diesem Feld in aufsteigender Folge vorliegen. Suche die Indexposition, für die gilt: a[i]
== schl“. Ist die Suche ergebnislos, dann gib „Nicht Gefunden“ zurück.
Lösungsverfahren: Es wird geprüft, ob der gesuchte Schlüssel sich in der Mitte des Array befindet. Falls
dies nicht der Fall ist, wird „schl < a[mitte]“ im linken Teil (von der Mitte aus gesehen) und für
„schl > a[mitte]“ im rechten Teil des Array gesucht. Diese Strategie kann fortgesetzt werden, bis
die Suche erfolgreich war oder der Schlüssel nicht gefunden wurde.
3. Sortieren104
Aufgabenstellung: Schreibe ein Programm, das die in einem Datenfeld („array“) gespeicherten ganzen
Zahlen nach einem einfachen Sortierverfahren („Sortieren durch Austauschen, Bubble Sort“) in
aufsteigende Sortierreihenfolge bringt.
Lösungsverfahren (Algorithmus): Man kann jeweils den ersten Schlüssel gegen alle weiteren Schlüssel
im Arbeitsspeicherfeld vergleichen und so an der ersten Position den kleinsten Schlüssel nach (N-1)
Vergleichen ermitteln. Danach vergleicht man jeweils den Schlüssel aus der zweiten Position mit allen
nachfolgenden Schlüsslwerten. Nach (N-2) Vergleichen steht der zweitkleinste Sachlüssel an der
zweiten Position. Bei Fortsetzung dieser Verfahrensweise ist schließlich nur noch ein einziger Schlüssel
vorhanden, der dann auf der richtigen, der letzten Position steht.
104 pr22301
122
Programmieren in Java
Bsp.:
1
37 22 18
22 37 37
18 18 22
9
9 9
25 25 25
2
9
9
37 37
22 22
18 18
25 25
9
22
37
18
25
3
9
18
37
22
25
9
18
37
22
25
9
18
22
37
25
4
9
18
22
37
25
9
18
22
25
37
Abb.: Tabelle mit 5 Schlüsseln zur Demonstration des Bubble-Sort
Vorschlag für die Implementierung:
import java.util.*;
class BubbleSort
{
public static void main(String args[])
{
int[] x;
x = new int[10];
Random zufallsZahl = new Random();
// Initialisieren des Array x
for (int i = 0; i < 10; i++)
{
x[i] = zufallsZahl.nextInt();
}
// Ausgabe des noch nicht sortierten x
System.out.println("Vor dem Sortieren:");
for (int i = 0; i < 10; i++)
{
System.out.println("x["+i+"] = " + x[i]);
}
boolean sortiert = false;
// Sortiere das Feld x
while (!sortiert)
{
sortiert = true;
for (int i=0; i < 9; i++)
{
if (x[i] > x[i+1])
{
int temp = x[i];
x[i] = x[i+1];
x[i+1] = temp;
sortiert = false;
}
}
}
// Gib das sortierte Feld x aus
System.out.println();
System.out.println("Nach dem Sortieren:");
for (int i = 0; i < 10; i++) {
System.out.println("x["+i+"] = " + x[i]);
}
}
}
123
Programmieren in Java
2.2.3.4 Mehrdimensionale Datenfelder
Java definiert mehrdimensionale Arrays durch Arrays von Arrays.
Zweidimensionale Datenfelder
Ein zweidimensionale Feld entspricht einer zweidimensionalen Wertetabelle, z.B.:
Z0
Z1
Z2
Z3
Z4
S0
1
1
1
1
1
S1
2
2
2
2
2
S2
3
3
3
3
3
S3
4
4
4
4
4
Das vorliegende Datenfeld umfaßt 5 Zeilen und 4 Spalten. Ein Datenfeld „M“ mit 4 Zeilen
und 5 Spalten ist folgendermaßen aufgebaut.
M[0][0]
M[1][0]
M[2][0]
M[3][0]
M[0][1]
M[1][1]
M[2][1]
M[3][1]
M[0][2]
M[1][2]
M[2][2]
M[3][2]
M[0][3]
M[1][3]
M[2][3]
M[3][3]
M[0][4]
M[1][4]
M[2][4]
M[3][4]
Zweidimensionale Datenfelder werden, wie das folgende Beispiel zeigt, auf gleiche
Weise erstellt und initialisiert wie eindimensionale Felder.
class Einheitsmatrix
{
publich static void main(String args[])
{
double[][] EM;
EM = new double[4][4];
for (int zeile = 0; zeile < 4; zeile++)
{
for (int spalte = 0; spalte < 4; spalte++)
{
if (zeile != spalte)
{
EM[zeile][spalte] = 0.0;
}
else {
EM[zeile][spalte] = 1.0;
}
}
}
}
}
Da mehrdimensionale Arrays als geschachtelte Arrays gespeichert werden, ist es
möglich, „nicht-rechteckige“ Arrays zu erzeugen, z.B.:
124
Programmieren in Java
public class KeinRechteckFeld
{
public static void main(String args[])
{
int a[][] = { {0},
{1,2},
{3,4,5},
{6,7,8,9}
};
for (int i = 0; i < a.length; i++)
{
for (int j = 0; j < a[i].length; j++)
{
System.out.print(a[i][j] + " ");
}
System.out.println();
}
}
}
Beispiele
1. Das 8-Damen-Problem105
Aufgabenstellung: Acht Damen sollen so auf einem Schachbrett positioniert werden, daß sie sich nicht
schlagen können, d.h.: Zwei Damen stehen nie in derselben Zeile oder Spalte oder Diagonale.
Algorithmus zur Lösung: Zu Beginn wird ein zweidimensionales Feld mit 0 gefüllt.
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
Danach wird zufällig im zweidimensionalen Feld eine „Dame“ gesetzt. Das Setzen der Dame wird durch
eine 9 markiert.
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
9
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
Durch das Positionieren der Dame sind Positionierungen von weiteren Damen in derselben Zeile und
Spalte wie die soeben positionierte Dame, aber auch in den zugehörigen Diagonalen nicht erlaubt. Die
nicht mehr zulässigen Positionen werden mit 1 markiert.
105 vgl. pr22305
125
Programmieren in Java
0
1
0
1
0
1
0
0
0
0
1
1
1
0
0
0
1
1
1
9
1
1
1
1
0
0
1
1
1
0
0
0
0
1
0
1
0
1
0
0
1
0
0
1
0
0
1
0
0
0
0
1
0
0
0
1
0
0
0
1
0
0
0
0
Damen dürfen jetzt nur noch auf die mit 0 markierten Komponenten des zweidimensionalen Felds
gebracht werden. Das geschieht solange bis alle Komponenten des zweidimensionalen Felds mit 1 oder
9 gefüllt sind. Wurden beim Füllen des Felds insgesamt 8 Damen positioniert, dann wurde eine Lösung
erreicht, die so aussehen könnte:
1
1
1
1
9
1
1
1
1
9
1
1
1
1
1
1
1
1
1
9
1
1
1
1
1
1
1
1
1
9
1
1
1
1
1
1
1
1
1
9
1
1
9
1
1
1
1
1
9
1
1
1
1
1
1
1
1
1
1
1
1
1
9
1
Vorschlag zur Implementierung:
import java.util.*;
public class Damen
{
// Konstanten
final int N = 8;
// Variable
boolean fuellen;
boolean ausgeben;
Random rand = new Random();
int zeile, spalte;
int[][] brett;
int aktZahl = 0;
int[] positionen = new int[N];
// Methoden
int zRand(int mod)
{
return Math.abs(rand.nextInt() % mod);
}
public void initialisiere()
{
fuellen = false;
ausgeben = false;
aktZahl = 0;
brett = new int[N][N];
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
{
brett[i][j] = 0;
}
}
public void nimmPosition()
126
Programmieren in Java
{
do {
zeile = zRand(N);
// System.out.println(zeile);
spalte = zRand(N);
// System.out.println(spalte);
} while (brett[zeile][spalte] != 0);
brett[zeile][spalte] = 9;
aktZahl++;
}
public void bewertePosition()
{
for (int i = 0; i < 8; i++)
{
if (i != spalte) brett[zeile][i] = 1;
}
for (int i = 0; i < 8; i++)
{
if (i != zeile) brett[i][spalte] = 1;
}
int lDzeile = zeile;
int lDspalte = spalte;
// Beruecksichtigung der nach links laufenden Diagonale
while ((lDzeile > 0) && (lDspalte > 0))
{
lDzeile--;
lDspalte--;
}
do {
if ((lDzeile != zeile) && (lDspalte != spalte))
{
brett[lDzeile][lDspalte] = 1;
}
lDzeile++;
lDspalte++;
} while ((lDzeile < 8) && (lDspalte < 8));
// ausgabe();
int rDzeile = zeile;
int rDspalte = spalte;
// Beruecksichtiung der nach rechts laufenden Diagonale
while ((rDzeile > 0) && (rDspalte < 7))
{
rDzeile--;
rDspalte++;
}
do {
if ((rDzeile != zeile) && (rDspalte != spalte))
{
brett[rDzeile][rDspalte] = 1;
}
rDzeile++;
rDspalte--;
} while ((rDzeile < 8) && (rDspalte >= 0));
}
public void pruefe()
{
int nullen = 0;
int anzDamen = 0;
for (int i = 0; i < 8; i++)
127
Programmieren in Java
for (int j = 0; j < 8; j++)
{
if (brett[i][j] == 0) nullen++;
if (brett[i][j] == 9) anzDamen++;
}
if (nullen == 0)
fuellen = true;
if (anzDamen == 8) ausgeben = true;
}
public void ausgabe()
{
System.out.println();
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 8; j++)
{
System.out.print(brett[i][j] + " ");
}
System.out.println();
}
System.out.println();
int k = 0;
for (int i = 0; i < brett.length; i++)
for (int j = 0; j < brett[i].length; j++)
{
if (brett[i][j] == 9) positionen[k++] = j;
}
for (k = 0; k < N; k++)
System.out.print(positionen[k]+" ");
System.out.println();
}
public void erzeugeDamen()
{
do
{
initialisiere();
do {
nimmPosition();
// ausgabe();
bewertePosition();
// ausgabe();
if (aktZahl > 4) pruefe();
} while (!fuellen);
} while (!ausgeben);
ausgabe();
}
}
public class Damentest
{
public static void main(String args[])
{
Damen d = new Damen();
d.erzeugeDamen();
}
}
128
Programmieren in Java
2. Pascal’sches Dreieck
1
1
1
2
1
1
1
1
3
4
1
3
6
5
6
1
10
15
1
4
10
20
1
5
15
1
6
1
Das Pascal'sche Dreieck wird mit Hilfe eines nicht rechteckigen Arrays abgebildet. Für
die ersten Elemente kann geschrieben werden:
int element[][] = {
{1},
{1,1},
{1,2,1}
};
In der folgenden Implementierung wird zu jeder Ebene dynamisch ein Feld mit der
passenden Länge hinzugefügt.
Bsp:106
public class Pascal
{
public static void main(String args[])
{
int dreieck[][] = new int[7][];
for (int i = 0; i < dreieck.length; i++)
{
dreieck[i] = new int[i+1];
for (int j = 0; j <= i; j++)
{
if ((j == 0) || (j == i))
dreieck[i][j] = 1;
else
dreieck[i][j] = dreieck[i - 1][j- 1] + dreieck[i - 1][j];
System.out.print(dreieck[i][j] + " ");
}
System.out.println();
}
}
}
106 vgl. pr22331, Pascal.java
129
Programmieren in Java
Multidimensionale Datenfelder
Zweidimensionale Datenfelder bilden nicht das Ende. Java kann Felder mit 3, 4 oder
mehr Dimensionen unterstützen. Es handelt sich dabei allerdings um ein Array mit
Arrays (die wiederum Arrays enthalten können usw. über beliebig viele
Dimensionen) 107.
Bsp.: Mehrdimensionale Felder
import java.util.*;
public class MehrdimFeld
{
static Random rand = new Random();
static int zRand(int mod)
{
return Math.abs(rand.nextInt()) % mod;
}
public static void main(String args[])
{
// 1. Bsp: Erzeugen eines mehrdimensionalen Felds
// mit Werten primitiver Typen
int[][] a1 = {
{ 1, 2, 3, },
{ 4, 5, 6, },
};
for (int i = 0; i < a1.length; i++)
for (int j = 0; j < a1[i].length; j++)
ausgabe("a1[" + i + "][" + j + "] = " + a1[i][j]);
// 2. Bsp.: Dreidimensionales Feld mit fester Laenge
int[][][] a2 = new int[2][2][4];
for (int i = 0; i < a2.length; i++)
for (int j = 0; j < a2[i].length; j++)
for (int k = 0; k < a2[i][j].length;k++)
ausgabe("a2[" + i + "][" + j + "][" + k + "] = "
+ a2[i][j][k]);
// 3. Bsp.: Dreidimensionales Feld mit variablen Laengen
int[][][] a3 = new int[zRand(7)][][];
for (int i = 0; i < a3.length; i++)
{
a3[i] = new int[zRand(5)][];
for (int j = 0; j < a3[i].length; j++)
{
a3[i][j] = new int[zRand(5)];
}
}
for (int i = 0; i < a3.length; i++)
for (int j = 0; j < a3[i].length; j++)
for (int k = 0; k < a3[i][j].length;k++)
ausgabe("a3[" + i + "][" + j + "][" + k + "] = "
+ a3[i][j][k]);
// Mehrdimensionales Feld mit nicht primitiven Objekten
Integer[][] a4 = {
{ new Integer(1), new Integer(2) },
{ new Integer(3), new Integer(4) },
{ new Integer(5), new Integer(6) },
};
107 Im Java werden multidimensionale Arrays streng genommen nicht unterstützt
130
Programmieren in Java
for (int i = 0; i < a4.length; i++)
for (int j = 0; j < a4[i].length; j++)
ausgabe("a4[" + i + "][" + j + "] = " + a4[i][j]);
// Array mit nicht primitiven Objekten der stueckweise
// aufgebaut wird
Integer[][] a5;
a5 = new Integer[3][];
for (int i = 0; i < a5.length; i++)
{
a5[i] = new Integer[3];
for (int j = 0; j < a5[i].length; j++)
a5[i][j] = new Integer(i * j);
}
for (int i = 0; i < a5.length; i++)
for (int j = 0; j < a5[i].length; j++)
ausgabe("a5[" + i + "][" + j + "] = " + a5[i][j]);
}
static void ausgabe(String s)
{
System.out.println(s);
}
}
2.2.3.5 Die Klasse Arrays
Seit dem JDK1.2 gibt es die Klasse Arrays im Paket java.util mit nützlichen
Methoden zum Zugriff auf Arrays:
public static void fill(int[] a, int wert)
public static int binarySearch(int[] a, int schl)
public static void sort(int[] a)
public static boolean equals(int[] a1, int [] a2)
Diese Methoden stehen auch in vergleichbaren Versionen für andere primitive
Datentypen zur Verfügung.
131
Programmieren in Java
2.3 Ausdrücke
Ein Ausdruck ist das Ergebnis einer Verknüpfung von Operanden und Operatoren nach
den syntaktischen Regeln der Sprache. Ausdrücke werden üblicherweise zur
Durchführung von Operationen (Manipulationen) an Variablen oder Werten verwendet.
Ausdrücke gehören zu den kleinsten ausführbaren Einheiten eines Programms. Sie
dienen zur Verzuweisung an Variable, zur Durchführung numerischer berechnungen und
zur Formulierung logischer Bedingungen.
Ein Ausdruck besteht immer aus mindestens einem Operanden, auf dem der Operator
angewandt wird. Nach dem Typ der Operanden unterscheidet man numerische,
relationale, logische, bitweise Operatoren. Jeder Ausdruck hat einen Rückgabewert,
der durch die Anwendung des Operators auf die Operanden entsteht. Der Typ des
Rückgabewerts bestimmt sich aus den Typen der Operanden und der Art des
verwendeten Operators.
2.3.1 Arithmetische Ausdrücke
Jede Programmiersprache hat einen Mechanismus für arithmetische Berechnungen. In
Java werden solche Berechnungen in arithmetischen Ausdrücken durchgeführt.
2.3.2 Bewertung von Ausdrücken
Bei der Bewertung von Ausdrücken spielen Operatorassoziativität, Operatorvorrang und
Bewertungsreihenfolge eine Rolle.
Operatorassoziativität
Alle arithmetischen Operatoren assoziieren von links nach rechts, d.h.: Falls derselbe
Operator in einem Ausdruck mehr als einmal vorkommt, dann wird der am weitesten
links stehende zuerst bewertet, gefolgt von dem rechts daneben stehenden, usw. Die
Assoziativitätsregel bestimmt, wie Kombinationen des gleichen Operators bewertet
werden können.
Priorität
Java hält sich, wie die grundlegende Arithmetik, strikt an die Reglen der Vorrangigkeit.
Die multiplikativen Operatoren (*, / und %) haben Vorrang vor den additiven
Operatoren (+ und -). Immer wenn die Bewertungsreihenfolge von Operatoren in einem
Ausdruck geändert werden soll, müssen Klammern benutzt werden. Jeder Ausdruck in
Klammern wird zuerst bewertet. Der Vorrang der einstelligen arithmetischen Operatoren
steht über allen anderen arithmetischen Operatoren.
132
Programmieren in Java
Bewertungsreihenfolge
Die Vorrangregeln helfen bei der Bewertung, welche Operatoren in einem Ausdruck
zuerst benutzt werden und welche Operanden zu welchen Operatoren gehören. Die
Regeln für die Bewertungsreihenfolge helfen festzulegen, wann welche Operanden
bewertet werden. Die drei folgenden Regeln bestimmen, wie ein Ausdruck bewertet
wird:
-
Bei allen binären Operatoren wird der linke Operand vor dem rechten bewertet.
Zuerst werden die Operanden, danach die Operatoren bewertet.
Falls mehrere Parameter, die durch Kommata voneinander getrennt sind, durch einen Methodenaufruf zur
Verfügung gestellt werden, werden diese Parameter von links nach rechts bewertet.
2.3.3 Typkonvertierungen
Java ist eine typisierte Sprache. Es finden gründliche Typüberprüfungen statt, und es
gelten strikte Beschränkungen für die Konvertierung von Werten eines Typs zu einem
anderen. Unter „Casting“ versteht man die Umwandlung von einem Datentyp in einen
anderen.
Java unterstützt explizite Konvertierungen und „ad hoc“-Konvertierungen.
Ad-hoc-Konvertierungen. Hier gibt es folgende Regeln für numerische Datentypen:
- Bei Operationen mit ausschl. ganzzahligen Operanden wird, falls einer der beiden Operanden den
Datentyp long hat, der andere ebenfalls zu long konvertiert; ansonsten werden beide Operanden zu
int konvertiert. Das Ergebnis ist dann ebenfalls vom Typ int. Ist allerdings der ausgegebene Wert zu
groß, um im Wertebereich von int dargestellt zu werden, wird der Typ long verwendet.
- Bei Operationen mit wenigstens einem Gleitpunkt-Operanden wird, wenn einer der Operanden den
Datentyp double hat, der andere ebenfalls zu double konvertiert. Das Ergebnis ist dann ebenfalls
vom Datentyp double, anderenfalls werden beide Operanden zum Datentyp float konvertiert. Das
Ergebnis ist ebenfalls vom Typ float.
Explizite Konvertierungen. Sie sind immer nötig, wenn eine Umwandlung in einen
anderen Datentyp gewünscht wird und diese nicht „ad hoc“ eintritt. „Casting“ muß dann
angewendet werden. Der zugehörige (Festlegungs-)Operator besteht aus einem
Typnamen in runden Klammern. Er ist ein einstelliger Operator mit hoher Priorität und
steht vor seinen Operanden. Er hat immer die folgende Form: „(Datentyp) Wert“.
Der (Festlegungs-)Operator bestimmt den Wert seines Operanden, der auf den in
Klammern bezeichneten Typ festgelegt wird. Es gibt folgende Casting-Operatoren:
Operator
(byte)
Beispiel
(byte) (x/y)
(short) x
(short)
(int) (x/y)
(int)
(long) x
(long)
(float) x
(float)
(double) x
(double)
(char) x
(char)
(boolean) 0
(boolean)
Abb.: Casting-Operatoren
Erläuterung
Wandelt das Errgebnis von x/y in einen Wert vom Datentyp byte
um
Wandelt x in einen Wert vom Datentyp short um
Wandelt das Ergebnis von x/y in einen Wert vom Datentyp int
Wandelt x in einen Wert vom Datentyp long um
Wandelt x in einen Wert vom Datentyp float um
Wandelt x in einen Wert vom Datentyp double um
Wandelt x in einen Wert vom Datentyp char um
Wandelt 0 in einen booleschen Datentyp um
133
Programmieren in Java
Casting hat eine höhere Priorität als Arithmetik. Deshalb müssen arithmetische
Operationen in Verbindung mit Casting in Klammern gesetzt werden
Nicht alle Konvertierungen sind möglich. Variable eines arithmetischen Typs können auf
jeden anderen arithmetischen Typ festgelegt werden. Boolesche Werte können nicht auf
irgendeinen anderen Wert festgelegt werden. Die Umkehrung funktioniert mit
Einschränkungen: 0 und 1 lassen sich in boolesche Werte konvertieren.
Bsp.108: Konvertieren primitiver Typen
public class KonvPrim
{
public static void main(String args[])
{
char z = 'a';
System.out.println("char z = '" + z + '\'');
System.out.println("Unicode von z: " + (int) z);
int i = 17;
System.out.println("int i = " + i);
long l = 4L;
System.out.println("long l = " + l);
double d = 17.3;
System.out.println("double d = " + d);
float f = 4.5F;
System.out.println("float f = " + f);
double sd;
float sf;
long
sl;
int si;
sd = i + l + d + f;
System.out.println("sd = i + l + d + f = " + sd);
// sf = i + l + d + f;
/* Inkompatibler Typ! */
// System.out.println("sf = i + l + d + f = " + sf);
sf = (float) (i + l + d + f);
// sl = i + l + d + f;
/* Inkompatibler Typ! */
// System.out.println("sl = i + l + d + f = " + sl);
sl = i + l + (long) d + (long) f;
System.out.println("sl = l + i + (long) d + (long) f = " + sl);
sl = (long) (i + l + d + f);
System.out.println("sl = (long) (i + l + d + f) = " + sl);
// si = i + l + d + f;
/* Inkompatibler Typ! */
// System.out.println("si = i + l + d + f = " + si);
si = i + (int) l + (int) d + (int) f;
System.out.println("sl = l + (int) i + (int) d + (int) f = " + sl);
si = (int) (i + l + d + f);
System.out.println("sl = (int) (i + l + d + f) = " + si);
}
}
Konvertieren von Objekten. Mit Einschränkungen lassen sich Klasseninstanzen in
Instanzen anderer Klassen konvertieren. Die Klassen müssen allerdings durch
Vererbung miteinander verbunden sein. Allgemein gilt: Ein Objekt einer Klasse kann auf
108 vgl. pr23301
134
Programmieren in Java
seine Superklasse festgelegt werden. Spezifische Informationen der Subklasse gehen
dabei verloren. Das Konvertieren erfolgt immer nach folgender Form:
(Klassenname) Objekt.
Bsp.109: Konvertieren Datentyp Object
import java.applet.Applet;
import java.awt.*;
import java.util.Vector;
public class MittleresDrittel extends Applet
{
int appletHoehe;
int appletBreite;
Vector endpunkt = new Vector();
public void init()
{
Dimension d = getSize();
appletHoehe = d.height - 1;
appletBreite = d.width - 1;
// Anhaengen an Liste
endpunkt.addElement(new Float(0.0f));
endpunkt.addElement(new Float(1.0f));
}
public void paint(Graphics g)
{
float x1, x2;
Float tempFloat;
g.setColor(Color.yellow);
g.fillRect(0,0,appletBreite,appletHoehe);
g.setColor(Color.black);
for (int i = 0; i < appletHoehe; i+=5)
{
// Zeichne die Linien
for (int j = 0; j < endpunkt.size(); j+=2)
{
tempFloat = (Float) endpunkt.elementAt(j);
x1
= tempFloat.floatValue();
tempFloat = (Float) endpunkt.elementAt(j+1);
x2
= tempFloat.floatValue();
g.drawLine(Math.round(x1 * appletBreite),i,
Math.round(x2 * appletBreite),i);
}
// Entferne das mittlere Drittel aus den Linien
schneideausSegment();
tempFloat = (Float) endpunkt.elementAt(0);
x1 = tempFloat.floatValue();
System.out.println(x1);
tempFloat = (Float) endpunkt.elementAt(1);
x2 = tempFloat.floatValue();
System.out.println(x2);
if (Math.round(x1*appletBreite) ==
Math.round(x2*appletBreite)) break;
}
}
public void schneideausSegment()
109 pr23302
135
Programmieren in Java
{
int index = 0;
float luecke;
Float tempFloat1, tempFloat2;
int stop = endpunkt.size();
for (int i = 0; i < stop; i += 2)
{
schneideausMittleresDrittel(index,index+1);
index += 4;
}
}
public void schneideausMittleresDrittel(int links, int rechts)
{
float luecke;
float x1, x2;
Float tempFloat1, tempFloat2;
// Zugriff an der angegebenen Position
tempFloat1 = (Float) endpunkt.elementAt(links);
tempFloat2 = (Float) endpunkt.elementAt(rechts);
luecke = tempFloat2.floatValue() - tempFloat1.floatValue();
x1 = tempFloat1.floatValue() + luecke/3.0f;
x2 = tempFloat2.floatValue() - luecke/3.0f;
// Einfuegen an der angegebenen Stelle in der Liste
endpunkt.insertElementAt(new Float(x2),rechts);
endpunkt.insertElementAt(new Float(x1),rechts);
}
}
2.3.4 Vergleichsoperatoren
Java hat eine Menge von Operatoren für das Vergleichen von zwei oder mehr Größen.
Diese Operatoren lassen sich aufteilen in
-
relationale Operatoren. Sie sind zum Ordnen von Größen bestimmt, ob etwa ein Wert größer oder
kleiner als ein anderer ist.
Gleichheitsoperatoren. Sie sagen nur aus, ob zwei Werte gleich sind
2.3.5 Logische Ausdrücke
Logische Operationen können auf zwei verschiedene Arten ausgeführt werden:
-
Short-turn-Operatoren (logisches AND && und logisches OR ||). Sie operieren nur mit booleschen
Variablen.
Bitweise Operatoren. Sie operieren mit jedem Bit zweier ganzzahliger Operanden.
136
Programmieren in Java
2.4 Anweisungen
2.4.1 Blöcke und Anweisungen
Methoden und statische Initialisatoren werden in Java durch Anweisungsblöcke
definiert. Ein Anweisungsblock besteht in der Regel aus einer Reihe von Anweisungen,
die in geschweiften Klammern stehen. Das bedeutet: Es können in diesem Block lokale
Variablen deklariert werden, die außerhalb des Blocks nicht verfügbar sind, und deren
Existenz erlischt, wenn der Block ausgeführt wurde.
2.4.2 Leere Anweisungen
In Java können leere Anweisungen erstellt werden. Für den Compiler sieht eine leere
Anweisung nur wie ein zusätzliches Semikolon aus.
2.4.3 Benannte Anweisungen
Jede Anweisung darf in Java eine „Benennung“ haben. Die „Benennung“ hat die
gleichen Eigenschaften wie jeder andere Bezeichner. Die Reichweite der „Benennung“
erstreckt sich über den ganzen Block. Der Benennung folgt ein Doppelpunkt.
„Benennungen“ werden nur von den Sprunganweisungen break und continue
benutzt.
2.4.4 Deklarationen
Eine Deklarationsanweisung definiert eine Variable, egal ob Klasse, Schnittstelle,
Datenfeld, Objekt oder primitiver Typ. Das Format einer solchen Anweisung hängt
davon ab, welcher der 5 verschiedenen Formen deklariert wird.
Typname variablenName;
bzw.
Typname variablenName = initialerWert;
137
Programmieren in Java
2.4.5 Ausdrucksanweisungen
In Java gibt es sieben verschiedene Arten von Ausdrucksanweisungen:
Ausdrucksanweisung
Zuordnung
Prä-Inkrement
Prae-Dekrement
Post-Inkrement
Post-Dekrement
Methodenaufruf
Zuweisungsausdruck
Beispiel
x=13;
++wert;
--wert;
wert++;
wert--;
System.out.println("Aller Anfang ist schwer!");
byte x = new byte;
Abb.: Die sieben Ausdrucksanweisungen in Java
Bsp.:Demonstrationsprogramm zur Wirkungsweise der Inkrement- und DekrementOperatoren110
// Demonstration der Operatoren ++ und -public class AutoInkr
{
public static void main(String[] args)
{
int i = 1;
ausgabe("i: " + i);
ausgabe("++i: " + ++i); // Pre-Inkrement
ausgabe("i++: " + i++); // Post-Inkrement
ausgabe("i: " + i);
ausgabe("--i: " + --i); // Pre-Dekrement
ausgabe("i--: " + i--); // Post-Inkrement
ausgabe("i: " + i);
}
static void ausgabe(String s)
{
System.out.println(s);
}
}
/* Ausgabe
i: 1
++i: 2
i++: 2
i: 3
--i: 2
i--: 2
i: 1
*/
110 vgl. pr24500
138
Programmieren in Java
2.4.6 Auswahlanweisungen
if-Anweisungen
Eine if-Anweisung testet eine boolesche Variable oder einen Ausdruck zur Feststellung,
ob eine Anweisung oder ein Anweisungsblock ausgeführt werden soll. Hat die
boolesche Variable den Wert true, wird der Block ausgeführt. Falls nicht, springt die
Programmkontrolle zur nächsten Anweísung hinter dem Block.
if (boolscher_Ausdruck)
anweisung111
if-else-Anweisungen
Ergänzt if um einen else-Teil. Dieser else-Teil reicht die Kontrolle an eine
Anweisung oder Block weiter, wenn der boolesche Wert im if-Teil der Anweisung
false war.
if (boolscher_Ausdruck)
anweisung
else
anweisung
Bsp.: Demonstrationsprogramm zur if-Anweisung 112
import java.lang.*;
public class IfDemo extends Object
{
public static void main(String[] args)
{
int i;
int abs;
// if - else
i = 13;
System.out.println("i = " + i);
if (i >= 0)
{
abs = i;
}
else {
abs = -i;
}
System.out.println("abs = " + abs);
i = -13;
System.out.println("i = " + i);
if (i >= 0)
{
abs = i;
}
else {
111 „anweisung“ bedeutet: Eine einfache Anweisung, die nur von einem Semikolon abgeschlossen ist bzw.
eine zusammengesetzte Folge von anweisungen, die von { ... } umschlossen ist.
112 Vgl. pr24601
139
Programmieren in Java
abs = -i;
}
System.out.println("abs =
// if
i = 13;
abs = i;
System.out.println("i = "
if (abs < 0)
{
abs = -abs;
}
System.out.println("abs =
i = -13;
abs = i;
System.out.println("i = "
if (abs < 0)
{
abs = -abs;
}
System.out.println("abs =
// Konditionaloperator
i = -13;
System.out.println("i = "
abs = ( i < 0 ? -i : i);
System.out.println("abs =
i = 13;
System.out.println("i = "
abs = ( i < 0 ? -i : i);
System.out.println("abs =
" + abs);
+ i);
" + abs);
+ i);
" + abs);
+ i);
" + abs);
+ i);
" + abs);
}
}
switch-Anweisungen
Eine switch-Anweisung ermöglicht die Weitergabe des Kontrollflusses an eine von
vielen Anweisungen in ihrem Block mit Unteranweisungen (ist abhängig vom Wert des
Ausdrucks in der switch-Anweisung).
switch (integraler_Selektor)
{
case integraler_Wert1: anweisung; break;
case integraler_Wert2: anweisung; break;
case integraler_Wert3: anweisung; break;
.....
default: anweisung;
}
Ein integraler_Selektor ist ein Ausdruck zur Produktion eines integralen Werts113.
„switch“ vergleicht das Ergebnis einer Auswertung vom integralen Selektor mit jedem
integralen Wert. Falls ein passender Wert gefunden wird, wird die zugehörige
Anweisung (einfach oder zusammengesetzt) ausgeführt. Anderenfalls kommt es zur
113 Integrale Werte sind bspw. vom Typ „int“, „char“. Nichtintegrale Typen. Z.B. float, String, müssen über eine
Serie von „if“-Anweisungen „switch“ simulieren
140
Programmieren in Java
Ausführung der bei „default“ angegebenen Anweisung. Die „break“-Anweisung ist
optional. Fehlt sie, dann werden die folgenden Anweisungen ausgeführt, bis ein „break“
auftritt.
Bsp.: Demonstrationsprogramm zur switch-Anweisung 114
import java.lang.*;
public class SwitchDemo extends Object
{
public static void main(String[] args)
{
int dezimal = 10;
System.out.print("dezimal = " + dezimal + " hex: ");
switch (dezimal)
{
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
System.out.print("" + dezimal);
break;
case 10:
System.out.print("A");
break;
case 11:
System.out.print("B");
break;
case 12:
System.out.print("C");
break;
case 13:
System.out.print("D");
break;
case 14:
System.out.print("E");
break;
case 15:
System.out.print("F");
break;
default:
System.out.print("Keine gueltige Hex-Ziffer");
break;
}
System.out.println();
}
}
114 vgl. pr24601
141
Programmieren in Java
2.4.7 Iterationsanweisungen
while-Anweisung
Die while-Anweisung testet eine boolesche Variable oder einen Ausdruck. Ist er
true, wird die Unteranweisung oder der Block solange ausgeführt, bis sich der Wert
false einstellt. Ist die Variable oder der Ausdruck false, wird die Kontrolle an die
nächste Anweisung nach der Unteranweisung oder nach dem Block der whileAnweisung weitergegeben.
while (boolescher_Ausdruck)
anweisung
Bsp.115:
public class WhileDemo extends Object
{
public static void main(String args[])
{
double a = 2.0;
int
n = 10;
System.out.println("a = " + a);
System.out.println("n = " + n);
int absn = (n < 0) ? -n : n;
double aHochn = 1.0;
int i = 1;
while (i <= absn)
{
aHochn *= a;
i++;
}
if ( n < 0)
{
aHochn = 1.0 / aHochn;
}
System.out.println("aHochn = " + aHochn);
}
}
do-Anweisung
Die do-Anweisung testet eine boolesche Variable oder einen Ausdruck. Solange dieser
den Wert true hat, wird die Unteranweisung oder der Block ausgeführt. Erst wenn die
boolesche Variable oder der Ausdruck den Wert false hat, wird die Wiederholung
eingestellt und die Schleife verlassen. Der Code-Block innerhalb der do-Anweisung
wird auf jeden Fall mindestens einmal ausgeführt.
do
anweisung
while (boolescher_Ausdruck)
115 pr24700
142
Programmieren in Java
Bsp.116:
public class DoDemo extends Object
{
public static void main(String args[])
{
double a = 2.0;
int
n = 10;
System.out.println("a = " + a);
System.out.println("n = " + n);
int absn = (n < 0) ? -n : n;
double aHochn = 1.0;
int i = 1;
do
{
if (n == 0)
{
break;
}
aHochn *= a;
i++;
} while (i <= absn);
if ( n < 0)
{
aHochn = 1.0 / aHochn;
}
System.out.println("aHochn = " + aHochn);
}
}
for-Anweisung
Sie besteht aus
-
-
-
for, gefolgt von optionalem Leerraum, gefolgt von einer öffnenden Klammer.
Initialisierungsteil. Er enthält eine durch Kommata getrente Reihe von
Deklarations- und Zuweisungsanweisungen, die durch ein Semikolon beendet wird.
Die Deklarationen haben nur Gültigkeit für den Bereich der for-Anweisung und
ihrer Unteranweisungen. Die Zuweisungen werden nur einmal vor der esrten
Wiederholung der Unteranweisung oder des Blocks gemacht.
Testteil. Enthält eine boolesche Variable oder einen Ausdruck, der einmal je
Schleife neu bewertet wird. Falls der Ausdruck false wird, geht die Kontrolle zur
nächsten Anweisung nach der for-Anweisung und ihrer Unteranweisung oder ihrem
Block weiter. Die Zuweisungen werden nur einmal vor der ersten Wiederholung der
Unteranweisung oder des Blocks gemacht.
Inkrementteil. Enthält eine durch Kommata getrennte Reihe von Ausdrücken, die
einmal je Durchlauf der Schleife bewertet werden. Dieser Teil wird gewöhnlich dazu
verwendet, einen Index, der im Testteil überprüft wird, zu inkrementieren. Am Ende
des Teils steht ein Semikolon.
116 pr24700
143
Programmieren in Java
Jeder der drei Ausdrücke kann entfallen.
for
(initialisierung;
naechster_Schritt)
anweisung
Bsp.:
public class ForDemo extends Object
{
public static void main(String args[])
{
double a = 2.0;
int
n = 10;
System.out.println("a = " + a);
System.out.println("n = " + n);
int absn = (n < 0) ? -n : n;
double aHochn = 1.0;
for (int i = 1; i <= absn; i++)
{
aHochn *= a;
}
if ( n < 0)
{
aHochn = 1.0 / aHochn;
}
System.out.println("aHochn = " + aHochn);
}
}
144
boolescher_Ausdruck;
Programmieren in Java
2.4.8 Sprung-Anweisungen
break-Anweisung
Unteranweisungsblöcke von Schleifen und switch-Anweisungen können durch
Verwendung einer break-Anweisung verlassen werden. Eine unbezeichnete breakAnweisung springt zur nächsten Zeile nach der aktuellen (innersten) Wiederholungs- und
switch-Anweisung. Mit einer bezeichneten break-Anweisung am Anfang einer Schleife
kann an eine Anweisung mit dieser Bezeichnung in der derzeitigen Methode
gesprungen werden. Am Anfangsteil der Schleife muß ein Label (eine Bezeichnung) mit
einem Doppelpunkt stehen, z.B.: „label1:“.
continue-Anweisung
Der aktuelle Schleifendurchlauf wird unterbrochen. Es wird zum Anfang der Schleife
zurückgekehrt, falls hinter continue kein „Bezeichner“ steht. Anderenfalls wird zu einer
äußeren Schleife zurückgekehrt, die eine Markierung (label) gleichen Namens enthält.
Eine continue-Anweisung darf nur in einem Unterweisungsblock einer
Iterationsanweisung stehen (while, do oder for).
Bsp.117: „break“ und „continue“ innerhalb von „for“- bzw. „while“-Schleifen
public class BreakundContinue
{
public static void main(String[] args)
{
for (int i = 0; i < 100; i++)
{
if (i == 13) break;
// raus aus der Schleife
if (i % 9 != 0) continue; // naechster Schleifendurchlauf
System.out.println(i);
}
int i = 0;
// eine "unendliche Schleife
while (true)
{
i++;
int j = i * 27;
if (j == 1269) break;
// raus aus der Schleife
if (i % 10 != 0) continue; // zurueck an den Anfang der Schleife
System.out.println(i);
}
}
}
117 pr24800
145
Programmieren in Java
Marken (labels)
Eine Markierung bzw. ein „label“ bewirkt in Verbindung mit der „break“- bzw.
„continue“-Anweisung eine Unterbrechnug des Schleifendurchgangs und einen
Sprung zu der dem „label“ folgenden Anweisung. Sinnvollerweise steht dann ein
„label“ am Anfang der Schleifen, z.B.:
label1:
äußere_Iteration
{
innere_Iteration
{
// ...
break;
// ...
continue;
// ...
continue label1;
// ...
break label1;
}
}
// 1. Fall
// 2. Fall
// 3. Fall
// 4. Fall
Im 1. Fall bricht „break“ aus der inneren Iteration heraus und führt in die äußere
Iteration. Im 2. Fall verzweigt „continue“ auf den Anfang der inneren Iteration. Im 3.
Fall führt „continue label1“ aus der inneren und äußeren Iteration heraus auf
„label1“. Die Iteration wird mit dem Anfang der äußeren Iteration fortgesetzt. Im 4. Fall
bricht „break label1“ aus der inneren Iteration heraus auf label1. Es kommt aber
nicht zu einem erneuten Eintritt in die Iterationen, sondern zu einem Ausbruch aus
beiden Iterationen.
Bsp.118: Marken im Zusammenhang mit „for“- bzw. „while“-Schleifen
1. Marken im Zusammenhang mit „for“
public class MarkiertesFor
{
public static void main(String[] args)
{
int i = 0;
aussen:
// hier soll keine Anweisung stehen
for (; true; )
// Endlos-Schleife
{
innen:
// hier soll keine Anweisung stehen
for (; i < 10; i++)
{
ausgabe("i = " + i);
if (i == 2)
{
ausgabe("continue");
continue;
}
if (i == 3)
{
ausgabe("break");
i++;
118 pr24800
146
Programmieren in Java
break;
}
if (i == 7)
{
ausgabe("continue aussen");
i++;
continue aussen;
}
if (i == 8)
{
ausgabe("break aussen");
break aussen;
}
for (int k = 0; k < 5; k++)
{
if (k == 3)
{
ausgabe("continue innen");
continue innen;
}
}
}
}
}
static void ausgabe(String s)
{
System.out.println(s);
}
}
/* Ausgabe
i = 0
continue innen
i = 1
continue innen
i = 2
continue
i = 3
break
i = 4
continue innen
i = 5
continue innen
i = 6
continue innen
i = 7
continue innen
i = 8
break aussen
*/
2. Marken im Zusammenhang mit „while“
public class MarkiertesWhile
{
public static void main(String[] args)
{
int i = 0;
aussen:
while (true)
{
ausgabe("Aeussere While-Schleife");
147
Programmieren in Java
while (true)
{
i++;
ausgabe("i = " + i);
if (i == 1)
{
ausgabe("continue");
continue;
}
if (i == 3)
{
ausgabe("continue aussen");
continue aussen;
}
if (i == 5)
{
ausgabe("break");
break;
}
if (i == 7)
{
ausgabe("break aussen");
break aussen;
}
}
}
}
static void ausgabe(String s)
{
System.out.println(s);
}
}
/* Ausgabe
Aeussere While-Schleife
i = 1
continue
i = 2
i = 3
continue aussen
Aeussere While-Schleife
i = 4
i = 5
break
Aeussere While-Schleife
i = 6
i = 7
break aussen
*/
return-Anweisung
Alle Funktionen haben einen eindeutigen Typ, der gleichzeitig Typ des Rückgabewerts
ist. Mögliche Typen für die Rückgabe sind primitive Typen, Datenfelder (Arrays),
Klassen, Schnittstellen. Es gibt einen speziellen Typ für eine Funktion ohne
Rückgabewert: „void“.
Der Rückgabewert ist der Wert einer return-Anweisung.
148
Programmieren in Java
throw-Anweisung
Eine throw-Anweisung erzeugt eine Laufzeit-Ausnahme.
2.4.9 Synchronisationsanweisungen
Sie wird für den Umgang mit „Multithreading“ benutzt.
2.4.10 Schutzanweisungen
Java kennt drei Schutzanweisungen: try, catch, finally. Sie werden zur
Handhabung von Ausnahmen in einer Methode benutzt, die eine Ausnahmesituation
hervorrufen kann.
2.4.11 Unerreichbare Anweisungen
Es ist das Schreiben einer Methode mit Codezeilen möglich, die nie erreicht werden
können, z.B. Zeilen zwischen einer bedingungslosen return-Anweisung und der nächsten
Bezeichnung oder dem Ende eines Blocks. Solche Anweisungen erzeugen einen Fehler
beim Kompilieren.
149
Programmieren in Java
2.5 Klassen
Klassen definieren Zustand und Verhalten von Objekten. Jedes Java-Programm besteht
aus einer Sammlung von Klassen. Alle Klassen in Java haben eine gemeinsame
Oberklasse, die Klasse Object. Auch Java selbst (als Entwicklungsplattform) ist aus
Klassen aufgebaut, die mit dem JDK frei verfügbar sind. Das eigentliche RUNTIMEModul besteht aus der Datei Java Core Classes (classes.zip), die normalerweise
nicht entpackt wird und im Unterverzeichnis \lib des JDK vohanden ist. Die Datei
enthält den vollständigen kompilierten Code von Java.
Jede Klasse besteht formal aus zwei Teilen: der Deklaration und dem Body (Körper).
Generell haben Klassendeklarationen folgendes Format:
Modifizierer class NeueKlasse extends NameSuperklasse
implements NameSchnittstelle
Es gibt vier Eigenschaften einer Klasse, die in einer Deklaration definiert werden
können: Modifizierer, Klassenname, Superklasse, Schnittstellen
Modifizierer
Sie stehen am Beginn der Klassendeklaration und legen fest, wie die Klasse während
der weiteren Entwicklung gehandhabt werden kann. Klassen haben einen
voreingestellten, „freundlichen“ Defaultstatus. Er wird immer dann verwendet, wenn kein
Modifizierer am Anfang einer Klassendefinition steht. „Freundlich“ bedeutet: Die Klasse
darf erweitert und von anderen Klassen benutzt werden, aber nur von Objekten innerhalb
desselben Pakets. Die Grundeinstellung bezieht sich also auf die Sichtbarkeit von
anderen Klassen und deren Objekten. Falls davon abgewichen werden soll, ist einer der
folgenden Modifizierer zu verwenden: public , final, abstract.
Öffentliche Klassen – der Modifizierer public. Eine Klasse wird als öffentlich
deklariert, wenn man den Modifizierer public vor die Klassendeklaration setzt. Alle
Objekte dürfen auf public-Klassen zugreifen, d.h.: Sie können von allen Objekten
benutzt und erweitert werden, ganz egal zu welchem Paket sie gehören.. Die
Deklaration einer öffentlichen Klasse muß immer identisch sein mit dem Namen, unter
dem die Quelle dieser Datei gespeichert ist.
Finale Klassen – der Modifizierer final. Finale Klassen dürfen keine Subklassen
haben. Der Modifizierer final muß am Beginn der Klassendeklaration gesetzt sein.
Abstrakte Klassen – der Modifizierer abstract. Von einer derartig beschriebenen
Klasse wird nie eine direkte Instanz benötigt und kann auch nie eine Instanz gebildet
werden. Sie darf keine Implementierung einer Methode enthalten. In einer abstrakten
Klasse gibt es mindestens eine nicht vollständig angegebene Methode.
Superklasse und das Schlüsselwort extends
Wird der Name einer Klasse nach dem Schlüsselwort extend angegeben, dann wird
damit diese Klasse als Superklasse spezifiziert, auf der eine neue Klasse aufbaut.
Durch Erweiterung der Superklasse entsteht eine neue Kopie dieser Klasse, an der
Veränderungen möglich sind. Die neue Klasse erbt alle Daten und Methoden, die in der
Originalklasse definiert wurden.
150
Programmieren in Java
2.6 Methoden
2.6.1 Die Deklaration
Die Deklarationen von Methoden haben folgendes Aussehen119:
Zugriffsspezifizierer Modifizierer Returnwert NameMethode(Parameter)
throws Exceptionliste
Methodenunterschrift120. Unter Unterschrift einer Methode versteht man eine
Kombination aus Teilen der Definition: dem Namen der Methode, dem Rückgabetyp
und den verschiedenen Parametern.
2.6.2 Die Zugriffsspezifizierung
Freundliche Methoden – der voreingestellte Defaultstatus.
Öffentliche Methoden – der Zugriffsspezifizierer public.
Geschützt Methoden – der Zugriffsspezifizierer protected.
Prvate Methoden – der Zugriffsspezifizierer private.
Privat geschützt – die Zugriffsspezifizierer private und protected in Kombination.
Methoden, die als private protected deklariert wurden, sind sowohl für eine
Klasse als auch für eine Subklasse vefügbar, aber nicht für den Rest des Pakets oder
auch für Klassen außerhalb des Pakets. Das bedeutet: Subklassen einer gegebenen
Klasse können Methoden, die private protected deklariert wurden aufrufen.
Instanzen der Subklasse können dies aber nicht.
2.6.3 Die Methodenmodifizierer
Klassenmethoden – der Modifizierer static.
Abstrakte Methoden – der Modifizierer abstract
Finale Methoden – der Modifizierer final. Das Schlüsselwort final vor einer
Methodendeklaration verhindert, daß irgendwelche Subklassen die derzeitige Klasse
dieser Methode überschreiben. Methoden, die auf keinem Fall geändert werden sollen,
sollten deshalb immer als final deklariert werden.
119 Kursiv Geschriebenes ist optional
120 Oft wird für denselben Zusammenhang der Begriff „methodensignatur“ verwendet.
151
Programmieren in Java
Native Methoden – der Modifizierer native. Native Methoden sind Methoden, die
nicht in Java geschrieben sind, aber dennoch innerhalb von Java verwendet werden
sollen. Der Modifizierer native wird vor der Methode deklariert, der Body (Körper) der
Methode wird durch ein Semikolon ersetzt.
Methoden synchronisieren – der Modifizierer synchronized. Wird das
Schlüsselwort sychronized vor eine Methodendeklaration gesetzt, dann werden
Datenverletzungen verhindert, die entstehen können, wenn zwei Methoden gleichzeitig
versuchen auf dieselben Daten zuzugreifen.
2.6.4 Rückgabewerte von Methoden
Rückgabewerte vonJava-Methoden können von jedem erlaubten Datentyp 121 sein.
Eine Methode muß immer einen Wert zurückgeben (und zwar genau den Datentyp, der
in der Deklaration angegeben wurde), es sei denn, sie wurde mit void deklariert. Die
Methode hat dann keinen Rückgabewert.
2.6.5 Methodenname und Parameterliste
Bzgl. der Methodennamen gelten die gleichen Regeln wie bei allen Token.
Eine Parameterliste hat folgende Struktur: Datentyp variablenname, Datentyp
variablenname, .... Die Anzahl der Parameter ist beliebig und kann Null sein.
2.6.6 Rekursion
2.6.6.1 Rekursive Funktionen
Eine Funktion ist rekursiv, falls die Ausführung des Rumpfs der Funktion wiederum zum
Aufruf der Funktion führt. Man unterscheidet
Direkte Rekursion (Eine Funktion ruft sich selbst im Rumpf auf)
und
Indirekte Rekursion (Der rekursive Aufruf befidet sich nicht im Funktionsrumpf. So ruft bspw. eine
Funktion A eine Funktion B auf und diese startet in ihrem Rumpf die Funktion A).
Wie Wiederholungsanweisungen neigen auch rekursive Funktionen zur Gefahr nicht
abbrechbarer Berechnungen. Eine Terminierung ist unbedingt erforderlich. Das
geschieht über eine Bedingung von der der rekursive Aufruf abhängt..
121 Nicht nur primitive Datentypen, sondern auch komplexe Objekte.
152
Programmieren in Java
Bei jeder rekursiven Anwendung wird ein Satz lokaler, gebundener Variablen kreiert.
Sie haben zwar denselben Namen wie Objekte des vorangegengenen Aufrufs der
Prozedur, besitzen aber verschiedene Werte. Die Namen beziehen sich immer auf die
zuletzt erzeugten Variablen.
2.6.6.2 Rekursion und Iteration
Man kann zeigen, daß sich jeder rekursive Algorithmus in einen iterativen umwandeln
läßt. Da die Iteration in den meisten Fällen wesentlich effizienter ist als die die
Rekursion, ist die Frage berechtigt, weshalb man überhaupt die Rekusion verwendet.
Für die Rekursion spricht:
1.
2.
Es gibt bestimmte rekursiv formulierte Algorithmen, die schneller oder wenigstens gleich schnell
arbeiten als vergleichbare iterative.
Es lassen sich viele Probleme rekursiv „sehr einfach“ lösen.
Rekursiv formulierte Algorithmen bieten sich insbesondere an, wenn das
zugrundeliegende Problem oder die zu behandelnden Daten rekursiv definiert sind. Das
ist aber noch keine Garantie dafür, daß ein rekursiver Algorithmus auch der beste Weg
zur Lösung des Problems ist.
153
Programmieren in Java
3. Grafische Benutzeroberflächen und Applets
3.1 Ereignisse in grafischen Benutzeroberflächen
Mit Hilfe einfacher Benutzerschnittstellen des Graphical User Interface (GUI))
lassen sich Ein- und Ausgaben122 bequem gestalten. Generell sollte Ein-/Ausgabe in
Java über ein „GUI“ in Abhängigkeit von den vom Betriebssystem registrierten
Ereignisarten und Zustandsänderungen erfolgen.
3.1.1 Gestaltung von GUI mit Hilfe der AWT-Klassen
Java enthält ein einfach zu bedienendes System für Gestaltung grafische
Benutzeroberflächen: das Abstract Windowing Toolkit (AWT). Die Fähigkeiten
des AWT umfassen:
-
Grafische Operationen zum Zeichnen von Linien oder Füllen von Flächen und zur Ausgabe von Text
Methode zur Steuerung des Programmablaufs auf der Basis von Nachrichten für Tatstatur-, Mausund Fensterereignisse.
Dialogelemente zur Kommunikation mit dem Anwender und Funktion zum Design von Dialogboxen.
Grafikfunktionen zur Darstellung von Bitmaps und Ausgabe von "Sound".
Zum Einbinden der Grafikfähigkeiten dient die Anweisung
import java.awt.*;
zu Beginn der Klassendefinition. Danach stehen alle Klassen aus dem Paket
java.awt zur Verfügung.
Zur Ausgabe von grafischen Elementen benötigt eine Anwendung ein Fenster, das eine
Applikation (im Gegensatz zu einem Applet) selbst erzeugen muß. Das AWT enthält
verschiedene Fensterklassen:
Panel
Component
Applet
Container
Window
Dialog
FileDialog
Frame
Component
Container
Abstrakte Klasse mit der Aufgabe: Repäsentation von Programmelementen, die eine
Größe und Position haben und die auf eine Vielzahl von Ereignissen reagieren
können bzw. Ereignisse senden können
Abstrakte Klasse mit der Aufgabe: Aufnahme von Komponenten innerhalb anderer
Komponenten. Container stellt für das Hinzufügen bzw. Entfernen von Komponenten
Methoden bereit und realisiert mit Hilfe von "Layout-Manager"-Klassen Positionierung
122 Standardein- und Standardausgabe spielen in Java nur im Rahmen des „Debugging“ eine Rolle.
154
Programmieren in Java
Panel
Applet
Window
Frame
Dialog
FileDialog
und Anordnung von Komponenten.
Ist die konkrete Klasse mit den Eigenschaften von Component und Container. Sie
erbt alle Eigenschaften von Container, kann Komponenten aufnehmen und mit Hilfe
des Layoutmanagers auf dem Bildschirm anordnen.
Erweitert die Funktionalität der Klasse Applet um Methoden, die für das Ausführen
von Applets von Bedeutung sind. Damit entsteht ein Programmelement, das eine
Größe und eine Position hat, auf Ereignisse reagieren kann und in der Lage ist,
weitere Komponenten aufzunehmen.
Bestimmt ein Top-Level-Window ohne Rahmen, Titelleiste und Menü. Sie ist für
Anwendungen geeignet, die Rahmenelemente selbst zeichnen oder volle Kontrolle
über das gesamte Fenster benötigen.
Repräsentiert ein Top-Level-Window mit Rahmen, Titelleiste und optionalem Menü.
Realisiert modale und nicht modale Dialoge.
Stellt ein Standard-Dateidialog des jeweiligen Systems bereit. Dieser kann beim
Laden oder Speichern einer datei zur Eingabe oder zur Auswahl eines Dateinamens
verwendet werden.
Abb. Fensterklassen-Hierarchie
Alle Fensterklassen sind von einer gemeinsamen abstrakten Basisklasse abgeleitet,
die die Funktionalität für Aussehen, Positionierung, Ausgabe und Laufzeitverhalten(
Fokus, Mauszeiger, Sichtbarkeit) bereitstellt.
Component
{ abstract }
public String getName()
public void setName(String name)
public Container getParent()
public boolean contains(int x, int y)
public boolean isVisible()
public void setVisible(boolean b)
public boolean isEnabled()
public void setEnabled(boolean b)
public boolean isShowing()
public Color getForeground()
public void setForeground(Color c)
public Color getBackground()
public void setBackground(Color c)
public Font getFont()
public void setFont(Font f)
public Point getLocation()
public Point getLocationOnScreen()
public void setLocation(int x, int y)
public void setLocation(Point p)
public Dimension getSize()
public void setSize(int breite, int hoehe)
public void setSize(Dimension d)
public Rectangle getBounds()
public void setBounds(int x, int y, int breite, int hoehe)
public void setBounds(Rectangle r)
public Graphics getGraphics()
public FontMetrics getFontMetrics(Font font)
public void paintAll(Graphics g)
public void repaint()
public boolean imageUpdate(Image bild, int flags, int x, int y, int breite, int hoehe)
public Image createImage(ImageProducer erz)
public Image createImage(int breite, int hoehe)
public boolean prepareImage(Image bild, ImageObserver obs)
155
Programmieren in Java
public boolean prepareImage(Image bild, int breite, int hoehe, ImageObserver obs)
public int checkImage(Image bild, ImageObserver obs)
public int checkImage(Image bild, int breite, int hoehe, ImageObserver obs)
public void addComponentListener(ComponentListener l)
public void removeComponentListener(ComponentListener l)
public void addFocusListener(FocusListener l)
public void removeFocusListener(FocusListener l)
public void addKeyListener(KeyListener l)
public void removeKeyListener(KeyListener l)
public void addMouseListener(MouseListener l)
public void removeMouseListener(MouseListener l)
public void addMouseMotionListener(MouseMotionListener l)
public void removeMouseMotionListener(MouseMotionListener l)
public final void enableEvents(long eventsToEnable)
public final void disableEvents(long eventsToDisable)
protected void processComponentEvent(ComponentEvent e)
protected void processFocusEvent(FocusEvent e)
protected void processKeyEvent(KeyEvent e)
protected void processMouseEvent(MouseEvent e
protected void processMouseMotionEvent(MouseEvent e)
public boolean gotFocus(Event evt) //deprecated, -> processFocusEvent(FocusEvent e)
public boolean lostFocus(Event evt, Object was)
public void requestFocus()
public void add(PopupMenu popup)
public void remove(MenuComponent popup)
public String toString()
Abb.: Die Klasse Component
Container sind Fenster oder Fensterbereiche, in denen andere Kontrollelemente
platziert werden können.
Component
Container
{abstract}
public int countComponents()
public Component getComponent(int n)
public Components[] getComponents()
public Insets getInsets()
public Component add(Component comp)
public void remove(Component comp)
public void removeAll()
public LayoutManager getLayout()
public void setLayout(LayoutManager mgr)
public Dimension getPreferredSize()
public Dimension getMinimumSize()
public Dimension getMaximumSize()
public void invalidate()
public void paint(Graphics g) //container
public void update(Graphics g) //container
protected void processEvent(AWTEvent e) //container
public void addNotify() //container
public void removeNotify() //container
Abb.: Die Klasse Container
156
Programmieren in Java
Zur Anzeige eines Fensters auf dem Bildschirm muß eine der Fensterklassen, Window,
Frame, Dialog, Applet, FileDialog instanziert werden. Zum Ableiten einer
eigenen Fensterklasse wird in der Regel die Klasse Frame oder Dialog verwendet,
die beibe aus der Klasse Window abgeleitet sind.
Container
Window
<< Methoden >>
public void show()
public boolean isShowing()
public void dispose()
public void pack()
public void toFront()
public void toBack()
public void addWindowListener(WindowListener l)
Frame
Dialog
public static final int DEFAULT_CURSOR
<< Konstruktor >>
public Frame()
public Frame(String titel)
<< Methoden >>
public String getTitle()
public void setTitle(String titel)
public MenuBar getMenuBar()
public void setmenuBar(MenuBar mb)
public boolean isResizable()
public void setResizable(boolean b)
public void remove(MenuComponent m)
<< Konstruktor >>
public Dialog(Frame eltern)
public Dialog(Frame eltern, boolean modal)
<< Methoden >>
public boolean isModal()
public void setModal(boolean b)
public void show()
public boolean isResizable()
public void setResizable(boolean b)
Abb.: Die Klassen Frame und Dialog
Bsp.123: Ein-, Ausgabe über Textfelder in einem ersten GUI
Ein erstes GUI soll aus einem editierbaren und einem nicht editierbaren Textfeld bestehen. Den
beiden Textfeldern soll jeweils ein Label mit der Beschriftung „Eingabestring:“ bzw.
„Ausgabestring“ zugeordnet sein. Diese Komponenten sollen automatisch von links nach
rechts und von oben nach unten angeordnet werden in einem Panel, das selbst wiederum
einziges Objekt in einem Fenster (Frame) mit dem Titel „Echo“ ist.
import java.lang.*;
import java.awt.*;
public class EchoMitBeno
{
// Einlesen und Ausgeben von Zeichenketten ueber
// eine grafische Benutzeroberflaeche
public static void main(String args[])
{
123 pr14160
157
Programmieren in Java
TextField eingabeTextFeld = new TextField(20);
Label eingabeTextFeldLabel = new Label("Eingabestring:");
eingabeTextFeld.setEditable(true);
TextField ausgabeTextFeld = new TextField(20);
Label ausgabeTextFeldLabel = new Label("Ausgabestring:");
ausgabeTextFeld.setEditable(false);
Panel panel = new Panel();
panel.add(eingabeTextFeldLabel);
panel.add(eingabeTextFeld);
panel.add(ausgabeTextFeldLabel);
panel.add(ausgabeTextFeld);
Frame fenster = new Frame("Echo");
fenster.add(panel);
fenster.pack();
fenster.setVisible(true);
}
}
Die Implementierung des vorliegenden Programms zeigt das folgende Ausgabefenster:
Das vorliegende Beispiel zeigt noch einige Mängel:
- Zwar kann in das für die Eingabe vorgesehene Textfeld ein String eingegeben werden, das zweite
Textfeld ist allerdings nicht zugänglich. Es passiert somit nicht gerade viel. Es werden noch keine
Ereignisse aufgefangen und behandelt.
- Noch nicht einmal kann das Fenster geschlossen werden, und die Applikation muß (nach dem
Schließen des Frame) mit "CTRL-C" explizit abgebrochen werden.
3.1.2 Ereignisbehandlung unter grafischen Benutzeroberflächen
Im Mittelpunkt der Programmierung unter einer GUI steht die Kommunikation zwischen
System und Anwendungsprogramm. Die Anwendung wird über alle Arten von
Ereignissen und Zustandsänderungen vom System durch Versenden von Nachrichten
(z.B. über Mausklick, Tastatureingaben, Veränderungen an Größe und Lage der
Fenster) informiert. Die Reaktion auf die Nachrichten erfolgt in spezielllen
Ereignisempfängern (EventListeners), die das zum Ereignis passende EmpfängerInterface implementieren. Damit ein Ereignisempfänger Nachrichten einer bestimmten
Ereignisquelle erhält, muß er sich bei der Quelle registrieren lassen, d.h.: Es muß eine
EventListener-Klasse geschrieben, instanziert und bei der Ereignisquelle registriert
werden. Zum Empfang von Nachrichten muß ein Objekt eine Reihe von Methoden
implementieren, die von der Nachrichtenquelle, bei der es sich registriert hat, aufgerufen
werden können. Die Ereignisempfänger stellen diese Methoden durch Implementierung
von Interfaces bereit, die aus der Klasse EventListener des Pakets java.util
abgeleitet sind.
158
Programmieren in Java
Ereignistypen. Im JDK 1.1 werden Ereignistypen durch eine Hierarchie von
Ereignisklassen repräsentiert, die alle aus der Klasse java.util.EventObject124
abgeleitet sind.
EventObject
AWTEvent
ComponentEvent
FocusEvent
InputEvent
KeyEvent
ActionEvent
AdjustmentEvent
ContainerEvent
ItemEvent
TextEvent
WindowEvent
MouseEvent
Abb.: Spezifische Ereignisklassen
Die Hierarchie der AWT-spezifischen Ereignisklassen beginnt mit der Klasse
AWTEvent und befindet sich im Paket java.awt. AWTEvent ist Superklasse aller
Ereignisklassen des AWT, die sich im Paket java.awt.event befinden. Dieses
Paket ist in jede Klasse einzubeziehen, die sich mit dem Event-Handling von AWTAnwendungen beschäftigt.
EventListener-Interface. Je Ereignisklasse gibt es ein EventListener-Interface. Es
definiert eine seperate Methode für jede Ereignisart der Ereignisklasse. So besitzt
bspw. das Interface MouseListener die Methoden mouseClicked,
mouseEntered, mouseExited, mousePressed und mouseReleased, die beim
Eintreffen des jeweiligen Ereignis aufgerufen werden.
EventListener
FocusListener
ActionListener
AdjustementListener ItemListener
KeyListener
MouseListener
MouseMotionListener
ComponentListener
ContainerListener
WindowListener
Abb.: Hierarchie der EventListener-Interfaces
Jede der Methoden eines Listener-Interface enthält als einziges Argument ein Objekt
vom zugehörigen Ereignistyp. Alle Methoden sind vom Typ void.
124 Speichert das Objekt, das die Nachricht ausgelöst hat und gibt durch Aufruf von "public Object getSource()"
das Objekt an.
159
Programmieren in Java
3.1.3 Anwendung lokaler Klassen für die Ereignisbehandlung
Im GUI-Objekt, das einen Event-Handler benötigt wird eine lokale Klasse zur
Implementierung des passenden Interface angegeben. Lokale Klassen werden lokal zu
einer anderen Klasse erzeugt. Sie sind nur innerhalb dieser Klasse definiert und
sichtbar. Objekte der lokalen Klasse können nur aus der erzeugenden Klasse produziert
werden. Die lokale Klasse kann aber auf alle Instanzmerkmale der erzeugenden Klasse
zugreifen.
Eine Variante lokale Klassen sind anonyme Klassen. Sie werden ebenfalls lokal zu
einer anderen Klasse erzeugt, kommen aber ohne Klassennamen aus. Dazu werden sie
bei der Übergabe eines Objekts an eine Methode oder als Rückgabewert einer
Methode innerhalb einer einzigen Anwendung definiert und instanziert. Damit einer
anonymen Klasse überhaupt eine sinnvolle Aufgabe zugeführt werden kann, muß sie
aus einer anderen Klasse abgeleitet sein oder ein bestehendes Interface
implementieren.
Bsp.: ActionListener-Interface für die Übernahme eines Textfeld-Inhalts in ein
Textfeld zur Ausgabe
Falls das Textfeld für die Ausgabe den im Textfeld für die Eingabe angegebenen Text wiedergeben
soll, muß ein ActionListener (eine zusätzlich innere, anonyme Klasse) mit der Methode public
void actionPerformed (ActionEvent a) definiert werden. Diese Methode umfaßt Aufrufe
der Methoden getText() und setText() der Klasse TextField. Darüber kann in das
Eingabetextfeld eingegebener Text in das Ausgabefeld ausgegeben werden. Da die "Methode
actionPerformed" der anonymen inneren Klasse auf "eingabeTextFeld" bzw.
"ausgabeTextFeld" zugreift, ist außerhalb von main() anzugeben:
private static TextField eingabeTextFeld = new TextField(20);
private static TextField ausgabeTextFeld = new TextField(20);
Zum Schließen des Fensters wird über eine anonyme innere Klasse ein
WindowAdapter (eine Art WindowListener) mit der Methode "public void
windowClosing(WindowEvent e)" definiert und an den "Frame" eingekettet.
Wird der Frame geschlossen, dann wird diese Methode aufgerufen, die über
System.exit(0) die Applikation beendet.
Eine Adapterklasse implementiert ein Interface mit mehreren Mehoden und erlaubt es
somit abgeleiteten Klassen, nur noch die Methoden zu überlagern, die tatsächlich von
Interesse sind. Passende Adapterklasssen stellt das Paket java.awt.event bereit, z.B.
FocusAdapter, KeyAdapter, MouseAdapter, MouseMotionAdapter,
ComponentAdapter, ContainerAdapter, WindowAdapter. Die Registrierung
erfolgt mit der Methode addWindowListener, die in den Klassen Dialog und
Frame zur Verfügung steht.
Bsp.125:
Textfeldbearbeitung
WindowAdapter
import
import
import
public
{
mit
"ActionListener-Interface"
java.lang.*;
java.awt.*;
java.awt.event.*;
class EchoMitBeno extends Object
125 pr14160
160
und
Programmieren in Java
// Einlesen und Ausgeben von Zeichenketten ueber
// eine grafische Benutzeroberflaeche
private static TextField eingabeTextFeld = new TextField(20);
private static TextField ausgabeTextFeld = new TextField(20);
public static void main(String args[])
{
Frame fenster = new Frame("Echo");
fenster.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
// TextField eingabeTextFeld = new TextField(20);
Label eingabeTextFeldLabel = new Label("Eingabestring:");
eingabeTextFeld.setEditable(true);
eingabeTextFeld.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
String s = eingabeTextFeld.getText();
ausgabeTextFeld.setText(s);
}
});
// TextField ausgabeTextFeld = new TextField(20);
Label ausgabeTextFeldLabel = new Label("Ausgabestring:");
ausgabeTextFeld.setEditable(false);
Panel panel = new Panel();
panel.add(eingabeTextFeldLabel);
panel.add(eingabeTextFeld);
panel.add(ausgabeTextFeldLabel);
panel.add(ausgabeTextFeld);
// Frame fenster = new Frame("Echo");
fenster.add(panel);
fenster.pack();
fenster.setVisible(true);
}
}
Alternativ zur vorliegenden Realisierung der Ereignisbehandlung über eine anonyme
Klasse, kann auch ein Listener über eine lokale Klasse implementiert werden.
Gewöhnlich ist die Anwendung eine Subklasse von Frame.
import java.lang.*;
import java.awt.*;
import java.awt.event.*;
public class Vorl11b extends Frame
{
private static Label eingabeTextFeldLabel = new Label("Eingabestring:");
private static TextField eingabeTextFeld = new TextField(20);
private static Label ausgabeTextFeldLabel = new Label("Ausgabestring:");
private static TextField ausgabeTextFeld = new TextField(20);
public Vorl11b()
{
eingabeTextFeld.setEditable(true);
ausgabeTextFeld.setEditable(false);
Panel panel = new Panel();
161
Programmieren in Java
panel.add(eingabeTextFeldLabel);
panel.add(eingabeTextFeld);
panel.add(ausgabeTextFeldLabel);
panel.add(ausgabeTextFeld);
eingabeTextFeld.addActionListener(new TFL());
add(panel);
pack();
setVisible(true);
}
class TFL implements ActionListener
{
public void actionPerformed(ActionEvent ae)
{
String s = eingabeTextFeld.getText();
ausgabeTextFeld.setText(s);
}
}
public static void main(String args[])
{
Vorl11b vorl11b = new Vorl11b();
vorl11b.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
}
}
Schließlich kann das Interface auch direkt implementiert werden. Zweckmäßigerweise
ist dann die Anwendung eine Subklasse von Frame.
import java.lang.*;
import java.awt.*;
import java.awt.event.*;
public class Vorl11a extends Frame implements ActionListener
{
private static Label eingabeTextFeldLabel = new Label("Eingabestring:");
private static TextField eingabeTextFeld = new TextField(20);
private static Label ausgabeTextFeldLabel = new Label("Ausgabestring:");
private static TextField ausgabeTextFeld = new TextField(20);
public Vorl11a()
{
eingabeTextFeld.setEditable(true);
ausgabeTextFeld.setEditable(false);
Panel panel = new Panel();
panel.add(eingabeTextFeldLabel);
panel.add(eingabeTextFeld);
panel.add(ausgabeTextFeldLabel);
panel.add(ausgabeTextFeld);
eingabeTextFeld.addActionListener(this);
add(panel);
pack();
setVisible(true);
}
public void actionPerformed(ActionEvent ae)
162
Programmieren in Java
{
System.out.println(ae.getActionCommand());
String s = eingabeTextFeld.getText();
ausgabeTextFeld.setText(s);
}
public static void main(String args[])
{
Vorl11a vorl11a = new Vorl11a();
vorl11a.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
}
}
3.1.4 Externe und interne Darstellung von numerischen Werten
Numerische Werte werden
- extern (z.B. im Textfeld eines GUI) durch Zeichenketten dargestellt
- intern als Werte von einem der elementaren Datentypen byte, short, int, long,
float oder double oder als Objekte von einem der Referenzdatentypen (WrapperKlassen) Byte, Short, Integer, Long oder Double. Diese Klassen stellen
Methoden zur Umwandlung von numerischen Werten in Zeichenketten und umgekehrt
zur Verfügung. Da nicht jede beliebige Zeichenkette als interne Darstellung einer Zahl
interpretiert werden kann, kann beim Versuch der Umwandlung eine
NumberFormatExpression auftreten, die normalerweise mit einer try-catchAnweisung entsprechend behandelt werden sollte.
Bsp.126: Einlesen und Ausgeben von numersichen Werten
import java.lang.*;
import java.awt.*;
import java.awt.event.*;
public class EchoZahl extends Object
{
// Einlesen und Ausgeben von Zeichenketten ueber
// eine grafische Benutzeroberflaeche
private static TextField eingabeTextFeld = new TextField(20);
private static TextField ausgabeTextFeld = new TextField(20);
public static void main(String args[])
{
Frame fenster = new Frame("Echo");
fenster.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
126 pr14160
163
Programmieren in Java
}
});
// TextField eingabeTextFeld = new TextField(20);
Label eingabeTextFeldLabel = new Label("Zahleneingabe:");
eingabeTextFeld.setEditable(true);
eingabeTextFeld.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
Double dRef = null;
String s = eingabeTextFeld.getText();
try {
dRef = new Double(s);
}
catch (NumberFormatException nfe)
{
System.err.println(nfe.toString());
}
double d = dRef.doubleValue();
d++;
String s1 = Double.toString(d);
String s2 = String.valueOf(d);
ausgabeTextFeld.setText(s2);
}
});
// TextField ausgabeTextFeld = new TextField(20);
Label ausgabeTextFeldLabel = new Label("Ausgabestring:");
ausgabeTextFeld.setEditable(false);
Panel panel= new Panel();
panel.add(eingabeTextFeldLabel);
panel.add(eingabeTextFeld);
panel.add(ausgabeTextFeldLabel);
panel.add(ausgabeTextFeld);
// Frame fenster = new Frame("Echo");
fenster.add(panel);
fenster.pack();
fenster.setVisible(true);
}
}
3.1.5 Low-Level-Events
Von der Klasse ComponenEvent sind Event-Klassen für "Low-Level"-Ereignisse
abgeleitet. Sie sind für den Transfer von elementaren Nachrichten zuständig, die von
Fenstern und Dialogelementen stammen.
Component-Events
Wird eine Komponente verschoben oder ihre Größe bzw. ihr Anzeigezustand verändert,
dann wird ein Componen-Event generiert. Da Fenster und alle Dialogelemente aus der
Klasse Component abgeleitet sind, gelten die hier angegebenen Ereignisse für nahezu
alle GUI-Elemente.
Der Empfänger für ComponentEvent muß das Interface ComponentListener
implementieren und bekommt Events des Typs ComponentEvent übergeben.
164
Programmieren in Java
ComponentEvent erweiter AWTEvent und stellt neben getID, getSource die
Methode public Component getComponent() bereit, mit der die Komponente
ermittelt werden kann, die die Nachricht ausgelöst hat. Die Registrierung der
Empfängerklasse erfolgt mit
public void addComponentListener(ComponentListener l).
Ereignismethode
componentShown
componentHidden
componentMoved
componentResized
Bedeutung
Eine Komponente wurde sichtbar
Eine Komponente wurde unsichtbar
Eine Komponente wurde verschoben
Die Größe der Komponente hat sich verändert
Abb.: Übersicht zu den Methoden von ComponentListener
Window-Events
Ein WindowEvent wird generiert, falls sich am Status eines Fensters eine Änderung
ergeben hat, die für das Anwenderprogramm interessant sein könnte. Ein Empfänger
für Window-Events muß das Interface WindowListener implementieren.
WindowEvent stellt neben getID, getSource die Methode public Window
getWindow() zur Verfügung, mit der das Fenster ermittelt werden kann, das die
Nachricht ausgelöst hat. Die Registrierung der Empfängerklasse erfolgt über die
Methode public void addWindowListener(WindowListener l) , die in
den Klassen Dialog und Frame zur Verfügung steht.
Ereignismethode
windowActivated
windowClosed
windowClosing
windowDeactivated
windowDeiconified
windowIconified
windowOpened
Bedeutung
Das Fenster wurde aktiviert. Die Methode wird nach dem Erstellen des
Fensters aufgerufen und wenn ein Fenster, das im Hintergrund stand,
erneut in den Vordergrund gelangt.
Das Fenster wurde geschlossen
Das Fenster soll geschlossen werden. Diese Methode wird aufgerufen,
wenn der Anwender das Fenster über die TitelLeiste, das Systemmenü
oder die Tastenkombination ALT+F4 schließen will. Die Anwendung hat
den Code bereit zu stellen, der das Fenster schließt. Standardmäßig
reagiert das Programm nicht auf diese Benutzeraktionen
Das Fenster wurde deaktiviert, also in den Hintergrung gestellt
Das Fenster wurde wiederhergestellt, nachdem es zuvor auf Symbolgröße
verkleinert wurde
Das Fenster wurde auf Symbolgröße verkleinert
Das Fenster wurde geöffnet.
Abb.: Übersicht zu den Methoden von WindowListener
Mouse-Events
Ein Mouse-Event entsteht, wenn der Anwender (innerhalb der Client-Area des Fensters)
eine der Maustasten drückt oder losläßt. Dabei reagiert das Programm sowohl auf
Klicks der linken als auch der ( - falls vorhanden -) der rechten Maustaste und zeigt an,
welche der Umschalttasten STRG, ALT, UMSCHALT oder META während des
Mausklicks gedrückt waren. Es ist möglich zwischen einfachen oder doppelten
Mausklicks zu unterscheiden. Ein Empfänger für Mouse-Events erweitert die Klasse
InputEvent und stellt neben getID, getSource eine Reihe zusätzlicher Methoden
bereit. Die Registrierung der Empfängerklasse erfolgt mit public void
165
Programmieren in Java
addMouseListener(MouseListener l), die in allen Klassen zur Verfügung
steht, die aus Component abgeleitet sind.
Ereignismethode
mousePressed
mouseReleased
mouseClicked
Bedeutung
Die Maustaste wurde gedrückt
Die gedrückte Maustaste wurde losgelassen
Eine Maustaste wurde gedrückt und wieder losgelassen. Die Methode wird
nach mouseReleased aufgerufen.
Der Mauszeiger wurde in den Client-Bereich der auslösenden Komponente
hineinbewegt
Der Mauszeiger wurde aus dem Client-Bereich der auslösenden Komponente
herausbewegt.
mouseEntered
mouseExited
Abb.: Übersicht zu den Methoden von MouseListener
Die Ermittlung der Position des Mauszeigers kann über
public int getX(); // liefert die X-Koordinate
public int getY(); // liefert die Y-Koordinate
public Point getPoint(); /* liefert X- und Y-Koordinate des Punkts, an dem
sich der Mauszeiger beim Auftreten des Ereignisses befindet */
ermittelt werden. Koordinatenwerte werden relativ zum Ursprung der auslösenden
Komponente angegeben.
Weiterhin gibt es in MouseEvent
die Methode public boolean
isPopupTrigger(). Darüber kann abgefragt werden, ob das Klickereignis den
Ausruf eines Popup-Menüs anzeigen soll. Die Methode public
int
getClickCount() liefert die Anzahl der Mausklicks.
Für die Beabeitung von Mouse-Events stehen außerdem die aus InputEvent
127geerbten Methoden
public
public
public
public
boolean
boolean
boolean
boolean
isShiftDown();
isControlDown();
isMetaDown();
isAltDown();
zur Verfügung.
MouseMotionEvents
Sie geben Auskunft über die Bewegung des Mauszeigers. Ein Empfänger für
"MouseMotionEvents" muß das Interface MouseMotionListener implementieren.
Es wird mit public void addMouseMotionListener(MouseListener l)
registriert. Die Methode steht allen Objekten der Klasse Component oder daraus
abgeleiteteter Klassen zur Verfügung. Die Methoden von MouseMotionListener
bekommen Events des Typs MouseEvent übergeben. Damit stehen diesselben
Methoden wie bei MouseEvent zur Verfügung.
Das Interface MouseMotionListener definiert
127 InputEvent ist Basisklasse von MouseEvent und KeyEvent. Sie stellt Methoden bereit, die allgemeine
Informationen über den Zustand der Umschalttasten STRG, ALT, UMSCHALT oder META zum Zeitpunkt des
Ereignisses liefern
166
Programmieren in Java
public abstract void mouseMoved(MouseEvent e);
// Aufruf bei Bewegung einer Maus ohne Drücken der Maustaste
public abstract void mouseDragged(MouseEvent e);
/* Aufruf bei Bewegung der Maus und gedrückter rechter
Maustaste */
oder
linker
Fokus-Events
Der Fokus zeigt an, welches Fenster Tastatureingaben erhält. Sind mehrere Fenster
gleichzeitig geöffnet, so kann immer nur eines von ihnen den Fokus beanspruchen. Sind
auf einem aktiven Fenster mehrere Dialogelemente aktiv, so kann ebenfalls nur eines
davon den Fokus erhalten, denn jedes Dialogelement wird ebenfalls durch ein (meist
unsichtbares) Fenster dargestellt.
Ein Empfänger für Fokus-Events muß das Interface FocusListener implementieren
und bekommt Events des Typs FocusEvent übergeben. FocusEvent erweitert
ComponentEvent und stellt neben getID(), getSource() die Methode public
boolean isTemporary() bereit. Sie zeigt an, ob der Fokuswechsel temporär oder
permanent ist. Die Registrierung von Focus-Events erfolgt über public void
addFocusListener(FocusListener l), die allen Objekten des Typs
Component oder daraus abgeleiteten Objekten zur Verfügung steht, Das Interface
FocusListener enthält zwei unterschiedliche Methoden:
public abstract
// Aufruf, wenn
public abstract
// Aufruf, wenn
void focusGained(FocusEvent e)
die Komponente den Fokus erhält
void focusLost(FocusEvent e)
die Komponente den Fokus abgibt
Über die Methode public void requestFocus() kann eine Komponente den
Fokus für sich selbst beanspruchen bzw. ihn einer anderen Komponenten zuweisen.
Key-Events
Ein Empfänger für Key-Events muß das Interface KeyListener implementieren und
bekommt Events des Typs KeyEvent übergeben. KeyEvent erweitert die Klasse
InputEvent, die aus ComponentEvent abgeleitet ist, und stellt neben getID,
getSource eine Reihe von Methoden zur Erkennung und Berabeitung von
Tastaturcodes zur Verfügung. Die Registrierung erfolgt mit der Methode public
void addKeyListener (KeyListener l), die auf allen Objekten des Typs
Component oder daraus abgeleiteter Klassen zur Verfügung steht. Das Interface
KeyListener definiert drei unterschiedliche Methoden:
public abstract void keyTyped(KeyEvent e);
public abstract void keyPressed(KeyEvent e);
public abstract void keyReleased(KeyEvent e);
Die Taste, die gedrückt wurde, erhält man über die folgenden Methoden der Klasse
KeyEvent bereitgestellt:
public int getKeyCode()
liefert virtuelle Tastencodes, die in KeyEvent als symbolische Konstanten definiert wurden.Hier wird beim
Drücken der Taste A immer der Code VK_A geliefert, unabhängig davon, ob UMSCHALT gedrückt wurde
oder nicht.
167
Programmieren in Java
Symbolischer Name
VK_0..VK_9
VK_A..VK_Z
VK_ENTER
VK_SPACE
VK_TAB
VK_ESCAPE
VK_BACK_SPACE
VK_F1..VK_F12
VK_HOME, VK_END
VK_PAGE_UP, VK_PAGE_DOWN
VK_DOWN, VK_UP
VK_LEFT, VK_RIGHT
VK_INSERT, VK_DELETE
Bedeutung
0..9
A..Z
Enter
Leertaste
Tabulator
Escape
Rückschritt
Die Funktionstasten F1 .. F12
Home, End
Bild hoch, Bild runter
Cursor hoch, Cursor runter
Cursor links, Cursor rechts
Einfg, Entf
Abb.: Tabelle der virtuellen Key-Codes
public char getKeyChar()
liefert das Zeichen, das der gedrückten Zeichentste entspricht, z.B. "a", wenn Taste A gedrückt wurde,
und "A", wenn die Tastenkombination UMSCHALT + A gedrückt wurde. Funktionstasten werden nicht
übertragen. Der Rückgabewert ist hier KeyEvent.CHAR_UNDEFINED.
getKeyCode()
Zeichentaste129: VK_UNDEFINED
Funktionstaste130: keyPressed131 Zeichentaste: VK_...
Funktionstaste: VK_...
keyTyped128
getKeyChar()
Zeichentaste: Taste als char
Funktionstaste: Zeichentaste: Taste als char
Funktionstaste: CHAR_UNDEFINED
Rückgabecode bei Tastatur-Ereignissen
Zusätzlich stehen fogende aus InputEvent geerbten Methoden zur Verfügung:
public
public
public
public
boolean
boolean
boolean
boolean
isShiftDown();
isControlDown();
isMetaDown();
isAltDown();
Bsp.: Das folgende Programm132 schreibt alle möglichen Ereignisse zu Low-LevelEvents auf.
import java.awt.*;
import java.awt.event.*;
public class PR14166 extends Frame
{
PR14166()
{
addComponentListener(new CL());
128 zeigt das Verhalten beim Aufruf der Listener-Methode keyTyped
129 Tasten, mit denen Buchstaben, Ziffern oder sonst. Unicode-Zeichen eingegeben werden, wie z.B. a, A, 1, 2, %
aber auch ESC, SPACE, TAB
130 Dazu gehören bspw. F1, F2, Pos 1 aber auch die Umschalttasten: STRG, ALT, UMSCHALT
131 zeigt das Verhalten beim Aufruf von keyPressed
132 vgl. pr14160
168
Programmieren in Java
addFocusListener(new FL());
addKeyListener(new KL());
addMouseListener(new ML());
addMouseMotionListener(new MML());
}
class CL implements ComponentListener
{
public void componentMoved(ComponentEvent e)
{
System.out.println(e.toString());
}
public void componentResized(ComponentEvent e)
{
System.out.println(e.toString());
}
public void componentHidden(ComponentEvent e)
{
System.out.println(e.toString());
}
public void componentShown(ComponentEvent e)
{
System.out.println(e.toString());
}
}
class FL implements FocusListener
{
public void focusGained(FocusEvent e)
{
System.out.println(e.toString());
}
public void focusLost(FocusEvent e)
{
System.out.println(e.toString());
}
}
class KL implements KeyListener
{
public void keyPressed(KeyEvent e)
{
System.out.println(e.toString());
}
public void keyReleased(KeyEvent e)
{
System.out.println(e.toString());
}
public void keyTyped(KeyEvent e)
{
System.out.println(e.toString());
}
}
class ML implements MouseListener
{
public void mouseClicked(MouseEvent e)
{
// requestFocus();
System.out.println(e.toString());
}
public void mousePressed(MouseEvent e)
{
169
Programmieren in Java
System.out.println(e.toString());
}
public void mouseReleased(MouseEvent e)
{
System.out.println(e.toString());
}
public void mouseEntered(MouseEvent e)
{
System.out.println(e.toString());
}
public void mouseExited(MouseEvent e)
{
System.out.println(e.toString());
}
}
class MML implements MouseMotionListener
{
public void mouseDragged(MouseEvent e)
{
System.out.println(e.toString());
}
public void mouseMoved(MouseEvent e)
{
System.out.println(e.toString());
}
}
public static void main(String[] args)
{
PR14166 f = new PR14166();
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
f.setSize(200,200);
f.setVisible(true);
}
}
170
Programmieren in Java
3.1.6 Der Weg eines Ereignisses
Jede Ereignisquelle besitzt eine Reihe von Methoden, die für das Aufbereiten und
Verteilen der Nachricht zuständig sind. Beim Weiterreichen einer Nachricht wird
innerhalb der Nachrichtenquelle die Methode processEvent aufgerufen. Diese
verteilt die Nachricht anhand ihres Typs an spezialisierte Methoden, deren Name sich
nach dem Typ der zugehörigen Ereignisquelle richtet:
protected void processComponentEvent(ComponentEvent e)
protected void processFocusEvent(FocusEvent e)
protected void processKeyEvent(KeyEvent e)
protected void processMouseEvent(MouseEvent e)
protected void processMouseMotionEvent(MouseEvent e)
protected void processActionEvent(ActionEvent e)
Abb.: Spezialisierte Methoden für das Event-Handling
"processEvent" bzw. die spezialisierten Methoden für das Event-Handling werden
nur aufgerufen, wenn der entsprechende Ereignistyp für diese Ereignisquelle aktiviert
wurde. Dies geschieht in folgenden Fällen:
-
Ein passender Ereignisempfänger wurde über die zugehörige addEventListener-Methode
registriert.
Ein Ereignistyp wurde explizit durch Aufruf der Methode protected final void
enableEvents(long eventsToEnable) aktiviert. Die Methode erwartet eine Maske, die durch
eine bitweise Oder-Verknüpfung passender Konstanten aus der Klasse AWTEvent
zusammengesetzt werden kann. Folgende Bitmasken akzeptiert diese Methode:
ACTION_EVENT_MASK
ADJUSTMENT_EVENT_MASK
COMPONENT_EVENT_MASK
CONTAINER_EVENT_MASK
FOCUS_EVENT_MASK
ITEM_EVENT_MASK
KEY_EVENT_MASK
MOUSE_EVENT_MASK
MOUSE_MOTION_EVENT_MASK
TEXT_EVENT_MASK
WINDOW_EVENT_MASK
Die Namen dieser Bitmasken verweisen auf die Namen der Ereignisklassen, für deren
Objekte sie den Ereignisfilter durchlässig schalten. Sollen mehrere aktiviert werden,
dann ist diese Kombination durch Addition bzw. bitweises Oder möglich, z.B.:
enableEvents(ACTION_EVENT_MASK + WINDOW_EVENT_MASK)
bzw.
enableEvents(ACTION_EVENT_MASK | WINDOW_EVENT_MASK)
Sobald ein soches Ereignis erzeugt wird, sieht sein Weg dann folgendermaßen aus:
1. Die Methode processEvent der eigenen Klasse erhält das Ereignis
2. Nach der Ereignisverarbeitung wird die Methode der Subklasse aufgerufen
3. Diese prüft den Ereignistyp und ruft dann typspezifische process...Event-Methoden auf, z.B.
processActionEvent, processFocusEvent.
4. Diese prüfen, ob Listener registriert sind, und rufen sie gegebenenfalls auf.
171
Programmieren in Java
5. Nach diesen Aufrufen wird die Methode processEvent der nächsten Superklasse aufgerufen.
6. Sie verfährt wieder so, wie unter den Ziffern 3 bis 5 beschrieben. Das wiederholt sich so lange, bis
processEvent von ComponentEvent durchlaufen wird.
Bsp.: Das folgende Programm schreibt alle möglichen Ereignisse zu Low-Level-Events
auf.
import java.awt.*;
import java.awt.event.*;
public class PR14168 extends Frame
{
PR14168()
{
// Bekanntmachen der im Programm zu bearbeitenden Ereignisse
enableEvents(
AWTEvent.COMPONENT_EVENT_MASK |
AWTEvent.FOCUS_EVENT_MASK |
AWTEvent.KEY_EVENT_MASK |
AWTEvent.MOUSE_EVENT_MASK |
AWTEvent.MOUSE_MOTION_EVENT_MASK |
AWTEvent.CONTAINER_EVENT_MASK);
}
public void processEvent(AWTEvent e)
{
System.out.println(e.toString());
super.processEvent(e);
}
public static void main(String[] args)
{
PR14168 f = new PR14168();
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
f.setSize(200,200);
f.setVisible(true);
}
}
172
Programmieren in Java
3.2 Kommunikation Anwender – Programm über Dialoge
bzw. Menüs
3.2.1 Dialoge
Der Anwender verkehrt im Rahmen grafischer Benutzeroberflächen über vom
Programm vorgegebene Dialoge. Diese bestehen aus einem Fenster und einer Reihe
von Dialogelementen (z.B. Textfelder, Buttons, Listboxen) zur Darstellung und Erfassung
programmspezifischer Daten. Das Design der Dialoge wird von Layoutmangern
unterstützt, die sich um Größe und Anordnung der einzelnen Dialogelemente kümmern.
Erstellen eines Dialogs
Dafür sind 4 Schritte nötig:
-
Anlegen eines Fensters
Zuordnen eine Layoutmanagers
Einfügen von Dialogelementen
Anzeigen des Fensters
Anlegen eines Fensters. Ein Dialogfenster kann wahlweise aus der Klasse Frame oder
Dialog abgeleitet werden. "Dialog" erlaubt "modale Dialoge133" und verhindert das
Verändern der Fenstergröße durch den Anwender. Im Gegensatz zum Frame kann ein
"Dialog"-Fenster keine Menüleiste erzeugen und dem Fenster kein Icon134 zuordnen.
Zuordnen eines Layoutmanagers. Es wird über die Methode "public void
setLayout(LayoutManager mgr)" der Klasse Container realisiert. Java stellt fünf
Layoutmanager135
bereit:
FlowLayout
BorderLayout,
GridLayout
GridBagLayout und CardLayout.
Neben den Fähigkeiten eines Layoutmanagers bestimmt in der Regel die Reihenfolge
der Aufrufe von der "add"-Methode des Fensters die tatsächliche Anordnung der
Komponenten auf dem Bildschirm.
Schachteln von Layoutmanagern. Dazu wird an die Stelle, die das Sublayout erhalten
soll, einfach ein Objekt der Klasse Panel eingefügt, das einen eigenen Layoutmanager
erhält. Dieses Panel kann mit Dialogelementen bestückt werden, die entsprechend dem
zugeordneten Sublayout formatiert werden, z.B.:
import java.awt.*;
import java.awt.event.*;
public class PR14171 extends Frame
{
public PR14171()
133 Modale Dialaloge verhindern die Interaktion des Anwenders mit anderen Fenstern der Anwendung bis zum
Schließen des Dialogfensters.
134 Falls ein Fenster (unter Windows) minimiert wird, zeigt es ein Icon an. Mit Hilfe eines Doppelklicks auf das
Icon kann eine ursprüngliche Größe des Fensters wiederhergestellt werden. Mit Hlife der Methode "public
void setIconImage(Image bild)" kann einem Fenster ein Icon zugeordnet werden, das beim
Minimieren angezeigt wird.
135 Vgl. 5.4.2
173
Programmieren in Java
{
addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
setVisible(false);
dispose();
System.exit(0);
}
});
// Layout festelegen und Komponenten hinzufuegen
int i = 0;
Panel p1 = new Panel();
p1.setLayout(new GridLayout(3,1));
p1.add(new Button("Schaltflaeche " + ++i));
p1.add(new Button("Schaltflaeche " + ++i));
p1.add(new Button("Schaltflaeche " + ++i));
Panel p2 = new Panel();
p2.setLayout(new BorderLayout());
p2.add("North",new Button("Schaltflaeche " + ++i));
p2.add("South",new Button("Schaltflaeche " + ++i));
p2.add("West",new Button("Schaltflaeche " + ++i));
p2.add("East",new Button("Schaltflaeche " + ++i));
p2.add("Center",new Button("Schaltflaeche " + ++i));
// Hauptfenster
setLayout(new GridLayout(1,2));
add(p1);
add(p2);
pack();
}
public static void main(String args[])
{
PR14171 f = new PR14171();
f.setVisible(true);
}
}
Das vorliegende Programm zeigt nach dem Aufruf das folgende Fenster:
Einfügen von Dialogelementen. Es erfolgt über
public Component add(Component komponente)
public Component add(Component komponente, int pos)
public Component add (String name, Component komponente)
// erwartet einen String-Parameter, der bei bestimmten Layout-Managern
// (z.B. BorderLayout) Informationen zur Positionierung der Elemente ("bei
// BorderLayout: "South", "East", "West", "North", "Center") angibt.
174
Programmieren in Java
der Klasse Container. Mit "public void remove(Component
komponente)" können bereits an das Fenster übergebene Komponenten gelöscht
werden.
Anzeigen eines Dialogfensters. Es erfolgt durch einen Aufruf von "setVisible".
Zweckmäßig sollte zuvor "public void pack()" der Klasse Window zur
Anpassung der Fenstergröße an den für die Darstellung der Dialogelemente
erforderlichen Platz ausgeführt werden.
Popup-Fenster der Klasse Dialog
Die Klasse Dialog136 stellt ein Popup-Fenster bereit und ermöglicht die Erzeugung
"modaler" bzw. "nicht modaler" Dialoge. "Modal" bedeutet: Das Dialogfeld blockiert
andere Fenster, während es angezeigt wird, z.B.137:
import java.awt.*;
import java.awt.event.*;
public class InfoDialog extends Dialog
{
protected Button schalter;
protected Label label;
public InfoDialog(Frame eltern, String titel, String nachricht)
{
super(eltern,titel,false);
this.setLayout(new BorderLayout(15,15));
label = new Label(nachricht,Label.CENTER);
this.add("Center",label);
schalter = new Button("Okay");
schalter.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
dispose();
setVisible(false);
}
});
Panel p = new Panel();
p.setLayout(new FlowLayout(FlowLayout.CENTER,15,15));
p.add(schalter);
this.add("South",p);
this.pack();
}
public static void main(String args[])
{
Frame f = new Frame("InfoDialog-Test");
f.setSize(300,100);
f.setVisible(true);
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
136 vgl. 5.3.4
137 vgl. pr14170
175
Programmieren in Java
{
// dispose();
// setVisible(false);
System.exit(0);
}
});
InfoDialog dlg = new InfoDialog(f,"Dialog-Demo",
"Diese Demo wurde nach einer Vorlage von"
+ "David Flanagan geschrieben");
dlg.setVisible(true);
}
}
Dialogfenster sind Übergangsfenster. Sie dienen dazu, den Benutzer über Ereignisse
zu informieren oder Eingabe vom Benutzer anzufordern. Im Gegensatz zu Frames
haben Dialogfelder im allg. keine Titelleiste oder Schaltfläche zum Schließen des
Fensters.
Ein Dialogfenster ist, wie ein Frame, ein Panel, in dem Komponenten der
Benutzeroberfläche angeordnet, gezeichnet sowie Grafikoperationen ausgeführt werden
können.
Mit der Klasse FileDialog kann ein plattformspezifischer Dateidialog erzeugt
werden.
Dialog
FileDialog
public static final int LOAD
public static final int SAVE
<< Konstruktor >>
public FileDialog(Frame eltern)
// erzeugt Datei-Dialog ohne Titel zum Öffnen einer Datei
public FileDialog(Frame eltern, String titel)
//erzeugt Dialog zum Öffnen einer Datei mit einem Titel im
// Fensterrahmen
public FileDialog(Frame eltern, String titel, int mode)
// erzeugt Dialog mit einem Titel im Fensterrahmen. Da der
// Dialog sowohl für Sichern und Laden ausgelegt ist, sind
// weder LOAD oder SAVE mit anzugeben.
<< Methoden >>
public String getDirectory()
// liefert das Verzeichnis des Dialogs
public synchronized void setDirectory(String dir)
// setzt das Verzeichnis, das zu Beginn ausgegeben werden soll
public String getFile()
// liefert die ausgewählte datei des Dialogs
public synchronized void setFile(String file)
// Datei, die zu Beginn angezeigt wird
public FilenameFilter getFilenameFilter()
// liefert FilenameFilter.-Objekt zurück
public synchronized void setFilenameFilter(FilenameFilter filter)
// setzt den FilenameFilter
Abb.: Die Klasse FileDialog
176
Programmieren in Java
Vererbungskette. FileDialog leitet sich von Dialog ab. Dialog ist eine Ableitung
von Window. Window ist ein Container und dieser wiederum ein Component.
Bsp.: Laden und Speichern von Dateien mit Unterstützung durch ein Dateidialog-feld 138.
import java.awt.*;
import java.awt.event.*;
public class DateiDialog extends Frame
{
TextField dateiName = new TextField();
TextField verzeichnis = new TextField();
Button oeffnen = new Button("Oeffnen");
Button sichern = new Button("Sichern");
public DateiDialog()
{
setTitle("Dateidialog-Test");
Panel p = new Panel();
p.setLayout(new FlowLayout());
oeffnen.addActionListener(new OeffnenL());
p.add(oeffnen);
sichern.addActionListener(new SichernL());
p.add(sichern);
add(p,BorderLayout.SOUTH);
verzeichnis.setEditable(false);
dateiName.setEditable(false);
p = new Panel();
p.setLayout(new GridLayout(2,1));
p.add(dateiName); p.add(verzeichnis);
add(p,BorderLayout.NORTH);
}
class OeffnenL implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
FileDialog d = new FileDialog(DateiDialog.this,
"Welche Datei soll geoeffnet werden?");
d.setFile("*.java");
d.setDirectory(".");
// Aktuelles Verzeichnis
d.show();
String datei = "*.*";
if ((datei = d.getFile()) != null)
{
dateiName.setText(datei);
verzeichnis.setText(d.getDirectory());
}
else {
dateiName.setText("Cancel wurde gedrueckt!");
verzeichnis.setText("");
}
}
}
class SichernL implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
FileDialog d = new FileDialog(DateiDialog.this,
138 pr14160
177
Programmieren in Java
"Welche Datei soll gesichert werden?",
FileDialog.SAVE);
d.setFile("*.java");
d.setDirectory(".");
// Aktuelles Verzeichnis
d.show();
String datei = "*.*";
if ((datei = d.getFile()) != null)
{
dateiName.setText(datei);
verzeichnis.setText(d.getDirectory());
}
else {
dateiName.setText("Cancel wurde gedrueckt!");
verzeichnis.setText("");
}
}
}
public static void main(String args[])
{
Frame f = new DateiDialog();
f.addWindowListener(
new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
f.setSize(250,110); f.setVisible(true);
}
}
Bei Applets ist es vom Browser abhängig, ob Instanzen von FileDialog eingesetzt
werden können. Die meisten Browser erzeugen lediglich einen Fehler.
Vordefinierte Dialogelemente
Jedes Dialogelement wird in Java durch eine eigene Klasse repräsentiert. Zur
Aufnahme eines Dialogelements in einen Dialog wird eine neue Instanz der
gewünschten Klasse angelegt und das resultierende Element mit "add" in den Dialog
eingefügt. Alle Dialogelemente sind aus der Klasse Component abgeleitet. Sie
verfügen über die grundlegenden Eigenschaften eines Fensters, besitzen Größe und
Position und sind in der Lage, Nachrichten zu empfangen und zu bearbeiten.
178
Programmieren in Java
Component
Button
TextComponent
TextField
Container
TextArea
Menu
Panel
Applet
MenuComponent
MenuBar
Checkbox
MenuItem
Window
Frame
Dialog
Abb.: Komponenten des AWT
Vordefinierte Dialogelemente unter den Komponenten des AWT sind:
Labels 139,
Schaltflächen(Buttons)140,
Kontrollkästchen
und
Optionsfelder141,
142
143
144
Auswahlmenüs , Listenfelder , Textbereiche und Textfelder , Schieberegler145.
Das Dialogelement „Canvas“
Die meisten AWT-Komponenten bieten Möglichkeiten für die Ausführung von
Zeichenoperationen. Zeichenbereiche sind Komponenten, die nur speziell zum
Zeichnen ausgerichtet sind. Zeichenbereiche können keine anderen Komponenten
enthalten, akzeptieren aber Ereignisse. Außerdem können sie Animationen und Bilder
anzeigen. Zum Erstellen eines Zeichenbereichs wird die Klasse Canvas benutzt. Das
eigentliche Zeichnen erfolgt in der Methode paint(Graphics g). Diese Methode
wird immer dann aufgerufen, wenn die Zeichenfäche neu gezeichnet werden muß.
Typische Situationen, in denen paint(Graphics g) aufgerufen wird, sind:
- Der die Zeichenfläche umfassende Rahmen (Frame) wird ertmals erstellt
- Die Methode repaint() wird explizit aufgerufen
- Die Zeichenfläche wurde teilweise oder ganz von anderen Fenstern überdeckt
- Die Größe der Zeichenfläche hat sich verändert
139 vgl. 5.2.2
140 vgl. 5.2.1
141 vgl. 5.2.3
142 vgl. 5.2.4
143 vgl. 5.2.5
144 vgl. 5.2.6
145 vgl. 5.2.7
179
Programmieren in Java
Durch Überlagerung von public void paint(Graphics g) sorgt eine CanvasKomponente für die Darstellung auf dem Bildschirm. Der Punkt (0,0) des
übergebenen Graphics-Objekts entspricht dabei der linken oberen Ecke des
Ausgabebereichs.
Bsp.146:
import java.awt.*;
public class KreuzHaar extends java.applet.Applet
{
GridLayout g
= new GridLayout(1,1);
MeinCanvas can = new MeinCanvas();
public void init()
{
setLayout(g);
add(can);
}
}
class MeinCanvas extends java.awt.Canvas
{
public void paint(Graphics g)
{
int x = getSize().width / 2;
int y = getSize().height / 2;
g.setColor(Color.black);
g.drawLine(x-10,y,x-2,y);
g.drawLine(x+10,y,x+2,y);
g.drawLine(x,y-10,x,y-2);
g.drawLine(x,y+10,x,y+2);
}
}
Da die Klasse Canvas aus Component abgeleitet ist, bekommt ein Canvas-Objekt alle
Ereignisse zugestellt, die auch an eine Komponente gehen. Hierzu zählen: Tastatur-,
Maus-, Mausbewegungs-, Fokus- und Komponentenereignisse.
146 pr52900
180
Programmieren in Java
3.2.2 Menüs
Jedes Fenster / Frame kann eine eigene Menüleiste besitzen. Jede Menüleiste kann
mehrere Menüs enthalten und jedes Menü beliebige Einträge147.
Menüs dienen zur Auswahl von Funktionen und Optionen in Anwendungen. Menüs
können entweder in der Form von Menubars an der Oberseite von Fenstern oder als
Popupmenüs an einer beliebigen Stelle auftreten.
Ein Menü ist entweder in einer Menüleiste eines Frame-Objekts eingebettet (Klasse
MenuBar) oder erscheint auf Abruf als Kontextmenü (Klasse PopupMenu). Damit sind
entweder Untermenüs (Klasse Menu) oder Menüeinträge (Klasse MenuItem)
enthalten.
Jedes Menü besitzt ein „label“ und ein „aktionKommando“ (ein String, der das Menü
beim ActionEvent kennzeichnet und der ausgelöst wird, wenn der Eintrag angeklickt
wird). Für Menüs können Shortcuts, also Tastaturkürzel, angegeben werden.
Ein MenuItem ist ein Eintrag in einem Menü, der sich bei Selektion einer Option ändert
(CheckboxMenuItem) oder ein Kommando absetzt.
147 vgl. 5.3.3
181
Programmieren in Java
MenuComponent
{ abstract }
public String getName()
public void setName(String name)
public MenuContainer getParent()
public Font getFont()
public void setFont(Font f)
public final void dispatchEvent(AWTEvent e)
protected void processEvent(AWTEvent e)
public String toString()
MenuItem
MenuBar
<< Konstruktoren >>
public MenuItem()
public MenuItem(String label)
public MenuItem(String label, MenuShortcut s)
<< Methoden >>
public void addNotify()
public String getLabel()
public void setLabel(String label)
public boolean isEnabled()
public void setEnabled(boolean b)
public MenuShortcut getShortcut()
public void setShortcut(MenuShortcut s)
protected final void enableEvents(long eventsToEnable)
protected final void disableEvents(long eventsToEnable)
public void setActionCommand(String kommando)
public String getActionCommand()
public void addActionListener(ActionListener l)
public void removeActionListener(ActionListener l)
protected void processEvent(AWTEvent e)
protected processActionEvent(ActionEvent e)
...
CheckboxMenuItem
<< Konstruktor >>
public MenuBar()
<< Methoden >>
public void addNotify()
public void removeNotify()
public Menu getHelpMenu()
public void setHelpMenu(Menu m)
public Menu add(Menu m)
public void remove
(MenuComponent m)
public int getMenuCount()
public Menu getMenu(int i)
public Enumeration shortcuts()
public MenuItem
getShortcutMenuItem
(MenuShortcut s)
<< interface >>
MenuContainer
Menu
<< Konstruktoren >>
public CheckboxMenuItem()
public CheckboxMenuItem(String label)
public CheckboxMenuItem(String label,
boolean state)
<< Methoden >>
public void addItemListener(ItemListener l)
public void addNotify()
public boolean getState()
protected void processEvent(AWTEvent e)
...
<< Konstruktoren >>
public Menu()
public Menu(String label)
<< Methoden >>
public MenuItem add(MenuItem m)
public void add(String label)
public void addNotify()
public MenuItem getItem(int index)
public void insert(MenuItem mi, int index)
public void insert(String label,int index)
...
182
Programmieren in Java
3.2.3 Popup-Menüs
Popup-Menüs sind nicht wie normale Menüs an eine bestimmte Position gebunden,
sondern tretem meistens dort auf, wo der Benutzer mit der rechten Maustaste geklickt
hat. Für Popup-Menüs ist die Klasse Popup-Menu zuständig. Menüeinträge können
mit der Klasse Menu erzeugt werden.
Menu
PopupMenu
<< Konstruktor >>
public PopupMenu()
// erzeugt ein Popup-Menu
public PopupMenu(String label)
<< Methoden >>
public void addNotify()
public void show(Component origin, int x, int y)
// läßt das Popup-Menu auf der Komponenten
// origin an der Position x, y aufspringen.
Abb.: Die AWT-Menüklasse PopupMenu
Bsp.: 148
import java.awt.*;
import java.awt.event.*;
public class PopMenDemo extends Frame
{
PopupMenu popmen = new PopupMenu();
public PopMenDemo()
{
addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
popmen.add(new MenuItem("Eintrag 1"));
popmen.addSeparator();
popmen.add(new MenuItem("Eintrag 2"));
popmen.add(new MenuItem("Eintrag 3"));
popmen.add(new MenuItem("Eintrag 4"));
add(popmen);
popmen.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
148 pr53330, PopMenDemo.java
183
Programmieren in Java
System.out.println(e.getActionCommand() + " wurde gedrueckt");
}
});
addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent m)
{
if (m.getModifiers() == m.BUTTON3_MASK)
popmen.show(PopMenDemo.this,m.getX(),m.getY());
}
});
setSize(300,300);
setVisible(true);
}
public static void main(String args[])
{
new PopMenDemo().show();
}
}
184
Programmieren in Java
3.3 Grundlagen der Applet-Erstellung
3.3.1 HTML-Grundlagen
Die Einbindung von Java-Applets in HTML-Seiten
Java-Applets werden innerhalb von HTML-Seiten über Referenzen eingebunden.
Referenzen können Überschriften, Texte, Kapitel, Unterkapitel, Absätze, Grafiken bzw.
Links zu anderen Seiten sein. HTML ist eine Dokumentationsbeschreibungs-sprache
mit der logische Strukturen eines Dokuments beschrieben werden. Über Klartext
(ASCII-Text) gibt ein Dokumentenformat Empfehlungen an eine Darstellungssoftware
(Browser) zur Darstellung der Dokumentstruktur.
Außerdem wird beschrieben, welche Funktionalität wie auszuführen ist, damit sie dem
geplanten Layout und der vorgegebenen Funktionalität entspricht. Es gibt keine
verbindliche Darstellungsvorschrift, deshalb kann die Ausführung von HTML-Seiten in
verschiedenen Browsern oft unterschiedlich anfallen.
Die Basis von HTML
Die Sprache HTML baut auf der Sprache SGML auf. HTML ist die Abkürzung für
Hypertext Markup Language und wurde aus der in der ISO-Norm 87779:1986
festgeschriebenen Spache SGML (Structured Generalized Markup Language)
entwickelt. Ein in HTML geschiebenes Dokument kann außer Text, Grafiken und
multimediale Elemente (Sound, Video, usw.) enthalten (Referenz auf Grafik- oder
Multimedia-Dateien). Weiterhin können mit gewissen Einschränkungen über HTML
Datenbankabfragen formuliert, Resultate optisch aufbereitet und Menüstrukturen
aufgebaut werden.
Die Normung der HTML realisiert und kontrolliert das World Wide Web Consortium
(W3C) mit Sitz in Genf. Ende 1997 kam es zu zur offiziellen Verabschiedung des
Standards.
HTML-Steueranweisungen
Tags. Alle HTML-Steueranweisungen werden in sog. Tags geschrieben, die von spitzen
Klammern (< >) begrenzt sind. Man unterscheidet zwischen Einleitungs- und AbschlußTags. Die Abschluß-Tags sind beinahe identisch mit dem Einleitungs-Tag. Sie besitzen
lediglich zusätzlich einen Slash (/) nach dem „<“-Zeichen.
Groß- und Kleinschreibung spielt bei HTML-Steueranweisungen keine Rolle.
HTML-Seiten haben zwingend die Extension .htm oder .html.
185
Programmieren in Java
Das Grundgerüst einer HTML-Seite
Eine HTML-Seite wird immer in die Anweisung <html> am Anfang und </html> am
Ende eingeschlossen.
Konkrete Referenzierung eines Java Applet
Die konkrete Referenzierung eines Java-Applet wird mit dem Applet-Tag <applet
...> eingeleitet. Zwischen dem einleitenden Tag und dem abschließenden Applet-Tag
(</applet>) können benötigte Parameter oder beliebiger Text eingegeben werden.
Die einfachste Form der Applet-Referenz. Ohne irgendwelche Parameter wird ein JavaApplet eingebunden mit
<APPLET CODE = "klassenelement" WIDTH = Wert HEIGHT = Wert>
</APPLET>
klassenelement: Applet-Klasse
WIDTH = Wert: Breite des Applet in Pixel
HEIGHT = Wert: Höhe des Applet in Pixel
Das <APPLET>-Tag erzeugt keinen Absatz, deshalb sollte es in einem allgemeinen
Text-Tag stehen, z.B. <P> oder in einem Überschriften-Tag (<H1>, <H2> usw.).
Optionale Parameter des <Applet>-Tag
Parameter
CODEBASE
ARCHIVE
OBJECT
ALT
NAME
ALIGN
VSPACE
HSPACE
Bedeutung
Hier kann ein alternatives Verzeichnis149 für das Laden von Klassendateien
angegeben werden. Fehlt diese Angabe, wird das Dokumentenverzeichnis
genommen.
Angabe des JAR-Archivs, aus dem die Klassendateien und sonstige Resourcen
des Applet genommen werden
Name der Datei, die den serialisierten Inhalt des Applet enthält.
Alternativer Text für Browser, die das Applet verstehen, aber Java nicht unterstützen
Eindeutiger Name für das Applet. Er kann zur Unterscheidung mehrerer
kommunizierender Applets auf einer Web-Seite verwendet werden.
Vertikale Anordnung des Applets in einer Textzeile. Hier kann einer der Werte left,
right, top, texttop, middle, absmiddle, baseline, bottom, absbottom angegeben
werden.
Rand über und unter dem Applet
Rand links oder rechts vom Applet
Neben den Parametern des „Applet-Tag“ können auch Parameter an das Applet selbst
übergeben werden. Jeder Parameter kann durch ein <PARAM>-Tag über zwei Attribute
für Name (name) und Wert (value) festgelegt werden. Das <PARAM>-Tag steht
zwischen einem öffnenden und schließenden <APPLET>-Tag.
Die Parameter werden beim Laden an das Applet weitergereicht. Innerhalb des Applets
können sie mit der Methode public String getParameter(String name)
abgefragt werden.
149 Pfadname, in dem sich die Klassen befinden
186
Programmieren in Java
3.3.2 Die interne Arbeitsweise eines Applets
Ein Java-Applet besitzt im Gegensatz zu einer Java-Anwendung keine main()Methode, die beim Laden gestartet wird und das Programm solange am Leben hält, bis
es der Benutzer beendet. In einem Applet sorgen vier Methoden dafür, daß sich das
Applet in seiner Umgebung korrekt verhält.
Panel
Applet
<< Konstruktor >>
public Applet()
<< Methoden >>
public void destroy()
public AppletContext getAppletContext()
public String getAppletInfo()
public AudioClip getAudioClip(URL url)
public AudioClip getAudioClip(URL url,String name)
public URL getCodeBase()
public URL getDocumentBase()
public Image getImage(URL url)
public Image getImage(URL url, String name)
public Locale getLocale()
public String getParameter(String name)
public String[][] getParameterInfo()
public void init()
public boolean isActive()
public void play(URL url)
public void play(URL url, String name)
public void resize(Dimension d)
public void resize(int width, int height)
public void setStub(AppletStub stub)
public void showStatus(String msg)
public void start()
public void stop()
Abb. Die Klasse Applet
187
Programmieren in Java
1. Das Erstellen eines Applets
Zum Erstellen eines Applet muß immer eine „Subklasse“ der Klasse Applet150
erzeugt werden. Java setzt voraus, daß eine Applet-Subklasse public deklariert
wurde. Erkennt Java ein Applet auf einer Web-Seite, dann wird die AppletAusgangsklasse und die Hilfsklasse, die diese erste Klasse evtl. benutzt, über das Netz
geladen. Java erstellt eine Instanz dieser Klasse, alle systembezogenenen Methoden
werden an diese Instanz geschickt. Mehrere Applets auf der gleichen oder auf
unterschiedlichen Seiten verwenden andere Instanzen, so daß sich jedes Applet auf
dem gleichen System evtl. anders verhält.
2. Applet-Methoden
Applets können zahlreiche, unterschiedliche Aktivitäten umfassen, die verschiedenen
wichtigen Ereignissen im Lebenszyklus eines Applet entsprechen, z.B. Initialisieren,
Zeichnen, Mausereignisse. Jeder Aktivität ist eine entsprechende Methode zugeordnet,
d.h.: Falls eine Ereignis stattfindet, ruft der Browser (oder ein Java ähnliches Werkzeug)
diese spezifische Methode auf.
Zum Reagieren auf solche Ereignisse sind bestimmte Verhaltensweisen vorzusehen.
Das geschieht durch Überschreiben der jeweiligen Methode in der Applet-Subklasse.
Unterschiedliche Applet-Verhalten bedeutet: Jeweils andere Methoden müssen
überschieben werden. Die folgenden Methoden bestimmen den Lebenszyklus eines
Applet:
Rückkehr zur HTML-Seite
init()
start()
stop()
destroy()
Verlassen der HTML-Seite
Abb.: Methoden im Lebenszklus eines Applet
Die Methode init() wird nach dem Laden des Applet ausgeführt. Sie dient zur
Initialisierung.
Die Methode start() wird automatisch nach Aufruf der Methode init() aufgerufen
bzw. dann, wenn das Applet in den Zustand „aktiv“ versetzt wird. Applets können in zwei
Zuständen sein: aktiv und inaktiv. Nach dem Laden eines Applets ist dieses zunächst
inaktiv. Das Applet wechselt in den Zustand aktiv, wenn es erstmalig auf dem Bildschirm
erscheint. Von dort aus wechselt es seinen Zustand zwischen aktiv und inaktiv. Wodurch
dieser Zustandswechsel genau ausgelöst wird, ist abhängig vom Kontext des Applel,
d.h. in der Regel vom verwendetet Web-Browser.
150 Alle Applets müssen java.applet.Applet in die Datei mit der Definition der Applet-Klasse importieren.
java.applet.* vollzieht das Einbunden von java.applet.Applet automatisch. Fast alle Applets (die mit grafischen
Schnittstellen) benötigen auch java.awt.*
188
Programmieren in Java
Die Methode stop() wird aufgerufen, wenn die HTML-Seite, in der das Applet
eingebunden ist, verlassen wird bzw. das Applet in den Zustand inaktiv versetzt wird.
Die Methode destroy() zerstört das Applet, nachdem es gestoppt wurde und der
Kontext des Applet sich für eine Zerstörung entscheidet. Die Methode destroy()
sorgt dafür, daß alle vom Applet belegte Ressourcen wieder freigegeben werden.
Vorhandene, vom Applet erzeugte Threads werden ebenfalls zerstört.
Die paint()-Methode wird in Java immer aufgerufen, wenn ein Applet gezeichnet
werden muß, z.B. bein erstmaligen Zeichnen des Applet, beim Verschieben des AppletFenster, beim Überlagern des Applet-Fenster durch ein anderes Fenster. Die paint()Methode hat folgende Gestalt:
public void paint(Graphics g)
{
....
}
paint() besitzt ein Argument: eine Instanz der Klasse Graphics. Dieses Objekt
wird vom Browser erstellt und an paint() abgegeben. Es muß sichergestellt sein, daß
die Graphics-Klasse151 in den Code der Applet-Subklasse importiert152 wird.
Bsp.: „Aller Anfang ist schwer!. Dieser Spruch soll mit Hilfe eines Applet gezeigt
werden.. Die zugehörige Quellcodedatei „AllerAnfangApplet.java“ umfaßt153:
import java.applet.*;
import java.awt.*;
public class AllerAnfangApplet extends Applet
{
Font f;
String spruch;
//
public void init()
{
f = new Font("Helvetica",Font.BOLD,24);
this.spruch = "Aller Anfang ist schwer!";
}
//
public void paint(Graphics g)
{
// Oval mit Farbe yellow
g.setColor(Color.yellow);
g.fillOval(10,10,330,100);
// Roter Rahmen; da Java keine Linienbreite kennt,
// wird die Linienbreite durrch 4 Ovale, (die sich
// um Pixelbreite unterscheiden,) simuliert
g.setColor(Color.red);
g.drawOval(10,10,330,100);
g.drawOval( 9, 9,332,102);
g.drawOval( 8, 8,334,104);
g.drawOval( 7, 7,336,106);
g.setColor(Color.black);
151 Teil des Pakets java.awt
152 Normalerweise geschieht dies über: import java.awt.Graphics
153 vgl. pr32101
189
Programmieren in Java
g.setFont(f);
g.drawString(this.spruch,40,70);
}
}
Die zugehörige HTML-Datei AllerAnfangApplet.html umfaßt:
<HTML>
<HEAD>
<TITEL>Hallo!</TITLE>
<BODY>
<CENTER>
<APPLET CODE="AllerAnfangApplet.class" WIDTH=350 HEIGHT=125>
</APPLET>
</CENTER>
</BODY>
</HEAD>
</HTML>
In einem Browser führt das zu der folgenden Darstellung:
Java-Applets zeichnen sich durch Überschreiben der „paint“-Methode selbst. Wie
wird die „paint“-Methode aufgerufen?
Es gibt drei verschiedene Methoden zum Neuzeichnen eines Applet:
public void paint(Graphics g)
Sie zeichnet tatsächlich die Grafik des Applets in den Zeichenbereich. Sie wird immer aufgerufen, wenn
ein Applet neu gezeichnet 154 werden muß. Das in der „paint“-Methode abgebene Graphics-Objekt
enthält den Grafikstatus, d.h. die aktuellen Merkmale der Zeichnungsoberfläche.
public void repaint()
Sie kann jederzeit aufgerufen werden, wann auch immer das Applet neu gezeichnet werden muß. Sie ist
der Auslöser, die „paint“-Methode sobald wie möglich aufzurufen und das Applet neu zu zeichnen.
Sollten die repaint()-Anweisungen schneller ablaufen, als Java diese verarbeiten kann, werden evtl. einige
übersprungen. In vielen Fällen ist die Verzögerung zwischen dem Aufruf von repaint() und der eigentlichen
Aktualisierung des Fensters vernachlässigbar.
154 Dies ist immer beim ersten Aufruf des Applets der Fall, aber auch jedesmal dann, wenn das Applet-Fenster
verschoben oder zwischenzeitlich von einem anderen Fenster überlagert wurde.
190
Programmieren in Java
public void update(Graphics g)
Sie wird von repaint() aufgerufen. Die „update“-Methode löscht den vollständigen Zeichenbereich155
und ruft anschließend die „paint“-Methode auf, die dann das Applet vollständig neu zeichnet. Der
Aufruf der „paint“-Methode erfolgt also nicht direkt über die „repaint“-Methode, sondern indirekt über
die „update“-Methode.
Applets werden aus Sicherheitsgründen gewissen Einschränkungen unterworfen:
-
-
Applets können das Dateisystem des Bernutzers nicht lesen und nicht beschreiben, abgesehen von
bestimmten Verzeichnissen (die vom Benutzer durch eine Zugriffskontrolliste, die standardäßig leer
ist, bestimmt werden). Einige Browser lassen keinerlei Schreib- und Leseaktion des Applets auf dem
Client zu.
Applets können auf dem Client keinerlei Programme ausführen.
Applets können normalerweise nur mit dem System kommunizieren, auf denen sie gespeichert sind.
Applets können keine keine nativen Programme der lokalten Plattform laden, auch keine
gemeinsame Bibliotheken (wie DLL’s).
3. Methoden zur Ereignisbehandlung in Applets
Ein Applet kann auch auf Ereignisse wie Mausbewegungen reagieren. Für soche
Ereignisse (z.B. Drücken der Maustaste) stellt Java Ereignisbehandlungs-Methoden
des JDK 1.0 bzw. JDK 1.1 zur Verfügung.
Bsp.156: Ein Applet zum Zeichnen von Punkten an den Stellen, an denen eine
Maustaste gedrückt wurde.
// Import der Pakete
import java.awt.*;
import java.awt.event.*;
// Top-Level Klassen-Deklaration des Applets
public class MausDownPunktApplet extends java.applet.Applet
{
// Variablen-Deklarationen
private int mausX, mausY;
// private boolean mausKlick = false;
// Methoden, die ueberschrieben werden
public void init()
{
setBackground(Color.yellow);
addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
mausX = e.getX(); mausY = e.getY();
repaint();
}
});
}
/*
public boolean mouseDown(Event e, int x, int y)
{
mausX = x; mausY = y;
mausKlick = true;
155 Das ruft oft den unangenehmen Flimmereffekt bei schnellen Bildsequenzen hervor.
156 pr32305
191
Programmieren in Java
repaint();
return true;
}
*/
// Ausgabemethode
public void paint(Graphics g)
{
g.setColor(Color.blue);
// if (mausKlick)
// {
g.fillOval(mausX,mausY,20,20);
// mausKlick = false;
// }
}
}
Nach jedem Mausklick wird ein blauer Punkt in die Zeichenfläche gebracht. Das
Bild wird neu gezeichnet. Die repaint()-Methode ruft vor der Ausführung der
paint()-Methode die update()-Methode auf, die anschließend paint()
aufruft. „update()“ leert in Originalform den Anzeigebereich des Applets. Wird
update() überschrieben, z.B. durch
public void update(Graphics g)
{
paint(g);
}
entfällt das Leeren des Anzeigebereichs.
// Import der Pakete
import java.awt.*;
import java.awt.event.*;
// Top-Level Klassen-Deklaration des Applets
public class MausDownPunkteApplet extends java.applet.Applet
{
// Variablen-Deklarationen
private int mausX, mausY;
// private boolean mausKlick = false;
// Methoden, die ueberschrieben werden
public void init()
{
setBackground(Color.yellow);
addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
mausX = e.getX(); mausY = e.getY();
repaint();
}
});
}
/*
public boolean mouseDown(Event e, int x, int y)
{
mausX = x; mausY = y;
mausKlick = true;
repaint();
return true;
192
Programmieren in Java
}
*/
// Ausgabemethode
public void paint(Graphics g)
{
g.setColor(Color.blue);
// if (mausKlick)
// {
g.fillOval(mausX,mausY,20,20);
// mausKlick = false;
// }
}
public void update(Graphics g)
{
paint(g);
}
}
Eine überschriebene update()-Methode kann den Flimmereffekt erheblich senken.
3.3.3 „Multithreading“-fähige Applets
Mit Threads können in Java Applets so erstellt werden, daß alle oder auch einzelnen
Codeteile in ihrem eigenen Thread laufen, ohne andere Teile des Systems zu
beeinflussen. Ein Applet kann im wesentlichen über vier Schritte Multithreadingfähig gemacht werden:
1. Erweitern der Unterschrift des Applets um implements Runnable
2. Hinzufügen einer Instanzvariablen, die den Thread des Applet enthält
3. Reduktion der start()-Methode, so daß sie außer dem Start des Threads keine weiteren Threads
enthält
4. Hinzufügen der run()-Methode, die den eigentlichen Code enthält, den das Applet ausführen soll.
Bsp.: Ein Applet zur Anzeige von Datum und Uhrzeit, jede Sekunde wird
aktualisiert157. Nach den bisher vorliegenden Erkenntnissen müßte das
zugehörige Applet folgende Gestalt haben:
import java.awt.Graphics;
import java.awt.Font;
import java.util.Date;
//
// Top Level Deklaration des Applets
//
public class DigitalUhr extends java.applet.Applet
{
// Variablen-Deklaration
Font einFont = new Font("TimesRoman",Font.BOLD,24);
Date datum;
// Eigene Methoden
// Methoden, die ueberschrieben werden
public void start()
157 vgl. pr42001
193
Programmieren in Java
{
// Ausfuehrung des Applet
while (true)
{
datum = new Date();
repaint(); // Aufruf der repaint()-Methode
try {Thread.sleep(1000); } // Pause von 1000 Millisekunden
catch(InterruptedException e) {}
}
}
// Optional, aber sehr wahrscheinlich – die Ausgabemethode
public void paint(Graphics g)
{
g.setFont(einFont); // Setzen des aktuellen Font
g.drawString(datum.toString(),10,50); // Ausgabe Datum
// Da paint() wiederholt mit jeweils dem aktuellen Wert von
// „datum“ aufgerufen wird, wird die Zeichenkette jede Sekunde
//zur Ausgabe des neuen Datums aufgerufen
}
}
In der start()-Methode nimmt die while-Schleife alle Systemressourcen für
sich in Anspruch (einschl. der Anzeige am Bildschirm). Deshalb funktioniert die
digitale Uhr nicht. Außerdem kann das Applet nicht gestoppt werden, da die
stop()-Methode nicht aufgerufen werden kann. Die Lösung des Problems liegt
im erneuten Schreiben des Applets mit Threads. Das Applet muß dazu mit den
vorgegebenen vier Arbeitsschritten erweitert werden.
import
import
import
//
// Top
//
public
java.awt.Graphics;
java.awt.Font;
java.util.Date;
Level Deklaration des Applets
class DigitalThreadUhr extends java.applet.Applet
implements Runnable
{
// Variablen-Deklaration
Font einFont = new Font("TimesRoman",Font.BOLD,24);
Date datum;
Thread faden;
// Eigene Methoden
// Methoden, die ueberschrieben werden
public void start()
{
if (faden == null)
{
faden = new Thread(this);
faden.start();
}
}
public void stop()
{
if (faden != null)
{
faden.stop();
faden = null;
}
194
Programmieren in Java
}
public void run()
{
// Ausfuehrung des Applet, hier findet die Animation statt
while (true)
{
datum = new Date();
repaint(); // Aufruf der repaint()-Methode
try {Thread.sleep(1000); } // Pause von 1000 Millisekunden
catch(InterruptedException e) {}
}
}
// Optional, aber sehr wahrscheinlich – die Ausgabemethode
public void paint(Graphics g)
{
g.setFont(einFont); // Setzen des aktuellen Font
g.drawString(datum.toString(),10,50); // Ausgabe Datum
// Da paint() wiederholt mit jeweils dem aktuellen Wert von
// "datum" aufgerufen wird, wird die Zeichenkette jede Sekunde
// zur Ausgabe des neuen Datums aufgerufen
}
}
Das folgende Gerüst umfaßt ein Muster für „multithreading“-fähige Applets:
// Name der Klasse:
// Beschreibung:
//
//
//
//
Import
import
import
import
der Pakete
java.lang.*;
java.applet.*;
java.awt.*;
// Top-Level-Klassen-Deklaration bzw. Definition des Applets
public Klassenname extends java.applet.Applet
{
// Variablen-Deklarationen bzw. Definitionen
// ...
// Eigene Methoden
// ...
// Methoden, die ueberschrieben werden
//
public void init()
{
// ...
}
public void start()
{
// ...
}
public void stop()
{
// ...
}
public void destroy()
{
// ...
195
Programmieren in Java
}
// Optional: die Ausgabemethode
public void paint(Graphics g)
{
// ..
}
// Bei Multithreading: Verwendung der run-Methode
public void run()
{
// ...
}
}
3.3.4 Animation in Applets
Animationsschritte
Eine Animation umfaßt in Java zwei Schritte:
1.
2.
Aufbau und Ausgabe eines Animationsrahmens (-fenster).
Entsprechende häufige Wiederholung der Zeichnung, um den Eindruck von Bewegung zu
vermitteln (Abspielen einer Animation).
Aufbau eines Animationsrahmens
Dazu gehört alles das, was die Animation vorbereitet, z.B.:
-
Ermitteln der Größe des Ausgabebereichs
Positionieren der Animation
Erstellen oder Laden der einzelnen Animationsbilder
Aufbau von einzelnen Animationssequenzen
Abspielen einer Animation
Die paint()-Methode wird von Java aufgerufen, wenn ein Applet gezeichnet werden
muß. Java kann aber auch aufgefordert werden, ein Bild zu einem bestimmten Zeitpunkt
nachzuzeichnen. Tut man das wiederholt und schnell genug mit der repaint()Methode, dann entsteht eine Animation. repaint() ist eine Anfrage an Java, das
Applet so schnell wie möglich zu zeichnen.
import java.awt.*;
import java.util.*;
public class PendelAppl1 extends java.applet.Applet implements Runnable
{
// Instanzvariable
// Position vom Zentrum des schwingenden Pendels
int x, y;
//
double thetaMax = (double) 0.35;
196
Programmieren in Java
double thetaMin = (double) –0.35;
// Die initiale Position vom Pendel
double theta = (double) 0.;
//
double wechsel = (double) 0.01;
//
int xStart = 150, yStart = 20;
// Radius des Pendels
double r = (double) 200;
// Durchmesser des Balls
int d = 20;
Thread faden;
// Methoden
public void init()
{
setBackground(Color.yellow);
}
public void start()
{
if (faden == null)
{
faden = new Thread(this);
faden.start();
}
}
public void stop()
{
if (faden != null)
{
faden.stop();
faden = null;
}
}
public void run()
{
while (true)
{
x = xStart + (int)(r * Math.sin(theta));
y = yStart + (int)(r * Math.cos(theta));
if ((theta >= thetaMax) | (theta <= thetaMin))
wechsel = -wechsel;
theta += wechsel;
repaint();
try { Thread.sleep(10); }
catch(InterruptedException e) {}
}
}
public void paint(Graphics g)
{
g.setColor(Color.blue);
g.drawLine(xStart,yStart,x,y);
g.setColor(Color.red);
g.fillOval(x-d/2,y-d/2,d,d);
}
}
197
Programmieren in Java
Reduktion von Flimmereffekten in Animationen
Die Methode update() ist die Ursache für das Flimmer-Problem. Da das AppletFenster zwischen den Einzelbildern gelöscht wird, springen die Bereiche des AppletFenster, die sich ändern, kurz zwischen dem Zustand Löschen und Neuzeichnen hin und
her, d.h. sie flimmern. Zwei Verfahrensweisen können das Flimmern von Java-Applets
einschränken:
-
Überschreiben der update-Methode so, daß sie entweder den Bildschirm nicht löscht oder nur Teile
löscht, die geändert wurden.
Überschreiben der Methoden update() und paint(), Verwenden doppelter Pufferung.
1. Überschreiben der update()-Methode158
update() löscht den Bildschirm durch Füllen mit der aktuellen Hintergrundfarbe, setzt
die aktuellen Farbe auf die Vordergrundfarbe und ruft anschließend paint() auf. Das
Überschreiben von update() muß sicherstellen, daß so etwas Ähnliches geschieht.
import java.awt.*;
public class PendelAppl2 extends java.applet.Applet implements Runnable
{
// Instanzvariable
// Position vom Zentrum des schwingenden Pendels
int x, y;
//
double thetaMax = (double) 0.35;
double thetaMin = (double) –0.35;
// Die initiale Position vom Pendel
double theta = (double) 0.;
//
double wechsel = (double) 0.01;
//
int xStart = 150, yStart = 20;
// Radius des Pendels
double r = (double) 200;
// Durchmesser des Balls
int d = 20;
Thread faden;
int xAlt, yAlt;
// Methoden
public void init()
{
setBackground(Color.white);
}
public void start()
{
if (faden == null)
{
faden = new Thread(this);
faden.start();
}
}
158 vgl. pr32212
198
Programmieren in Java
public void stop()
{
if (faden != null)
{
faden.stop();
faden = null;
}
}
public void run()
{
while (true)
{
x = xStart + (int)(r * Math.sin(theta));
y = yStart + (int)(r * Math.cos(theta));
if ((theta >= thetaMax) | (theta <= thetaMin))
wechsel = -wechsel;
theta += wechsel;
repaint();
try { Thread.sleep(10); }
catch(InterruptedException e) {}
}
}
public void update(Graphics g)
{
g.setColor(Color.Yellow);
g.drawLine(xStart,yStart,xAlt,yAlt);
g.fillOval(xAlt-d/2,yAlt-d/2,d,d);
g.setColor(Color.blue);
g.drawLine(xStart,yStart,x,y);
g.setColor(Color.red);
g.fillOval(x-d/2,y-d/2,d,d);
paint(g);
}
public void paint(Graphics g)
{
xAlt = x;
yAlt = y;
}
}
2. Double Buffering
Mit „double buffering“ wird eine zweite Oberfläche geschaffen, in der alles vorgezeichnet
und dann auf einmal in die Zeichnungsoberfläche des Applet ausgegeben wird.
Bsp.159:
import java.awt.*;
public class Pendel extends java.applet.Applet implements Runnable
{
// Instanzvariable
// Position vom Zentrum des schwingenden Pendels
int x, y;
//
double thetaMax = (double) 0.35;
double thetaMin = (double) –0.35;
159 pr32213, Pendel.java
199
Programmieren in Java
// Die initiale Position vom Pendel
double theta = (double) 0.;
//
double wechsel = (double) 0.01;
//
int xStart = 150, yStart = 20;
// Radius des Pendels
double r = (double) 200;
// Durchmesser des Balls
int d = 20;
Thread faden;
int xAlt, yAlt;
Image backgroundImage;
Graphics backgroundGraphics;
// Methoden
public void init()
{
setBackground(Color.white);
backgroundImage = createImage(this.size().width,
this.size().height);
backgroundGraphics = backgroundImage.getGraphics();
}
public void start()
{
if (faden == null)
{
faden = new Thread(this);
faden.start();
}
}
public void stop()
{
if (faden != null)
{
faden.stop();
faden = null;
}
}
public void run()
{
while (true)
{
x = xStart + (int)(r * Math.sin(theta));
y = yStart + (int)(r * Math.cos(theta));
if ((theta >= thetaMax) | (theta <= thetaMin))
wechsel = -wechsel;
theta += wechsel;
repaint();
try { Thread.sleep(10); }
catch(InterruptedException e) {}
}
}
public void update(Graphics g)
{
paint(g);
}
public void paint(Graphics g)
{
backgroundGraphics.setColor(Color.white);
200
Programmieren in Java
backgroundGraphics.drawLine(xStart,yStart,xAlt,yAlt);
backgroundGraphics.fillOval(xAlt-d/2,yAlt-d/2,d,d);
backgroundGraphics.setColor(Color.blue);
backgroundGraphics.drawLine(xStart,yStart,x,y);
backgroundGraphics.setColor(Color.red);
backgroundGraphics.fillOval(x-d/2,y-d/2,d,d);
g.drawImage(backgroundImage,0,0,this);
xAlt = x;
yAlt = y;
}
}
3.3.5 Das Laden und Anzeigen von Bildern
Den Umgang mit Bildern ermöglicht die Klasse Image des Pakets java.awt. In
einem Applet können Methoden der Klassen Applet und Graphics zum Laden und
Anzeigen von Bildern herangezogen werden. Bilder werden als seperate Dateien
außerhalb der .class-Dateien von Java gespeichert. Falls die Image-Klasse
verwendet wird, muß das Bild im Format .GIF oder .JPG vorliegen.
Laden von Bildern. Es erfolgt mit der Methode getImage()aus der Applet-Klasse, die
mit einem oder zwei Argumenten aufgerufen werden kann:
-
Aufruf von getImage mit einem Argument (ein Objekt vom Typ URL160)
Aufruf mit zwei Argumenten (Basis URL des Bilds (URL-Objekt)) und ein String, der den relativen
Pfad oder den Dateinamen des aktuellen Bilds angibt.
Die Klasse Applet besitzt zwei Methoden zum Erzeugen einer Basis-URL ohne
Angaben fester Adressen im Programm:
-
die Methode getDocumentBase() gibt ein URL-Objekt zurück, das den Ordner (das Verzeichnis)
repräsentiert, die die Webseite mit dem Applet enthält.
die Methode getCodeBase() gibt ein Verzeichnis (Ordner) zurück, das das Verzeichnis
repräsentiert, in dem sich die .class-Datei der Hauptklasse des Applet befindet.
Ausgabe von Bildern. Mit der Methode drawImage() der Graphics-Klasse kann ein
Bild, das in ein Image-Objekt geladen wurde, angezeigt werden. drawImage() hat 4
Argumente:
-
das Image-Objekt, das angezeigt werden soll
die x- und y-Koordinate
das Schlüsselwort this
Mit paint() kann das Bild zur Anzeige gebracht werden:
public paint(Graphigs g)
{
g.drawImage(imageObjekt, xKoord, yKoord, this);
}
Bsp.161:
import java.awt.*;
import java.applet.*;
160 Adressen im World Wide Web werden durch URL-Objekte repräsentiert. Die Klasse URL (Uniform Resource
Locator) ist Teil des Pakets java.net
161 vgl. pr33501
201
Programmieren in Java
public class ZeichneBild extends Applet
{
private Image bild;
public void init()
{
bild = getImage(getDocumentBase(),"B04240900.jpg");
resize(250, 200);
}
public void paint(Graphics g)
{
int xPos = 10;
g.drawImage(bild,xPos,10,this);
}
}
3.3.6 Die Ausgabe von Sound
Das JDK bietet Möglichkeiten zur Ausgabe von Sound 162 an. Die Ausgabe von Sound
kann über zwei Methoden der Klasse Applet erfolgen:
public void play(URL url)
public void play(URL url, String name)
hier kann entweder die URL einer Sound-Datei oder die Kombination von VerzeichnisURL und Dateinamen angegeben werden. Die Übergabe der URLs geschieht über die
Applet-Methoden:
public URL getCodeBase()
public URL getDocumentbase()
Die Methoden liefern eine URL des Verzeichnisses, aus dem das Applet gestartet wurde bzw. in dem die
aktuelle HTML-Seite liegt. Der nachteil dieser Vorgehensweise ist, daß die Sound-datei bei jedem Aufruf
neu geladen werden muß.
public getAudioClip(URL url, String name)
Hier wird ein Objekt der Klasse AudioClip beschafft, das dann abgespielt werden kann.
AudioClip stellt drei Methoden zur Verfügung:
public void play()
startet die zuvor geladene Sound-Datei und spielt sie genau einmal ab.
public void loop()
startet die zuvor geladene Sound-Datei und spielt den Sound in einer Endlosschleife
immer wieder ab.
public void stop()
Darüber kann die zuvor mit loop() iniziierte Schleife beendet werden.
162 Das JDK 1.2 ermöglicht die Soundausgabe in Applets und Applikationen. Frühere Versionen gestatten
Soundausgabe nur für Applets. Die Ausgabe war auf Sound beschränkt, die im AU-Format (stammt aus der SunWelt und legt ein Sample im Format 8 Bit Mono, Sampling-Rate 8 kHz, µ -lawKompression ab) vorliegen mußte.
Seit dem JDK 1.2 werden auch die Sample-Formate WAV und AIFF sowie die Midi-Formate Typ 0 und Typ 1 und
RMF unterstützt. Zudem gibt es einige Shareware- oder Freeware-Tools, die zwischen verschieden Formaten
konvertieren können (z.B. CoolEdit oder Gold-Wave).
202
Programmieren in Java
Bsp.163:
import java.net.*;
import java.applet.*;
public class PR33602
{
public static void main(String args[])
{
if (args.length >= 1)
{
try {
URL url = new URL(args[0]);
AudioClip clip = Applet.newAudioClip(url);
clip.play();
try {
Thread.sleep(10000);
}
catch (InterruptedException e)
{}
}
catch (MalformedURLException e)
{
System.out.println(e.toString());
}
}
}
}
163 Vgl. pr33602, Aufruf z.B.: java PR33602 file:///c:/winnt\media\ringin.wav
203
Programmieren in Java
3.3.7 Die Klasse JApplet
Diese Klasse befindet sich im Paket javax.swing, gehört aber logisch zu den
Applets. Sie erweitert die Applets um typische Swing-Eigenschaften.
java.awt.Panel
java.applet.Applet
javax.swing.JApplet
<< Konstruktor >>
public JApplet()
<< Methoden >>
public void setJMenuBar(JMenuBar menuBar)
public JMenuBar getJMenuBar()
public void setLayout(LayoutManager manager)
public Container getContentPane()
public void setContentPane(Container contentPane)
....
Abb. Die Klasse JApplet
3.3.8 Das Interface AppletContext
Die Referenz vom Typ des Interface AppletContext dient vorwiegend
Kommunikation zwischen Applet und dem Webbrowser bzw. anderen Applets.
zur
<< interface >>
AppletContext
public Applet getApplet(String name)
public Enumeration getApplets()
public AudioClip getAudioClip(URL url)
public Image getImage(URL url)
public void showDocument(URL url)
public void showDocument(URL url, String target)
public void showStatus(String status)
Abb.: Das Interface AppletContext
Mit Hilfe von z.B. getAppletContext().getApplets() kann auf andere Applets
Bezug genommen werden. Die Methode showDocument() lädt eine Webseite in den
Browser, auf dem das Applet läuft.
204
Programmieren in Java
3.4 Grafische Benutzeroberfläche mit ObservableObserver
Das Zusammenwirken und die Kommunikation zwischen Awendung und grafischer
Benutzeroberfläche sollte folgendermaßen gestaltet sein:
-
Anwendung und grafische Benutzeroberfläche sollen möglichst unabhängig voneinander sein, in
jedem Fall aber sauber voneinander getrennt sein (Anwendungsklasse, GUI-Klasse).
Jedes GUI-Objekt muß seinen Anwendungsfall kennen, um desssen Methoden aufrufen zu können
Eigentlich müßte umgekehrt jedes Anwendungsobjekt „sein GUI-Objekt“ kennen, z.B. zur Anzeige
der Anfangswerte
Das Hauptprogramm „degeneriert“ zur Erzeugung eines Anwendungs- und GUI-Objekts.
Die Klasse Observable
In Java muß das Interesse an einem Objekt durch eine eigene Klasse ausgedrückt
werden. Die eigene Klasse muß von der Klasse Observable abgeleitet sein. Die
Observable-Klasse erlaubt es einem Objekt, andere Objekte zu informieren, wenn es
eine Änderung erfährt.
public class java.util.Observable
{
Observable();
public void addObserver(Observer o);
public void deleteObserver(Observer o);
public void deleteObservers();
public int countObservers);
protected void setChanged();
portected void clearChanged();
public boolean hasChanged();
public void notifyObservers();
public void notifyObservers(Object arg);
}
Methoden: Die wichtigsten Methoden beim Erzeugen einer Subklasse von
Observable sind „setChanged“ und „notifyObservers“. Die „setChanged“Methode markiert, daß Observable verändert wurde. Beim Aufruf von
„notifyObservers“ werden die Observer benachrichtigt.
protected void setChanged()
setzt ein internes Flag für die Modifikation (wird von „notifyObservers“ verwendet). Es wird
automatisch gelöscht, wenn „notifyObservers“ aufgerufen wird, kann aber auch manuell mit der
„clearChanged“-Methode gelöscht werden.
protected void clearChanged()
public void notifyObservers()
prüft, ob das changed-Flag gesetzt wurde. Ist dies nicht der Fall, kann keine Mitteilung über eine
Änderung versendet werden.
public void notifyObservers(Object arg)
Das Argument kann zur Übergabe zusätzlicher Information über die Modifikation dienen. Ohne Parameter
entspricht der Aufruf einem Aufruf mit dem Argument Null.
public boolean hasChanged()
205
Programmieren in Java
stellt fest, ob ein Objekt von Interesse modifiziert wurde.
public void addObserver(Observer obs)
Mit dieser Methode können Observer ihr Interesse an einem beliebigen Observable bekunden.
public void deleteObserver (Observer obs)
Mit dieser Methode kann ein Observer die Überwachung der entsprechenden Observables beenden
public void deleteObservers()
löscht die Liste aller Observer
public int countObservers()
liefert die Anzahl der für in Observable registrierten Observer
Das Interface Observer
Jede Klasse, die Mitteilungen über Modifikationen eines Observable bekommen
möchte, muß das Observer-Interface implementieren. Dieses Interface besteht aus
einer einzigen Methode:
public abstract void update(Observable obs, Object arg)
Sie wird bei einer Objektmodifikation aufgerufen. „obs“ ist das Observable, das soeben
geändert wurde. Wurde „notifyObservers“ ohne Argument aufgerufen, dann ist
arg Null.
Observable-Observer: Kopplung von Anwendung und GUI
Die Klasse Observable164 erlaubt einem Objekt, Informationen an andere Objekte zu
geben, wenn es eine Änderung erfährt. Beim Erstellen von Benutzeroberflächen kann es
verschiedene Möglichkeiten zur Änderung eines Datenobjekts geben. Eine solche
Änderung kann wiederum die Aktualisierung verschiedener Teile einer Anzeige zur
Folge haben.
Bsp.:165
import java.awt.*;
import java.awt.event.*;
import java.util.*;
class BoxObservable extends Observable
{
public void notifyObservers(Object b)
{
setChanged();
super.notifyObservers(b);
}
}
public class BoxObserver extends Frame
{
Observable notifier = new BoxObservable();
public BoxObserver(int grid)
{
setTitle("Demonstration Observer");
164 Das Konzept für diese Klasse ist eine Leihgabe von Smalltalk und unter dem namen Model-View-Controller
(MVC) bekannt.
165 pr34100
206
Programmieren in Java
setLayout(new GridLayout(grid,grid));
for (int x = 0; x < grid; x++)
for (int y = 0; y < grid; y++)
add(new OCBox(x, y, notifier));
}
public static void main(String args[])
{
int grid = 8;
if (args.length > 0)
grid = Integer.parseInt(args[0]);
Frame f = new BoxObserver(grid);
f.setSize(500,400); f.setVisible(true);
f.addWindowListener(
new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{ System.exit(0); }
});
}
}
class OCBox extends Canvas
implements Observer
{
Observable notifier;
int x, y;
Color fFarbe = neueFarbe();
static final Color[] farben = {
Color.black, Color.blue, Color.cyan,
Color.darkGray, Color.gray, Color.green,
Color.lightGray, Color.magenta, Color.orange,
Color.pink, Color.red, Color.white, Color.yellow
};
OCBox(int x, int y, Observable notifier)
{
this.x = x; this.y = y;
notifier.addObserver(this); this.notifier = notifier;
addMouseListener(new ML());
}
static final Color neueFarbe()
{
return farben[(int) (Math.random() * farben.length)];
}
public void paint(Graphics g)
{
g.setColor(fFarbe); Dimension d = getSize();
g.fillRect(0, 0, d.width, d.height);
}
class ML extends MouseAdapter
{
public void mousePressed(MouseEvent e)
{ notifier.notifyObservers(OCBox.this); }
}
public void update(Observable o, Object arg)
{
OCBox clicked = (OCBox) arg;
if (nextTo(clicked))
{ fFarbe = clicked.fFarbe; repaint(); }
}
private final boolean nextTo(OCBox b)
207
Programmieren in Java
{
return Math.abs(x - b.x) <= 1 && Math.abs(y - b.y) <= 1;
}
}
3.5 Swing
Swing 166 ist eine Erweiterung des Abstract Windowing Toolkit, vollständig in
Java 2 integriert und bietet eine wesentlich verbesserte Funktionalität. Alle Elemente
von Swing sind Bestandteile des Pakets javax.swing, die über import
javax.swing.* in Anwendungen einbezogen werden kann.
Swing-Komponenten werden auf gleiche Weise verwendet wie die Komponenten des
AWT: Erzeugen einer Komponete über den Konstruktor, Aufruf der Methoden der
Komponente.
166 vgl. 5.6
208
Programmieren in Java
3.6 Java Beans
3.6.1 Wiederverwendbare Softwarekomponenten
Unter einer Software-Komponente 167 versteht man:
Eine Komponente ist ein Stück Software, das klein genug ist, um es in einem Stück zu erzeugen und
pflegen zu können, groß genug ist, um eine sinnvoll einsetzbare Funktionalität zu bieten und eine
individuelle Unterstützung zu rechtfertigen, sowie mit standardisierten Schnittstelle ausgestattet ist, um
mit anderen Komponenten zusammen zu arbeiten.
3.6.2 Komponenten für Java: Beans
JavaBeans ist das Komponentenmodell von Java. Diese Komponenten haben eine
genauere und für Programmierwerkzeuge verständlichere Schnittstellendefintion als
Java-Klassen. Die zur Beschreibung und Analyse benötigten Interfaces und Klassen
stehen im Package java.beans. Sie arbeiten sehr eng mit den Klassen zur
Laufzeitanalyse von Java-Klassen im Paket java.lang.reflect zusammen. Die
Schnittstellen von JavaBeans bezeichnet man als Features. Sie besteht aus drei Teilen:
JavaBeans
Properties
Methoden
Ereignisse
Abb.: Die JavaBean-Features
1. Properties sind die öffentlichen, mit Namen versehenen Attribute oder Eigenschaften. Über diese
Properties sind Beans an die Vorstellungen des Programmierers anpassbar, ohne daß er er den
Quellcode ändern und darauf irgendein Zugriff haben muß. Die Implementierung von Properties erfolgt
durch entsprechende lesende und schreibende Methoden, die einer Namenskonvention („DesignPattern“168) gehorchen. Die Java-Beans-Architektur unterscheidet zwischen sog. Indexed, Bound und
Constraint Properties.
2. Die öffentlichen Methoden, die eine Bean ausführen kann
3. Ein JavaBean erzeugt Ereignisse, z.B. dann, wenn sich ihre Properties ändern.
Diese drei Arten von Features sind im Interface BeanInfo dokumentiert.
167 Die Definition wurde zitiert nach: Griffel, Frank: Componentenware – Konzepte und Techniken eines
Softwareparadigma, dpunkt-Verlag, 1998
168 Unter Design Pattern sind einfache Muster auf der Basis von Methodennamen und Signaturen gemeint, z.B.
Ein Property mit dem Namen Color, für das sowohl schreibende als auch lesende Zugriffe erlaubt sind wird
durch Implementierung der Methoden „public Color getColor()“ und „public void setColor()“ dargestellt.
Das Entwurfsmuster für das Verbinden eines Event-Handlers mit einer Event-Source besteht aus einer
Methode, die mit „set“ beginnt und mit „Listener“ endet und ein Argument besitzt. Außerdem muß eine
Vererbung vom EventListener-Interface vorhanden sein.
209
Programmieren in Java
3.6.3 Enterprise Java Beans
„Java Beans“ wurde als ein lokales Komponentenmodell169 entworfen. Das Java-BeansModell sieht aber auch die Nutzung entfernter Ressourcen vor: Enterprise Java Beans.
Damit werden Anbindungen zur folgenden Application Interfaces möglich:
- Java Database Connectivity -API: Es gestattet den Zugriff auf relationale Datenbanken.
- Remote Method Invocation: RMI stellt ein API zur Verfügung, mit dessen Hilfe die Kommunikation
zweier Komponenten über Adreß- oder Maschinenräume hinweg möglich ist. RMI ist eine Art
objektorientierter RPC-Mechanismus (Remote Procedure Call), der speziell für Java entwickelt wurde.
- CORBA
169 D.h.: Die Kommunikation zwischen Komponenten findet auf einer Maschine statt.
210
Programmieren in Java
4. Grafik und Bildverarbeitung
Mit einer Instanz von Graphics kann gezeichnet werden, z.B.:
Graphics meineGrafik;
meineGrafik = getGraphics();
meineGrafik.drawString("mache irgendwas",20,40);
Die in der Klasse Component als public Graphics getGraphics() definierte
Methode gibt den „Graphics“-Kontext von der Komponente zurück bzw. Null, wenn die
Komponente keinen aktuellen Grafikbezug hat.
Die meisten Zeichenvorgänge werden jedoch in der paint()-Methode durchgeführt.
Java-Applets zeichnen sich selbst neu nach Überschreiben der paint()-Methode. Es
gibt drei verschieden Methoden170 zum Neuzeichnen:
public void paint(Graphics g)
public void repaint()
public void update(Graphics g)
Graphics
{ abstract }
.........
<< Konstruktor >>
protected Graphics()
<< Methoden >>
public abstract Graphics create()
public Graphics create(int x, int y, int width, int height)
public abstract void translate(int x, int y)
public abstract Color getColor()
public abstract void setColor(Color farbe)
public abstract void setXORMode(Color farbe)
public abstract Font getFont()
public abstract void setFont(Font f)
public FontMetrics getFontMetrics()
public abstract Rectangle getClipBounds()
public abstract void clipRect(int x, int y, int width, int height)
public abstract void setClip(int x, int y, int width, int height)
public abstract Shape getClip()
public abstract void drawLine(int x1, int y1, int x2, int y2)
public abstract fillRect(int x, int y, int width, int height)
public void drawRect(int x, int y, int width, int height)
public abstract void clearRect(int x, int y, int width, int height)
public abstract void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight)
public abstract fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight)
public abstract void drawOval(int x, int y, int width, int height)
public abstract void fillOval(int x, int y, int width, int height)
public abstract void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle)
public abstract void drawString(String str, int x, int y)
public abstract boolean drawImage(Image bild, int x, int y, ImageObserver observer)
Abb.: Die Klasse Graphics
170 vgl. 3.2.2
211
Programmieren in Java
Ein Graphics-Objekt erzeugt nicht der Entwickler, sondern er bekommt es dem Kontext
gestellt, auf dem er zeichnen möchte. Die Klasse Graphics hat keinen öffentlichen
Konstruktor.
4.1 Allgemeine Zeichenvorgänge
4.1.1 Punkte, Linien, Kreise, Bögen, Polygone
Das Koordinatensystem. Der Ausgangspunkt (0,0) des Java-Koordinatensystems ist
die obere linke Ecke. Von dieser Stelle führen positive x-Werte nach rechts und positive
y-Werte nach unten. Die Angaben der Koordinaten erfolgen in Pixel. Alle Pixelwerte sind
Ganzzahlen.
Punkte. Ein Punkt ist durch zwei oder mehr Koordinaten gekennzeichnet. In Java gibt es
keine Funktion, mit der Punkte gezeichnet werden. Diese können nur durch Liniebefehle
erzeugt werden.
Zeichnen einer Linie. Es geschieht mit der Methode: public abstract void
drawLine(int x1, int y1, int x2, int y2). (x1,y1) bestimmt den
Anfangspunkt, (x2,y2) bestimmt den Endpunkt der Linie.
Bsp.: Ein Applet mit zufällig verteilten Linien171
import java.awt.*;
// Top Level Deklaration des Applets
public class LinienApplet extends java.applet.Applet
implements Runnable
{
// Variablen-Deklaration
int x1 = 0;
int x2 = 0;
int y1 = 0;
int y2 = 0;
float rot, gruen, blau;
Color linienFarbe;
// Eigene Methoden
// Methoden, die ueberschrieben werden
public void init()
{
setBackground(Color.lightGray);
}
public void start()
{
if (faden == null)
{
faden = new Thread(this);
faden.start();
}
}
public void stop()
171 vgl. pr42005
212
Programmieren in Java
{
if (faden != null)
{
faden.stop();
faden = null;
}
}
public void run()
{
// Ausfuehrung des Applet
while (true)
{
x1 = (int) (Math.random() * this.size().width);
x2 = (int) (Math.random() * this.size().width);
y1 = (int) (Math.random() * this.size().height);
y2 = (int) (Math.random() * this.size().height);
rot = (float) Math.random();
gruen = (float) Math.random();
blau = (float) Math.random();
linienFarbe = new Color(rot,gruen,blau);
repaint(); // Aufruf der repaint()-Methode
try {faden.sleep(1000); } // Pause von 1000 Millisekunden
catch(InterruptedException e) {}
}
}
public void paint(Graphics g)
{
g.setColor(linienFarbe);
g.drawLine(x1,y1,x2,y2);
}
}
Die Methode drawLine zeichnet Linien mit einer Dicke von einem Pixel.
Zeichnen eines Rechtecks. Dafür gibt es die Methode: public void
drawRect(int x, int y, int width, int height). (x1,y1) bestimmt
die obere linke Ecke eines Rechtecks, (width, height) legen Breite und Höhe
des Rechtecks fest.
Zeichnen eines gefüllten Rechtecks. Es wird ermöglicht durch die Methode: public
abstract void fillRect(int x, int y, int width, int height).
Die Farbe, mit der das Rechteck gefüllt werden soll, kann mit der folgenden Methode
gesetzt werden: public abstract void setColor(Color c).
Bsp.: Ein Applet mit zufällig verteilten Rechtecken172
// zeichne Rechtecke
import java.applet.*;
import java.awt.*;
public class RechteckeAppl3 extends Applet
{
// Instanzvariable
int appletHoehe;
int appletBreite;
int rechteckHoehe;
int rechteckBreite;
int rechteckTop;
int rechteckLinks;
172 vgl. pr41103
213
Programmieren in Java
Color rechteckFarbe;
int anzRechtecke = 100;
// Methoden
public void init()
{
Dimension d = size();
appletHoehe = d.height;
appletBreite = d.width;
repaint();
}
public void paint(Graphics g)
{
setBackground(Color.white);
g.setColor(Color.black);
g.drawRect(0,0,appletBreite – 1,appletHoehe – 1);
for (int i = 0; i < anzRechtecke; i++)
{
rechteckTop
= bestimmeZufallszahl(appletHoehe);
rechteckLinks = bestimmeZufallszahl(appletBreite);
rechteckHoehe = bestimmeZufallszahl(
appletHoehe - rechteckTop);
rechteckBreite = bestimmeZufallszahl(
appletBreite – rechteckLinks);
rechteckFarbe = new Color(bestimmeZufallszahl(255),
bestimmeZufallszahl(255),
bestimmeZufallszahl(255));
g.setColor(rechteckFarbe);
g.fillRect(rechteckLinks,rechteckTop,rechteckBreite-1,
rechteckHoehe – 1);
}
}
private int bestimmeZufallszahl(int bereich)
{
double ausgangsgroesse;
ausgangsgroesse = Math.random();
return (int) (ausgangsgroesse * bereich);
}
}
Löschen eines Rechtecks. Das übernimmt die Methode public abstract void
clearRect(int x, int y, int width, int height).
Kopieren eines Rechtecks. Dafür gibt es die Methode public abstract void
copyArea(int x, int y, int width, int height, int dx, int
dy).
Zeichnen eines 3D-Rechtecks. Es erfolgt mit Hilfe der Methode public void
draw3DRect(int x, int y, int width, int height, boolean
raised).
Zeichnen eines gefüllten 3D-Rechtecks.
Zeichnen abgerundeter Rechtecke. Sie können gezeichnet werden mit public
abstract void drawRoundRect(int x, int y, int width, int
height, int arcWidth, int arcHeight). „arcWidth“ bestimmt den
Winkel der Abrundung auf der horizontalen, „arcHeight“ den Winkel auf der vertikalen
Ebene. Je größer die Winkel sind, desto stärker gerundet erscheint das Rechteck.
214
Programmieren in Java
Zeichnen abgerundeter, gefüllter Rechtecke. Dafür existiert die Methode public
abstract void fillRoundRect(int x, int y, int width, int
height, int arcWidth, int arcHeight).
Zeichnen von Polygonen. Hierfür kann die Methode public abstract void
drawPolygon(int[] xPunkte, int[] yPunkte, int nPunkte)
herangezogen werden. Es gibt zwei Möglichkeiten beim Zeichnen von Polygonen:
- Weitergabe der beiden Datenbereiche (Arrays) mit den x- und y-Koordinaten der Punkte, z.B.173:
import java.awt.*;
public class ZeichnePolyAppl1 extends java.applet.Applet
{
// Instanzvariable
// Definition des Felds mit den x-Koordinaten
int xKoord[] = {20,50,70,40,20,20};
// Definition des Felds mit den y-Koordinaten
int yKoord[] = {30,10,20,70,50,30};
// Methoden
public void init() { setBackground(Color.yellow); }
public void paint(Graphics g)
{
// Zeichne ein 5-Eck
g.setColor(Color.red);
g.drawPolygon(xKoord,yKoord,6);
}
}
- Weitergabe einer Instanz der Polygon-Klasse, z.B.174:
import java.awt.*;
public class ZeichnePolyAppl2 extends java.applet.Applet
{
// Instanzvariable
// Definition des Felds mit den x-Koordinaten
int xKoord[] = {20,50,70,40,20,20};
// Definition des Felds mit den y-Koordinaten
int yKoord[] = {30,10,20,70,50,30};
// Anzahl Ecken
int anzEcken = xKoord.length;
// Methoden
public void init() { setBackground(Color.yellow); }
public void paint(Graphics g)
{
// Zeichne ein 5-Eck
g.setColor(Color.red);
Polygon poly = new Polygon(xKoord,yKoord, anzEcken);
g.drawPolygon(poly);
}
}
Das Zeichnen von gefüllten Polygonen. Dazu dient die Methode public abstract
void fillPolygon(int[] xPunkte, int[] yPunkte, int nPunkte).
173 pr41105
174 pr41105
215
Programmieren in Java
Bsp.175:
import java.awt.*;
public class ZeichnePolyAppl3 extends java.applet.Applet
{
// Instanzvariable
// Definition des Felds mit den x-Koordinaten
int xKoord[] = {20,50,70,40,20};
// Definition des Felds mit den y-Koordinaten
int yKoord[] = {30,10,20,70,50};
// Methoden
public void init() { setBackground(Color.yellow); }
public void paint(Graphics g)
{
// Zeichne ein 5-Eck
g.setColor(Color.red);
g.fillPolygon(xKoord,yKoord,5);
}
}
Zeichnen eines Linienzuges mit der Methode abstract void drawPolygon(int
xPunkte[], int yPunkte[], int nPunkte) durch die gegebenen
Koordinaten in der Vordergrundfarbe. Die Figur wird dabei nicht geschlossen, wenn
nicht Strat- und Endkoordinaten gleich sind. Mit nPunkte werden die Anzahl der
gezeichneten Linien kontrolliert.
Die Klasse Polygon. Sie ist eine Erweiterung des Interface Shape . Ein Polygon-Objekt
verwaltet eigenständig seine Koordinaten, von außen können Elemente aufgenommen
werden.
java.awt.Polygon
protected Rectangle bounds
public int npoints
public int[] xpoints
public int[] ypoints
<< Konstruktor >>
public Polygon()
public Polygon(int[] xpoints, int[] ypoints, int npoints)
<< Methoden >>
public void addPoint(int x, int y)
public boolean contains(double x, double y)
public boolean contains(double x, double y, double w, double h)
public boolean contains(int x, int y)
...
public Rectangle getBounds();
...
public boolean inside(int x, int y)
...
public void translate(int deltaX, int deltaY)
Abb.: Die Klasse Polygon
175 pr41105
216
Programmieren in Java
Das Zeichnen von Kreisen und Ellipsen. Es erfolgt über die Methode public
abstract void drawOval(int x, int y, int width, int height).
(x,y) gibt die Koordinaten der oberen linken Ecke des umschreibenden Rechtecks
an.
Das Zeichnen von gefüllten Kreisen und Ellipsen. Es erfolgt über die Methode public
abstract void fillOval(int x, int y, int width, int height)
Das Zeichnen von Bögen. Java stellt die folgende Methode dafür zur Verfügung:
public abstract void drawArc(int x, int y, int breite, int
hoehe, int startWinkel, int bogenWinkel). „startWinkel“ bestimmt
den Anfangswinkel von einer (gedachten) horizontalen Mittellinie aus gesehen, ab dem
der Bogen gezeichnet werden soll. „bogenWinkel“ legt fest, wie weit der Bogen ab
dem Startpunkt gezeichnet wird und in welche Richtung er geht. Die positive Richtung in
Java ist entgegen dem Uhrzeigersinn.
Das Zeichnen von gefüllten Bögen. Es erfolgt über die Methode public abstract
void fillArc(int x, int y, int breite, int hoehe, int
startWinkel, int bogenWinkel)
Das Zeichnen von Zeichenketten erfolgt mit Hilfe der Methoden
public abstract void drawString(String str, int x, int y)
public void drawChars(char[] daten, int offset, int length, int x, int y)
public void drawBytes(byte[] daten, int offset, int length, int x, int y)
4.1.2 Farbangaben
Java setzt Farben aus sog. Primärfarben (Rot, Grün, Blau) des Lichts zusammen (RGBModell). Eine Farbe im RGB-Modell wird durch die Angabe, wieviel rotes, grünes und
blaues Licht in der Farbe enthalten sind, bestimmt. Dies kann entweder mit einer
Ganzzahl zwischen 0 und 255 oder einer Gleitpunktzahl zwischen 0.0 und 1.0
geschehen.
Farbe
Weiß (white)
Hellgrau (lightGray)
Grau (gray)
Dunkelgrau (darkGray)
Schwarz (black)
Rot (rot)
Grün (green)
Blau (blue)
Gelb (yellow)
Orange (orange)
Pink (pink)
Magenta (magenta)
Cyan (cyan)
Rot-Anteil
255
192
128
64
0
255
0
0
255
255
255
255
0
Grün-Anteil
255
192
128
64
0
0
255
0
255
200
175
0
255
Blau-Anteil
255
192
128
64
0
0
0
255
0
0
175
255
255
Abb.: Gebräuchliche Farbwerte RGB-Werte)
Neben dem RGB-Farbmodell unterstützt Java auch das HSB-Farbmodell. Dieses stellt
eine Farbe durch die drei Parameter Farbton, Intensität und Helligkeit dar.
217
Programmieren in Java
Die Color-Klasse. Auf drei Arten kann eine Farbe erzeugt werden:
public Color(int red, int green, int blue)
Damit wird eine Farbe mit Rot-, Grün- und Blau-Werten zwischen 0 und 255 erzeugt.
public Color(int rgb)
erzeugt ein Color-Objekt aus dem rgb-Wert, der die Farben rot, grün, blau kodiert. Der Rot-Anteil befindet
sich in den Bits 16 bis 23, der Grünanteil in 8 bis 15 und auch der Blauanteil in 0 bis 7. Jede Farbe ist
durch ein Byte repräsentiert.
public Color(float red, float green, float blue)
Eine gängige Farbe kann schneller über die Standardfarbobjekte der in der Color-Klasse definierten
verschiedenen Klassenvariablen gewonnen werden, z.B.:
public
public
public
public
public
public
public
public
public
public
public
public
public
static
static
static
static
static
static
static
static
static
static
static
static
static
Color
Color
Color
Color
Color
Color
Color
Color
Color
Color
Color
Color
Color
white;
lightGray;
gray;
darkGray;
black;
red;
blue;
green;
yellow;
magenta;
cyan;
orange;
pink;
Ermitteln der RGB-Werte von einem bestimmten Farbobjekt. Es erfolgt über
public int getRed();
public int getGreen();
public int getBlue();
Setzen von Farben. Es wird möglich durch die Methode: public abstract void
setColor(Color c). Der Parameter bestimmt das gewünschte Farbobjekt.
Setzen von Hintergrundfarben. Normalerweise ist die Hintergrundfarbe eines Applets
weiß oder dunkelgrau (je nach Container). Individuell kann die Hintergrundfarbe eines
Applets gesetzt werden durch: public void setBackground(Color c).
Parameter ist das gewünschte Farbobjekt.
Setzen von Vordergrundfarben. Falls die Farbe für alle Zeichenobjekte innerhalb eines
Applets pauschal festgesetzt werden soll, dann kann die Methode public void
setForeground(Color c) verwendet werden.
218
Programmieren in Java
4.1.3 Textausgabe über den Zeichen-Modus
Die Graphics-Klasse enthält auch Methoden zum Zeichnen von Textzeichen und
Zeichenketten (z.B. die drawString() Methode). Zusätzlich spielen die Font-Klasse176
und die Fontmetrics-Klasse177 beim Textzeichnen eine Rolle.
Font
FontMetrics
{ abstract }
public static final int PLAIN
public static final int BOLD
public static final int ITALIC
protected String name
protected int style
protected int size
public Font getFont()
public int getLeading()
public int getAscent()
public int getDescent()
public int getHeight()
public int getMaxAscent()
public int getMaxDescent()
public int getMaxAdvance()
public int charWidth(int zeichen)
public int charWidth(char zeichen)
public int stringWidth(String str)
public int[] getWidths()
public String toString()
<< Konstruktoren >>
public Font(String name, int style, int size)
<< Methoden >>
public String getName()
public int getStyle()
public int getSize()
public boolean isPlain()
public boolean isBold()
public boolean isItalic()
public static Font getFont(String nm)
public static Font getFont(String nm, Font font)
public static Font decode(String str)
public int hashCode()
public boolean equals(Object obj)
public String toString()
Abb.: Die Klassen Font und FontMetrics
Die Klasse Font
Texte werden in einem standarmäßig bereitgestellten Font ausgegeben. Soll ein
anderer Font zur Textausgabe verwendet werden, so muß ein Objekt der Klasse Font
erzeugt und dem verwendeten Graphics-Objekt zugewiesen werden.
Das Erzeugen neuer Font-Objekte wird über die Parameter name, style und size
des Konstruktors der Klasse Font gesteuert:
public Font(String name, int style, int size)
name: Name des gewünschten Font. In allen Java-Systemen sollen „SansSerif“, „Serif“, und
„Monospaced“ unterstützt werden. Unter Windows werden die Standardnamen auf die „True-Type-Fonts“
„Arial“, „TimesNewRoman“ und „CourierNew“ abgebildet.
style: Auswahl der Ausprägung (fett, kursiv)
176 Die Font-Klasse stellt bestimmte Fonts dar (Name, Stil, Fontgröße)
177 Die Fontmetrics-Klasse enthält Informationen über den Font wie tatsächliche Höhe und Breite eines
bestimmten Zeichens.
219
Programmieren in Java
Name
Font.PLAIN
Font.BOLD
Font.ITALIC
Wert
0
1
2
Bedeutung
Standard-Font
fett178
kursiv
size: Angabe der Größe der gewünschten Schriftart in Pixel (Punktgrößen)
public void setFont(Font font) wird zum Eintragen des Font-Objekts in den
Grafikkontext verwendet.
public Font getFont() ermittelt den aktuellen Font.
Die Ermittlung, welche Zeichensätze auf einem System installiert sind, kann über
Toolkit.getDefaultToolkit().getFontList()
erfolgen.
String[]
getFontList() gibt die Namen der verfügbaren Zeichensätze zurück, z.B.:179
import java.awt.*;
class ListFont
{
public static void main(String args[])
{
// herkoemmliche Methode zur Ermittlung der
// Schriftarten in einem Java-System
String fonts[] = Toolkit.getDefaultToolkit().getFontList();
for (int i = 0; i < fonts.length; i++)
System.out.println(fonts[i]);
}
}
Die Klasse FontMetrics
Diese Klasse bietet Methoden zur Bestimmung der Größe der angezeigten Zeichen in
der festgelegten Schrift an.
Begriffe für Schriften und Text.
Baseline (Grundlinie): Damit ist die imaginäre Linie gemeint, auf der der Text steht.
Descent (Unterstand): Damit ist gemeint, wie weit ein Buchstabe über die Grundlinie geht.
Ascent (Überstand): damit ist gemeint, wie weit ein Buchstabe über die Grundlinie geht.
Leading (Zeileabstand): Damit ist der Raum zwischen dem Descent eines Buchstabens und dem Ascent
der nächsten Zeile gemeint.
Methoden.
Methode
stringWidth()
charWidth()
getAscent()
getDescent()
Aktion
Gibt die volle Breite einer Zeichenmkette in Pixel aus
Gibt die Breite eines bestimmten zeichens aus
Gibt die Entfernung zwischen der Grundlinie und der oberen Grenze der Buchstaben
aus
Gibt die Entfernung zwischen der Grundlinie und der unteren Grenze der Buchstaben
aus (z.B. p und g)
178 BOLD und ITALIC können auch gemeinsam verwendet werden, indem beide Konstanten mit „+“
zusammengefügt werden.
179 pr41300
220
Programmieren in Java
getLeading()
Gibt den Abstand zwischen dem Überstand einer Zeile und dem Überstand der
nächsten Zeile aus
getHeight()
gibt die Gesamthöhe der Schrift aus, d.h. die Summe von Überstand, Unterstand und
Zeilenabstand
Abb.: Fontmetrics-Methoden
Bsp.180:
import java.applet.*;
import java.awt.*;
public class AllAnfAppl extends Applet
{
private static final String spruch = "Aller Anfang ist schwer!";
private Font font;
private FontMetrics fontMetrics;
private int spruchBreite;
private int spruchAscent;
private int spruchDescent;
private int spruchX;
private int spruchY;
// Methoden
public void init()
{
setBackground(Color.white);
font = new Font("Helvetica",Font.BOLD,24);
fontMetrics = null;
}
//
public void paint(Graphics g)
{
// Oval mit Farbe yellow
g.setColor(Color.yellow);
g.fillOval(0,0,getSize().width,getSize().height);
// Roter Rahmen; da Java keine Linienbreite kennt,
// wird die Linienbreite durrch 4 Ovale, (die sich
// um Pixelbreite unterscheiden,) simuliert
g.setColor(Color.red);
g.drawOval( 3, 3,getSize().width-6,getSize().height-6);
g.drawOval( 2, 2,getSize().width-4,getSize().height-4);
g.drawOval( 1, 1,getSize().width-2,getSize().height-2);
g.drawOval( 0, 0,getSize().width,getSize().height);
g.setColor(Color.black);
g.setFont(font);
if (fontMetrics == null)
{
fontMetrics
= g.getFontMetrics();
spruchBreite = fontMetrics.stringWidth(spruch);
spruchAscent = fontMetrics.getAscent();
spruchDescent = fontMetrics.getDescent();
}
int breite = getSize().width;
int hoehe = getSize().height;
spruchX = (breite - spruchBreite) / 2;
spruchY = (hoehe + spruchAscent - spruchDescent) / 2;
g.drawString(spruch,spruchX,spruchY);
180 pr41301
221
Programmieren in Java
}
}
4.1.4 Grössenangaben
Für die meisten Größenangaben werden int-Werte benutzt. Darüber hinaus gibt es
auch alterantive Formulierungsmöglichkeiten mit den Klassen Dimension, Point und
Rectangle.
<< interface >>
Shape
Point
public int x
public int y
Rectangle
public int y
public int y
public int width
public int height
<< Konstruktoren >>
public Rectangle(Rectangle r)
public Rectangle(int x, int y, int width, int height)
public Rectangle(int width, int height)
public Rectangle(Point p, Dimension d)
public Rectangle(Point p)
public Rectangle(Dimension d)
<< Methoden >>
public Rectangle getBounds()
public void setBounds(int x,int y, int width, int height)
public void setBounds(Rectangle r)
public Point getLocation()
public void setLocation(Point p)
public void setLocation((int x, int y)
public void translate(int x, int y)
public Dimension getSize()
public void setSize(int width, int height)
public boolean contains(Point p)
public boolean contains(int x, int y)
public boolean isEmpty()
public int hashCode()
public boolean equals(Object obj)
public String toString()
<< Konstruktoren >>
public Point()
public Point(Point p)
public Point(int x, int y)
<< Methoden >>
public Point getLocation()
public void setLocation(Point p)
public void setLocation(int x, int y)
public void move(int x, int y)
public void translate(int x, int y)
public int hashCode()
public boolean equals(Object obj)
public String toString()
Dimension
public int width
public int height
<< Konstruktoren >>
public Dimension()
public Dimension(Dimension d)
public Dimension(int width, int height)
<< Methoden >>
public Dimension getSize()
public void setSize(Dimension d)
public boolean equals(Object obj)
public String toString()
Abb.: Die Klassen Dimension und Rectangle
Das Interface Shape wird von den bisher bekannten geometrischen Formen (z.B.
Rectangle, Polygon) implementiert und außerdem von den neuen geometrischen
Formen im Paket java.awt.geom.
222
Programmieren in Java
4.1.5 Clipping-Operationen
Alle primitiven Zeichenoperationen wirken auf den ganzen Bildschirm. Man kann dies
durch das Setzen eines sog. Clipping-Bereichs einschränken, aus dem dann nicht mehr
heraus gezeichnet werden kann. Clipping ist eine Eigenschaft des aktuellen „Graphic“
Objekts. Mit der Methode clipRect(int x, int y, int breite, int
hoehe) läßt sich der Bereich einschränken, dann erfolgen alle Operationen in diesem
Bereich.
223
Programmieren in Java
4.2 Bildverarbeitung
Bilder sind neben Text das wichtigste visuelle Gestaltungsmerkmal. In Java können
Grafiken an verschiedenen Stellen eingebunden werden, z.B. als Grafiken in
Zeichenflächen (Canvas) oder als Icons in Buttons. Über Java können GIF-Bilder181 und
JPEG-Bilder geladen werden.
Jede Grafik wird als Exemplar der Klasse Image erzeugt.
4.2.1 Klassen zur Bildverarbeitung
Toolkit
{ abstract }
public abstract Image getImage(String dateiName)
public abstract Image getImage(Url url)
public abstract Image createImage(ImageProducer erzeuger)
public abstract boolean prepareImage(Image bild, int breite, int hoehe,ImageObserver observer)
public abstract int checkImage(Image bild, int breite, int hoehe, ImageObserver observer)
....
Image
{ abstract }
public abstract int getHeight(ImageObserver obs)
public abstract int getWidth(ImageObserver obs)
public abstract ImageProducer getSource()
public abstract Graphics getGraphics()
public abstract void flush()
public abstract Object getProperty(String attribut, ImageObserver obs)
public Image getScaledInstance(int breite, int hoehe, int modus)
<< interface >>
ImageObserver
public static final int ALLBITS
public static final int SOMEBITS
public static final int ABORT
public static final int ERROR
public static final int PROPERTIES
public abstract boolean imageUpdate(Image bild, int infoFlags, int x, int y, int breite, int hoehe)
181 Das GIF-Format (Graphics Interchange Format) ist ein komprimierendes Verfahren, das 1987 von CompuServe-
Betreibern zum Austausch von Bildern entwickelt wurde. GIF-Bilder können bis zu 1600 mal 1600 Punkte
umfassen. Die Komprimierung (nach dem LZW-Verfahren) ist verlustfrei. Jedes GIF-Bild kann aus maximal 256
Farben bestehen (bei einer Palette von 16,7 Millionen Farben).
224
Programmieren in Java
MediaTracker
public static final int ABORTED
public static final int COMPLETE
public static final int ERRORED
public static final int LOADING
<< Konstruktor >>
public MediaTracker(Component comp)
<< Methoden >>
public void addImage(Image bild, int id)
public void addImage(Image bild, int id, int breite, int hoehe)
public boolean checkAll()
public void waitForAll() throws InterruptedException
public boolean checkID(int id)
public void waitForID(int id) throws InterruptedException
...
Abb.: Image-Klassen
Die Klassen zur Bildverarbeitung befinden sich im Package java.awt.image. Mit
Hilfe der Klasse java.awt.Toolkit kann ein Bild aus einer angegebenen Quelle
mit getImage() geladen werden. Dabei wird ein Objekt vom Typ java.awt.Image
zurückgegeben. Der Ladezustand eines Objekts (z.B eines Bilds) kann überwacht
werden,
wenn
die
zum
Objekt
zugehörige
Klasse
das
Interface
java.awt.image.ImageObserver implementiert. Das tun alle AWT- und SwingKontrollelemente. Zur vereinfachten Benutzung beim Laden mehrerer Bilder gibt es die
Klasse java.awt.MediaTracker.
Die
weiteren
Klassen
im
Paket
java.util.image ermöglichen Skalierung, Rotation, Filtern und andere
Veränderungen der Bilder. Als Bildformate werden GIF und JPG unterstützt.
In Java 2 kamen Klassen zum pixelorientierten Erzeugen und Bearbeiten von Bildern im
Paket java.awt.image und dem neuen java.awt.image.renderable hinzu.
Bsp.: Ein Bildbetrachter182
import java.awt.*;
import java.awt.event.*;
public class ImageViewer extends Frame implements ActionListener
{
private Image image;
private Frame frame;
public ImageViewer()
{
setTitle("Bildbetrachter");
// konstruiere die Menuezeile
MenuBar mbar = new MenuBar();
Menu menue = new Menu("Datei");
MenuItem menueItem = new MenuItem("Oeffnen", new MenuShortcut((int)
'O'));
menueItem.addActionListener(this);
menue.add(menueItem);
mbar.add(menue);
setMenuBar(mbar);
182 pr42300
225
Programmieren in Java
// Schliessen des Fenster mit X
frame = this;
addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{ System.exit(0); }
});
setSize(600,400);
}
public void paint(Graphics g)
{
if (image != null)
{
g.drawImage(image,0,0,this);
setSize(image.getWidth(this),image.getHeight(this));
}
}
public void actionPerformed(ActionEvent e)
{
FileDialog d = new FileDialog(frame,"Oeffne Grafikdatei",
FileDialog.LOAD);
d.setFile("*.jpg;*gif");
d.show();
String file = d.getDirectory() + d.getFile();
image = Toolkit.getDefaultToolkit().getImage(file);
if (image != null) repaint();
}
public static void main(String args[])
{
new ImageViewer().show();
}
}
Mit der Klasse MediaTracker kann festgestellt werden, ob ein Bild für die Anzeige
bereit ist. Ein MediaTracker, z.B. für ein Applet, kann erzeugt werden über
MediaTracker meinTracker = new MediaTracker(this);
„this“ bezieht sich auf das Applet. Ein Bild wird angezeigt über die Methode
getImage(), z.B. über Image bild = getImage(getDocumentBase(),“B......jpg“).
Der Mediatracker wird angewiesen, dieses Bild über
meinTracker.addImage(bild,0)
zu beobachten. Mit waitForID() kann gewartet werden, bis das Bild fertig geladen
wirde, z.B. meinTracker.waitForID(0). Mit waitForAll() kann gewartet
werden, bis alle Bilder geladen sind. Falls nicht die ganze Zeit gewartet werden soll, bis
das Bild geladen ist, kann der Ladevorgang mit der Methode statusID() überwacht
werden. Beim Aufruf von statusID() wird eine Identifikation (ID) übergeben, für die
ein Status angelegt werden soll, und ein boolscher Operator mit einer Angabe, ob der
Ladevorgang für das Bild gestartet werden soll oder nicht, z.B.:
meinTracker.statusID(0,true)
Soll der Status aller Bilder überprüft werden, dann erfolgt der
meinTracker.statusAll(true).
Die
Methoden
statusID()
226
Aufruf
bzw.
Programmieren in Java
statusAll() geben eine ganze Zahl zurück, die durch folgende Flags symbolisiert
wird:
MediaTracker.ABORTED
MediaTracker.COMPLETE
MediaTracker.LOADING
MediaTracker.ERRORED
//
//
//
//
Das
Das
Das
Das
Laden von Bildern wurde abgebrochen
Bilder wurden komplett geladen
Bilder werden noch geladen
Laden von Bildern ist fehlerhaft
Mit checkID() bzw. checkALL() kann überprüft werden, ob ein Bild bzw. alle Bilder
geladen wurde(n):
public
public
public
public
boolean
boolean
boolean
boolean
checkAll()
checkAll(boolean startLoading)
checkID(int id)
checkID(int id,boolean startLoading)
Falls „startLoading“ true ist, wird der Ladevorgang für alle Bilder, die noch nicht
geladen sind, gestartet.
4.2.2 Bildproduzenten und Bildkonsumenten
Bildproduzenten sind die Quellen für Bilddateien. Bildkonsumenten sind Objekte, die
Bilder verwenden183. In Java ist die Aufgabe von Bildproduzenten bzw. Bildkonsumenten
durch die Schnittstelle ImageProducer und ImageConsumer abgebildet.
Das Interface ImageProducer beschreibt Methoden zum Bereitstellen von Pixeln
eines Bildes. Klassen, die die Schnittstelle implementieren, stellen Bildinformationen
einer speziellen Quelle dar. So ist die Klasse MemoryImageSource eine
vorgefertigte Klasse, die ImageProducer implementiert. Sie produziert
Bildinformationen aus einem Array von Pixeln, die im Speicher gehalten werden.
Die Schnittstelle ImageConsumer beschreibt Methoden, die einem Objekt den Zugriff
auf die Bilddatei des Produzenten erlauben.
Das Produzenten/Konsumenten-Modell verwendet eine ColorModel-Klasse. Bilder, die
zwischen Produzenten und Konsumenten ausgetauscht werden, werden aus
Datenfeldern von Ganzzahlen erstellt. Jede Ganzzahl steht für die Farbe eines Pixels.
Die ColorModel-Klasse verfügt über Methoden zum Herausziehen von roten, grünen,
blauen und „alpha“-Komponenten.
Die „alpha“-Komponente steht für die Transparenz einer farbe. Ein „alpha“-Wert von 255
bedeutet, daß die Farbe vollkommen lichtdurchlässig ist. Ein „alpha“-Wert von 0 zeigt
an, daß die Farbe vollständig transparent ist.
Das Standard-Farbmodell ist das RGBdefault-Modell mit dem die 4 Farbkomponenten
in eine Form „0xaarrggbb“ gebracht werden. Die 8 Bit ganz links sind der alpha-Wert,
die nächsten 8 Bit sind die „Rot“-Komponente, danach kommt die „Grün“-Komponente
und zum Schluß kommen 8 Bits für Blau.
Bsp.: Eine Farbe von „0x12345678“ würde eine „alpha“-Komponente von 0x12 (ziemlich
transparent) haben, eine Rotkomponente von 0x34, eine Grünkomponente von 0x56 und
eine Blaukomponente von 0x78.
183 typischerweise einfache Zeichenroutinen, die das Bild auf dem Bildschirm anzeigen.
227
Programmieren in Java
4.2.3 Bildfilter
ImageFilter liegen zwischen Produzenten und
Bildinformationen und nehmen Einfluß auf die Größe.
Subklassen der Klasse ImageFilter:
Konsumenten,
verändern
BufferedImageFilter. Mit diesem Filter läßt sich ein Objekt vom Typ BufferedImageOp übergeben,
mit dem unterschiedliche Manipulationen ermöglicht werden. BufferedImageOp ist eine Schnittstelle, die
von AffineTransformOp, ConvolveOp, LookupOp implementiert wird.
CropImageFilter. Bildteile werden herausgeschnitten.
ReplicateScaleFilter. Zum Vergrößern / Verkleinern von Bildern.
RGBImageFilter. Dieser allgemeine Filter ist für die eigene Filterklasse gedacht.
Bsp.:184 Das folgende Applet nimmt ein Bild und setzt ihm ein CropImageFilter auf,
damit es nur einen Teil des Bildes anzeigt.
import java.awt.*;
import java.awt.image.*;
import java.applet.*;
public class CropImage extends Applet
{
private Image originalImage;
private Image croppedImage;
private ImageFilter cropFilter;
public void init()
{
// Beschaffe das Original
originalImage = getImage(getDocumentBase(),"B01000800.jpg");
// Erzeuge einen Filter
cropFilter = new CropImageFilter(0,0,480,360);
// Erzeuge ein Bild, das die verkuerzte Version umfasst
croppedImage = createImage(new FilteredImageSource(
originalImage.getSource(),cropFilter));
}
public void paint(Graphics g)
{
g.drawImage(originalImage,0,0,this);
g.drawImage(croppedImage,480,0,this);
}
}
184 pr42300
228
Programmieren in Java
4.2.4 Kopieren von Speicher in ein Bild bzw. von Bildern in einen Speicher
4.2.4.1 Kopieren von Speicher in ein Bild
Ein möglicher Typ für das Produzieren von Bildern ist ein Datenfeld mit ganzen Zahlen,
die für die Farbe eines jeden Pixels stehen. Möglich wird das durch die Klasse
MemoryImageSource.
Bsp.: Das folgende Applet erzeugt ein Speicherbild, eine MemoryImageSource und
zeichnet das Bild im Zeichenbereich.
import java.applet.*;
import java.awt.*;
import java.awt.image.*;
/* das Applet zeichnet ein Image und benutzt dazu
einen Array mit Pixeln
*/
public class SpeicherBild extends Applet
{
private final static int b = Color.blue.getRGB();
private final static int r = Color.red.getRGB();
private final static int g = Color.green.getRGB();
int pixels[] = {
b, b, b, b, b, b, b, b, b, b,
b, b, b, b, b, b, b, b, b, b,
b, b, g, g, g, g, g, g, b, b,
b, b, g, g, g, g, g, g, b, b,
b, b, g, g, r, r, g, g, b, b,
b, b, g, g, r, r, g, g, b, b,
b, b, g, g, g, g, g, g, b, b,
b, b, g, g, g, g, g, g, b, b,
b, b, b, b, b, b, b, b, b, b,
b, b, b, b, b, b, b, b, b, b
};
Image meinBild;
public void init()
{
/* Erzeigen des Bilds aus dem Array pixels.
Die Pixels werden zeilenweise von Postion 0
aus dem Array gelesen. eine Zeile umfasst 10
Postionen.
*/
meinBild = createImage(
new MemoryImageSource(10,10,pixels,0,10));
}
public void paint(Graphics g)
{
// Zeichen das Bild.
// Breite und Hoehe des Bilds werden 10fach vergroessert
g.drawImage(meinBild,0,0,100,100,this);
}
}
229
Programmieren in Java
Abb.: Darstellung von SpeicherBild
4.2.4.2 Kopieren von Bildern in einen Speicher
Die PixelGrabber-Klasse nimmt ein Bild und macht aus diesem ein Datenfeld mit
ganzen Zahlen. Ein PixelGrabber wird auf ein Image-Objekt aufgesetzt und füllt ein
Ganzzahl-Feld mit den Farbwerten, die die Anteile der Farben rot, grün und blau
enthalten.
Der PixelGrabber ist für Modifikationen bereits existierender Bilder nützlich.
class java.awt.image.PixelGrabber implements ImageConsumer
Konstruktor.
public PixelGrabber(Image img, int x, int y, int breite, int hoehe, int
feld[], int verschiebung, int scanSize)
erzeugt ein PixelGrabber-Objekt, das ein Rechteck von RGB-Farben aus dem Feld holt. Das Rechteck
ist durch die Maße x, y, breite, hoehe beschrieben. Die Farbe für einen Punkt (i,j) sind im Feld an
der Position (j-y)*scanSize+(i-x)+verschiebung. Mit der Umwandlung wird noch nicht
begonnen. Sie muß mit der Funktion grabPixels() angeregt werden.
Methoden.
public boolean grabPixels() throws InterruptedException
Die Werte von einem Image oder ImageProducer werden geholt. Die Funktion kann von außen
unterbrochen werden.
public int getHeight()
liefert die Höhe des Pixelfelds. Ist die Höhe nicht verfügbar, dann ist das Ergebnis –1.
public int getWidth() liefert die Breite des Pixelfelds.
Bsp.: Das nachfolgende Bild lädt ein Bild und gibt die Farbinformationen auf der
Konsole aus.
import
import
import
import
javax.swing.*;
java.awt.*;
java.awt.event.*;
java.awt.image.*;
public class PR42400 extends Frame
{
Image bild;
int breite, hoehe;
230
Programmieren in Java
int pixels[];
public PR42400()
{
/*
bild = Toolkit.getDefaultToolkit().getImage(
"d:\\jdk1.3.1\\pgj\\progr\\pr42400\\B01000800.jpg");
*/
bild = new ImageIcon("B01000800.jpg").getImage();
if (bild == null) System.out.println("Kein Bild");
breite = bild.getWidth(this);
System.out.println(breite);
hoehe = bild.getHeight(this);
pixels = new int [breite * hoehe];
PixelGrabber grabber = new PixelGrabber(bild,0,0,breite,hoehe,
pixels,0,breite);
try {
grabber.grabPixels();
}
catch(InterruptedException e)
{
System.err.println("Fehler beim Holen der Pixel");
}
setSize(breite,hoehe);
repaint();
addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent m)
{
int pixel = pixels[breite * m.getY() + m.getX()];
int alpha = (pixel >> 24) & 0xff;
int rot
= (pixel >> 16) & 0xff;
int gruen = (pixel >> 8) & 0xff;
int blau = (pixel)
& 0xff;
System.out.println("R=" +rot+ " G=" +gruen+ "B=" +blau);
}
});
}
public void paint(Graphics g)
{
if (bild != null)
g.drawImage(bild,0,0,this);
}
public static void main(String args[])
{
Frame f = new PR42400();
f.show();
}
}
231
Programmieren in Java
4.3 2D Java
2D Java ist Bestandteil der Java Foundation Classes (JFC), ein Teil dessen, was Sun
Swing nennt und sämtliche Techniken umfaßt, die die GUI-Fähigkeit von Java
ausmachen. Das neue Java-2D-API ist ein Satz von Klassen für erweiterte 2D-Grafik
und die Bildverarbeitung. Die abstrakte Klasse Graphics2D erweitert die
Funktionalität von Graphics um Transformationen, Linienstärke und –stil.
Graphics
{ abstract }
Graphics2D
{ abstract }
Stroke stroke
Paint paint
AffineTransform transform
Composite composite
// Definition von Linienstiften
// Erweiterung des Farbmodells
// erlaubt Veränderungen der Form
// gibt an, wie Objekte verschiedener Farben überlagert werden
// sollen, erlaubt weiche Farbübergänge (Anti-Aliasing)
<< Konstruktor >>
protected Graphics2D()
<< Methoden >>
public abstract void addRenderingHints(Map hints)
public abstract void clip(Shape s)
public abstract draw(Shape s)
public abstract void drawImage(Image img, AffineTransform xForm, ImageObserver obs)
public abstract void fill(Shape s)
public abstract GraphicsConfiguration getDeviceConfiguration()
public abstract FontRenderContext getFontRenderContext()
public abstract Paint getPaint()
public abstract Object getRenderingHint(RenderingHints.Key hintKey)
public abstract Stroke getStroke()
public abstract AffineTransform getTransform()
public abstract void rotate(double theta)
public abstract void scale(double sx, double sy)
public abstract void setComposite(Composite comp)
public abstract setPaint(Paint paint)
public abstract void setRenderingHint(RenderingHints.Key hintKey, Object hintValue)
public abstract void setTransform(AffineTransform Tx)
public abstract void transform(AffineTransform Tx)
public abstract void translate(double tx, double ty)
public abstract void translate(int x, int y)
...
Abb.: Die Klasse Graphics2D
232
Programmieren in Java
4.3.1 Das Zeichnen unter Java 2D-API
Zeichnen unter dem AWT Drawing Model
Dieses Modell wurde bisher zum Zeichnen verwendet, z. B.:
import java.awt.*;
import java.applet.*;
public class MaleGefRechteck extends Applet
{
// Zeichnen unter dem AWT Drawing Model
public void paint(Graphics g)
{
g.setColor(Color.red);
g.fillRect(300,300,200,100);
}
}
Die Vorgehensweise beim Bearbeiten für jede Komponente kann aus diesem Bsp.
unmittelbar abgeleitet werden:
1. Spezifikation der notwendigen Atribute für die zu zeichnende Form, z.B. die Farbe mit „setColor“.
2. Definition der zu zeichnenden Form
3. Festlegen des genauen Aussehens der Form, z.B. über eine passende Grafikmethode.
Zeichnen unter Java 2D-API
Das Java 2D-API umfaßt zusätzliche unterstützende Features zur Spezifizierung von
Zeichenstiften, komplexe Formen und diverse Zeichenprozesse. Zur Nutzung dieser
Möglichkeit muß der Graphics-Parameter der paint()-Methode in ein Graphics2DObjekt gebracht werden („gecastet“) werden, z.B.:
import java.awt.*;
import java.applet.*;
import java.awt.geom.*;
public class MaleGefRechteck extends Applet
{
public void paint(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
// Spezifikation der Attribute
g2d.setColor(Color.red);
// Definition der Form (Verwende Even-Odd-Regel)
GeneralPath path = new GeneralPath();
path.moveTo(300.0f,400.0f); // untere linke Ecke
path.lineTo(500.0f,400.0f); // untere rechte Ecke
path.lineTo(500.0f,300.0f); // obere rechte Ecke
path.lineTo(300.0f,300.0f); // obere linke Ecke
path.closePath();
// Schliessen des Rechtecks
// Fuellen der Form
g2d.fill(path);
}
}
233
Programmieren in Java
Eine Polygon-Klasse wie unter dem AWT gibt es in dem 2D-API nicht. Hier wird ein
neuer Weg eingeschlagen, der über die Klasse GeneralPath geht. Damit lassen sich
beliebige Formen bilden. Dem Pfad werden verschiedene Punkte hinzugefügt, die dann
verbunden werden. Die Punkte müssen nicht zwingend, wie bei einem Polygon mit
Linien verbunden werden, sondern lassen sich auch durch quadratische oder kubische
Kurven verbinden. Da Graphics2D eine Unterklasse von Graphics ist, lassen sich
natürlich alle AWT-Operatoren weiter verwenden.
Verfahrensweise beim Zeichnen mit den Java 2D-API-Klassen
1. Spezifikation der notwendigen beschreibenden Attribute
2. Definition der Form, eines Textstrings oder eines Bilds.
Das Java 2D-API behandelt Positionsangeben (Pfade), Texte und Bilder gleichartig.
Sie können rotiert, skaliert, verzerrt und mit diversen Methoden zusammengesetzt
werden. Das „Shape“-Interface definiert einen ganzen Satz von Methoden zur
Beschreibung von geometrischen PATH-Objekten. GeneralPath ist eine Implementation
vom Shape-Interface, das zur Definition von beliebig komplexen Formen verwendet
werden kann.
<< interface >>
java.awt.Shape
.........
java.awt.Polygon
java.awt.geom.Line2D
java.awt.geom.GeneralPath
java.awt.geom.Area
java.awt.geom.RectangularShape
java.awt.geom.Arc2D
java.awt.geom.Ellipse2D
java.awt.geom.Rectangle2D
Abb.: das Interface Shape und implementierende Klassen
draw() aus der Klasse Graphics2D nimmt ein Shape-Objekt und zeichnet es. ShapeObjekte sind Linien, Polygone oder auch Kurven.
abstract public void draw(Shape s) zeichnet die Forrm im aktuellen
Graphics2D-Kontext.
3. Festlegen vom Aussehen der Form, des Textstrings oder des Bildes über passende
Graphics2D-Methoden.
234
Programmieren in Java
Java 2D API-Klassen185
Außer der Klasse java.awt.Graphics2D umfaßt Java 2D API Klassen in den
Paketen java.awt.geom (geometrische Formen, Pfade und Transformationen),
java.awt.font, java.awt.color (Farbräume), java.awt.image und
java.awt.image.renderable (Bitmaps und Filter) sowie java.awt.print
(Drucken).
Die Klassen für geometrische Objekte, die sich von der Klasse Shape ableiten, sind
Polygon, RectangularShape, Rectangle, Area, QuadCurve2D und
CubicCurve2D186. Eine besondere Klasse, die auch von Shape abgeleitet ist, heißt
GeneralPath. Damit lassen sich mehrere Objekte einer Figur zusammensetzten. Die
Klasse Rectangle2D, RoundRectangle2D, Aread2D und Ellipse2D erben von
der Klasse RectangularShape und sind Objekte, die durch eine recteckige Box
umgeben sind.
4.3.2 Eigenschaften geometrischer Objekte
Dicke und Art der Linien bestimmen
Die Methode setStroke() kann definieren
- die Dicke (width)
- die Eigenschaft wie ein Liniensegment beginnt und abschließt
- die Art, wie sich die Linien verbinden
- ein Linien-Pattern (dash attributes)
Unterstützt wird setStroke() durch die Schnittstelle Stroke, die konkret durch
BasicStroke implementiert wird, z.B.
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
BasicStroke pen =
new BasicStroke(2.0f,BasicStroke.CAP_BUTT,BasicStroke.JOIN_ROUND)
g2.setStroke(pen);
}
Für BasicStroke gibt es 9 Konstruktoren. Der hier verwendete Konstruktor von
BasicStroke har drei Argumente:
- einen float-Wert, der die Linienstärke angibt
- einen int-Wert, der die Art des Linienendes festlegt
- einen int-Wert, der den Stil des Verbindungsstücks zwischen zwei Liniensegmenten festlegt
185 pr43210
186 Beschreibung quadratischer und kubischer Kurvensegmente. Das sind Kurven, die durch zwei Endpunkte
und durch Kontrollpunkte dazwischen gegeben sind. Kubische Kurvensegmente werden auch Bézier-Kurven
genannt.
235
Programmieren in Java
Besonders bei breiten Linie ist es interessant, wie die Linie endet. Hier läßt sich aus
CAP-BUTT (keine Abschlußpunkte), CAP_ROUND (Anzeige von Kreisen an beiden
Enden) und CAP_SQUARE auswählen.
Die möglichen Stile für Verbindungselemente sind JOIN_MITER (Abschluß von Linien
so, daß sie senkrecht aufeinander stehen), JOIN_ROUND (Abschluß der Linien durch
abgerundete Ecken) und JOIN-BEVEL (Eine Linie wird zwischen den äußeren
Eckpunkten gezogen).
Bsp187.: Das folgende Applet zeigt Verbindungselemente zwischen Linien
import java.awt.*;
import java.applet.*;
import java.awt.geom.*;
public class StrokeDemo extends Applet
{
public void paint(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
BasicStroke pen1 = new BasicStroke(10.0f,BasicStroke.CAP_BUTT,
BasicStroke.JOIN_MITER);
g2d.setStroke(pen1);
g2d.draw(new Line2D.Float(10f,10f,210f,10f));
BasicStroke pen2 = new BasicStroke(10.0f,BasicStroke.CAP_ROUND,
BasicStroke.JOIN_MITER);
g2d.setStroke(pen2);
g2d.draw(new Line2D.Float(10f,30f,210f,30f));
BasicStroke pen3 = new BasicStroke(10.0f,BasicStroke.CAP_SQUARE,
BasicStroke.JOIN_MITER);
g2d.setStroke(pen3);
g2d.draw(new Line2D.Float(10f,50f,210f,50f));
BasicStroke pen4 = new BasicStroke(10.0f,BasicStroke.CAP_BUTT,
BasicStroke.JOIN_MITER);
g2d.setStroke(pen4);
g2d.draw(new Line2D.Float(10f,70f,100f,70f));
g2d.draw(new Line2D.Float(100f,70f,100f,120f));
g2d.draw(new Line2D.Float(100f,120f,10f,120f));
g2d.draw(new Line2D.Float(10f,120f,10f,70f));
BasicStroke pen5 = new BasicStroke(10.0f,BasicStroke.CAP_BUTT,
BasicStroke.JOIN_BEVEL);
g2d.setStroke(pen5);
g2d.draw(new Line2D.Float(120f,70f,210f,70f));
g2d.draw(new Line2D.Float(210f,70f,210f,120f));
g2d.draw(new Line2D.Float(210f,120f,120f,120f));
g2d.draw(new Line2D.Float(120f,120f,120f,70f));
BasicStroke pen6 = new BasicStroke(10.0f,BasicStroke.CAP_BUTT,
BasicStroke.JOIN_MITER);
g2d.setStroke(pen6);
g2d.draw(new Line2D.Float(30f,140f,10f,160f));
g2d.draw(new Line2D.Float(30f,140f,50f,160f));
BasicStroke pen7 = new BasicStroke(10.0f,BasicStroke.CAP_BUTT,
BasicStroke.JOIN_BEVEL);
g2d.setStroke(pen7);
187 pr43100
236
Programmieren in Java
g2d.draw(new Line2D.Float(90f,140f,70f,160f));
g2d.draw(new Line2D.Float(90f,140f,110f,160f));
BasicStroke pen8 = new BasicStroke(10.0f,BasicStroke.CAP_BUTT,
BasicStroke.JOIN_ROUND);
g2d.setStroke(pen8);
g2d.draw(new Line2D.Float(150f,140f,130f,160f));
g2d.draw(new Line2D.Float(150f,140f,170f,160f));
float dash[] = {10, 2};
BasicStroke pen9 = new BasicStroke(2.0f,BasicStroke.CAP_BUTT,
BasicStroke.JOIN_MITER,
1,
dash,0);
g2d.setStroke(pen9);
g2d.draw(new Rectangle2D.Float(10f,180f,50f,50f));
BasicStroke pen10 = new BasicStroke(10.0f,BasicStroke.CAP_BUTT,
BasicStroke.JOIN_ROUND);
g2d.setStroke(pen10);
g2d.draw(new Rectangle2D.Float(10f,250f,60f,70f));
BasicStroke pen11 = new BasicStroke(10.0f,BasicStroke.CAP_BUTT,
BasicStroke.JOIN_BEVEL);
g2d.setStroke(pen11);
g2d.draw(new Rectangle2D.Float(90f,250f,60f,70f));
BasicStroke pen12 = new BasicStroke(10.0f,BasicStroke.CAP_BUTT,
BasicStroke.JOIN_MITER);
g2d.setStroke(pen11);
g2d.draw(new Rectangle2D.Float(170f,250f,60f,70f));
}
}
237
Programmieren in Java
Füllmuster
Auch die Muster, mit denen Linien und Kurven gezeichenet werden, lassen sich ändern.
Das Objekt RenderingHints
4.3.3 Affine Transformation
Eine affine Transformation eines Objekts ist eine Translation (Verschiebung,
Skalierung), Rotation bzw. Scherung 188. Bei diesen Transformationen bleiben parallele
Linien nach der Translation auch parallel. Zur Durchführung dieser Operationen existiert
die Klasse AffineTransform. Dem Graphics2D-Kontext können diese
Transformationen zugewiesen werden (z.B. über setTransform()).
4.3.4 Farbmanagement unter 2D-Java
4.3.5 2D-Bildverarbeitung
188 Ein Objekt wird geschert, wenn es entlang der Koordinatenachse verzogen wird. Im zweidimensionalen gibt es
zwei Scherungsarten: Entlang der x-Achse und der y-Achse.
238
Programmieren in Java
5. AWT
5.1 Bestandteile des AWT
In Java wird die Kommunikation mit dem Anwender hauptsächlich über ein eigenes
Konzept realisiert – dem Abstract Windowing Toolkit. Das AWT besitzt zur
Kommunikation mit dem Anwender ein Application Programming Interface (API).
Darüber können allgemeine Komponenten der Benutzeroberfläche, z.B. Schaltflächen
oder Menüs, plattformunabhängig genutzt werden.
5.2 Die AWT-Komponenten
Die AWT stellt Komponenten für den Anwender bereit. Die von Anfang an vorhandenen
Komponenten189 des AWT sind: Schaltflächen (Buttons), Labels, Kontrollkästchen
(Checkbuttons), Optionsfelder (Radiobuttons), Listen, Auswahlfelder, Textfelder, Menüs,
Zeichenbereiche.
Zusätzlich gibt es Container, in die die Komponenten zum Erstellen vollständiger und
sinnvoller Anwendungsschnittstellen integriert sein müssen. Die Container im AWT sind
Fenster, Panels, Frames, Dialoge.
Ein weiterer Bestandteil der AWT sind Layout-Manager. Ein Layout-Manager ist in
jedem Container enthalten. Er findet – angepaßt an die jeweilige Situation –
automatisch heraus, an welche Stelle die Komponenten am besten passen. Der AWT
stellt 5 verschiedene Typen von Layout-Managern zur Verfügung: Flow-, Border-, Grid-,
Gridbag-Layout.
Schließlich stellt AWT die Mittel zur Reaktion auf Ereignisse zur Verfügung, die vom
Anwender über Komponenten ausgelöst werden können.
Die Wurzel fast aller AWT-Komponenten ist die Klasse Component. Sie enthält die
grundlegenden Anzeige- und Event-Handling-Funktionen.
Component
Canvas
Container
Panel
Applet
TextComponent
Window
Frame
Button
TextField
Dialog
Abb. AWT-Klassenhierarchie
189 Java 1.2 und das Swing-Konzept erweitern diese Komponenten noch einmal um einen satz von neuen und
ergänzenden Komponenten.
239
Programmieren in Java
5.2.1 Schaltflächen (Buttons)
Component
Button
<< Konstruktoren >>
public Button()
public Button(String label)
<< Methoden >>
public void addActionListener(ActionListener l)
public void addNotify()
public String getActionCommand()
public String getLabel()
protected String paramString()
protected void processActionEvent(ActionEvent e)
protected void processEvent(AWTEvent e)
public void removeActionListener(ActionListener l)
public void setActionCommand(String kommando)
public void setLabel(String label)
Abb. Die Klasse Button
Erzeugen. Schaltflächen (Buttons) sind Elemente einer grafischen Benutzeroberfläche,
die auf Knopfdruck Aktionen in der Fensterklasse auslösen. Mit den folgenden
Konstruktoren kann eine Schaltfläche erstellt werden:
-
Button() erzeugt eine leere Schaltfläche ohne Beschriftung
Button(String label) erzeugt eine Schaltfläche mit der durch das String-Objekt bezeichneten
Beschriftung.
In einem Applet190 reicht dafür aus: add(new Button("Auf los geht's
los!"));.
Beschriftungen einer Schaltfläche können dynamisch zur Laufzeit gesetzt und wieder
verändert werden. Dazu dient die Methode: public void setLabel(String
label). Welche Beschriftung die Schaltfläche angenommen hat, ermittelt die Methode
public String getLabel();.
Reaktionen auf die Betätigung von Schaltflächen (JDK 1.1). Falls ein Button gedrückt
wird, sendet es ein Action-Event an seine Ereignisempänger. Diese müssen das
Interface ActionListener implementieren und sich durch den Aufruf von
„addActionListener“ registrieren:
public void addActionListener(ActionListener l)
public void removeActionListener(ActionListener l)
Das Action-Event führt im Ereignisempfänger zum Aufruf der Methode public
void
actionPerformed(ActionEvent
ae)
,die
die
Methode
190 Abgeleitet von der Panel-Klasse
240
Programmieren in Java
getActionCommand aufrufen kann, mit der die Beschriftung des Button abgefragt
werden kann. Falls das Action-Kommando nicht mit der Beschriftung identisch ist, kann
es in der Button-Klasse durch „public void setActionCommand(String
kommando)“ geändert werden.
Bsp.: Setzen von Hintergrundfarben nach verschiedenen Buttonklicks191
import java.awt.*;
import java.awt.event.*;
public class SchaltApplet extends java.applet.Applet
{
private Button schalt1 = new Button("Rot");
private Button schalt2 = new Button("Blau");
private Button schalt3 = new Button("Gruen");
private Button schalt4 = new Button("Gelb");
private Button schalt5 = new Button("Schwarz");
public void init()
{
setBackground(Color.white);
schalt1.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
setBackground(Color.red);
}
});
schalt2.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
setBackground(Color.blue);
}
});
schalt3.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
setBackground(Color.green);
}
});
schalt4.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
setBackground(Color.yellow);
}
});
schalt5.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
setBackground(Color.black);
}
});
this.setLayout(new BorderLayout(15,15));
Panel p = new Panel();
p.setLayout(new FlowLayout(FlowLayout.CENTER,15,15));
191 pr52105
241
Programmieren in Java
p.add(schalt1);
p.add(schalt2);
p.add(schalt3);
p.add(schalt4);
p.add(schalt5);
this.add("South",p);
// this.pack();
}
}
Reaktionen auf die Betätigung von Schaltflächen (JDK 1.0). Die Komponenten innerhalb
der AWT-Oberfläche besitzen eine action()-Methode, die aufgerufen wird, wenn bei
einer Komponente eine Aktion ausgführt wurde. Beim Betätigen (Auslösen) einer
Schaltfläche wird die action()-Methode aufgerufen. Die action()-Methode gehört
zu den Ereignisbehandlungsmethoden des Event Handling. Die Syntax der action()Methode ist bei allen Komponenten identisch:
public boolean action(Event ereignis, Object welcheAktion).
Ereignis: Ereignis, das bei der Komponente aufgetreten ist
welcheAktion: steht für das, was geschehen ist
Bei Schaltflächen ist die Art der Aktion (welcheAktion) ganz einfach über das Label
(Beschriftung) der Schaltfläche auszuwerten, die ausgelöst wurde. Der „Event“Parameter enthält spezifische Informationen über die Aktion, z.B.:
event.target (Komponente, bei der die Aktion eingetreten ist)
event.when (Zeitpunkt, zu dem die Aktion geschehen ist)
Mit dem instanceof-Operator kann die event.target-Variable überprüft werden,
ob die Aktion auch für das Objekt erfolgt, das gewünscht wird.
Bsp.: Setzen von Hintergrundfarben nach verschiedenen Buttonklicks192
import java.awt.*;
public class KnopfAktApplet extends java.applet.Applet
{
public void init()
{
setBackground(Color.white); this.setLayout(new BorderLayout(15,15));
Panel p = new Panel();
p.setLayout(new FlowLayout(FlowLayout.CENTER,15,15));
p.add(new Button("Rot"));
p.add(new Button("Blau"));
p.add(new Button("Gruen"));
p.add(new Button("Gelb"));
p.add(new Button("Schwarz")); this.add("South",p);
}
public boolean action(Event e, Object arg)
{
// Test auf Schaltflaechenaktion
if (e.target instanceof Button) aendereFarbe((String) arg);
return true;
}
void aendereFarbe(String knopfName)
{
// Aendern der Hintergrundfarbe
192 vgl. pr52105
242
Programmieren in Java
if (knopfName.equals("Rot"))
else if (knopfName.equals("Blau"))
else if (knopfName.equals("Gruen"))
else if (knopfName.equals("Gelb"))
else if (knopfName.equals("Schwarz"))
else;
setBackground(Color.red);
setBackground(Color.blue);
setBackground(Color.green);
setBackground(Color.yellow);
setBackground(Color.black);
}
}
5.2.2 Labels
Labels sind Zeichenketten zur Beschriftung anderer Komponenten der
Benutzeroberfläche. Ein Label umfaßt eine Zeile Text, die auf dem Bildschirm angezeigt
wird und vom Programm veränder werden kann.
Konstruktoren.
public Label()
public Label(String text)
public Label(String text, int ausrichten)
Der Parameter „ausrichten“ bestimmt die Ausrichtung des Texts. Hier kann eine der Konstanten
Label.LEFT, Label.RIGHT, Label.CENTER übergeben werden.
Methoden.
public void setText(String text)
// Zugriff auf die Textzeile des Label
public String getText()
// Zugriff auf die Textzeile des Label
public void setAlignment(int ausrichten)
// Ausrichten der Textzeile
public int getAlignment()
// Ausrichten der Textzeiele
5.2.3 Kontrollkästchen und Optionsfelder
Erzeugen von Kontrollkästchen. Ein Kontrollkästchen (Checkbutton / Checkbox) hat
zwei Bestandteile: ein Label (Text, der neben dem Kontrollkästchen angelegt wird) und
einem Zustand (boolesche Variable, die angibt, ob die Box eingeschaltet wurde oder
nicht193). Zum Erzeugen von Kontrollkästchen gibt es 5 Konstruktoren
-
-
Checkbox()
(Kontrollkästchen ohne Label)
Checkbox(String label)
(Kontrollkästchen mit Beschriftung)
Checkbox(String label, boolean zustand)
Checkbox mit Vorgabe des Anfangszustands
Checkbox(String label, CheckboxGroup cbg, boolean zustand)
(Kontrollkästchen, das jenach gesetzter boolescher Zustandsvariable vorselektiert (true) ist oder nicht
(false). Das mittlere Argument ist bei Kontrollkästchen immer auf Null gesetzt. Falls das nicht der Fall
ist, dient der Parameter cbg zum Gruppieren von Radiobuttons.
Checkbox(String label, boolean zustand, CheckboxGroup cbg)
193 Standardmäßig ist die Box ausgeschaltet (Wert ist false oder „off“).
243
Programmieren in Java
Plazieren von Kontrollkästchen in einem Container. Es erfolgt über die Methode add(),
z.B.: add(new Checkbox("Java"));.
Überprüfen und Setzen von Kontrollkästchen. Mit der Methode public boolean
getState() kann überprüft werden, ob ein Kontrollkästchen angeglickt wurde. Die
Methode public void setState(boolean zustand) setzt den Status (true
für gesetzt). Die getlabel()-Methode fragt das Label des jeweiligen
Kontrollkästchens ab.
Reaktion auf Zustandsänderungen. Eine Checkbox generiert ein Item-Event, wenn die
Markierung vom Anwender gesetzt oder zurückgenommen wird. Zur Reaktion auf
dieses Event ist durch den Aufruf von addItemListener ein Objekt, das das
Interface ItemListener implementiert, bei der Checkbox zu registrieren. In diesem
Fall wird ein Ereignisempfänger die Methode itemStateChanged mit einem
Argument vom Typ ItemEvent aufgerufen:
public abstract void itemStateChanged(ItemEvent e)
Über das ItemEvent kann die Methode getItemSelectable aufgerufen werden,
mit der ermittelt werden kann, durch welche Checkbox das Ereignis ausgelöst wurde:
public ItemSelectable getItemSelectable().
Der Rückgabewert kann in ein Objekt des Typs Checkbox konvertiert werden. Durch
Aufruf von getState() kann der aktuelle Zustand bestimmt werden.
Bsp.:
import java.awt.*;
import java.awt.event.*;
public class PR52301 extends Frame
{
Checkbox
cb1 = new Checkbox("Checkbox 1"),
cb2 = new Checkbox("Checkbox 2",true),
cb3 = new Checkbox("Checkbox_3",false);
public PR52301()
{
setLayout(new GridLayout(3,1));
setBackground(Color.lightGray);
cb1.addItemListener(new CBL());
cb2.addItemListener(new CBL());
cb3.addItemListener(new CBL());
add(cb1);
add(cb2);
add(cb3);
}
public class CBL implements ItemListener
{
public void itemStateChanged(ItemEvent e)
{
Checkbox cb = (Checkbox) e.getItemSelectable();
System.out.println(cb.getLabel() + ": " + cb.getState());
}
}
public static void main(String args[])
{
PR52301 f = new PR52301();
f.addWindowListener(new WindowAdapter()
244
Programmieren in Java
{
public void windowClosing(WindowEvent e)
{ System.exit(0); }
});
f.setSize(100,150); f.setVisible(true);
}
}
Defintion zur CheckboxGroup. Eine CheckboxGroup ist die Java-Variante einer Gruppe
von Radiobuttons (Optionsfelder), von den genau immer einer aktiviert wird. Wird eine
anderer Button aktiviert, so änder er seinen internen Status auf „true“ und der zuvor
gesetzte wird „false“. Eine CheckboxGroup ist nichts Anderes als eine Checkbox,
deren CheckboxGroup-Parameter gesetzt ist.
Erzeugen von Optionsfeldern (Radiobuttons). Erforderlich ist zunächst eine CheckboxGruppe, die über die betreffenden Konstruktoren erstellt werden kann. Dieser Gruppe
werden die einzelnen Optionsfelder hinzugefügt.
Plazieren von Optionsfeldern in einem Container. Auch Optionsfelder müssen wie alle
Komponenten nach der Erzeugung in einen Container gebracht werden. Das geschieht
über die Methode add().
Setzen und Überprüfen von Optionsfeldern. Die Methoden getState() und
getLabel() funktionieren auch bei Optionsfeldern. Zum Zugriff auf die Gruppe eines
Optionsfelds und zum Ändern gibt es die Methoden:
public CheckboxGroup getCheckboxGroup()
public void setCheckboxGroup(CheckboxGroup g)
Zum Holen und Setzen des momentan ausgewählten Optionsfelds dienen
public Checkbox getCurrent()
public void setCurrent(Checkbox box)
Bsp.:
import java.awt.*;
import java.awt.event.*;
public class PR52302 extends Frame
{
CheckboxGroup cbg = new CheckboxGroup();
public PR52302()
{
setLayout(new GridLayout(3,1));
setBackground(Color.lightGray);
Checkbox cb1 = new Checkbox("rot",cbg,false);
Checkbox cb2 = new Checkbox("blau",cbg,false);
Checkbox cb3 = new Checkbox("gruen",cbg,false);
cb1.addItemListener(new CBL());
cb2.addItemListener(new CBL());
cb3.addItemListener(new CBL());
add(cb1); add(cb2); add(cb3);
}
public class CBL implements ItemListener
{
public void itemStateChanged(ItemEvent e)
{
Checkbox cb = (Checkbox) e.getItemSelectable();
if (cb.getLabel() == "rot")
{
if (cb.getState())
245
Programmieren in Java
{ System.out.println("rot"); }
}
if (cb.getLabel() == "blau")
{
if (cb.getState())
{ System.out.println("blau"); }
}
if (cb.getLabel() == "gruen")
{
if (cb.getState())
{ System.out.println("gruen"); }
}
}
}
public static void main(String args[])
{
PR52302 f = new PR52302();
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{ System.exit(0); }
});
f.setSize(100,150); f.setVisible(true);
}
}
Reaktionen auf Kontrollfelder und Radiobuttons im JDK1.0. Kontrollfelder und
Radiobuttons verfügen wie alle Komponenten über die action()-Methode. Sie wird
bei einer Auswahl aufgerufen, sobald ein Feld selektiert wird.
Bsp.194:
import java.awt.*;
public class CheckAktApplet extends java.applet.Applet
{
CheckboxGroup meineCheckboxGruppe = new CheckboxGroup();
public void init()
{
add(new Checkbox("Blau",meineCheckboxGruppe,false));
add(new Checkbox("Rot",meineCheckboxGruppe,false));
add(new Checkbox("Gruen",meineCheckboxGruppe,false));
add(new Checkbox("Gelb",meineCheckboxGruppe,true));
// Hintergrundfarbe passend zur Voreinstellung
setBackground(Color.yellow);
}
public boolean action(Event e, Object welcheAktion)
{
// Test auf Kontrollkaestchen oder Radiobutton
if (!(e.target instanceof Checkbox))
return false;
// Instanz der Ereignisse
Checkbox welcheAuswahl = (Checkbox) e.target;
// Status des Radiobuttons zur Kontrolle
boolean CheckboxStatus = welcheAuswahl.getState();
// Auswahl Blau
if (welcheAuswahl.getLabel() == "Blau")
{
if (CheckboxStatus) setBackground(Color.blue); return true;
194 pr54305
246
Programmieren in Java
}
// Auswahl Rot
if (welcheAuswahl.getLabel() == "Rot")
{
if (CheckboxStatus) setBackground(Color.red);
return true;
}
// Auswahl Gruen
if (welcheAuswahl.getLabel() == "Gruen")
{
if (CheckboxStatus) setBackground(Color.green); return true;
}
if (welcheAuswahl.getLabel() == "Gelb")
{
if (CheckboxStatus) setBackground(Color.yellow);
return true;
}
return false; // keine der Optionen wird aufgefangen
}
}
5.2.4 Auswahlmenüs
Es handelt sich um Popup- bzw. Pulldown-Menüs, die sich beim Anklicken öffnen und
mehrere auszuwählende Optionen anzeigen, von denen dann eine Option ausgewählt
werden kann.
Konstruktor: public Choice()
// erstellt ein Choice-Objekt mit einer leeren Liste.
Bearbeiten der Liste (Combobox) zum Auswahlmenü: public
void
addItem(String item) Jeder Aufruf von addItem hängt das übergebende
Element „item“ an das Ende der Liste an. Die Elemente werden in der Reihenfolge der
Aufrufe von addItem eingefügt.
Zugriff auf die Elemente der Combobox: public int getSelectedIndex()
liefert den Index des ausgewählten Elements. Durch Übergabe dieses Werts (an
„getItem“ kann das aktuelle Element beschafft werden: public String
getItem(int n). Über public String getSelectedItem() kann ohne
Kenntnis der internen Nummer (Index) das ausgewählte Element beschafft werden.
Programmseitige Auswahl eines Elements:
public void select(int)
// Auswahl über den Index
public void select(String s)
// Auswahl über den angegebenen Wert von String
Ereignisbehandlung. Ebenso wie eine Checkbox sendet auch ein Choice-Element
„Item“-Ereignisse, wenn ein Element ausgewählt wurde. Zur Reaktion auf dieses
Element muß ein ItemListener durch Aufruf von public
void
addItemListener(ItemListener l) registriert sein. Ein „ItemEvent“ wird
immer dann gesendet, wenn ein Element ausgewählt wurde. In diesem Fall wird ein
Ereignisempfänger die Methode itemStateChanged mit einem Argument vom Typ
ItemEvent
aufrufen:
public
abstract
void
itemStateChanged(ItemEvent e). Das „ItemEvent“ stellt die Methode
public ItemSelectable getItemSelectable() zur Verfügung, mit der
ermittelt werden kann, durch welches Choice-Element das Ereignis ausgewählt wurde.
247
Programmieren in Java
Zusätzlich gibt es public Object getItem(), die das ausgewählte Element als
String zur Verfügung stellt.
Bsp.:
import java.awt.*;
import java.awt.event.*;
public class PR14174 extends Frame
{
TextField t
= new TextField(30);
Choice auswahl = new Choice();
public PR14174()
{
auswahl.addItem("rot");
auswahl.addItem("blau");
auswahl.addItem("gruen");
auswahl.addItem("pink");
auswahl.addItem("orange");
auswahl.addItem("gelb");
auswahl.addItem("cyan");
add(auswahl);
pack();
}
public static void main(String args[])
{
PR14174 f = new PR14174();
f.addWindowListener(
new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{ System.exit(0); }
});
f.setBackground(Color.lightGray);
f.setVisible(true);
}
}
5.2.5 Listenfelder
Eine Instanz der Klasse List ist eine listenartige Darstellung von Werten, aus denen
Anwender einen oder mehrere auswählen kann. Anders als ein Choice-Element ist ein
Element der Klasse List ständig in voller Größe auf dem Bildschirm sichtbar.
Erzeugen von Listenfeldern.
public List()
// legt eine leere Liste an, deren dargestellte Größe durch den
// Layoutmanager begrenzt wird.
public List(int groesse)
// Der Parameter groesse legt die Anzahl der angezeigten Zeilen fest.
public List(int groesse, boolean multiselect)
// ist multiselect "true", kann der Anwender nicht nur ein Element,
// sondern auch andere Elemente auswählen
Mehrfachauswahl ausgewählter Elemente. „List“ bietet nahezu dieselbe Funktionalität
wie Choice an. Zusätzlich stehen zur Verfügung:
public int[] getSelectedIndexes()
// liefert einen Array mit den Indexpositionen der ausgewählten
248
Programmieren in Java
// Elemente.
public String[] getSelectedItems()
// liefert eine Liste der Werte.
Auswahl einzelner Elemente.
public void select(int index)
public void deselect(int index)
Entfernen, Verändern von Elementen.
public void delItem(int index)
// löscht das Element an der Position index
public void remove(int index)
// löscht das Element an der Position index
public void replaceItem(String neuerWert,int index)
// ersetzt das Element an der Position index durch die Postion
// neuerWert.
Item- und Action-Ereignisse. Ein Listenelement sendet Item- und Action-Ereignisse.
Ein Action-Ereignis wird generiert, wenn ein Listenelement durch Doppelklick
ausgewählt wurde. Ein Item-Ereignis wird ausgelöst, nachdem in der Liste ein Element
ausgewählt wurde.
Bsp.:
import java.awt.*;
import java.awt.event.*;
public class PR14177 extends Frame
{
String[] farben = {"rot","blau","gruen","pink","orange",
"gelb","cyan"};
List lst = new List(6,true);
public PR14177()
{
setLayout(new FlowLayout());
for (int i = 0; i < 7; i++)
lst.addItem(farben[i]);
add(lst);
lst.addItemListener(new ILL());
lst.addActionListener(new ALL());
pack();
}
public class ILL implements ItemListener
{
public void itemStateChanged(ItemEvent e)
{
List lst = (List) e.getItemSelectable();
String str = lst.getSelectedItem();
int pos = ((Integer) e.getItem()).intValue();
System.out.println("event.getSelectedItem: " + str + " " + pos);
}
}
class ALL implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
Object obj = e.getSource();
if (obj instanceof List)
{
System.out.println("Listenfeld-Aktion: " + e.getActionCommand());
}
}
249
Programmieren in Java
}
public static void main(String args[])
{
PR14177 f = new PR14177();
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
// f.setVisible(false);
System.exit(0);
}
});
f.setBackground(Color.lightGray);
// f.setSize(100,160);
f.setVisible(true);
}
}
5.2.6 Textbereiche und Textfelder
Das AWT verfügt über Klassen zur Eingabe von Text (Klasse TextField für Textfelder
und Klasse TextArea für Textbereiche). Textfelder begrenzen die Menge des
einzugebenden Textes und können nicht gescrollt werden. In Textbereichen können
mehrere Zeilen verarbeitet werden, außerdem sind Textbereiche scrollbar.
TextField
Ein TextField dient zur Darstellung von Text. Anwender und Programm können den
dargestellten Text einlesen und verändern.
Erzeugen von Textfeldern. Es stehen folgende Konstruktoren zur Verfügung:
public TextField()
erzeugt ein leeren Textfeld, in das der Anwender Text eingeben kann.
public TextField(int anzSpalten)
Erzeugt ein leeres Textfeld bestimmter Breite. Die Anzahl einzugebender Zeichen ist nicht begrenzt. Ist
der Text länger, so scrollt er innerhalb des Textfelds
public TextField(String text)
Über den Parameter text kann eine Zeichenkette vorgegeben werden, die beim aufruf des Textfelds
vorgelegt wird.
public TextField(String text, int anzSpalten)
Methoden zum Zugriff bzw. Verändern von Textfeldern.
public String getText();
public void setText(String text)
Mit
public int getColumns()
kann die Anzahl der darstellbaren Zeichen eines Textfelds abgefragt und mit
public void setColumns(int anzSpalten)
verändert werden.
250
Programmieren in Java
Markieren von Text.
public void selectAll()
//markiert den kompletten Bereich
public void select(int erstes, int letztes)
//markiert den Bereich von „erstes“ bis „letztes“. Das „Zählen“ beginnt bei 0.
Mit
public int getSelectionStart()
bzw.
public int getSelectionEnd()
kann die aktuelle Auswahl abgefragt werden. Mit
public int getCaretPosition()
und
public void setCaretPosition(int position)
kann auf die aktuelle Cursorposition zugegriffen werden.
Bearbeiten von Textfeldern.
Über
public void setEditable(boolean erlaubt)
// Aufruf mit Parameter false unterbindet weitere Eingaben
bzw.
public boolean isEditable()
// Abfrage des aktuellen Status
kann man die Veränderung von Text verhindern. Mit
public void setEchoChar(char z)
(Übergabe eines Zeichens, das beim Tastendruck anstelle des vom anwender eingegebenen Zeichens
ausgegeben wird)
bzw.
public char getEchoChar()
besteht die Möglichkeit verdeckter Eingaben (z.B. für Paßwörter).
Generieren eines Action-Event erfolgt beim Drücken der Enter-Taste innerhalb des
Textfelds. Die Methode String getActionCommand() des Action-Events liefert
den Inhalt des Textfelds.
Generieren eines Text-Ereignisses bei jeder Änderung im Textfeld. Ein Empfänger kann
mit der Methode addTextListener vom Textfeld registriert werden. Als Argument
wird ein Objekt erwartet, das das Interface TextListener implementiert. Beim
Auftreten eines Text-Ereignisses wird vom TextListener die Methode
textValueChanged mit einem „TextEvent“ als Parameter aufgerufen. „TextEvent“
ist aus „AWTEvent“ abgeleitet und erbt die Methoden getID(), getSource().
Typischerweise
wird
innerhalb
von
public
void
textValueChanged(TextEvent e) mit getSource() das zugehörige Textfeld
beschafft und mit getText() auf seinen Inhalt zugegriffen.
Bsp.:
import java.awt.*;
import java.awt.event.*;
public class PR14170 extends Frame
{
Button
b1 = new Button("Hole Text"),
b2 = new Button("Setze Text");
TextField
t1 = new TextField(30),
t2 = new TextField(30),
t3 = new TextField(30);
String s = new String();
251
Programmieren in Java
public PR14170()
{
setLayout(new FlowLayout());
b1.addActionListener(new B1A());
b2.addActionListener(new B2A());
t1.addTextListener(new T1());
t1.addActionListener(new T1A());
add(b1);
add(b2);
add(t1);
add(t2);
add(t3);
}
class T1 implements TextListener
{
public void textValueChanged(TextEvent e)
{
t2.setText(t1.getText());
}
}
class T1A implements ActionListener
{
private int zaehler = 0;
public void actionPerformed(ActionEvent e)
{
t3.setText("t1 ActionEvent " + zaehler++);
}
}
class B1A implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
s = t1.getSelectedText();
if (s.length() == 0) s = t1.getText();
t1.setEditable(true);
}
}
class B2A implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
t1.setText("Eingefuegt durch Button 2: " + s);
t1.setEditable(false);
}
}
public static void main(String args[])
{
PR14170 f = new PR14170();
f.addWindowListener(
new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
f.setSize(300,200);
252
Programmieren in Java
f.setBackground(Color.lightGray);
f.setVisible(true);
}
}
TextArea
Mit dieser Klasse können mehrzeilige Textfelder erzeugt werden. Zusätzlich kann der
Text in allen Richtungen „scrollen“, so daß auch größere Texte bequem bearbeitet
werden können.
Konstruktoren.
public TextArea()
//erzeugt ein leeres TextArea-Objekt in einer vom System vorgegebenen Größe.
public TextArea(int zeilen, int spalten)
//erzeugt eine leeres TextArea-Objekt mit einer bestimmten Anzahl von sichtbaren Zeilen
//und Spalten.
public TextArea(String text, int zeilen, int spalten)
//erzeugt ein TextArea-Objekt mit Text.
public TextArea(String text, int zeilen, int spalten, int scrollbars)
//erzeugt ein TextArea-Objekt, in dem über scrollbars die Ausstattung der TextArea mit
//Schieberegistern festgelegt werden kann. Dazu stellt TextArea die Konstanten
//SCROLLBARS_BOTH, SROLLBARS_NONE, SCROLLBARS_VERTICAL_ONLY,
//SCROLLBARS_HORIZONTAL_ONLY zur Verfügung, die als Argumente übergeben werden können.
Zusätzliche in der Klasse bereitgestellte Methoden zum Verändern von Teilen des Texts.
Neben den bereits in der Klasse TextField angegebenen Methoden stehen zur
Verfügung:
public void insert(String str, int pos)
//verändert die Zeichenkette str ab Position pos. Der dahinter stehende Text wird entsprechend
//nach hinten verschoben.
public void append(String str)
//hängt den über str übergebenen Text hinten an.
public void replaceRange(String text, int start, int ende)
//ersetzt den Text zwischen start und end durch den String text.
Senden von Text-Events. Ein Objekt der Klasse TextArea sendet Text-Events, so
wie es bei TextField beschrieben wurde.
5.2.7 Schieber und Bildlaufleisten
Listen und Textbereiche besitzen standardmäßig die Eigenschaft bei Bedarf nicht in
den Anzeigebereich passenden Inhalt über Bildlaufleisten und Schieber zu scrollen.
Bildlaufleisten können auch individuell erstellt werden und dienen zur (quasi-) analogen
Anzeige bzw. Eingabe eines Werts aus einem vorgebenen Wertebereich.
Der Schieberegler kann horizotal und vertikal angeordnet werden und besitzt einen
Schieber, dessen Größe veränderlich ist. Der interne Wert eines Schiebereglers und
die Anzeigeposition seines Schiebers sind untrennbar miteinander verbunden. Ändert
der Anwender die Position des Schiebers, ändert sich automatisch auch sein interner
Wert. Wird vom Programm der Wert verändert, führt das automatisch zu einer
Positionierung des Schiebers. Zur Änderung des aktuellen Werts einer Bildlaufleiste
253
Programmieren in Java
können drei verschieden Teile der Bildlaufleiste verwendet werden:
-
Pfeile an beiden Enden zum Erhöhen / Erniedrigen des Werts um eine Einheit (standardmäßig 1)
Ein Bereich in der Mitte zum Erhöhen / Erniedrigen eines Werts um je eine größere Einheit
(standardmäßig 10)
Ein Bildlauffeld (Schieber) in der Mitte, der den momentan gewählten Wert anzeigt. Durch
Verschieben des Bildlauffelds mit dem Mauszeiger kann innerhalb der Bildlaufleiste ein anderer Wert
gewählt werden.
Nötig ist nur die Festlegung eines Höchst- und Mindestwerts für die Bildlaufleiste. Der
Rest wird von Java erledigt.
Konstruktoren.
public Scrollbar()
//erzeugt einen vertikalen Schieberegler mit 0,0 als anfänglichen Höchst-, Mindestwert.
public Scrollbar(int orientierung)
//orientierung bezeichnet die Ausrichtung und kann über Scrollbar.HORIZONTAL bzw.
//Scrollbar.VERTICAL festgelegt werden.
public Scollbar(int orientierung, int wert, int bh, int minimum,
maximum)
//orientierung...bestimmt die Ausrichtung des Schiebereglers
//wert...Anfangswert des Schiebereglers (Wert zwischen dem Höchst- und Mindestwert
//bh...Breite bzw. Höhe der dem Schieberegler zugeteilten Box (Seite)
//minimum, maximum...bezeichnet den Mindest-, Höchstwert des Schiebereglers
int
Methoden der Scrollbar-Klasse versorgen die Handhabung der Werte des
Schiebereglers:
Methode
public
public
public
public
public
public
Bedeutung
Zugriff auf den aktuellen Wert des Schiebereglers
Setzt den aktuellen Wert des Schiebereglers
Bestimmt die Ausrichtung des Schiebereglers
Zugriff auf den Mindestwert
Zugriff auf den Höchstwert
Zugriff der Parameter, die die Stärke der Veränderung des
Werts beim Klicken auf die Buttons bzw. die Schaltfläche
zwischen Schieber und Button bestimmen.
public void setUnitIncrement(int l) Veränderung der Parameter, die die Stärke der Veränderung
des Werts beim Klicken auf die Buttons zwischen Schieber
und Button bestimmen
public int getBlockIncrement()
Zugriff der Parameter, die die Stärke der Veränderung des
Werts beim Klicken auf die Buttons bzw. die Schaltfläche
zwischen Schieber und Button bestimmen
public void setBlockIncrement(int l) Veränderung der Parameter, die die Stärke der Veränderung
des Werts beim Klicken auf die Schaltfläche zwischen
Schieber und Button bestimmen
int getValue()
void setValue(int wert)
int getOrientation()
int getMinimum()
int getMaximum()
int getUnitIncrement()
Ereignisse.
Ein
Scrollbar
sendet
Adjustment-Ereignisse
an
seine
Ereignisempfänger. Diese müssen das Interface AdjustmentListener
implementieren
und
sich
durch
Aufruf
von
public
void
addAdjustmentListener(AdjustmentListener
l) registrieren. Das
Adjustment-Ereignis führt im Ereignisempfänger zum Aufruf der Methode:
public abstract void adjustmentValueChanged(AdjustmentEvent
e), die ein AdjustmentEvent übergeben bekommt. Dieses besitzt die Metode
getAdjustable(), mit der der auslösende Scrollbar bestimmt werden kann.
Zusätzlich gibt es die Methode getAdjustmentType(), die Auskunft darüber gibt,
254
Programmieren in Java
welche Benutzeraktion zur Auslösung des Ereignisses führte.
Konstanten, die von getAdjustmentType Bedeutung
zurückgegeben werden
AdjustmentEvent.UNIT_INCREMENT
Der Wert durch Klicken eines „Button“ um eine
Einheit erhöht.
AdjustmentEvent.UNIT_DECREMENT
Der Wert durch Klicken eines „Button“ um eine
Einheit erniedrigt.
AdjustmentEvent.BLOCK_INCREMENT
AdjustmentEvent.BLOCK_DECREMENT
AdjustmentEvent.TRACK
Der Wert wurde durch Klicken der Schaltfläche
zwischen Button und Schieber um eine Seite erhöht
Der Wert wurde durch Klicken der Schaltfläche
zwischen Button und Schieber um eine Seite
vermindert
Der Wert wurde durch Ziehen des Schiebers
verändert
Bsp.:
import java.awt.*;
import java.awt.event.*;
public class PR14175 extends Frame
{
public PR14175()
{
Panel p = new Panel();
p.setLayout(new BorderLayout());
Scrollbar hsb = new Scrollbar(Scrollbar.HORIZONTAL,1,10,1,100);
hsb.addAdjustmentListener(new HsbAL());
p.add("South",hsb);
Scrollbar vsb = new Scrollbar(Scrollbar.VERTICAL,1,10,1,100);
vsb.addAdjustmentListener(new VsbAL());
p.add("East",vsb);
add(p);
pack();
}
public class HsbAL implements AdjustmentListener
{
public void adjustmentValueChanged(AdjustmentEvent e)
{
switch(e.getAdjustmentType())
{
case AdjustmentEvent.UNIT_INCREMENT:
System.out.println("Adjustment.UNIT_INCREMENT");
break;
case AdjustmentEvent.UNIT_DECREMENT:
System.out.println("Adjustment.UNIT_DECREMENT");
break;
case AdjustmentEvent.BLOCK_INCREMENT:
System.out.println("Adjustment.BLOCK_INCREMENT");
break;
case AdjustmentEvent.BLOCK_DECREMENT:
System.out.println("Adjustment.BLOCK_DECREMENT");
break;
case AdjustmentEvent.TRACK:
System.out.println("Adjustment.TRACK");
break;
}
System.out.println("Wert: " + e.getValue());
}
255
Programmieren in Java
}
public class VsbAL implements AdjustmentListener
{
public void adjustmentValueChanged(AdjustmentEvent e)
{
switch(e.getAdjustmentType())
{
case AdjustmentEvent.UNIT_INCREMENT:
System.out.println("Adjustment.UNIT_INCREMENT");
break;
case AdjustmentEvent.UNIT_DECREMENT:
System.out.println("Adjustment.UNIT_DECREMENT");
break;
case AdjustmentEvent.BLOCK_INCREMENT:
System.out.println("Adjustment.BLOCK_INCREMENT");
break;
case AdjustmentEvent.BLOCK_DECREMENT:
System.out.println("Adjustment.BLOCK_DECREMENT");
break;
case AdjustmentEvent.TRACK:
System.out.println("Adjustment.TRACK");
break;
}
System.out.println("Wert: " + e.getValue());
}
}
public static void main(String args[])
{
PR14175 f = new PR14175();
f.addWindowListener(
new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{ System.exit(0); }
});
f.setBackground(Color.lightGray);
f.setVisible(true);
}
}
5.2.8 ScrollPane
Ein ScrollPane ist ein Container für automatisches horizontales und vertikales
Scrolling. ScrollPane unterscheidet sich durch zwei Eigenschaften von einem
gewöhnlichen Panel:
-
Es kann genau ein Dialogelement aufnehmen und benötigt keinen eigenen Layoutmanager
Es ist in der Lage, eine virtuelle Ausgabefläche zu verwalten, die größer ist als die auf dem
Bildschirm zur Verfügung stehende Ausgabefläche.
Die von einem ScrollPane angezeigte Komponente arbeitet mit einer virtuellen
Ausgabefläche (und merkt nichts von evtl. Größenbeschränkungen auf dem Bildschirm).
Falls die benötigte Ausgabefläche größer ist als die anzeigbare, blendet ein ScrollPane
automatisch die erforderlichen Schieberegister ein, um Dialogelemente horizontal und
vertikal verschieben zu können.
256
Programmieren in Java
Konstruktoren.
public ScrollPane()
public ScrollPane(int scrollbarDisplayPolicy)
//srollbarDisplayPolicy definiert die Strategie zur Anzeige der Schieberegler entsprechend
//den in der folgende Liste aufgelisteten Konstanten:
ScrollPane.SCROLLBARS_AS_NEEDED
ScrollPane.SCROLLBARS_ALWAYS
ScrollPane.SCROLLBARS_NEVER
Die Schieberegler werden genau dann angezeigt,
wenn es erforderlich ist, d.h.: Es wird mehr Platz
benötigt, als für sie zur Anzeige zur Verfügung steht.
Die Schieberegler werden immer angezeigt
Die Schieberegler werden nie angezeigt, der
Bildschirm kann nur vom Programm aus verschoben
werden
5.2.9 Zeichenbereiche
Ein Canvas ist ein frei definiertes Dialogelement, das in der Grundversion praktisch
keine Funktionalität zur Verfügung stellt. Damit ein Canvas etwas Sinnvolles tun kann,
muß daraus eine eigene Klasse abgeleitet werden.
Konstruktor:
public Canvas()
Die Methode paint(Graphics g). Durch Überlagerung von public void
paint(Graphics g) sorgt eine Canvas-Komponente für die Darstellung auf dem
Bildschirm. Die vom System bereitgestellte Version zeichnet nur die Ausgabefläche in
der aktuellen Hintergrundfarbe. Eine überlagerte Version kann hier natürlich ein beliebig
komplexes Darstellungsverhalten erzielen. Der Punkt (0,0) des übergebenen GraphicsObjekts entspricht dabei der linken oberen Ecke des Ausgabebereichs.
Ereignisse. Da die Klasse Canvas aus Component abgeleitet ist, bekommt ein
Canvas-Objekt alle Ereignisse zugestellt, die auch an eine Komponente gehen. Hierzu
zählen Tastatur-, Maus-, Mausbewegungs-, Fokus- und Komponentenereignisse.
Bsp.:
Die von Canvas abgeleitete Klasse ZeichneCanvas
import java.awt.*;
import java.awt.event.*;
public class ZeichneCanvas extends Canvas
{
// Instanzvariable
public boolean zeichneObjekte;
private Font font;
// Konstruktoren
public ZeichneCanvas()
{
this(400,400);
}
public ZeichneCanvas(int breite,int hoehe)
{
super();
setSize(breite,hoehe);
setBackground(Color.white);
bereinigen();
font = new Font("Helvetica",Font.BOLD,24);
257
Programmieren in Java
}
// Instanzmethoden
public void bereinigen()
{
zeichneObjekte = false;
repaint();
}
public void zeichnen()
{
zeichneObjekte = true;
repaint();
}
public void paint(Graphics g)
{
System.out.println(
„Methode paint wurde aufgerufen, zeichneObjekte = „
+ zeichneObjekte);
if (zeichneObjekte)
{
// Individuelle Auspraegung der Zeichnung
int r = 8;
int i, j;
int x, y;
g.setColor(Color.red);
for (i=1; i<=10; ++i)
{
x = 100 – r * i;
y = (int) (40 + (i – 1) * 1.7321 * r);
for (j=1; j<=i; ++j)
{
g.fillOval(x,y,2*r,2*r);
x += 2 * r;
}
}
// g.drawLine(0,0,100,100);
// g.setColor(Color.blue);
// g.drawString("Hallo I2A und I2B",100,100);
// g.setColor(Color.red);
// g.draw3Drect(150,150,50,50,true);
}
}
}
Die Klasse ZeichneGUI benutzt die Klasse ZeichneCanvas. Sie bezieht die
Zeichenfläche in eine grafische Benutzeroberfläche mit Schaltknöpfen und
Textfeldern ein.
import java.awt.*;
import java.awt.event.*;
public class ZeichneGUI extends Frame
implements MouseMotionListener
{
// Instanzvariable
private TextField xFeld;
private TextField yFeld;
private Button zeichne;
private Button bereinige;
private ZeichneCanvas zeichenflaeche;
258
Programmieren in Java
// Konstruktoren
public ZeichneGUI()
{
this(200,200);
}
public ZeichneGUI(int breite,int hoehe)
{
super(); setTitle("Demonstration Zeichenflaeche");
addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{ System.exit(0); }
});
setLayout(new BorderLayout());
Panel panelN = new Panel(); panelN.add(new Label(„x: „));
xFeld = new TextField(5); xFeld.setEditable(false);
panelN.add(xFeld); panelN.add(new Label("y: "));
yFeld = new TextField(5); yFeld.setEditable(false);
panelN.add(yFeld);
add("North",panelN);
zeichenflaeche = new ZeichneCanvas(breite,hoehe);
zeichenflaeche.addMouseMotionListener(this);
add("Center",zeichenflaeche); Panel panelS = new Panel();
zeichne = new Button("Zeichne");
zeichne.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
zeichenflaeche.zeichnen();
}
});
panelS.add(zeichne);
bereinige = new Button("Bereinige");
bereinige.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
zeichenflaeche.bereinigen();
}
});
panelS.add(bereinige); add("South",panelS);
pack();
setVisible(true);
}
// Implementierung des MouseMotionListener-Interface
public void mouseMoved(MouseEvent e)
{
xFeld.setText(" " + e.getX()); yFeld.setText(" " + e.getY());
}
public void mouseDragged(MouseEvent e)
{
xFeld.setText(" " + e.getX()); yFeld.setText(" " + e.getY());
}
// Start der GUI
public static void main(String args[])
{
ZeichneGUI gui = new ZeichneGUI();
}
}
259
Programmieren in Java
5.3 Container
Container sind allgemeine Komponenten, die andere Komponenten oder auch andere
Container enthalten.
5.3.1 Panels
Eine sehr häufig vorkommende Container-Form ist das sog. Panel, das einen
Container darstellt, der am Bildschirm dargestellt werden kann. Ein Panel ist ein
„reiner“ Container, kein eigenes Fenster. Sein einziger Zweck ist es, Komponenten in
einem Fenster anzuordnen.
Erzeugen eines Panels. Das Erzeugen erfolgt mit Hilfe des Konstruktors „Panel()“,
z.B.: Panel meinPanel = new Panel();
Hinzufügen von einem Panel in einen anderen Container. Ein Panel kann bspw. mit
add(meinPanel) in ein Applet eingebaut werden.
Verschachteln von Panels. Panels lassen sich in beliebig vielen Ebenen verschachteln.
Sinn machen solche verschachtelten Panels vor allem in Verbindung mit den
verschiedenen Layouts.
5.3.2 Frames
Ein Frame ist ein voll funktionsfähiges Fenster mit eigenem Titel und Icon. Frames
können Pulldown-Menüs haben und verschieden gestaltete Mauszeiger verwenden.
Erzeugen von Frames. Mit der Frame-Klasse kann ein funktionsfähiges Fenster mit
Menüleiste erstellt werden. Zum Erzeugen von Frames stehen verschiedene
Konstruktoren zur Verfügung:
public Frame()
//Damit kann ein Frame erzeugt werden, der zu Beginn nicht sichtbar ist und keinen Titel hat.
public Frame(String titel)
//Diese Version vergibt einen Titel bei der Erzeugung, das Frame ist zu Beginn nicht sichtbar.
Verändern der Titelzeile. Sie läßt sich in den Klassen Frame und Dialog mit den
Methoden
public void setTitle(String titel)
// ändert die Beschriftung der Titelleiste in titel
public String getTitle()
// Abfrage der Titelleiste
beeinflussen.
Einstellen des Standard-Font. Ein Fenster hat einen Standard-Font, der zur Ausgabe
der Schrift verwendet wird, (wenn nicht im Grafik-Kontext ein anderer Font ausgewählt
wird). Der Standard-Font eines Fensters wird mit „public void setFont (Font
f)“ der Klasse Component eingestellt.
260
Programmieren in Java
Einstellen von Vorder- Und Hintergrundfarbe. Die Vordergrundfarbe dient zur Ausgabe
von Grafik- und Textobjekten, wenn im Grafik-Kontext keine andere Farbe gesetzt wird.
Wird die Einstellung nicht geändert, werden für Vordergrund- und Hintergrundfarbe die
unter Windows eingestellten Standardfarben verwendet. Mit
public void setBackground(Color farbe)
public void setForeground(Color farbe)
können Vordergrund- und Hintergrundfarbe des Fensters geändert werden.
Größe und Position eines Fensters können bestimmt werden über
public void setSize(int breite, int hoehe)
// verändert die Größe des Fensters auf den Wert (breite, hoehe)
public void setSize(Dimension d)
public void setBounds(int x, int y, int breite, int hoehe)
// positioniert ein Fenster der Größe (breite,hoehe) an die Position (x,y)
public void setBounds(Rectangle r)
public void setLocation(int x, int y)
// bewegt die linke obere Ecke an die Bildschirmposition (x,y)
public void setLocation(Point p);
Anzeigen, Wegblenden und Löschen von Frames. Die Konstruktoren haben das Frame
unsichtbar gelassen. Frames müssen vor Gebrauch sichtbar gemacht werden. Der 1.
Schritt besteht darin, einen Frame eine Größe mit der Methode public void
resize(int breite /* Breite vom Frame in Pixel */, int hoehe
/* Hoehe in Pixel */) zu geben. Sichtbar wird der Frame über die show()Methode195. Mit public void hide()196 kann der Frame wieder unsichtbar
gemacht werden. Über public void dispose() wird der Frame beendet. Mit
public boolean isShowing() kann geprüft werden, ob das Fenster bereits
angezeigt ist.
Standardlayout für Fenster: Border-Layout.
Hinzufügen von Komponenten: Rahmen sind Container wie Panels, andere
Komponenten können mit der add()-Methode hinzugefügt werden.
5.3.3 Menüs
Jedes Fenster / Frame kann eine eigene Menüleiste besitzen. Jede Menüleiste kann
mehrere Menüs enthalten und jedes Menü beliebige Einträge. Java unterstützt die
Konstruktion von Menüs durch eine Reihe speziell dafür vorgesehenen Klassen:
Klasse
MenuBar
Menu
MenuItem
CheckboxMenuItem
Bedeutung
Beschreibt die Menüzeile (-leiste)eines Fensters
Beschreibet ein einzelnes, der in der Menüzeile enthaltenen Menüs
Bildet die vom Anwender auswählbaren Einträge innerhalb der Menüs
195 Für show() aus dem Paket java.awt.Component gibt es als neue Variante die Methode
setVisible(boolean)
196 hide() ist in java.awt.Frame als „deprecated“ deklariert. Statt dessen steht setVisible(boolean) zur
Verfügung
261
Programmieren in Java
Die Menüleiste
Sie beschreibt das Hauptmenü eines Fensters. Sie befindet sich unterhalb der
Titelleiste am oberen Rand des Fensters und zeigt die Namen der darin enthaltenen
Menüs an. Eine Menüleiste wird durch Instanzen der Klasse MenuBar erzeugt:
Konstruktor. public MenuBar()
Einfügen von Menüs. public void add(Menu m).
Entfernen von bestehenden Menüs. public void remove(int index)
public void remove(MenuComponent m)
Zugriff auf ein beliebiges Menü. public Menu getMenu(int index).
getMenu liefert das Menüobjekt, das sich an der Position mit dem angegebenen Index
befindet. Zum Binden einer Menüleiste an ein Fenster mit dem angegebenen Index
(index) befindet.
Binden einer Menüleiste an einen Frame. public void setMenuBar(MenuBar
mb) .Durch Aufruf dieser Methode wird die angegebene Menüleiste im Fenster
angezeigt und beim Auswählen des Menüpunkts werden Nachrichen ausgelöst und an
das Fenster gesendet. Die Fensterklasse kann diese Nachrichen durch das
Registrieren eines Objekts vom Typ ActionListener bearbeiten.
Menüs.
Sie bilden die Bestandteile einer Menüleiste und werden durch Instanzen der Klasse
Menu repräsentiert.
Konstruktor. public Menu(String label)
// label gibt den Namen des Menüs an
Standardhilfe-Menü: public void setHelpMenu(Menu m) erzeugt ein
spezielles Standardhilfemenü.
Menüeinträge
Einfache Menüeinträge sind die elementaren Bestandteile eines Menüs. Sie besitzen
einen Text, mit dem sie dem Anwender die dahinterstehende Funktion anzeigen. Wenn
der zugehörige Menüpunkt aufgerufen wird, sendet das Programm eine Nachricht an
das zugehörige Fenster, die dann zum Aufruf der entsprechenden Methode führt.
Erzeugen von Menüeinträgen: public MenuItem(String label). „label“ ist
der Name des Menüeintrags.
Zugriff auf den Namen bzw. Setzen des Namens eines Menüeintrags:
public String getLabel()
public void setLabel(String label)
Neben dem Namen besitzt ein Menüeintrag eine interne Zustandsvariable. Sie zeigt an,
ob der Menüeintrag aktiv ist oder nicht. Nur ein aktiver Eintrag kann vom Anwender
ausgewählt werden und so eine Nachricht auslösen. Nach dem Aufruf des Konstruktors
ist ein Menüeintrag zunächst aktiviert. Er kann durch public
void
setEnabled(boolean b) mit Parameterwert „false“ deaktiviert und mit
Parameterwert true aktiviert werden. Über public boolean isEnabled() kann
der aktuelle Zustand abgefragt werden.
Methoden für die Bearbeitung von Menüeinträgen.
public
public
public
public
void
void
void
void
add(MenuItem m)
add(String label)
remove(int index)
remove(MenuComponent Item)
262
Programmieren in Java
Seperatoren für Menüeinträge. Ein Seperator wird als waagrechter Strich zur Trennung
der Menüeinträge angezeigt:
public void addSeparator()
// fügt den Separator hinter dem zuletzt eingefügten Menüeintrag ein
public void insertSeparator(int index)
// Einfügepostion kann frei angegeben werden.
Zugriff auf Menüeintrag. public MenuItem getItem(int index)
Anzahl der Einträge. public int getItemCount()
Die Klasse CheckboxMenuItem. Sie ist aus MenuItem abgeleitet und besitzt zusätzlich
eine interne Zustandsvariable, die zwischen true und false umgeschaltet werden kann.
Die visuelle Darstellung der Zustandsvariablen erfolgt durch Anfügen oder Entfernen
eines „Häkchens“ neben dem Menüeintrag.
Die Instanzierung eines CkeckboxMenuItem erfogt wie bei einem MenuItem. Zusätzlich
stehen die beiden Methoden setState() und getState() zum Setzen und
Abfragen des Zustands zur Verfügung:
public void setState(boolean status)
public boolean getState()
Menüaktionen: Ein Menü macht nur Sinn, wenn damit eine Aktion ausgelöst werden
kann. Diese Aktion kann wie jede andere Aktion mit der Methode actionPerformed
behandelt werden.
Bsp.: Zusammenstellung der wesentlichen Elemente zur Gestaltung von Menüs
import java.awt.*;
import java.awt.event.*;
public class PR53331 extends Frame
{
// TextField t = new TextField("Keine Anzeige",30);
public PR53331()
{
// Menueleiste erzeugen
MenuBar mb = new MenuBar();
// Menueleiste an Rahmen verankern
setMenuBar(mb);
// Menuepunkt erzeugen
Menu m1 = new Menu("Farben"); // Menuepunkt 1
// Ein Untermenue erzeugen
// Menueeintraege in der Menuezeile plazieren
mb.add(m1);
// Eintraege in Menuepunkt 1
MenuItem rot = new MenuItem("Rot");
m1.add(rot);
rot.addActionListener(new RotL());
MenuItem blau = new MenuItem("Blau");
m1.add(blau);
blau.addActionListener(new BlauL());
MenuItem gruen = new MenuItem("Gruen");
m1.add(gruen);
gruen.addActionListener(new GruenL());
MenuItem gelb = new MenuItem("Gelb");
m1.add(gelb);
gelb.addActionListener(new GelbL());
// Untermenue erzeugen
Menu unterMenue = new Menu("Farbanpassung");
263
Programmieren in Java
// Trennlinie
MenuItem trennLinie = new MenuItem("-");
m1.add(trennLinie);
// Untermenue hinzufuegen
m1.add(unterMenue);
MenuItem farbe = new MenuItem("Farbe");
MenuItem mono = new MenuItem("Mono");
farbe.addActionListener(new FarbeL());
mono.addActionListener(new MonoL());
unterMenue.add(farbe);
unterMenue.add(mono);
// Menue zur Dateibearbeitung
Menu d = new Menu("Datei");
MenuItem[] datei =
{
//
new MenuItem("Open"),
// menu shortcut
new MenuItem("Exit",new MenuShortcut(KeyEvent.VK_E))
};
ML ml = new ML();
datei[0].setActionCommand("Open");
datei[0].addActionListener(ml);
datei[1].setActionCommand("Exit");
datei[1].addActionListener(ml);
for (int i = 0; i < datei.length; i++)
d.add(datei[i]);
mb.add(d);
// CheckboxMenuItem
Menu s = new Menu("Sicherheit");
CheckboxMenuItem[] sicherung;
{
new CheckboxMenuItem("Wache"),
new CheckboxMenuItem("Versteck")
};
CMIL cmil = new CMIL();
sicherung[0].setActionCommand("Wache");
sicherung[0].addItemListener(cmil);
sicherung[1].setActionCommand("Versteck");
sicherung[1].addItemListener(cmil);
for (int i = 0; i < sicherung.length; i++)
s.add(sicherung[i]);
d.add(s);
// Hilfsmenue
Menu meineHilfe = new Menu("Hilfe");
mb.setHelpMenu(meineHilfe);
// Eintraege im Hilfsmenue
meineHilfe.add(new MenuItem("Info"));
meineHilfe.add(new MenuItem("Hilfe"));
// Textfeld initialisieren
// t.setEditable(false);
// add(t,BorderLayout.CENTER);
}
class ML implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
MenuItem ziel
= (MenuItem) e.getSource();
String aktionsKommando = ziel.getActionCommand();
264
Programmieren in Java
if (aktionsKommando.equals("Open"))
{
System.out.println("Oeffnen");
}
else if (aktionsKommando.equals("Exit"))
{
dispatchEvent(new WindowEvent(PR53331.this,
WindowEvent.WINDOW_CLOSING));
}
}
}
class CMIL implements ItemListener
{
public void itemStateChanged(ItemEvent e)
{
CheckboxMenuItem ziel = (CheckboxMenuItem) e.getSource();
String aktionsKommando = ziel.getActionCommand();
if (aktionsKommando.equals("Wache"))
{
System.out.println("Wache " + ziel.getState());
}
else if (aktionsKommando.equals("Versteck"))
{
System.out.println("Versteck " + ziel.getState());
}
}
}
class RotL implements ActionListener
{
public void actionPerformed(ActionEvent e)
{ setBackground(Color.red); }
}
class BlauL implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
setBackground(Color.blue);
}
}
class GruenL implements ActionListener
{
public void actionPerformed(ActionEvent e)
{ setBackground(Color.green); }
}
class GelbL implements ActionListener
{
public void actionPerformed(ActionEvent e)
{ setBackground(Color.yellow); }
}
class FarbeL implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
System.out.println("Im Untermenue wurde Farbe ausgewaehlt");
}
}
class MonoL implements ActionListener
{
public void actionPerformed(ActionEvent e)
265
Programmieren in Java
{
System.out.println("Im Untermenue wurde Mono gewaehlt");
}
}
public static void main(String args[])
{
PR53331 f = new PR53331();
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{ System.exit(0); }
});
f.setSize(300,200); f.setVisible(true);
}
}
5.3.4 Dialoge
Unter einem Dialog versteht man ein Popup-Fenster. Dialoge werden entweder „modal“
oder „non-modal“ erzeugt. „modal“ bedeutet: Das Dialogfeld blockiert andere Fenster,
wenn es angezeigt wird.
Erzeugen. Ein modaler Dialog muß immer von der Klasse Dialog abgeleitet sein.
Dialog bietet 4 Konstruktoren an
public
public
public
public
Dialog(Frame
Dialog(Frame
Dialog(Frame
Dialog(Frame
eltern)
eltern, boolean modal)
eltern, String titel)
eltern, String titel, boolean modal)
Der Parameter modal entscheidet, ob der Dialog modal wird oder nicht.
Zugriff auf modale bzw. „nicht modale“ Eigenschaft. Dafür gibt es die Methoden
public boolean isModal()
// Rückgabewert ist true, falls der Dialog modal ist. Anderenfalls ist er
// false.
public void setModal(boolean b)
Unterbinden der Veränderbarkeit der Größe eines Fensters. Das kann über
public void setResizable(boolean resizable)
public boolean isResizable()
geschehen bzw. überprüft werden.
266
Programmieren in Java
5.4 Die Layout-Manager
Das AWT-System kümmert sich weitgehend selbstständig um Größenanpassung und
Postionierung von Komponenten auf der Oberfläche. Je nach Plattform und
Bedingungen werden Komponenten in die Oberfläche optimal angepaßt. Über LayoutManager kann das AWT angewiesen werden, wo Komponenten im Verhältnis zu
anderen Komponenten stehen sollen. Der Layout-Manager bestimmt nach gewissen
Regeln, an welche Stelle die Komponenten am besten passen und ermittelt die
optimale Größe der Komponenten.
5.4.1 Layout-Regeln
Drei Aspekte bestimmen das Aussehen einer AWT-Oberfläche:
1.
Die Plattform und die Bedingungen, die unter dieser Plattform vorliegen. Java
ist plattformunabhängig, daher kann der Programmierer hier keine Aussagen
machen.
Die Reihenfolge, in der die AWT-Komponenten in einen Container eingefügt
werden.
Die Art des Layout-Managers. Das AWT umfaßt 5 verschiedene Typen von
Layout-Managern, die die Oberfläche unterschiedlich aufgliedern:
2.
3.
-
Flow-Layout
Grid-Layout
Border-Layout
Card-Layout
GridBag-Layout
Jedes Panel kann einen eigenen Layout-Manager verwenden.
Erstellen eines Layout-Managers. Zum Erstellen eines Layout-Managers für einen Panel
kann
(
in
der
init()-Methode)
die
Methode
public
void
setLayout(LayoutManager mgr) verwendet werden, z.B.: setLayout(new
FlowLayout());
267
Programmieren in Java
5.4.2 Die einzelnen Layout-Manager
Die FlowLayout-Klasse
<< interface >>
LayoutManager
FlowLayout
public static int CENTER
public static int LEADING
public static int LEFT
public static int RIGHT
public static int TRAILING
<< Konstruktoren >>
public FlowLayout()
public FlowLayout(int align)
public FlowLayout(int align, int hgap, int vgap)
//erzeugt ein FlowLayout mit angegebenen Alignment und einem
//horizontalen bzw. vertikalen Rand
<< Methoden >>
public void addLayoutComponent(String name, Component comp)
public int getAlignment()
//liefert das Alignment des FlowLayout-Managers
public void setAlignment(int align)
//setzt das Alignment mit Hilfe der Konstanten
//FlowLayout.RIGHT, FlowLayout.LEFT, FlowLayout.CENTER
...
Abb. Die Klasse FlowLayout
FlowLayout ordnet Komponenten von links nach rechts an, bis keine weiteren
Komponenten mehr in die Zeile passen. Dann geht es zur nächsten Zeile und bewegt
sich wieder von links nach rechts. Die Standard-.Ausrichtung für ein FlowLayout ist
zentriert.
Der FlowLayout-Manager ist der Standard-Layout-Manager für alle Applets.
Bsp.: Plazierung von Schaltflächen.
Die Komponenten werden in der Reihenfolge, in der sie in den Container eingefügt
werden, spaltenweise von links nach rechts eingeordnet, bis keine weiteren
Komponenten mehr in eine Zeile passen. So führt der folgende Quellcode
import java.awt.*;
public class FlowLayoutTestApplet extends java.applet.Applet
{
public void init()
{
setBackground(Color.yellow);
setLayout(new FlowLayout());
Button meinKnopf1 = new Button("Rot");
add(meinKnopf1);
Button meinKnopf2 = new Button("Blau");
268
Programmieren in Java
add(meinKnopf2);
Button meinKnopf3
add(meinKnopf3);
Button meinKnopf4
add(meinKnopf4);
Button meinKnopf5
add(meinKnopf5);
Button meinKnopf6
add(meinKnopf6);
Button meinKnopf7
add(meinKnopf7);
= new Button("Gruen");
= new Button("Pink");
= new Button("Rosa");
= new Button("Gelb");
= new Button("Cyan");
}
}
mit den im Applet-Tag angegebenen Abmessungen
<HTML>
<HEAD>
<TITLE>FlowLayout-Test</TITLE>
</HEAD>
<BODY>
<APPLET CODE="FlowLayoutTestApplet.class" WIDTH=100 HEIGHT=120>
</APPLET>
</BODY>
</HTML>
zur folgenden Darstellung
Eine Veränderung in der Größe des Applet macht den zeilenweisen Aufbau deutlich:
<HTML>
<HEAD>
<TITLE>FlowLayout-Test</TITLE>
</HEAD>
<BODY>
<APPLET CODE="FlowLayoutTestApplet.class" WIDTH=300 HEIGHT=100>
</APPLET>
</BODY>
</HTML>
269
Programmieren in Java
Die BorderLayout-Klasse
<< interface >>
LayoutManager
BorderLayout
public static String CENTER
public static String NORTH
public static String EAST
public static String SOUTH
public static String WEST
<< Konstruktoren >>
public BorderLayout()
public BorderLayout(int hgap, int vgap)
<< Methoden >>
public void addLayoutComponent(Component comp, Object constraints)
public void layoutContainer(Container target)
public Dimension preferredLayoutSize(Container target)
public void removeLayoutComponent(Component comp)
...
Abb. Die Klasse BorderLayout
Die BorderLayout-Klasse unterteilt einen Container in fünf Bereiche: North, South, East,
West, Center. Falls einem Container Komponenten hinzugefügt werden, geschieht das
über ein spezielles Exemplar der add()-Methode ( mit einem dieser fünf
Bereichsnamen).
Bsp.: BorderLayout-Applet, das einem Applet einige Schaltfläche hinzufügt.
Der Quellcode ist
import java.awt.*;
import java.applet.*;
public class BorderLayoutTestApplet extends Applet
{
public void init()
{
int i = 0;
setBackground(Color.yellow);
setLayout(new BorderLayout());
270
Programmieren in Java
add("North",new Button("Schaltflaeche " + i++));
add("South",new Button("Schaltflaeche " + i++));
add("East", new Button("Schaltflaeche "
+ i++));
add("West", new Button("Schaltflaeche "
+ i++));
add("Center",new Button("Schaltflaeche " + i++));
}
}
Für das Applet wurde folgende Größe vereinbart:
<HTML>
<HEAD>
<TITLE>FlowLayout-Test</TITLE>
</HEAD>
<BODY>
<APPLET CODE="BorderLayoutTestApplet.class" WIDTH=500 HEIGHT=100>
</APPLET>
</BODY>
</HTML>
Das führt zu dem folgenden Layout:
Die GridLayout-Klasse
Diese Klasse teilt den Container in ein Gitter mit gleich großen Zellen ein. Hinzugefügte
Komponenten werden von links nach rechts angeordnet, begonnen wird bei den linken,
oberen Zellen.
Bsp.: Plazieren von Schaltflächen
Der folgende Quellcode
import java.awt.*;
import java.applet.*;
public class GridLayoutTestAppl extends Applet
{
public void init()
{
setLayout(new GridLayout(7,3));
for (int i = 0; i < 20; i++)
add(new Button("Schaltflaeche " + i));
}
}
führt über die folgende Größe im Applet-Tag
<HTML>
<HEAD>
<TITLE>FlowLayout-Test</TITLE>
</HEAD>
271
Programmieren in Java
<BODY>
<APPLET CODE="GridLayoutTestAppl.class" WIDTH=300 HEIGHT=200>
</APPLET>
</BODY>
</HTML>
zu der folgenden Darstellung
Die GridBagLayout-Klasse und die GridBagConstraint-Klasse
Die Klasse GridBagLayout ermöglicht die Anordnung der Komponenten in einem
rasterähnlichen Layout. Zusätzlich zum GridLayout kann kontrolliert werden: Die
Werte einzelner Zellen im Raster, die Proportionen zwischen Zeilen und Spalten sowie
die Anordnung von Komponenten innerhalb der Zellen im Raster. Zur Erstellung eines
GridLayout dienen zwei Klassen:
- GridBagLayout, die den Layoutmanager bereitstellt
- GridBagConstraints, die die Eigenschaften jeder Komponente im Raster bestimmt.
Die GridBagLayout-Klasse ermöglicht einer Komponente auch die Belegung von
mehr als einer Zelle. Der gesamte Bereich, den die Komponente einnimmt, wird
„display area“ genannt. Bevor einem Container eine Komponente hinzugefügt wird,
müssen GridBagLayout Vorschläge unterbreitet werden, wo die Komponente
hingestellt werden soll. Die GridBagConstraint-Klasse besitzt verschiedene
Variable zur Steuerung der Anordnung der Komponenten:
-
-
-
die Variablen gridx und gridy (Koordinaten der Zelle in der die nächste Komponente plaziert
werden soll). Die obere linke Ecke von GridBagLayout liegt bei (0,0), der Standardwert ist
GridBagConstraints.RELATIVE. Für gridx ist das die erste rechte Zelle neben der letzten
hinzugefügten Komponente. Für gridy ist es genau die Zelle, die unterhalb der letzten Komponente
hinzugefügt wurde.
die Variablen gridwidth und gridheight bestimmen, wie viele Zellen hoch und breit eine
Komponente sein sollte (Standardwert ist 1). Der Wert GridBagConstraints.REMAINDER für
gridwidth bestimmt, daß eine Komponente die letzte in der Spalte sein sollte.
Die Variable fill gibt an, welche Dimension einer Komponente sich verändern soll, wenn eine
Komponente kleiner als der Anzeigebereich ist. Gültige Werte sind: NONE, BOTH, HORIZONTAL und
272
Programmieren in Java
-
-
-
VERTICAL. „GridBagConstraints.BOTH“ sorgt für eine Streckung der Komponenten in beiden
Richtungen so, daß sie den Anzeigebereich voll ausfüllen.
anchor wird eingesetzt, wenn die Komponente kleiner als ihr Anzeigebereich ist. Sie zeigt an, wo
die Komponente innerhalb des Anzeigebereichs plaziert werden soll. Der Standardwert ist
„GridBagConstraints.CENTER“. Er gibt an, welche Komponente im „Zentrum des
Anzeigebereichs“
stehen
soll.
Die
anderen
Komponenten
sind
Kompasspunkte:
GridBagConstraints.NORTH, GridBagConstraints.NORTHEAST etc.
Die Variable weightx und weighty definieren die Gewichtung für freien Raum innerhalb eines
Containers und bestimmen die relative Größe der Komponenten. Ein Komponente mit einem Wert
von 2.0 für weightx nimmt doppelt soviel Platz in horizontaler Richtung wie eine Komponente mit
dem Wert 1.0 für weightx.
Durch „insets“ (Objekte der Klasse Insets)197 wird der freizuhaltende Rand zwischen Komponente
und Gitter festgelegt. Die Standardwerte für alle 4 Seiten sind Null.
Die Elemente können zum Teil auch vergrößert werden. Die Pixel, die rechts und links zuzufügen
sind, gibt ipadx an. Wieviel oben und unten hinzukommt, speichert ipady. (ipadx, ipady sind
standardmäßig 0).
gridwidth
insets
ipady
fill
gridheight
ipadx
Bsp.198: Plazieren von Schaltflächen
import java.awt.*;
import java.util.*;
import java.applet.Applet;
public class GridBagBsp extends Applet
{
protected void macheSchalter(String name,
GridBagLayout gridbag,
GridBagConstraints c)
{
Button schalter = new Button(name);
gridbag.setConstraints(schalter, c);
add(schalter);
}
public void init()
{
GridBagLayout gridbag = new GridBagLayout();
GridBagConstraints c = new GridBagConstraints();
setFont(new Font("Helvetica", Font.PLAIN, 14));
setLayout(gridbag);
c.fill = GridBagConstraints.BOTH;
c.weightx = 1.0;
macheSchalter("Schaltflaeche1", gridbag, c);
197 Beim Erstellen eines neuen Layout-Managers erzeugte horizontale und vertikale Abstände dienen zur
Bestimmung des Platzes zwischen Komponenten in einem Panel. Die Klasse Insets bietet Eckeinsetzwerte für
oben, unten, links und rechts an, die dann verwendet werden, wenn das Panel gezeichnet wird.
198 vgl. pr54205
273
Programmieren in Java
macheSchalter("Schaltflaeche2", gridbag, c);
macheSchalter("Schaltflaeche3", gridbag, c);
// letzter Schalter in der Zeile:
c.gridwidth = GridBagConstraints.REMAINDER;
macheSchalter("Schaltflaeche4", gridbag, c);
// neue Zeile, weightx wiederherstellen:
c.weightx = 0.0;
macheSchalter("Schaltflaeche5", gridbag, c);
// vorletztes Element der Zeile:
c.gridwidth = GridBagConstraints.RELATIVE;
macheSchalter("Schaltflaeche6", gridbag, c);
// letztes Element:
c.gridwidth = GridBagConstraints.REMAINDER;
macheSchalter("Schaltflaeche7", gridbag, c);
// neue Zeile, Wert wiederherstellen:
c.gridwidth = 1;
c.gridheight = 2;
c.weighty = 1.0;
macheSchalter("Schaltflaeche8", gridbag, c);
c.weighty = 0.0;
c.gridwidth = GridBagConstraints.REMAINDER;
c.gridheight = 1;
macheSchalter("Schaltflaeche9", gridbag, c);
macheSchalter("Schaltflaeche10", gridbag, c);
resize(300, 100);
}
public static void main(String args[])
{
Frame f = new Frame("GridBag Layout Beispiel");
GridBagBsp bsp = new GridBagBsp();
bsp.init();
f.add("Center", bsp);
f.pack();
f.resize(f.preferredSize());
f.show();
}
Die CardLayout-Klasse
CardLayout199 plaziert die Elemente auf sich gegenseitig verdeckende Karten. Jeweils
eine dieser Karten kann sichtbar gemacht werden.
199 Das CardLayout ist als Notizbuchersatz durch die Einführung der Klasse JTabbedPane in Swing fast
überflüssig geworden
274
Programmieren in Java
Das Null-Layout
Ein Aufruf der Methode „setLayout“ mit dem Argument null erzeugt ein NullLayout. In diesem Fall verwendet das Fenster keinen Layoutmanager, sondern
überläßt die Positionierung der Komponenten der Anwendung. Je Komponente sind
drei Schritte auszuführen
Erzeugen der Komponente
Festlegung von Größe und Position der Komponente
Übergabe der Komponente an das Fenster.
-
Größe und Position des Dialogelements können mit
public void setBounds(int x, int y, int breite, int hoehe)
festgelegt werden. Der Punkt
(breite,hoehe) die Größe.
(x,y)
bestimmt
die
Bsp.:
import java.awt.*;
import java.awt.event.*;
public class NL extends Frame
{
public static void main(String args[])
{
NL f = new NL();
f.setVisible(true);
}
public NL()
{
addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
setVisible(false);
dispose();
System.exit(0);
}
});
setSize(300,250);
setLayout(null);
for (int i = 0; i < 5; i++)
{
Button schalter = new Button("Schalter" + (i+1));
schalter.setBounds(10+i*35,40+i*35,100,30);
add(schalter);
}
}
}
275
Anfangsposition
und
Programmieren in Java
5.5 Die Event-Modelle 1.0 und 1.1
5.5.1 Der AWT-Handler 1.0
Die Methode handleEvent
Die handleEvent()-Methode200 ist unter dem AWT-Event-Handler die allgemeinste
Art, wie das AWT auf irgendwelche Ereignisse eingeht, die auf der Benutzeroberfläche
stattfinden. Ereignisse werden innerhalb der handleEvent()-Methode interpretiert
und dann gezielt passende Methoden aufgerufen. Damit die handleEvent()Funktion nicht zu groß und unübersichtlich wird, ruft handleEvent() mehrere
Hilfsfunktionen (mouseEnter, keydown, action, ... ) auf, die die jeweils
zugeordneten Events bearbeiten. Die Verarbeitung von Ereignissen stützt sich auf die
Klasse Event. Die Instanzvariable „id“ der Klasse Event enthält die Art des Ereignisses
und liefert alle notwendigen Informationen. Das Attribut „target“ enthält die
Information, welches Objekt das Ereignis ausgelöst hat.
Bsp.: Darstellung einer Datei im Textbereich eines Fensters (Frame)
import java.awt.*;
import java.io.*;
public class DateiDarsteller extends Frame
{
// Instanzvariable
String dateiName;
// Konstruktoren
public DateiDarsteller() throws IOException
{
super("Dateidarsteller ");
MenuBar menue = new MenuBar();
Menu m = new Menu("Datei");
m.add(new MenuItem("Oeffnen"));
m.add(new MenuItem("Schliessen"));
menue.add(m);
// Installiere diesen Menuebalken
setMenuBar(menue);
this.show();
}
// Methoden
public void dateiDarstellen() throws IOException
{
try {
File d = new File(dateiName);
int groesse = (int) d.length();
int anzZeich = 0;
FileInputStream ein = new FileInputStream(d);
byte[] daten = new byte[groesse];
while (anzZeich < groesse)
anzZeich += ein.read(daten,anzZeich,groesse);
String s = new String(daten,0);
200 Die Methode ist in der Klasse Component definiert, von der die Klasse java.applet.Applet
abgeleitet ist. Dadurch steht die Methode allen Applets zur Verfügung,
276
Programmieren in Java
TextArea textBereich = new TextArea(s,24,80);
textBereich.setFont(new Font("Helvetiva",Font.PLAIN,10));
textBereich.setEditable(false);
this.add("Center",textBereich);
}
catch (IOException e) { }
this.show();
}
public boolean handleEvent(Event e)
{
if ((e.id == Event.ACTION_EVENT) && (e.target instanceof
MenuItem))
{
if (((MenuItem) e.target).getLabel().equals("Oeffnen"))
{
FileDialog fd = new FileDialog(this,"Dateidialog");
fd.show();
dateiName = fd.getFile();
System.out.println("Dateiname: " + dateiName);
try { this.dateiDarstellen(); }
catch (IOException a ) { }
return true;
}
if (((MenuItem) e.target).getLabel().equals("Schliessen"))
{
this.hide();
this.dispose();
System.exit(0);
return true; }
}
return false;
}
public static void main(String argv[]) throws IOException
{
try {
DateiDarsteller DD = new DateiDarsteller();
}
catch (IOException e) { System.out.println(e); }
}
}
Behandlung von Aktionsereignissen
Aktionsereignisse werden über public boolean action(Event e, Object
arg) behandelt. Zuerst muß innerhalb der methode überprüft werden, welche
Komponente der Benutzeroberfläche die Aktion erzeugt hat. Zur vereinfachten
Bearbeitung beinhaltet das Event-Objekt, das beim Aufruf von action() erhalten
wird, eine "target"-Instanzvariable, die eine Referenz zu dem Objekt, das das
Ereignis aufgenommen hat, enthält. Mit dem instanceof-Operator kann dann
bestimmt werden, von welcher Komponente das Ereignis ausging. Das zweite
Argument dient zum Bestimmen des Labels 201, der Elemente, des Inhalts der
201 vgl. pr52105
277
Programmieren in Java
Komponenten202. Anstatt des zusätzlichen Arguments kann auch die Methode203
getLabel verwendet werden.
Behandlung von Fokus-Ereignissen
Für die Ereignisse "Fokuserhalt" und "Fokusverlust" können die Methoden
public boolean getFocus(Enet evt, Object arg)
bzw.
public boolean lostFocus(Event evt, Object arg)
verwendet werden.
Ereignisse von Listenfeldern
Listenfelder erzeugen drei verschiedene Ereignisarten: Auswahl bzw. Abwahl eines
Eintrags in der Liste bzw. Doppelklick auf einen Eintrag. Ein Doppelklick auf einen
Eintrag kann mit der Methode action bearbeitet werden. Auswahl und Abwahl eines
Listeneintrags kann mit "handleEvent" und Überprüfen auf die Ereignisse mit
LIST_SELECT und LIST_DESELECT erfolgen.
5.5.2 Das Event-Handling in Java 1.1 bzw. Java 1.2
Im neuen Ereignismodell erben die Ereignisklassen von der
java.util.EventObject.
AWT-Events
erben
von
der
java.awt.AWTEvent.
Klasse
Klasse
EventListener
Falls eine Klasse im Ereignisbehandlungsmodell von Java 1.2 aus ein Ereignis
reagieren will, muß diese eine Schnittstelle implementieren, die dieses Ereignis
verarbeitet. Diese Schnittstellen werden als EventListener bezeichnet. Jeder
Listener behandelt eine bestimmte Ereignisart. Eine Klasse kann so viele
Listener implementieren, wie benötigt werden. Die folgenden EventListener
stehen zur Verfügung:
ActionListener
AdjustmentListener
Aktionsereignisse, die durch den Benutzer ausgelöst werden, z.B.
Klick auf eine Schaltfläche
Ereignisse, die erzeugt werden, wenn werte einer Komponente
eingestellt werden (z.B. Bewegung eines Schiebers einer
Bildlaufleiste)
202 Nicht vergessen: Das Argument in den richtigen Objekt-Typ zu casten
203 vgl. pr54305
278
Programmieren in Java
FocusListener
ItemListener
KeyListener
MouseListener
MouseMotionListener
WindowListener
Ereignisse, die erzeugt werden, wenn eine Komponente, z.B. ein
textfeld, den eingabefokus erhält oder verliert.
Ereignisse, die erzeugt werden, wenn ein Element, z.B. ein
Kontrollkästchen, verändert wurde
Tastaturereignisse, die bei Tastatureingaben erzeugt werden.
Mausereignisse, die erzeugt werden, wenn mit der maus geklickt wird,
die Maus in den Bereich einer Komponente eintritt bzw. diese wieder
verläßt
Mausereignisse, die die Bewegung einer Maus über eine Komponente
verfolgen
Ereignisse, die von Fenstern erzeugt werden
Das Paket java.awt.event beinhaltet alle elementaren EventListener. Über
import java.awt.event.* erfolgt das Importieren in die Anwendungen.
Event-Handling mit Hilfe lokaler Klasse
Für die Ereignisbehandlung werden lokale Klassen herangezogen. Im JDK 1.0 wurden
Klassen nur auf Paketen definiert, eine Schachtelung war nicht möglich. Im JDK 1.1
kann innerhalb bestehender Klassen eine neue Klasse definiert werden (Konzept der
„Inner Classes“), die nur innerhalb der bestehenden Klasse sichtbar ist. Objektinstanzen
der inneren Klasse können nur aus der umfassenden Klasse heraus erzeugt werden.
Allerdings kann die innere Klasse auf die Instanzvariablen der äußeren Klasse
zugreifen.
Mit Hilfe lokaler Klassen werden die benötigten EventListener implementiert. Dazu wird
in dem GUI-Objekt, das einen Event-Handler benötigt, eine lokale Klasse definiert (und
aus einer passenden AdapterKlasse abgeleitet).
Ein einführendes Bsp.: Event-Handling nach der Bearbeitung von Schaltflächen des
AWT in Version 1.0 und 1.1
// Einfangen von Klicks auf Schaltflaechen
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
public class
{
Button sch1
Button sch2
public void
{
SchalterAppl extends Applet
= new Button("Schaltflaeche 1");
= new Button("Schaltflaeche 2");
init()
sch1.addActionListener(new Sch1());
sch2.addActionListener(new Sch2());
add(sch1);
add(sch2);
}
class Sch1 implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
getAppletContext().showStatus("Schaltflaeche 1");
}
279
Programmieren in Java
}
class Sch2 implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
getAppletContext().showStatus("Schaltflaeche 2");
}
}
/*
public boolean action(Event e, Object welcheAktion)
{
if (e.target.equals(sch1))
getAppletContext().showStatus("Schaltflaeche 1");
else if (e.target.equals(sch2))
getAppletContext().showStatus("Schaltflaeche 2");
else
return super.action(e,welcheAktion);
return true;
}
*/
}
Einfügen / Entfernen passender Listener
Das neue Event-Modell von Java 1.1 bzw. 1.2 hat mit dem JDK 1.0 offensichtlich nur
noch wenig gemeinsam. Alle AWT-Komponenten haben im neuen AWT-Event-Handling
„addXXXListener()“- und „removeXXXListener()“-Methoden erhalten. „XXX“
beschreibt den Typ des Events. Die folgende Tabelle zeigt Ereignisse, „Listeners“,
Methoden
und
Komponenten,
die
über
addXXXListener()
bzw.
removeXXXListener() auf spezifische Ereignisse reagieren:
Event, „listener interface“ und „add“- bzw. „remove“- Komponenten, die dieses Ereignis unterstützen
Methoden
ActionEvent
Button, List, TextField, MenuItem und seine
ActionListener
Abkömmlinge einschl. CheckboxMenuItem, Menu
addActionListener()
und PopupMenu
removeActionListener()
AdjustmentEvent
Scrollbar,
AdjustmentListener
alles was mit der Erzeugung vom „Adjustable“addAdjustmentListener()
Interface zu tun hat
removeAdjustmentListener()
ComponentEvent
Component und seine Abkömmlinge, einschl.
ComponentListener
Button, Canvas, Checkbox, Choice, Container,
addComponentListener()
Panel, Applet, ScrollPane, Window, Dialog,
removeComponentListener()
FileDialog, Frame, Label, List, Scrollbar, TextArea,
TextField
ContainerEvent
Container und seine Abkömmlinge, einschl. Panel,
ContainerListener
Applet, ScrollPane, Window, Dialog, FileDialog,
addContainerListener()
Frame
removeContainerListener()
FocusEvent
Component und seine Abkömmlinge einschl.
FocusListener
Button, Canvas, Container, Panel, Applet,
addFocusListener()
ScrollPane, Window, Dialog, FileDialog, Frame,
removeFocusListener()
Label, List, Scrollbar, TextArea, TextField
280
Programmieren in Java
KeyEvent
KeyListener
addKeyListener()
removeKeyListener()
MouseEvent
MouseListener
addMouseListener()
removeMouseListener()
MouseEvent
MouseMotionListener
addMouseMotionListener()
removeMouseMotionListener()
WindowEvent
WindowListener
addWindowListener()
removeWindowListener()
ItemEvent
ItemListener
addItemListener()
removeItemListener()
TextEvent
TextListener
addTextListener()
removeTextListener()
Component und seine Abkömmlinge einschl.
Button, Canvas, Checkbox, Choice, Container,
Panel, Applet, ScrollPane, Window, Dialog,
FileDialog, Frame, Label, List, Scrollbar, TextArea,
TextField
Component und seine Abkömmlinge einschl.
Button, Canvas, Checkbox, Choice, Container,
Panel, Applet, ScrollPane, Window, Dialog,
FileDialog, Frame, Label, List, Scrollbar, TextArea,
TextField
Component und seine Abkömmlinge einschl.
Button, Canvas, Checkbox, Choice, Container,
Panel, Applet, ScrollPane, Window, Dialog,
FileDialog, Frame, Label, List, Scrollbar, TextArea,
TextField
Window und seine Abkömmlinge einschl. Dialog,
FileDialog und Frame
Checkbox, CkeckboxMenuItem, Choice, List und
alles,
was
das
ItemSelectable
Interface
implementiert
Alles was von TextComponent einschl. TextArea
und TextField abgeleitet ist
Abb.: Event- und Listener-Typen
Jeder Komponenten-Typ unterstützt nur bestimmte Ereignis-Typen:
Komponenten-Typ
Adjustable
Applet
Button
Canvas
Checkbox
CheckboxMenuItem
Choice
Component
Container
Dialog
FileDialog
Frame
Label
Ereignis, das durch die Komponente unterstützt
wird
AdjustmentEvent
ContainerEvent,
FocusEvent,
KeyEvent,
MouseEvent, ComponentEvent
ActionEvent,
FocusEvent,
KeyEvent,
MouseEvent,
ComponentEvent
ItemEvent, FocusEvent, KeyEvent, MouseEvent,
ComponentEvent
ActionEvent, ItemEvent
ItemEvent, FocusEvent, KeyEvent, MouseEvent,
ComponentEvent
FocusEvent,
KeyEvent,
MouseEvent,
ComponentEvent
ContainerEvent,
FocusEvent,
KeyEvent,
MouseEvent, ComponentEvent
ContainerEvent,
WindowEvent,
FocusEvent,
KeyEvent, MouseEvent, ComponentEvent
ContainerEvent,
WindowEvent,
FocusEvent,
KeyEvent, MouseEvent, ComponentEvent
ContainerEvent,
WindowEvent,
FocusEvent,
KeyEvent, MouseEvent, ComponentEvent
FocusEvent,
KeyEvent,
MouseEvent,
ComponentEvent
281
Programmieren in Java
List
Menu
MenuItem
Panel
PopupMenu
Scrollbar
ScrollPane
TextArea
TextComponent
TextField
Window
ActionEvent, FocusEvent, KeyEvent, MouseEvent,
ItemEvent, ComponentEvent
ActionEvent
ActionEvent
ContainerEvent,
FocusEvent,
KeyEvent,
MouseEvent, ComponentEvent
ActionEvent
AdjustmentEvent,
FocusEvent,
KeyEvent,
MouseEvent, ComponentEvent
ContainerEvent,
FocusEvent,
KeyEvent,
MouseEvent, ComponentEvent
TextEvent, FocusEvent, KeyEvent, MouseEvent,
ComponentEvent
TextEvent, FocusEvent, KeyEvent, MouseEvent,
ComponentEvent
ActionEvent, TextEvent, FocusEvent, KeyEvent,
MouseEvent, ComponentEvent
ContainerEvent,
WindowEvent,
FocusEvent,
KeyEvent, MouseEvent, ComponentEvent
Abb.: Komponenten-Typ und Ereignisse
Methoden für die Ereignisbehandlung
Da nun bekannt ist, welche Ereignisse eine bestimmte Komponente unterstützt, kann
das zugehörige „Listener“-Interface angegeben werden:
„Listener“-Interface
ActionListener
AdjustmentListener
ComponentListener
ComponentAdapter
ContainerListener
ContainerAdapter
FocusListener
FocusAdapter
KeyListener
KeyAdapter
MouseListener
MouseAdapter
MouseMotionListener
MouseMotionAdapter
WindowListener
Windowadapter
Methoden
actionPerformed(ActionEvent)
adjustmentValueChanged(
AdjustmentEvent)
componentHidden(ComponentEvent)
componentShown(ComponentEvent)
componentMoved(ComponentEvent)
componentResized(ComponentEvent)
componentAdded(ContainerEvent)
componentRemoved(ContainerEvent)
focusGained(FocusEvent)
focusLost(FocusEvent)
keyPressed(KeyEvent)
keyReleased(KeyEvent)
keyTyped(KeyEvent)
mouseClicked(MouseEvent)
mouseEntered(MouseEvent)
mouseExited(MouseEvent)
mousePressed(MouseEvent)
mouseReleased(MouseEvent)
mouseDragged(MouseEvent)
mouseMoved(MouseEvent)
windowOpened(WindowEvent)
windowClosing(WindowEvent)
windowClosed(WindosEvent)
windowActivated(WindowEvent)
windowDeactivated(WindowEvent)
windowIconified(WindowEvent)
windowDeiconified(WindowEvent)
282
Programmieren in Java
ItemListener
TextListener
itemStateChanged(ItemEvent)
textValueChanged(TextEvent)
Abb.: Listener-Interface
Adapter besorgen Default-Methoden für jede Methode im Interface. Nur die Methode,
die geändert wird, muß überschrieben werden.
Komponenten des AWT in Fenstern bzw. Applets unter Java 1.1
Die in den folgenden Programmen verwendeten Komponenten können in einem Fenster
(Frame) über die Instanz eines Applet dargestellt werden. Das Programm enthält
deshalb zusätzlich zu den für Applets nötigen Methoden eine main()-Methode, die eine
Instanz eines Applets innerhalb eines Frame aufbaut.
Bsp.: Bearbeitung von zwei Schaltflächen und einem Textfeld 204
// Eine Anwendung und ein Applet
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
public class SchalterApplet extends Applet
{
Button sch1 = new Button("Schaltflaeche 1");
Button sch2 = new Button("Schaltflaeche 2");
TextField t = new TextField(20);
public void init()
{
sch1.addActionListener(new Sch1());
sch2.addActionListener(new Sch2());
add(sch1);
add(sch2);
add(t);
}
class Sch1 implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
t.setText("Schaltflaeche 1");
}
}
class Sch2 implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
t.setText("Schaltflaeche 2");
}
}
// Schliessen der Applikation
static class WL extends WindowAdapter
{
public void windowClosing(WindowEvent e)
204 pr55202
283
Programmieren in Java
{ System.exit(0); }
}
// main()-Methode fuer die Applikation
public static void main(String[] args)
{
SchalterApplet applet = new SchalterApplet();
Frame einRahmen = new Frame("Schalterdarstellung");
einRahmen.addWindowListener(new WL());
einRahmen.add(applet,BorderLayout.CENTER);
einRahmen.setSize(300,200);
applet.init();
applet.start();
einRahmen.setVisible(true);
}
}
284
Programmieren in Java
5.6 Benutzerschnittstellen mit Swing
Die Oberflächentechnik „Swing“ (Paket: javax.swing) von Java 2 umfaßt eine
Vielzahl mächtiger Klassen (, z.B. Tabellen, Baumansichten). Swing benutzt den ModelView-Controller205-Ansatz, d.h. die konsequente Trennung der Daten und er Darstellung.
Swing-Elemente fallen in zwei Gruppen:
- Fenster, die direkt vom Betriebssystem dargestellt werden, z.B. JFrame, JDialog oder JApplet.
Diese sind von den jeweiligen AWT-Komponenten abgeleitet.
- Kontrollelemente, die von Java selbst gerendert werden. Diese sind Subklassen von JComponent.
javax.swing, javax.swing.event, javax.swing.table, javax.swing.tree, javax.swing.text
Interfaces
Klassen
- ButtonModel
EreignisKontrollFenster
- ComboBoxModel
behandlung
Elemente
- ListModel
- TreeModel
- ListDataEvent
- JComponent
- JFrame
- TableModel
- ListSelectionEvent
- JButton
- JDialog
- TableColumnModel
- TableModelEvent
- JCheckBox
- JPanel
- BoundedRangeModel - TreeModelEvent
- JRadioButton
- JApplet
- Renderer
- TreeExpansionEvent - JLabel
- JFileChooser
- ListCellRenderer
- TreeSelectionEvent - JTextField
- JColorChooser
- TableCellRenderer
- CaretEvent
- JPasswordField - JDesktopPane
- TableCellEditor
- MenuEvent
- JTextArea
- JInternalFrame
- TreeCellRenderer
- PopupMenuEvent
- JComboBox
- JOptionPane
- TreeCellEditor
- JList
- JScrollPane
- ComboBoxEditor
- JScrollBar
- JTabbedPane
- TreeNode
- JSlider
- MutableTreeNode
- JProgressBar
- Border
- JTree
- Scrollable
- JTable
- MenuElement
- Document
- Caret
ModelBorder
Bilder
- Style
implementierung
- StyledDocument
- DesktopManager
- AbstractListModel
- BorderFactory - ......
- Icon
- DefaultListModel
- TitledBorder
- SwingConstants
- AbstractTableModel - LineBorder
- *Listener
- ...
- ...
Menüs
- JMenuBar
- JMenuItem
- JCheckboxMenuItem
- JRadioBoxMenuItem
- JMenu
-JPopupMenu
- JToolBar
- JSeparator
Sonstiges
- ImageIcon
- ButtonGroup
- KeyStroke
- ...
205 Beim Model-View-Controller-Entwurfsmuster (MVC) unterscheidet man drei Arten von
Applikationselementen:
Model (Datenmodell)
Im Datenmodell werden Zustand und Funktionalität gespeichert. Es kann Anfragen vom View über seinen
Zustand beantworten und Zustandsänderungen vom Controller oder anderen Objekten verarbeiten.
View (Darstellung „look“)
Die Ansicht weiß nichts oder nur sehr wenig (über das Datenmodell und stellt lediglich die Daten dar).
Controller (Steuerung „feel“)
Die Steuerung stellt ein Modell der realen Welt dar und reagiert auf Benutzereingaben von Maus, Tastatur und
anderen Eingabegeräten.
285
Programmieren in Java
javax.swing.JComponent ist von java.awt.Container abgeleitet. Als
Container können alle JComponents insbesondere auch andere JComponents
enthalten.
Container
JComponent
public void addAncestorListener(AncestorListener l)
public void addNotify()
...
public boolean contains(int x, int y)
public JToolTip createToolTip()
...
public float getAlignmentX()
public float getAlignmentY()
...
public Border getBorder()
public Rectangle getBounds(Rectangle rv)
protected Graphics getComponentGraphics (Graphics g)
...
public Graphics getGraphics()
public int getHeight()
public Insets getInsets()
...
public Point getLocation(Point rv)
public Dimension getMaximumSize()
public Dimension getMinimumSize()
...
public Dimension getSize(Dimension rv)
public Point getToolTipLocation(MouseEvent event)
public String getToolTipText()
public String getToolTipText(MouseEvent event)
...
public int getWidth()
public int getX()
public int getY()
...
public void paint(Graphics g)
...
public void repaint(Rectangle r)
...
public void setToolTipText(String text)
protected void setUI(ComponentUI newUI)
public void setVisible(boolean aFlag)
...
public void update(Graphics g)
public void updateUI()
Abb. Die Klasse JComponent
286
Programmieren in Java
Swing-Komponenten sind Subklassen der Klasse JComponent.
Für das Einfügen in einen Swing-Frame ist folgendes Applikationsgerüst zweckmäßig:
Container und Komponenten müssen in einem Zwischen-Container mit dem Namen
Contentpane (Inhaltsbereich) eingefügt werden. Der JFrame ist in verschieden
Bereiche (panes) unterteilt. Der Hauptbereich ist der Inhaltsbereich, der die gesamte
Fläche des Frame repräsentiert, in den Komponenten eingefügt werden können. Das
Erzeugen einer Komponenten in den Inhaltsbereich umfaßt:
- Erzeugen eines JPanel-Objekts
- Einfügen aller Komponenten in das JPanel-Objekt über dessen add (Component)-Methode
- Implementieren des JPanel-Objekts über die Methode setContentPane(Container) zum
Inhaltsbereich. Das JPanel-Objekt soll einziges Argument sein.
Bsp.: Ein in eine Anwendung geschachteltes Applet, das die Besonderheiten von Swing
beim Hinzufügen eines Component-Objekts zeigt.
import
import
import
import
java.awt.*;
java.awt.event.*;
java.applet.*;
javax.swing.*;
public class JButtonDemo extends Applet
{
JButton b1
= new JButton("JButton1");
JButton b2
= new JButton("JButton2");
JTextField t = new JTextField(20);
public void init()
{
ActionListener al = new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
String name = ((JButton) e.getSource()).getText();
t.setText(name + " Gedrueckt");
}
};
b1.addActionListener(al);
add(b1);
b2.addActionListener(al);
add(b2);
add(t);
}
public static void main(String args[])
{
JButtonDemo applet = new JButtonDemo();
JFrame frame = new JFrame("JButtonDemo");
frame.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{ System.exit(0); }
});
frame.getContentPane().add(applet, BorderLayout.CENTER);
frame.setSize(300,100);
applet.init();
applet.start();
frame.setVisible(true);
}
}
287
Programmieren in Java
Die Swing-Klasse für Hauptfenster (JFrame) und Dialogboxen (JDialog) sind direkt von
den AWT-Klassen Frame und Dialog abgeleitet und erben all ihre Methoden. Der
Hauptunterschied ist, daß keine Kontrollelemente mehr direkt auf das Fenster platziert
werden, sondern auf einen Client-Container (mit dem Namen „ContentPane“). Nun
können beide Klassen Menüleisten aufnehmen (Typ JMenuBar) und besitzen Vorgabe
beim Schließen eines Fensters.
Beim Schließen eines Fensters verhält sich daher ein JFrame etwas anders als ein
AWT-Frame. Beim JFrame verschwindet das Fenster in den Hintergrund. Dieses
Verhalten kann mit der Funktion public setDefaultCloseOperation(int
operation) Damit sich ein JFrame, wie ein Frame verhält, wird angegeben:
setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE)
Neben
DO_NOTHING_ON_CLOSE
existieren drei weitere Konstanten (WindowConstants)
HIDE_ON_CLOSE
DISPOSE_ON_CLOSE
EXIT_ON_CLOSE
// verdeckt das fenster
//
// schliesst die anwendung
Über public int getDefaultCloseOperation() kann die eingestellte
Eigenschaft beim Schließen des Fensters bezogen werden.
288
Programmieren in Java
6. Utilities
6.1 Kollektionen (Collections)
Kollektionen (Collections) sind Datenstrukturen zur Aufnahme und Verarbeitung von
Datenmengen. Typische Collections sind Stacks, Queues, Priority Queues, Listen oder
Trees (Bäume).
In Java existieren seit der Version 1.0 die Collections Vector, Stack, Hashtable
und Bitset. Die Kritik an diesem Collection-Konzept führte zu einer Sammlung von 20
Klassen und Interfaces im Paket java.util des JDK 1.2. Im Wesentlichen sind dies:
Set, List und Map.
-
Eine List ist eine beliebig große Liste von Elementen beliebigen Typs, auf die wahlfrei und
sequentiell zugegriffen werden kann
Ein Set ist eine Menge von Elementen (ohne Duplikate), auf die mit typischen Mengenoperationen
zugegriffen werden kann.
Eine Map ist eine Abbildung von Elementen eines Typs auf Elemente eines anderen Typs (Menge
zusammengehöriger Paare).
Jede dieser Grundformen ist als Interface und den angegebenen Namen implementiert.
Zudem gibt es jeweils eine oder mehrere konkrete Implementierungen. So gibt es bspw.
für das Interface List die Implementierungsvarianten LinkedList bzw. die abstrakte
Implementierung AbstractList.
6.1.1 Durchwandern von Daten mit Iteratoren
Bei Datenstrukturen gibt es eine Möglichkeit, gespeicherte Daten unabhängig von der
Implementierung immer mit der gleichen Technik abzufragen. Bei Datenstrukturen
handelt es sich meistens um Daten in Listen, Bäumen oder ähnlichem und oft wird nur
die Frage nach der Zugehörigkeit eines Worts zum Datenbestand gestellt (z.B. „Gehört
das Wort dazu?“). Auch die Möglichkeit Daten in irgendeiner Form aufzuzählen, ist eine
häufig gestellte Aufgabe. Hierfür bieten sich Iteratoren an. In Java umfaßt das Interface
Enumeration die beiden Funktionen hasMoreElements() und nextElement(),
mit denen durch eine Datenstruktur iteriert werden kann.
public interface Enumeration
{
public boolean hasMoreElements();
// Test, ob noch ein weiteres Element aufgezählt werden kann
public Object nextElement() throws NoSuchElementException;
/* setzt den internen Zeiger auf das nächste Element, d. h. liefert das
das nächste Element der Enumertion zurück. Diese Funktion kann eine
NoSuchException auslösen, wenn nextElement() aufgerufen wird, obwohl
hasMoreElements() unwahr ist
*/
}
289
Programmieren in Java
Die Aufzählung erfolgt meistens über
for (Enumeration e = ds.elements(); e.hasMoreElements(); )
System.out.println(e.nextElements());
Die Datenstruktur ds besitzt eine Methode elements(), die ein Enumeration-Objekt
zurückgibt, das die Aufzählung erlaubt.
6.1.2 Die Klasse Vector
class java.util.Vector extends AbstractList
implements List, Cloneable, Collection,
Serializable
Die Klasse Vector beschreibt ein Array mit variabler Länge. Objekte der Klasse
Vector sind Repräsentationen einer linearen Liste. Die Liste kann Elemente
beliebigen Typs enthalten, ihre Länge ist zur Laufzeit veränderbar (Array mit variabler
Länge). Vector erlaubt das Einfügen von Elementen an beliebiger Stelle, bietet
sequentiellen und wahlfreien Zugriff auf die Elemente. Das JDK realisiert Vector als
Array von Elementen des Typs Object. Der Zugriff auf Elemente erfolgt über Indizes.
Es wird dazu aber kein Operator [], sondern es werden Methoden benutzt, die einen
Index als Parameter annehmen.
Anlegen eines neuen Vektors (Konstruktor): public Vector()
public Vector(int initialCapacity, int
capacityIncrement)
// Ein Vector vergrößert sich automatisch, falls mehr Elemente aufgenommen werden, als
// ursprünglich vorgesehen (Resizing). Dabei sollen initialCapacity und capacityIncrement
// passend gewählt werden.
Einfügen von Elementen: public void addElement(Object obj)
// Anhängen an des Ende der bisher vorliegenden Liste von Elementen
Eigenschaften: public boolean isEmpty()
// Prüfen, ob der Vektor leer ist
public int size()
// bestimmt die Anzahl der Elemente
public int capacity()
// bestimmt die interne Größe des Arrays. Sie kann mit ensureCapacity() geändert
// werden
Einfügen an beliebiger Stelle innerhalb der Liste:
public void insertElementAt(Object obj, int index) throws
ArrayIndexOutOfBoundsException
// fügt obj an die Position index in den "Vector" ein.
Zugriff auf Elemente: Für den sequentiellen Zugriff steht ein Iterator zur Verfügung.
Wahlfreier Zugriff erfolgt über:
public Object firstElement() throws NoSuchElementException;
public Object lastElement() throws NoSuchElementException;
public Object elementAt(int index) throws ArrayIndexOutOfBoundException;
firstElement() liefert das erste, lastElement() das letzte Element- Mit
elementAt() wird auf das Element an der Position index zugegriffen. Alle 3
Methoden verursachen eine Ausnahme, wenn das gewünschte Element nicht vorhanden
ist.
290
Programmieren in Java
Arbeitsweise des internen Arrays. Der Vector vergrößert sich automatisch, falls mehr
Elemente aufgenommen werden. Die Operation heißt Resizing.
Die Größe des Felds. Mit capacity() erhält man die interne Größe des Arrays. Sie kann mit
ensureCapacity() geändert werden. ensureCapacity(int minimumCapacity) bewirkt bei
einem Vector, daß er mindestens minCapacity Elemente aufnehmen soll.
Der Vektor verkleinert nicht die aktuelle Kapazität, falls sie schon höher als minCapacity ist. Zur
Veränderung dieser Größe, dient die Methode trimToSize(). Sie reduziert die Kapazität des Vectors
auf die Anzahl der Elemente, die gerade im Vector sind.
Die Anzahl der Elemente kann über die Methode size() erfragt werden. Sie kann über setSize(int
newSize) geändert werden. Ist die neue Größe kleiner als die alte, so werden die Elemente am Ende
des Vectors abgeschnitten. Ist newSize größer als die alte Größe, werden die neu angelegten
Elemente mit null initialisiert.
Bereitstellen des Interface Enumerartion. In der Klasse Vector liefert die Methode public
Enumeration elements() einen Enumerator (Iterator) für alle Elemente, die sich in Vector befinden.
Vector
<< Konstruktoren >>
public Vector()
// Ein Vector in der Anfangsgröße von 10 Elementen wird angelegt
public Vector(int startKapazitaet)
// Ein Vector enthält Platz für startKapazitaet Elemente
public Vector(int startKapazitaet, int kapazitaetsSchrittweite)
<< Methoden >>
public Object elementAt(int index)
// Das an der Stelle index befindliche Objekt wird zurückgegeben
public int size()
public Object firstElement()
public Object lastElement();
public void insertElementAt(Object obj, int index)
// fügt Object obj an index ein und verschiebt die anderen Elemente
public void setElementAt(Object obj, int index)
public copyInto(Object einArray[])
// kopiert die Elemente des Vektors in das Array einArray
// Falls das bereitgestellte Objektfeld nicht so groß ist wie der Vektor,
// dann tritt eine IndexOutOfBoundsException auf
public boolean contains(Object obj)
// sucht das Element, liefert true zurück wenn o im Vector vorkommt
public int indexOf(Object obj)
// sucht im Vector nach dem Objekt obj. Falls obj nicht in der Liste ist, wird
// -1 übergeben
public int lastIndexOf(Object obj)
public boolean removeElement(Object obj)
// entfernt obj aus der Liste. Konnte es entfernt werden, wird true
// zurückgeliefert
public void removeElementAt(int index)
// entfernt das Element an Stelle index
public void removeAllElements()
// löscht alle Elemente
public int capacity()
// gibt an, wieviel Elemente im Vektor Patz haben
// (, ohne daßautomatische Größenanpassung erfolgt)
public Object clone()
// Implementierung der clone()-methode von Object, d.h. eine Referenz
// des kopierten Feldes wird zurückgegeben. Die Kopie ist flach.
public String toString()
Abb.: Die Klasse Vector
291
Programmieren in Java
6.1.3 Die Klasse Stack
class java.util.Stack extends Vector
Ein Stack ist eine nach dem LIFO-Prinzip arbeitende Datenstruktur. Elemente werden
vorn (am vorderen Ende der Liste) eingefügt und von dort auch wieder entnommen. In
Java ist ein Stack eine Ableitung von Vector mit neuen Zugriffsfunktionen für die
Implementierung des typischen Verhaltens von einem Stack.
Konstruktor: public Stack();
Hinzufügen neuer Elemente: public Object push(Object item);
Zugriff auf das oberste Element:
public Object pop();
// Zugriff und Entfernen des obersten Element
public Object peek()
// Zugriff auf das oberste Element
Suche im Stack: public int search(Object o)
// Suche nach beliebigem Element,
// Rueckgabewert: Distanz zwischen gefundenem und
//
obersten Stack-Element bzw. –1,
//
falls das Element nicht da ist.
Test:public boolean empty()
// bestimmt, ob der Stack leer ist
Vector
Stack
public Stack()
public Object push(Object obj)
public Object pop()
public Object peek()
public int search(Object obj)
public boolean empty()
Abb.: Die Klasse Stack
Anwendungen:
1. Umrechnen von Dezimalzahlen in andere Basisdarstellungen
Aufgabenstellung: Defaultmäßig werden Zahlen dezimal ausgegeben. Ein Stapel, der Ganzzahlen
aufnimmt, kann dazu verwendet werden, Zahlen bezogen auf eine andere Basis als 10 darzustellen. Die
Funktionsweise der Umrechnung von Dezimalzahlen in eine Basis eines anderen Zahlensystem zeigen
die folgenden Beispiele:
292
Programmieren in Java
2810 = 3 ⋅ 8 + 4 = 34 8
72 10 = 1⋅ 64 + 0 ⋅ 16 + 2 ⋅ 4 + 0 = 1020 4
5310 = 1 ⋅ 32 + 1⋅ 16 + 0 ⋅ 8 + 1 ⋅ 4 + 0 ⋅ 2 + 1 = 1101012
Mit einem Stapel läßt sich die Umrechnung folgendermaßen unterstützen:
6
1
leerer Stapel
n = 355310
7
7
4
4
4
1
1
1
n%8=1
n/8=444
n%8=4
n/8=55
n%8=7
n/8=6
n%8=6
n/6=0
n = 44410
n = 5510
n = 610
n = 010
Abb.: Umrechnung von 355310 in 67418 mit Hilfe eines Stapel
Algorithmus zur Lösung der Aufgabe:
1) Die am weitesten rechts stehende Ziffer von n ist n%b. Sie ist auf dem Stapel abzulegen.
2) Die restlichen Ziffern von n sind bestimmt durch n/b. Die Zahl n wird ersetzt durch n/b.
3) Wiederhole die Arbeitsschritte 1) und 2) bis keine signifikanten Ziffern mehr übrig bleiben.
4) Die Darstellung der Zahl in der neuen Basis ist aus dem Stapel abzulesen. Der Stapel ist zu diesem
Zweck zu entleeren.
Implementierung: Das folgende kleine Testprogramm206 realisiert den Algorithmus und benutzt dazu eine
Instanz von Stack.
import java.util.*;
public class PR61210
{
public static void main(String[] args)
{
int zahl = 3553;
// Dezimalzahl
int b
= 8;
// Basis
Stack s = new Stack();
// Stapel
do
{
s.push(new Integer(zahl % b));
zahl /= b;
} while (zahl != 0);
while (!s.empty())
{
System.out.print(s.pop());
}
206 pr61210
293
Programmieren in Java
System.out.println();
}
}
2. Einlesen einer Folge ganzer Zahlen, Ausgabe der Zahlen in umgekehrter
Reihenfolge207
import java.io.*;
import java.util.*;
public class Reverse
{
public static void main (String args[]) throws IOException
{
int a[]=new int[20];
//Stapel
Stack s = new Stack();
// Einlesen der ganzen Zahlen, Aufnahme der Zahlen in einen ganzzahligen
// Array, Aufnahme der im ganzzahligen Array gespeicherten Zahlen in
// einen Stapel
BufferedReader ein=new BufferedReader(new InputStreamReader(System.in));
for ( int i=0;i<a.length;i++)
{
a[i]=Integer.parseInt(ein.readLine());
s.push(new Integer (a[i]));
}
//Entleeren des Stapels mit Ausgabe auf die Java-Konsole
while ( !s.empty() )// 1 Punkt
{
System.out.print(" "+((Integer)s.pop()).intValue());
}
System.out.println( );
}
}
Ein Stack ist ein Vector. Die Vector-Klasse wird von der Klasse Stack erweitert. Das ist
sicherlich nicht immer besonders sinnvoll. Funktionen, die im Gegensatz zur
Leistungsfähigkeit eines Stapels stehen sind add(), addAll(), addElement(),
capacity(), clear(), clone(), contains(), copyInto(), elementAt(),
.... .
207 vgl. pr62100
294
Programmieren in Java
6.1.4 Die Klasse Bitset für Bitmengen
class java.util.BitSet implements Cloneable, Serializable
Die Klasse Bitset bietet komfortable Möglichkeiten zur bitweisen Manipulation von
Daten.
Bitset anlegen und füllen. Mit zwei Methoden lassen sich die Bits des Bitsets leicht
ändern: set(int bitNummer) und clear(int bitNummer).
Mengenorintierte Operationen. Das Bitset erlaubt mengenorientierte Operationen mit
einer weiteren Menge.
BitSet
public void and(BitSet bs)
public void or(BitSet bs)
public void xor(BitSet bs)
public void andNot(Bitset set)
// löscht alle Bits im Bitset, dessen Bit in set gesetzt sind
public void clear(int index)
// Löscht ein Bit. Ist der Index negativ, kommt es
// zur Auslösung von IndexOutOfBoundsException
public void set(int index)
// Setzt ein Bit. Ist der Index negativ, kommt es
// zur Auslösung von IndexOutOfBoundsException
public boolean get(int index)
// liefert den Wert des Felds am übergebenen Index,
// kann IndexOutOfBoundsException auslösen.
public int size()
public boolean equals(Object o)
// Vergleicht sich mit einem anderen Bitset-Objekt o.
Abb.: Die Klasse BitSet
Bsp.208: Anwendung der Klasse BitSet
Primzahlen
bei der Konstruktion einer Menge von
import java.util.*;
public class Primtest
{
final static int MAXPRIM = 30;
public static void main(String args[])
{
BitSet b = new BitSet();
for (int i = 2; i <= MAXPRIM; i++)
{
boolean ok = true;
for (int j = 2; j < i; j++)
if (b.get(j) && (i % j) == 0)
{
ok = false;
break;
208 pr61310
295
Programmieren in Java
}
if (ok)
b.set(i);
}
for (int i = 1; i < MAXPRIM; i++)
{
if (b.get(i)) System.out.print(i + " ");
}
System.out.println();
}
}
296
Programmieren in Java
6.1.5 Die Klasse Hashtable und assoziative Speicher
Eine Hashtabelle (Hashtable) ist ein assoziativer Speicher, der Schlüssel (keys) mit
Werten verknüpft. Die Datenstruktur ist mit einem Wörterbuch vergleichbar. Die
Hashtabelle arbeitet mit Schlüssel/Werte Paaren. Aus dem Schlüssel wird nach einer
Funktion – der sog. Hashfunktion – ein Hashcode berechnet. Dieser dient als Index für
ein internes Array. Dieses Array hat zu Anfang ein feste Grösse. Leider hat dieses
Technik einen entscheidenden Nachteil. Besitzen zwei Wörter denselben Hashcode,
dann kommt es zu einer Kollision. Auf ihn muß die Datenstruktur vorbereitet sein. Hier
gibt es verschiedene Lösungsansätze. Die unter Java implementierte Variante benutzt
eine verkettete Liste (separate Chaining). Falls eine Kollision auftritt, so wird der
Hashcode beibehalten und der Schlüssel bzw. Wert in einem Listenelement an den
vorhandenen Eintrag angehängt. Wenn allerdings irgendwann einmal eine Liste
durchsucht werden muß, dann wird die Datenstruktur langsam. Ein Maß für den Füllgrad
ist der Füllfaktor (Load Factor). Dieser liegt zwischen 0 und 100 %. 0 bedeutet: kein
Listenelement wird verwendet. 100 % bedeutet: Es ist kein Platz mehr im Array und es
werden nur noch Listen für alle zukommenden Werte erweitert. Der Füllfaktor sollte für
effiziente Anwendungen nicht höher als 75% sein. Ist ein Füllfaktor nicht explizit
angegeben, dann wird die Hashtabelle „rehashed“, wenn mehr als 75% aller Plätze
besetzt sind.
class java.util.Hashtable extends Dictionary implements Map,
Cloneable, Serializable
Erzeugen von einem Objekt der Klasse HashTable:
public Hashtable()
/* Die Hashtabelle enthält eine Kapazität von 11 Einträgen und einen Füllfaktor von 75 % */
public Hashtable(int initialCapacity)
/* erzeugt eine Hashtabelle mit einer vorgebenen Kapazität und dem Füllfaktor 0.75 */
public Hashtable(int initialCapacity, float loadFactor)
/* erzeugt eine Hashtabelle mit einer vorgebenen Kapazität und dem angegebenen Füllfaktor */
Daten einfügen: public Object put(Object key, Object value)
/* speichert den Schlüssel und den Wert in der Hashtabelle. Falls sich zu dem
Schlüssel schon ein Eintrag in der Hashtabelle befand, so wird dieser
zurückgegeben. Anderenfalls ist der Rückgabewert null. Die Methode ist vorgegeben
vom Interface Map. Es überschreibt die Methode von der Superklasse Dictionary. */
Daten holen: public Object get(Object key)
Schlüssel entfernen. public Object remove(Object key)
Löschen der Werte. public void clear()
Test. public boolean containsKey(Object key)
// Test auf einen bestimmten Schlüssel
public boolean containsValue(Object value)
// Test auf einen bestimmten Wert
Aufzählen der Elemente. Mit keys() und elements() bietet die Hashtabelle zwei
Methoden an, die eine Aufzählung zurückgeben:
public Enumeration keys()
// liefert eine Aufzählung aller Schlüssel, überschreibt keys() in Dictionary.
public Enumeration elements()
// liefert eine Aufzählung der Werte, überschreibt elements() in Dictionary
297
Programmieren in Java
Wie üblich liefern beide Iteratoren ein Objekt, welches das Interface Enumeration
implementiert. Der Zugriff erfolgt daher mit Hilfe der Methoden hasMoreElements() und
nextElement().
Dictionary
{abstract}
public abstract Object put (Object key, Object value)
public abstract Object get(Object key)
public abstract Enumeration elements()
public abstract Enumeration keys()
public abstract int size()
public abstract boolean isEmpty()
public abstract Object remove(Object key)
Hashtable
Map
<< Konstruktor >>
public Hashtable(int initialKapazitaet)
public Hashtable(int initialKapazitaet, float Ladefaktor)
public Hashtable()
<< Methoden >>
public boolean contains(Object wert)
public boolean containsKey(Object key)
public void clear()
public Object clone()
protected void rehash()
public String toString()
Properties
<< Konstruktor >>
public Properties()
// legt einen leeren Container an
public Properties(Properties defaults)
// füllt eine Property-Liste mit den angegebenen Default-Werten
<< Methoden >>
public String getProperty(String key)
public String getProperty(String key, String defaultKey)
public void load(InputStream in) throws IOException
// Hier muß ein InputStream übergeben werden, der die daten der
// Property-Liste zur Verfügung stellt.
public void store(OutputStream out, String header)
public void list(PrintStream out)
public void list(PrintWriter out)
public Enumeration propertyNames()
// beschafft ein Enumerations-Objekt mit denen Eigenschaften
// der Property-Liste aufgezählt werden können
Abb.: Die Klassen Hashtable und Properties
298
Programmieren in Java
Die Klasse Hashtable ist eine Konkretisierung der abstrakten Klasse Dictionary. Diese
Klasse beschreibt einen assoziativen Speicher, der Schlüssel auf Werte abbildet und
über den Schlüsselbegriff einen effizienten Zugriff auf den Wert ermöglicht. Einfügen und
der Zugriff auf Schlüssel erfolgt nicht auf der Basis des Operators „==“, sondern mit Hilfe
der Methode „equals“. Schlüssel müssen daher lediglich inhaltlich gleich sein, um als
identisch angesehen zu werden.
Beispiele.
1. Hashtabelle zum Test der von Zufallszahlen der Methode Math.random()209.
import java.util.*;
class Zaehler
{
int i = 1;
public String toString()
{
return Integer.toString(i);
}
}
public class Statistik
{
public static void main(String args[])
{
Hashtable h = new Hashtable();
for (int i = 0; i < 10000; i++)
{
// Erzeuge eine Zahl zwischen 0 und 20
Integer r = new Integer((int)(Math.random() * 20));
if (h.containsKey(r))
((Zaehler) h.get(r)).i++;
else h.put(r,new Zaehler());
}
System.out.println(h);
}
}
2. Hashtabelle für direkten Zugriff auf Daten210
import java.io.*;
import java.util.*;
public class HashTabTest
{
public static void main(String [ ] args)
{
// Map map = new HashMap();
Hashtable h = new Hashtable();
String eingabeZeile
= null;
BufferedReader eingabe = null;
try {
209 pr61310
210 pr61310
299
Programmieren in Java
eingabe = new BufferedReader(
new FileReader("eing.txt"));
}
catch (FileNotFoundException io)
{
System.out.println("Fehler beim Einlesen!");
}
try {
while ( (eingabeZeile = eingabe.readLine() ) != null)
{
StringTokenizer str = new StringTokenizer(eingabeZeile);
if (eingabeZeile.equals("")) break;
String key
= str.nextToken();
String daten = str.nextToken();
System.out.println(key);
h.put(key,daten);
// map.put(key,daten);
}
}
catch (IOException ioe)
{
System.out.println("Eingefangen in main()");
}
try {
eingabe.close();
}
catch(IOException e)
{
System.out.println(e);
}
System.out.println("Uebersicht zur Hash-Tabelle");
// System.out.println(map);
System.out.println(h);
//h.printHashTabelle();
System.out.println("Abfragen bzw. Modifikationen");
// Wiederauffinden
String eingabeKey = null;
BufferedReader ein = new BufferedReader(
new InputStreamReader(System.in));
System.out.println("Wiederauffinden von Elementen");
while (true)
{
try {
System.out.print("Bitte Schluessel eingeben, ! bedeutet Ende: ");
eingabeKey = ein.readLine();
// System.out.println(eingabeKey);
if (eingabeKey.equals("!")) break;
String eintr = (String) h.get(eingabeKey);
// String eintr = (String) map.get(eingabeKey);
if (eintr == null)
System.out.println("Kein Eintrag!");
else
{
System.out.println(eintr);
System.out.println("Soll dieser Eintrag geloescht werden? ");
String antwort = ein.readLine();
// System.out.println(antwort);
if ((antwort.equals("j")) || (antwort.equals("J")))
{
300
Programmieren in Java
// System.out.println("Eintrag wird entfernt!");
h.remove(eingabeKey);
// map.remove(eingabeKey);
}
}
}
catch(IOException ioe)
{
System.out.println(eingabeKey +
" konnte nicht korrekt eingelesen werden!");
}
}
System.out.println(h);
// System.out.println(map);
// System.out.println("Sortierte Tabelle");
// Map sortedMap = new TreeMap(map);
// System.out.println(sortedMap);
}
}
Die Klasse Hashtable benutzt das Verfahren der Schlüsseltransformation (HashFunktion) zur Abbildung von Schlüsseln auf Indexpostionen eines Arrays. Die Kapazität
der Hash-Tabelle gibt die Anzahl der Elemente an, die insgesamt untergebracht werden
können. Der Ladefaktor zeigt an, bei welchem Füllungsgrad die Hash-Tabelle
vergrößert werden muß. Das Vergrößern erfolgt automatisch, falls die Anzahl der
Elemente innerhalb der Tabelle größer ist als das Produkt aus Kapazität und
Ladefaktor. Seit dem JDK 1.2 darf der Ladefaktor auch größer als 1 sein. In diesem Fall
wird die Hash-Tabelle erst dann vergrößert, wenn der Füllungsgrad größer als 100% ist
und bereits ein Teil der Elemente in den Überlaufbereichen untergebracht wurde.
Die Klasse Hashtable ist eine besondere Klasse für Wörterbücher. Ein Wörterbuch
ist eine Datenstruktur, die Elemente miteinander assoziiert. Das Wörterbuchproblem ist
das Problem, wie aus dem Schlüssel möglichst schnell der zugehörige Wert konstruiert
wird. Die Lösung des Problems ist: Der Schlüssel wird als Zahl kodiert (Hashcode) und
dient in einem Array als Index. An einem Index hängen dann noch die Werte mit
gleichem Hashcode als Liste an.
6.1.6 Die abstrakte Klasse Dictionary
Die Klasse Dictionary ist eine abstrakte Klasse, die Methoden anbietet, wie
Objekte (also Schlüssel und Wert) miteinander assoziiert werden:
public abstract Object put(Object key, Object value)
// fügt den Schlüssel key mit dem verbundenen Wert value in das Wörterbuch
// ein
public abstract Object get(Object key)
//
//
//
//
liefert das zu key gehörende Objekt zurück. Falls kein Wert mit dem
Schlüssel verbunden ist, so liefert get() eine null. Eine null als
Schlüssel oder Wert kann nicht eingesetz werden. In put() würde das zu
einer NullPointerException führen.
public abstract Object remove(Object key)
// entfernt ein Schlüssel/Wertepaar aus dem Wörterbuch. Zurückgegeben wird
// der assoziierte Wert.
301
Programmieren in Java
public abstract boolean isEmpty()
// true, falls keine Werte im Wörterbuch
public int size()
gibt zurück, wie viele Elemente aktuell im Wörterbuch sind.
public abstract Enumeration keys()
// liefert eine Enumeration für alle Schlüssel
public abstract Enumeration elements()
// liefert eine Enumeration über alle Werte.
2.2.7 Die Klasse Properties
Die Properties Klasse ist eine Erweiterung von Hashtable. Ein Properties Objekt
erweitert die Hashtable um die Möglichkeit, sich unter einem wohldefinierten Format
über einen Strom zu laden und zu speichern.
Erzeugen. public Properties()
// erzeugt ein leeres Propertes Objekt ohne Worte.
public Properties(Properties p)
// erzeugt ein leeres Properties Objekt mit Standard-werten aus den
// übergebenen Properties
Laden einer Properties-Liste. public void load(InputStream is)
Speichern einer Properties-Liste. public void store(OutputStream os,
String kennung). An den Kopf der Datei wird eine kennung geschrieben (die im
2. Argument angegeben ist). Die Kennung darf „null“ sein.
Enumeration. public void Enumeration propertyNames()
Zeichenketten suchen. Die Methode getProperty(String s).
public String getProperty(String s)
// sucht in den Properties nach der Zeichenkette
public String getProperty(String key, String default)
// sucht in den Properties nach der Zeichenkette key. Ist dieser nicht
// vorhanden, wird der String default zurückgegeben
Eigenschaften ausgeben. Die Methode list() wandert durch die Daten und gibt sie
auf einem PrintWriter aus:
public void list(PrintWriter pw)
// listet die Properties auf dem PrintWriter aus.
Bsp.: Systemeigenschaften der Java-Umgebung
import java.util.*;
import java.io.*;
public class SaveProp
{
public static void main(String args [])
{
try {
Properties p1 = System.getProperties();
FileOutputStream propAusFile = new
FileOutputStream("properties.txt");
p1.store(propAusFile,null);
propAusFile.close();
Properties p2 = new Properties();
302
Programmieren in Java
FileInputStream eing = new FileInputStream("properties.txt");
p2.load(eing);
p2.list(System.out);
}
catch(IOException e) { System.err.println(e);}
}
}
303
Programmieren in Java
6.2 Collection API
Die Java 2 Plattform hat Java erweitert um das Collection API. Anstatt Collection kann
man auch Container (Behälter) sagen. Ein Container ist ein Objekt, das wiederum
Objekte aufnimmt und die Verantwortung für die Elemente übernimmt. Im „util“-Paket
befinden sich sechs Schnittstellen, die grundlegende Eigenschaften der
Containerklassen definieren.
Das in Java 1.2 enthaltene Collections Framework beinhaltet im Wesentlichen drei
Grundformen: Set, List und Map. Jede dieser Grudformen ist als Interface
implementiert. Die Interfaces List und Set sind direkt aus Collection abgeleitet. Es
gibt auch noch eine abstrakte Implementierung des Interface, mit dessen Hilfe das
Erstellen eigener Collections erleichtert wird. Bei allen Collections, die das Interface
Collection implementieren, kann ein Iterator zum Durchlaufen der Elemente mit der
Methode „iterator()“ beschafft werden.
Zusätzlich fordert die JDK 1.2-Spezifikation für jede Collection-Klasse zwei
Konstruktoren:
- Einen parameterlosen Konstruktor zum Anlegen einer neuen Collection.
- Ein mit einem einzigen Collection-Argument ausgestatteter Konstruktor, der eine neue Collection anlegt und mit
den Elementen der als Argument übergebenen Collection auffüllt.
6.2.1 Die Schnittstellen Collection, Iterator, Comparator
Das Interface Collection bildet die Basis der Collection-Klasse und –Interfaces des JDK
1.2. Alle Behälterklassen implementieren das Collection Interface und geben den
Klassen damit einen äußeren Rahmen.
304
Programmieren in Java
Das Interface Collection
<< interface >>
Collection
public void clear();
// Optional: Löscht alle Elemente in dem Container. Eird dies vom Container nicht unterstützt,
// kommt es zur UnsupportedOperationException
public boolean add(Object o);
// Optional: Fügt ein Objekt dem Container hinzu und gibt true zurück, falls sich das Element
// einfügen läßt. Gibt false zurück, falls schon ein Objektwert vorhanden ist und doppelte Werte
// nicht erlaubt sind.
public boolean addAll(Collection c);
// fügt alle Elemente der Collection c dem Container hinzu
public boolean remove(Object o);
// Entfernen einer einzelnen Instanz. Rückgabewert ist true, wenn das Element gefunden und
// entfernt werden konnte
public boolean removeAll(Collection c);
// Oprtional: Entfernt alle Objekte der Collection c aus dem Container
public boolean contains(Object o);
// liefert true, falls der Container das Element enthält
// Rückgabewert ist true, falls das vorgegebene Element gefunden werden konnte
public boolean containsAll(Collection c);
// liefert true, falls der Container alle Elemente der Collection c enthält.
public boolean equals(Object o);
// vergleicht das angegebene Objekt mit dem Container, ob die gleichen Elemente vorkommen.
public boolean isEmpty();
// liefert true, falls der Container keine Elemente enthält
public int size();
// gibt die Größe des Containers zurück
public boolean retainAll(Collection c);
public Iterator iterator();
public Object[] toArray();
// gibt ein Array mit Elementen des Containers zurück
public Object[] toArray(Object[] a);
public int hashCode();
// liefert den Hashwert des Containers
public String toString()
// Rückgabewert ist die Zeichenketten-Repräsentation der Kollektion.
Abb.: Das Interface Collection
Die abstrakte Basisklasse AbstractCollection implementiert die Methoden des
Interface Collection (ohne iterator() und size()). AbstractCollection
ist die Basisklasse von AbstractList und AbstractSet.
305
Programmieren in Java
Das Interface Iterator
<< interface >>
Iterator
public boolean hasNext();
// gibt true zurück, wenn der Iterator mindestens ein weiteres Element enthält.
public Object next();
// liefert das nächste Element bzw. löst eine Ausnahme des Typs NoSuchElementException
// aus, wenn es keine weiteren Elemente gibt
public void remove();
// entfernt das Element, das der Iterator bei next() geliefert hat.
Abb.: Das Interface Iterator
Bei allen Collections, die das Interface Collection implementieren, kann ein Iterator zum
Durchlaufen der Elemente mit der Methode „iterator()“ beschafft werden.
Das Interface Comparator
Vergleiche zwischen Objekten werden mit speziellen Objekten vorgenommen, den
Comparatoren. Ein konkreter Comparator implementiert die folgende Schnittstelle.
<< interface >>
java.util.Comparator
public int compare(Object o1, Object o2)
// vergleicht 2 Argumente auf ihre Ordnung
public boolean equals(Object arg)
// testet, ob zwei Objekte bzgl. des Comparator-Objekts gleich sind
Abb. Das Interface Comparator
6.2.2 Die Behälterklassen und Schnittstellen des Typs List
Behälterklassen des Typs List fassen eine Menge von Elementen zusammen, auf die
sequentiell oder über Index (-positionen) zugegriffen werden kann. Wie Vektoren der
Klasse Vector211 hat das erste Element den Index 0 und das letzte den Index „size()
– 1“. Es ist möglich an einer beliebigen Stelle ein Element einzufügen oder zu löschen.
Die weiter hinten stehenden Elemente werden dann entsprechend weiter nach rechts
bzw. nach links verschoben.
211 seit Java 1.2 implementiert die Klasse Vector die Schnittstelle List
306
Programmieren in Java
Das Interface List212
<< interface >>
List
public void add(int index, Object element);
// Einfügen eines Elements an der durch Index spezifizierten Position
public boolean add(Object o);
// Anhängen eines Elements ans Ende der Liste
// Rückgabewert ist true, falls die Liste durch den Aufruf von add verändert wurde. Er ist false,
// wenn die Liste nicht verändert wurde. Das kann bspw. der Fall sein, wenn die Liste keine
// Duplikate erlaubt und ein bereits vorhandenes Element noch einmal eingefügt werden soll.
// Konnte das Element aus einem anderen Grund nicht eingefügt werden, wird eine Ausnahme
// des Typs UnsupportedOperationException, ClassCastException oder IllegalArgumentException
// ausgelöst
public boolean addAll(Collection c);
// Einfügen einer vollständigen Collection in die Liste. Der Rückgabewert ist true, falls die Liste
// durch den Ausfruf von add veränder wurde
public boolean addAll(int index, Collection c)
public void clear();
public boolean equals(Object object);
public boolean contains(Object element);
public boolean containsAll(Collection collection);
public Object remove(int index)
public boolean remove(Object element);
public boolean removeAll(Collection c);
// Alle Elemente werden gelöscht, die auch in der als Argument angebenen
// Collection enthalten sind.
public boolean retainAll(Collection c);
// löscht alle Elemente außer den in der Argument-Collection enthaltenen
public Object get(int index);
public int hashCode();
public Iterator iterator();
public ListIterator listIterator():
public ListIterator listIterator(int startIndex);
public Object set(int index, Object element);
public List subList(int fromIndex, int toIndex);
public Object[] toArray();
public Object[] toArray(Object[] a);
Abb.: Das Interface List
Auf die Elemente einer Liste läßt sich mit einem Index zugreifen und nach Elementen
läßt sich mit linearem Aufwand suchen. Doppelte Elemente sind erlaubt. Die
Schnittstelle List, die in ArrayList und LinkedList eine Implementierung findet, erlaubt
sequentiellen Zugriff auf die gespeicherten gespeicherten Elemente. Das Interface
List wird im JDK von verschiedenen Klassen implementiert:
AbstractList
ist eine abstrakte Basisklasse (für eigene List-Implementierungen), bei der alle
Methoden die Ausnahme UnsupportedOperationException auslösen und diverse
Methoden
abstract
deklariert
sind.
Die
direkten
Subklassen
sind
212 Da ebenfalls das AWT-Paket eine Klasse mit gleichen Namen verwendet, muß der voll qualifizierte Name in
Anwendungen benutzt werden.
307
Programmieren in Java
AbstractSequentialList,
ArrayList
und
Vector.java.util.
AbstractList implementiert bereits viele Methoden für die beiden ListenKlassen213:
abstract class AbstractList extends AbstractCollection implements List
<< Methoden >>
public void add(int index, Object element)
// Optional: Fügt ein Objekt an der spezifizierten stelle ein
public boolean add(Object o)
// Optional: Fügt das Element am Ende an
public boolean addAll(int index, Collection c)
// Optional: Fügt alle Elemente der Collection ein
public void clear()
// Optional: Löscht alle Elemente
public boolean equals(Object o)
// vergleicht die Liste mit dem Objekt
public abstract Object get(int index)
// liefert das Element an dieser Stelle
int hashCode()
// liefert HashCode der Liste
int indexOf(Object o)
// liefert Position des ersten Vorkommens für o oder –1,
// wenn das Element nicht existiert.
Iterator iterator()
// liefert den Iterator. Überschreibt die Methode AbstractCollection,
// obwohl es auch listIterator() für die spezielle Liste gibt. Die Methode
// ruft aber listIterator() auf und gibt ein ListIterator-Objekt zurück
Object remove(int index)
// löscht ein Element an Position index.
protected void removeRange(int fromIndex, int toIndex)
// löscht Teil der Liste von fromIndex bis toIndex. fromIndex wird mitgelöscht,
// toIndex nicht.
public Object set(int index, Object element)
// Optional. Ersetzt das Element an der Stelle index mit element.
public List subList(int fromIndex, int toIndex)
// liefert Teil einer Liste fromIndex (einschließlich) bis toIndex (nicht mehr dabei)
Abb.: Die abstrakte Klasse AbstractList
AbstractSequentialList
bereitet die Klasse LinkedList darauf vor, die Elemente in einer Liste zu verwalten
und nicht wie ArrayList in einem internen Array.
LinkedList
realisiert die doppelt verkettete, lineare Liste und implementiert List.
ArrayList
implementiert die Liste als Feld von Elementen und implementiert List. Da
ArrayList ein Feld ist, ist der Zugriff auf ein spezielles Element sehr schnell. Eine
LinkedList muß aufwendiger durchsucht werden. Die verkettete Liste ist aber
deutlich im Vorteil, wenn Elemente gelöscht oder eingefügt werden.
213 Der Aufruf einer optionalen Methode, die von der Subklasse nicht implementiert wird, führt zur
UnsupportedOperationException.
308
Programmieren in Java
<< interface >>
Collection
<< interface >>
List
LinkedList
ArrayList
<< Konstruktor >>
public LinkedList();
public LinkedList(Collection collection);
<< Konstruktor >>
public ArrayList();
public ArrayList(Collection collection);
public ArrayList(int anfangsKapazitaet);
<< Methoden >>
public void addFirst(Object object);
public void addLast(Object object);
public Object getFirst();
public Object getLast();
public Object removeFirst();
public Object removeLast();
<< Methoden >>
protected void removeRange
(int fromIndex, int toIndex)
// löscht Teil der Liste von
// fromIndex bis toIndex. fromIndex wird
// mitgelöscht, toIndex nicht.
Vector
Abb. LinkedList, ArrayList, Vector
Das Interface ListIterator
<< interface >>
ListIterator
public boolean hasPrevious();
// bestimmt, ob es vor der aktuellen Position ein weiteres Element gibt, der Zugriff ist mit
// previous möglich
public boolean hasNext();
public Object next();
public Object previous();
public int nextIndex();
public int previousIndex();
public void add(Object o);
// Einfügen eines neuen Elements an der Stelle der Liste, die unmittelbar vor dem nächsten
// Element des Iterators liegt
public void set(Object o);
// erlaubt, das durch den letzten Aufruf von next() bzw. previous() beschaffene Element zu
// ersetzen
public void remove();
Abb.: Das Interface ListIterator
ListIterator ist eine Erweiterung von Iterator. Die Schnittstelle fügt noch
Methoden hinzu, damit an aktueller Stelle auch Elemente eingefügt werden können. Mit
einem ListIterator läßt sich rückwärts laufen und auf das vorgehende Element
zugreifen.
309
Programmieren in Java
6.2.3 Behälterklassen des Typs Set
Ein Set ist eine Menge, in der keine doppelten Einträge vorkommen können. Set hat die
gleichen Methoden wie Collection. Standard-Implementierung für Set sind das
unsortierte HashSet (Array mit veränderlicher Größe) und das sortierte TreeSet
(Binärbaum).
Bsp.
import java.util.*;
public class SetBeispiel
{
public static void main(String args [])
{
Set set = new HashSet();
set.add("Gerhard");
set.add("Thomas");
set.add("Michael");
set.add("Peter");
set.add("Christian");
set.add("Valentina");
System.out.println(set);
Set sortedSet = new TreeSet(set);
System.out.println(sortedSet);
}
}
310
Programmieren in Java
<< interface >>
Collection
<< interface >>
Set
public boolean add(Object element);
public boolean addAll(Collection collection);
public void clear();
public boolean equals(Object object);
public boolean contains(Object element);
public boolean containsAll(Collection collection);
public int hashCode();
public Iterator iterator();
public boolean remove(Object element);
public boolean removeAll(Collection collection);
public boolean retainAll(Collection collection);
public int size();
public Object[] toArray();
public Object[] toArray(Object[] a);
HashSet
<< interface >>
SortedSet
public HashSet();
public HashSet(Collection collection);
public HashSet(int anfangskapazitaet);
public HashSet(int anfangskapazitaet,
float ladefaktor);
public Object first();
public Object last();
public SortedSet headSet(Object toElement);
public SortedSet subSet(Object fromElement,
Object toElement);
public SortedSet tailSet(Object fromElement);
public Comparator comparator();
TreeSet 214
public TreeSet()
public TreeSet(Collection collection);
public TreeSet(Comparator vergleich);
public TreeSet(SortedSet collection);
214 implementiert die sortierte Menge mit Hilfe der Klasse TreeMap, verwendet einen Red-Black-Tree als
Datenstruktur
311
Programmieren in Java
Die Schnittstelle Set
ist eine im mathematischen Sinne definierte Menge von Objekten. Die Reihenfolge wird
durch das Einfügen festgelegt. Wie von mathematischen Mengen bekannt, darf ein Set
keine doppelten Elemente enthalten. Besondere Beachtung muß Objekten geschenkt
werden, die ihren Wert nachträglich ändern. Die kann ein Set nicht kontrollieren. Eine
Menge kann sich nicht selbst als Element enthalten.
Zwei Klassen ergeben sich aus Set: die abstrakte Klasse AbstractSet und die
konkrete Klasse HashSet.
Die Schnittstelle SortedSet
erweitert Set so, daß Elemente sortiert ausgelesen werden können. Das
Sortierkriterium wird durch die Hilfsklasse Comparator gesetzt.
312
Programmieren in Java
6.2.4 Behälterklassen des Typs Map
Ein Map ist eine Menge von Elementen, auf die über Schlüssel zugegriffen wird. Jedem
Schlüssel (key) ist genau ein Wert (value) zugeordnet. Standard-Implementierungem
sind HashMap, HashTable und TreeMap.
Interface Map, SortedMap und implemetierende Klassen
<< interface >>
Collection
<< interface >>
Map
public void clear();
public boolean containsKey(Object key);
public boolean containsValue(Object value);
public Set entrySet();
public Object get(Object key);
public boolean isEmpty();
public Set keySet();
public Object remove(Object key);
public int size();
public Collection values();
HashMap
public HashMap();
public HashMap(Map collection);
public HashMap(int anfangskapazitaet);
public HashMap(int anfangskapazitaet,
int ladefaktor);
<< interface >>
SortedMap
public Comparator comparator();
public Object firstKey();
public Object lastKey();
public SortedMap headMap(Object toKey);
public SortedMap subMap(Object fromKey,
Object toKey);
public SortedMap tailMap(Object fromKey);
Hashtable
TreeMap
public TreeMap();
public TreeMap(Map collection);
public TreeMap(Comparator vergleich);
public TreeMap(SortedMap collection);
Abb.:
313
Programmieren in Java
Die Schnittstelle Map
Eine Klasse, die Map implementiert, behandelt einen assoziativen Speicher. Dieser
verbindet einen Schlüssel mit einem Wert. Die Klasse Hashtable erbt von Map. Map
ist für die implementierenden Klassen AbstractMap, HashMap, Hashtable,
RenderingHints, WeakHashMap und Attributes das, was die abstrakte Klasse
Dictionary für die Klasse Hashtable ist.
Die Schnittstelle SortedMap
Eine Map kann mit Hilfe eines Kriteriums sortiert werden und nennt sich dann
SortedMap. SortedMap erweitert direkt Map. Das Sortierkriterium wird mit einem
speziellen Objekt, das sich Comparator nennt, gesetzt. Damit besitzt auch der
assoziative Speicher über einen Iterator eine Reihenfolge. Nur die konkrete Klasse
TreeMap implementiert bisher eine SortedMap.
Die abstrakte Klasse AbstractMap
implementiert die Schnittstelle Map.
Die konkrete Klasse HashMap
implementiert einen assoziativen Speicher, erweitert die Klasse AbstractMap und
implementiert die Schnittstelle Map.
Die konkrete Klasse TreeMap
Erweitert AbstractMap und implementiert SortedMap. Ein Objekt von TreeMap hält
Elemente in einem Baum sortiert.
314
Programmieren in Java
6.2.5 Algorithmen
Die Wahl einer geeigneten Datenstruktur ist der erste Schritt. Im zweiten Schritt müssen
die Algorithmen implementiert werden. Die Java Bibliothek hilft mit einigen
Standardalgorithmen weiter. Dazu zählen Funktionen zum Sortieren und Suchen in
Containern und das Füllen von Containern. Zum flexiblen Einsatz dieser Funktionen
haben die Java-Entwickler die Klasse Collections bereitgestellt. Collections bietet
Algorithmen statischer Funktionen an, die als Parameter ein Collection Objekt erwarten.
Leider sind viele Algorithmen nur auf List Objekte definiert, z.B.
public static void shuffle(List list)
// würfelt die Werte einer Liste durcheinander
Bsp.
import java.util.*;
public class VectorShuffle
{
public static void main(String args[])
{
Vector v = new Vector();
for (int i = 0; i < 10; i++)
v.add(new Integer(i));
Collections.shuffle(v);
System.out.println(v);
}
}
public static void shuffle(List list, Random rnd)
// würfelt die werte der Liste durcheinander und benutzt dabei den Random Generator rnd.
Nur die Methoden min() und max() arbeiten auf allgemeinen Collection-Objekten.
6.2.5.1 Datenmanipulation
Daten umdrehen. Die Methode reverse() dreht die Werte einer Liste um. Die
Laufzeit ist linear zu der Anzahl der Elemente.
public static void reverse(List l)
// dreht die Elemente in der Liste um
Listen füllen. Mit der fill()-Methode läßt sich eine Liste in linearer Zeit belegen. Nützlich
ist dies, wenn eine Liste mit Werten initialisiert werden muß.
public static void fill (List l, Object o)
// füllt eine Liste mit dem Element o
Daten zwischen Listen kopieren. Die Methode copy(List quelle, List ziel)
kopiert alle Elemente von quelle in die Liste ziel und überschreibt dabei Elemente,
die evtl. an dieser Stelle liegen.
public static void copy(List quelle, List ziel)
// kopiert Elemente von quelle nach ziel. Ist ziel zu klein, gibt es eine IndexOutOfBoundsException
315
Programmieren in Java
6.2.5.2 Größter und kleinster Wert einer Collection
Die Methoden min() und max() suchen das größte und kleinste Element einer Collection.
Die Laufzeit ist linear zur Größe der Collection. Die Methoden machen keinen
Unterschied, ob die Liste schon sortiert ist oder nicht.
public static Object min(Collection c)
//
public static Object max(Collection c)
/* Falls min() bzw. max() auf ein Collection-Objekt angewendet wird, erfolgt die Bestimmung des
Minimums bzw. Maximums nach der Methode compareTo der Comparable Schnittstelle. Byte,
Character, Double, File, Float, Long, Short, String, Integer, BigInteger, ObjectStreamField, Date und
Calendar haben diese Schnittstelle implementiert. Lassen sich die Daten nicht vergleichen, dann gibt es
eine ClassCastException
*/
public static Object min(Collection c, Comparator vergl)
//
public static Object max(Collection c, Comparator vergl)
6.2.5.3 Sortieren
Die Collection Klasse bietet zwei sort() Methoden an, die die Elemente einer Liste
stabil215 sortieren. Die Methode sort() sortiert die Elemente in ihrer natürlichen
Ordnung, z.B.:
- Zahlen nach der Größe (13 < 40)
- Zeichenketten alphanumerisch (Juergen < Robert < Ulli)
Eine zweite überladene Form von sort() arbeitet mit einem speziellen Comparator
Objekt, das zwei Objekte mit der Methode compare() vergleicht.
public static void sort(List liste)
// sortiert die Liste
public static void sort(List liste, Comparator c)
// sortiert die Liste mit dem Comparator c
Die Sortierfunktion arbeitet nur mit List-Objekten. „sort()“ gibt es aber auch in der
Klasse Arrays.
Bsp.: Das folgende Programm sortiert eine Reihe von Zeichenketten in aufsteigender
Folge. Es nutzt die Methode Arrays.asList() zur Konstruktion einer Liste aus
einem Array216.
import java.util.*;
public class CollectionsSortDemo
215 Stabile Sortieralgorithmen beachten die Reihenfolge von gleichen Elementen, z.B. beim Sortieren von
Nachrichten in einem Email-Programm, zuerst nach dem Datum und anschließend nach dem Sender, soll die Liste
innerhalb des Datum sortiert bleiben.
216 Leider gibt es keinen Konstruktor für ArrayList, der einen Array mit Zeichenketten zuläßt.
316
Programmieren in Java
{
public static void main(String args[])
{
String feld[] =
{ "Regina","Angela","Michaela","Maria","Josepha",
"Amalia","Vera","Valentina","Daniela","Saida",
"Linda","Elisa"
};
List l = Arrays.asList(feld);
Collections.sort(l);
System.out.println(l);
}
}
Die Java Bibliothek bietet nicht viel zur Umwandlung von Feldern („Array“) in
dynamische Datenstrukturen. Eine Ausnahme bildet die Hilfsklasse Arrays, die die
Methode asList() anbietet. Die Behälterklassen ArrayList und LinkedList werden
über asList() nicht unterstützt, d.h. Über asList() wird zwar eine interne Klasse
ArrayList benutzt, die eine Erweiterung von AbstractList ist, aber nur das
notwendigste implementiert.
Sortieralgorithmus. Es handelt sich um einen optimierten „Merge-Sort“. Seine Laufzeit
beträgt N ⋅ log( N ) .
Die sort() Methode arbeitet mit der toArray() Funktion der Klasse List. Damit
werden die Elemente der Liste in einem Feld (Array) abgelegt. Schließlich wird die
sort() Methode der Klasse Arrays genutzt und mit einem ListIterator wieder
in die Liste eingefügt.
Daten in umgekehrter Reihenfolge sortieren. Das wird über ein spezielles
Comparator-Objekt geregelt, das von Collections über die Methode
reverseOrder() angefordert werden kann.
Bsp.:
import java.util.*;
public class CollectionsReverseSortDemo
{
public static void main(String args[])
{
Vector v = new Vector();
for (int i = 0; i < 10; i++)
{
v.add(new Double(Math.random()));
}
Comparator comparator = Collections.reverseOrder();
Collections.sort(v,comparator);
System.out.println(v);
}
}
Eine andere Möglichkeit für umgekehrt sortierte Listen besteht darin, erst die Liste mit
sort() zu sortieren und anschließend mit reverse() umzudrehen.
317
Programmieren in Java
6.2.5.4 Suchen von Elementen
Die Behälterklassen enthalten die Methode contains(), mit der sich Elemente
suchen lassen. Für sortierte Listen gibt es eine wesentlich schnellere Suchmethode:
binarySearch():
public static int binarySearch(List liste, Object key)
// sucht ein Element in der Liste. Gibt die Position zurück oder ein Wert kleiner 0,
// falls key nicht in der Liste ist.
public static int binarySearch(List liste, Object key, Comparator c)
// Sucht ein Element mit Hilfe des Comparator Objekts in der Liste. Gibt die Position zurück oder
// einen Wert kleiner als 0, falls der key nicht in der Liste ist.
Bsp.: Das folgende Programm sortiert (zufällig ermittelte) Daten und bestimmt Daten in
Listen mit Hilfe der binären Suche.
import java.util.*;
public class ListSort
{
public static void main(String [] args)
{
final int GR = 20;
// Verwenden einer natuerliche Ordnung
List a = new ArrayList();
for (int i = 0; i < GR; i++)
a.add(new VglClass((int)(Math.random() * 100)));
Collections.sort(a);
Object finde = a.get(GR / 2);
int ort = Collections.binarySearch(a,finde);
System.out.println("Ort von " + finde + " = " + ort);
// Verwenden eines Comparator
List b = new ArrayList();
// Bestimmt zufaellig Zeichenketten der Laenge 4
for (int i = 0; i < GR; i++)
b.add(Felder.randString(4));
// Instanz fuer den Comparator
AlphaVgl av = new AlphaVgl();
// Sortieren
Collections.sort(b,av);
// Binaere Suche
finde = b.get(GR / 2);
ort = Collections.binarySearch(b,finde,av);
System.out.println(b);
System.out.println("Ort von " + finde + " = " + ort);
}
}
318
Programmieren in Java
6.3 Die Klasse StringTokenizer
Die StringTokenizer-Klasse unterstützt das Aufteilen eines Strings in Teilworte bzw.
Zeichen, sog. Tokens. Tokens werden mit Hilfe von Trennzeichen erkannt, die ein Token
vom anderen trennen. Ein Token ist eine Zeichenkette, in der keine Trennzeichen
vorkommen. In der Regel werden Whitespaces217 als Trennzeichen verwendet. Der
StringTokenizer ist nicht an bestimmte Trennzeichen gebunden, sie können vielmehr frei
gewählt werden. Nur in der Voreinstellung sind Tabulator, Leerzeichen und Zeilentrenner
die Delimiter.
Konstruktor: public StringTokenizer(String str)
Ein StringTokenizer mit Whitespaces als Trennzeichen wird erzeugt. Der zu
untersuchende String ist str. Sollen andere Zeichen als Trennzeichen angegeben
werden, so müssen diese ebenfalls an den Konstruktor übergeben werden:
public StringTokenizer(String str, String delimiters)
Methoden:
public String nextToken() throws NoSuchElementException
gibt das jeweils nächste Token an
public boolean hasMoreTokens()
gibt Auskunft darüber, ob es noch weitere Tokens gibt.
public int countTokens()
gibt Auskunft darüber, wie viele Tokens in einem String vorhanden sind.
public String nextToken(String neueDelimiter)
bewirkt nach einem Aufruf die Veränderung der Trennzeichen.
Das Enumerations-Interface ist in die StringTokenizer-Klasse implementiert:
public boolean hasMoreElements()
{
return hasMoreTokens();
}
public Object nextElement()
{
return nextToken();
}
Bsp.: Ausgabe der Worte eines Satzes
String satz = "Dies ist ein Satz";
StringTokenizer tokenizer = new StringTokenizer(satz);
while (tokenizer.hasMoreTokens())
{
System.out.println(tokenizer.nextToken());
}
217 Zu den Whitespaces gehören Leerzeichen, Tabulator, Zeilenvorschub und Zeilenumbruch. Wird beim
Erzeugen eines Tokenizer keine Trennzeichen-Zeichenkette angegeben, so werden Whitespaces als Trennzeichen
angenommen.
319
Programmieren in Java
6.4 Die Klasse Random
Konstruktoren
Random()
// Erzeugt einen neuen Zufallszahlengenerator. Der Seed218 wird auf die aktuelle Zeit gesetzt.
Random(long seed)
// Erzeugt einen neuen Zufallszahlengenerator und benutzt den Parameter als Seed.
Methoden
public void setSeed(long seed)
// setzt den Zufallswert neu
Die Random-Klasse kann Zufallszahlen in vier verschiedenen Datentypen erzeugen:
public int nextInt()
// liefert die nächste Zufallszahl
public long nextLong()
// liefert die nächste Zufallszahö
public float nextFloat()
// liefert die nächste Zufallszahl zwischen 0.0 und 1.0
public double nextDouble()
erzeugt eine Zufallszahl im double-Format zwischen 0.0 und 1.0219.
218 Startwert für jede Zufallszahl ist ein 48 Bit Seed. Das Woer „Seed“ kommt vom englischen Wort für Samen
und deutet an, daß es bei der Generierung von Zufalsszahlen wie bei Pflanzen einen Samen gibt, der zu
Nachkommen führt. Aus diesem Startwert ermitteln sich anschließend die anderen Zahlen durch lineare
Kongruenzen. Dadurch sind die Zahlen nicht wirklich zufällig, sie gehorchen einem mathematischen Verfahren
219 wird von Math.random() verwendet.
320
Programmieren in Java
6.5 Datumsberechnungen
6.5.1 Die Klassen Date, Calendar, GregorianCalendar
Die Klasse Date
Die Date-Klasse repräsentiert ein bestimmtes Datum bzw. einen bestimmten Zeitpunkt.
Ausgangspunkt ist der 1. Januar 1970 0:00 h Greenwich-Zeit.
Date
<< Konstruktoren >>
public Date()
// erzeugt eine Datums-Angabe und initialisiert es mit der Zeit, die bei der Erzeugung gelesen
// wurde
public Date(long d)
// erzeugt ein Datums-Objekt und initialisiertes mit der angegebenen Anzahl von Millisekunden
// seit dem 1. Januar 1970, 00:00:00 GMT
<< Methoden >>
public long getTime()
// liefert die Anzahl der Millisekunden seit dem 1. Januar 1970, 00:00:00 GMT zurück
public void setTime(long t)
// setzt die Anzahl Millisekunden des Datums-Objekts neu. Die akuelle Zeit kann über
// today.setTime(new java.util.Date()) gesetzt werden.
public boolean before(Date d)
public boolean after(Date d)
// testet, ob das eigene Datum vor / nach dem Datum des Parameters ist. „true“, wenn getTime() für
// beide den gleichen Wert ergibt und der Parameter nicht null ist.
public int compareTo(Date d)
// vergleicht zwei Datum-Objekte und gibt 0 zurück, falls beide die gleiche Zeit repräsentieren.
//
public int compareTo(Object o)
// Ist das übergebene Objekt vom Typ Date, dann verhält sich die Funktion wie das zuvor
// angegebene compareTo(). Anderenfalls wirft die Methode eine ClassCastException
...
Abb.: Die Klasse Date
Bsp.220:
import java.util.Date;
class DatumundZeit
{
public static void main(String args[])
{
System.out.println(new Date() );
}
}
220 pr65100
321
Programmieren in Java
Die Klasse Calendar
(abstract class java.util.Calendar implements Serializable, Cloneable)
Sie wird eingesetzt zur Konvertierung zwischen verschiedenen Datumsformaten. Die
Klasse Calendar besitzt nur einige statisch Funktionen, z.B. getInstance(). Mit
der Klassenmethode getInstance() bekommt man ein nutzbares Format:
public static Calendar getInstance()
liefert einen Calendar in der Ausprägung von GregorianCalendar mit der dargestellten Zeitzone und
Lokalisierung.
Die Klasse GregorianCalendar
Die abstrakte Klasse Calendar wird durch die Klasse GregorianCalendar
implementiert. In der gegenwärtigen Implementierung deckt die Klasse den Zeitraum
von 4716 v. Chr. bis zum Jahre 5.000.000.
GregorianCalendar
<< Konstruktoren >>
public GregorianCalendar()
// erzeugt ein standardmäßiges GregorianCalendar-Objekt mit der aktuellen Zeit in der
// voreingestellten Zeitzone und Lokalisierung
public GregorianCalendar(int jahr, int monat, int tag)
// erzeugt ein GregorianCalendar-Objekt in der voreingestellten Zeitzone und Lokalisierung. Das
// Datum wird durch jahr, monat (0 <= monat <= 11) und tag festgelegt
public GregorianCalendar(int jahr, int monat, int tag, int stunde, int minute)
public GregorianCalendar(int jahr, int monat, int tag, int stunde, int minute, int sekunde)
<< Methoden >>
public final void set(int feld, int wert)
// setzt das Feld feld mit dem Wert wert
public final void set(int jahr, int monat, int tag)
// setzt die Werte für Jahr, Monat, Tag
public final void set(int jahr, int monat, int tag, int stunde, int minute)
public final void set(int jahr, int monat, int tag, int stunde, int minute, int sekunde)
public final int get(int feld)
// liefert den Wert für das Feld feld
...
Abb.: Die Klasse GregorianCalendar (class java.util.GregorianCalendar extends Calendar)
Das Abfragen und Setzen von Datumselementen erfolgt mit den überladenen Methoden
set() und get(). Beide erwarten als ersten Parameter einen Feldbezeichner. Er gibt
an, auf welches Datum / Zeitfeld zugegriffen werden soll. Die get()-Methoden liefern
den Inhalt des angegebenen Felds. Die set()-Methoden schreiben den als zweiten
Parameter übergebenen Wert in das Feld. Die nachfolgende Tabelle zeigt eine
Übersicht der Feldbezeichner und der Wertebereiche.
322
Programmieren in Java
Feldbezeichner
ERA
YEAR
MONTH
DAY_OF_MONTH
WEEK_OF_YEAR
WEEK_OF_MONTH
DAY_OF_YEAR
DAY_OF_WEEK
Minimalwert
0(BC)
1
0
1
1
1
1
1
Maximalwert
1(AD)
5000000
11
31
54
6
366
7
DAY_OF_WEEK_IN_MONTH
HOUR
HOUR_OF_DAY
MINUTE
SECOND
MILLISECOND
AM_PM
ZONE_OFFSET
-1
0
0
0
0
0
0
-12*60*60*1000
6
12
23
59
59
999
1
12*60*60*1000
DST_OFFSET
0
1*60*60*1000
Abb.: Konstanten aus der Klasse Calendar
Bsp.: Ausgabe wichtiger Datumsfelder221
import java.util.*;
public class DateDemo
{
public static void main(String args[])
{
GregorianCalendar cal = new GregorianCalendar();
cal.setTimeZone(TimeZone.getTimeZone("ECT"));
cal.setTime(cal.getTime());
printCalendar(cal);
cal.set(Calendar.DATE,12);
cal.set(Calendar.MONTH,Calendar.MARCH);
cal.set(Calendar.YEAR,1973);
printCalendar(cal);
}
public static void printCalendar(Calendar cal)
{
String wochenTag = (new String[]{"Sonntag",
"Montag","Dienstag","Mittwoch","Donnerstag",
"Freitag","Samstag"})
[cal.get(Calendar.DAY_OF_WEEK)];
System.out.println(wochenTag + ", " +
cal.get(Calendar.DATE) + "." +
(cal.get(Calendar.MONTH)+1) + "." +
cal.get(Calendar.YEAR) + ", " +
cal.get(Calendar.HOUR_OF_DAY) + ":" +
o(cal.get(Calendar.MINUTE)) + ":" +
o(cal.get(Calendar.SECOND)) + " und " +
cal.get(Calendar.MILLISECOND) + " ms ");
System.out.println("Es ist die " +
221 pr65100
323
Erklärung
Datum vor oder nach Christi
Jahr
Monat
Tag
Woche
Woche des Monats
Tag des Jahres
Tag der Woche (1 = Sonntag, 7 =
Samstag
Tag der Woche im Monat
Stunde von 12
Stunde von 24
Minute
Sekunden
Millisekunden
Vor 12, nach 12
Zeitzonenabweichnung
in
Millisekunden
Sommerzeitabweichnung
in
Millisekunden
Programmieren in Java
cal.get(Calendar.WEEK_OF_YEAR) + ". Woche im Jahr und ");
System.out.println(cal.get(Calendar.WEEK_OF_MONTH) +
". Woche im Monat\n");
}
public static String o(int i)
{
return (i >= 10) ? Integer.toString(i) : "0" + i;
}
}
Die Klasse TimeZone (abstract class java.util.TimeZone implements
Serializable, Cloneable)
Die Klasse TimeZone repräsentiert eine Zeitzone inklusive Zeitverschiebung und
benutzt dazu folgende statische Methoden:
public static TimeZone getDefault()
// gibt die Zeitzone für die Umgebung zurück
public static String[] getAvailableIDs()
// liefert alle verfügbaren IDs
public static TimeZone getTimeZone(String ID)
// liefert die Zeitzone für eine gegebene ID
Bsp.: Gib eine Aufzählung aller unterstützten Zeitzonen222 an.
import java.util.*;
public class AlleZonen
{
public static void main(String args[])
{
String s[] = TimeZone.getAvailableIDs();
Arrays.sort(s);
for (int i = 0; i < s.length; i++)
System.out.println(s[i]);
}
}
222 Unter Windows mit dem JDK 1.3 sind dies 321 Zeitzonen, pr65100
324
Programmieren in Java
6.5.2 Formatieren der Datumsangaben
Die Klasse DateFormat
DateFormat
<< Methoden>>
public final String format(Date d)
// formatiert das Datum in einen Datum / Zeit-String
public static final DateFormat getDateInstance()
public static final DateFormat getTimeInstance()
public static final DateFormat getDateTimeInstance()
// liefert einen Datum/Zeit-Formatierer mit dem vorgegebenen stil aus der Standardumgebung
public static final DateFormat getDateInstance(int dateStil)
public static final DateFormat getTimeInstance(int stil)
// liefert einen Datum/Zeit-Formatierer mit den Stil stil und der Standardsprache
public static final DateFormat getDateInstance(int stil, Locale lokal223)
public static final DateFormat getTimeInstance(int stil, Locale lokal)
// liefert einen Datum/Zeit-Formatierer und der Sprache lokal
public static final DateFormat getDateTimeInstance(int dateStil, int timeStil)
// gibt einen Datum/Zeit-Formatierer für die gesetzte Sprache im angegebenen
// Formatierungsstil zurück
public static final DateFormat getDateTimeInstance(int dateStil, int timeStil, Locale lokal)
// gibt ein Datum/Zeit-Formatierer für die Sprache lokal im angegebenen Formatierungsstil zurück
public Date parse(String fs) throws ParseException
// „parst“ ein Datum oder einen Zeit-String
...
Abb.: Die Klasse DateFormat
(abstract class java.text.DateFormat extends Format implements Cloneable, Serializable
Die Klasse DateFormat bietet die Methoden getDateInstance(),
getTimeInstance(),
getDateTimeInstance()
mit
den
Paramtern
DateFormat.SHORT,
DateFormat.MEDIUM,
DateFormat.LONG,
DateFormat.FULL an, die die Zeit bzw. das Datum auf vier verschiedene Arten
formatieren:
Konstante
SHORT
MEDIUM
LONG
FULL
Beispiel für das Datum
29.9.01
29.9.2001
22. September 2001
Samstag, 22. September 2001
Beispiel für die Zeit
21:51
21:51:45
21:53:20 GMT+02:00
21:53 Uhr GMT+02:00
Abb.: Konstanten aus DateFormat
Da DateFormat abstrakt ist, ist erst die aus DateFormat abgeleitete Klasse
SimpleDateFormat einsatzbereit.
223 Unter Locale sind für einige Sprachen Konstanten vordefiniert: ENGLISH, FRENCH, GERMAN, ITALIAN,
JAPANESE, KOREAN, CHINESE, SIMPLIFIED_CHINESE, TRADITIONAL_CHINESE
325
Programmieren in Java
Bsp. mit Sprachen224: Das folgende Programm erzeugt die Ausgabe für Deutschland
und anschließend für Italien.
import java.util.*;
import java.text.*;
public class SimpleDateFormatierer
{
public static void main(String args[])
{
Calendar cal;
cal = new GregorianCalendar(TimeZone.getTimeZone("ECT") );
cal.setTime(cal.getTime());
DateFormat format;
format = DateFormat.getDateTimeInstance(
DateFormat.FULL, DateFormat.MEDIUM);
System.out.println(format.format(cal.getTime() ) );
format = DateFormat.getDateTimeInstance(
DateFormat.FULL, DateFormat.MEDIUM, Locale.ITALY);
System.out.println(format.format(cal.getTime() ) );
}
}
Die Klasse SimpleDateFormat
Zum Formatieren eines Datum-Objekts muß zunächst ein Objekt von
SimpleDateFormat
erzeugt
werden.
Dieses
bekommt
dann
evtl.
Formatierungsanweisungen (über eine andere Methode oder über einen weiteren
Konstruktor) mit und formatiert dann mit der format() das Datum.
SimpleDateFormat
<< Konstruktoren >>
public SimpleDateFormat()
// erzeugt eine neues SimpleDateFormat-Objekt in der eingestellten Sprache
public SimpleDateFormat(String fs)
// erzeugt ein SimpleDateFormat-Objekt mit dem vorgegebenen Formatierungsstring in der
// voreingestellten Sprache
public SimpleDateFormat(String fs, Locale loc)
// erzeugt ein SimpleDateFormat-Objekt mit dem vorgebenen Formatierungsstring in der Sprache
// loc.
<< Methoden >>
public void applyPattern(String fs)
// setze den Formatieruns-String
public void applyLocalizedPattern(String pattern)
// sertzt den Formatierungs-string für die gegebenen Sprache
public String toPattern()
// liefert den Formatierungsstring
public String toLocalizedPattern()
// liefert den übersetzten Formatierungsstring
Abb.: Die Klasse
DateFormat)
SimpleDateFormat
(class
224 vgl. pr65200
326
java.text.SimpleDateFormat
extends
Programmieren in Java
Zur individuellen Anpassung der Ausgabe kann ein Formatierungsstring angegeben
werden. Diese Formatierungsanweisung wird entweder dem Konstruktor übergeben
oder kann nachträglich mit der Methode applyPattern() geändert werden.
Symbol
G
Y
M
MM
MMM
MMMM
D
H
H
M
S
S
E
EEEE
D
F
W
W
A
K
K
Z
‘
‘'
Bedeutung
Ära
Jahr
Monat im Jahr
Monat im Jahr m. Null
Monat im Jahr (kurz)
Monat im Jahr (lang)
Tag im Monat
Stunde (1-12)
Stunde am Tag (0-23)
Minute der Stunde
Sekunde der Minute
Millisekunde
Tag der Woche (kurz)
Tag der Woche (lang)
Tag im Jahr
Tag der Woche i. Mon.
Woche im Jahr
Woche im Monat
am/pm-Text
Stunde am Tg (1-24)
Stunde (0-11)
Zeitzone
Zeichen für Text
Einzelnes Hochkomma
Präsentation
Text
Nummer
Nummer
Nummer
Text
Text
Nummer
Nummer
Nummer
Nummer
Nummer
Nummer
Text
Text
Nummer
Nummer
Nummer
Beispiel
AD
1998
7
07
Sep
September
26
9
0
13
22
257
Mi
Mittwoch
304
3
13
Text
Nummer
Nummer
Text
Trennzeichen
Literal
AM
24
0
GMT+02:00
‘
Abb.: Symbole im Formatstring zur Steuerung der Ausgabe
6.6 Formatieren mit Format-Objekten
Zahlen, Datumsangaben und Text können auf verschiedene Art und weise formatiert
werden. Unter Java wird das Formatierungsverhalten in einer abstrakten Klasse Format
(abstract class java.text.Format implements Serializable,
Cloneable) fixiert. Format stellt die Methoden format() und parseObject()
bereit.
Im JDK erweitern drei Klassen Format: DateFormat, MessageFormat und
NumberFormat. Sie übernehmen die Ein-/Ausgabe für das Datum, allgemeine
Botschaften (Nachrichten) Zahlen. Jede der Klassen implementiert die Methoden zur
Ausgabe format() und zum Erkennen parseObject().
327
Programmieren in Java
7. Ein- und Ausgabe
In Java werden Ein-, Ausgabeoperationen über sog. Datenströme225 realisiert. Es
stehen zur Behandlung des Datenstrommodells zwei abstrakte Klassen zur Verfügung:
InputStream und OutputStream. Die beiden Klassen gehören zu dem Paket
java.io226 , das bei jedem Programm mit Ein-/Ausgabeoperationen importiert
werden muß.
Einfache Strom-Methoden erlauben nur das Versenden von Bytes mit Hilfe von
Datenströmen227. Ein nicht interpretierbarer Bytestrom kann von jeder beliebigen
Quelle kommen. Quelle bzw. Ziel des Stroms sind völlig verschiedene Erzeuger bzw.
Verbraucher von Bytes.
Allgemeine Methoden, die von jeder beliebigen Quelle lesen können, akzeptieren ein
Stromargument zur Bezeichnung der Quelle. Allgemeine Methoden zum Schreiben
akzeptieren einen Strom zur Zielbestimmung. Filter haben zwei Stromargumente. Sie
lesen vom ersten, verarbeiten die Daten und Schreiben ins zweite.
Zum Senden/Empfangen verschiedener Datentypen gibt es die Schnittstellen
DataInput und DataOutput. Sie legen Methoden zum Senden/Empfangen anderer
Java-Datentypen fest. Mit Hilfe der Schnittstellen ObjectInput und ObjectOutput
lassen sich ganze Objekte über einen Strom senden.
Alle Methoden, die sich mit Ein- und Ausgabeoperationen beschäftigen, werden in der
Regel mit throws IOException abgesichert. Diese Subklasse von Exception
enthält alle potentiellen I/O-Fehler, die bei der Verwendung von Datenströmen
auftreten können.
Die I/O-Exceptions können direkt mit einem try-catch-Block aufgefangen oder an
übergeordnete Methoden weitergegeben werden.
225 Der Begriff Strom kommt aus dem Betriebssystem Unix und bezieht sich auf „Pipes“. Eine Pipe ist ein nicht
interprtierbarer Strom von Bytes, der zur Kommunikation zwischen Programmen (bzw. „gegabelten Kopien“ eines
Programms) oder zum Lesen und Schreiben von verschiedenen Geräten und Dateien benutzt wird.
Ein Strom ist ein Kommunikationspfad zwischen der Quelle und dem Ziel eines Informationsblocks.
226 Die Klasse java.io enthält eine große Anzahl von Klassen zur Ein-/Ausgabe. Die meisten dieser Klassen
leiten sich von InputStream bzw. OutputStream ab.
227 Ein Strom von Bytes kann mit einem Wasserstrom verglichen werden. Wird aus einem Strom Wasser
entnommen, dann wird er als Eingabestrom benutzt. Wird in einen Strom Wasser geschüttet, dann wird er als
Ausgabestrom verwendet. Die Verbindung von Strömen kann mit dem Verbinden von Wasserschläuchen
verglichen werden.
328
Programmieren in Java
ByteArrayInputStream
FileInputStream
DataInput
BufferedInputStream
DataInputStream
FilterInputStream
InputStream
Object
PipedInputStream
SequenceInputStream
StringBufferedInputStream
LineNumberInputStream
PushbackInputStream
RandomAccessFile
ByteArrayOutputStream
FileOutputStream
OutputStream
FilterOutputStream
DataOutput
PipedOutputStream
Throwable
Exception
IOException
BufferedOutputStream
DataOutputStream
PrintStream
EOFException
FileNotFoundException
InterruptedIOException
UTFDataFormatException
Abb.: java.io
7.1 Die abstrakten Klassen InputStream und
OutputStream
7.1.1 InputStream
Aufgabenbeschreibung
Mit dieser Klasse können Leseoperationen eines Bytestroms verwirklicht werden.
Woher die Bytes kommen und wie sie befördert werden, spielt keine Rolle. Sie müssen
dem einlesenden Objekt nur zur Verfügung stehen. Die Aufgabe der Klasse InputStream
ist die Repräsentation von Klassen, die Eingaben aus verschiedenen Quellen
produzieren.
Klasse
ByteArrayInputStream
StringBufferInputStream
Funktion
Argument für den Konstruktor
Nutzungsmöglichkeit
Ein Puffer im arbeitsspeicher wird Der Puffer aus dem Bytes geholt
ald Eingabestrom benutzt.
werden.
Als Datenquelle. Verbunden mit
einem
FileInputStream-Objekt
kann daraus ein nützliches
Interface gestaltet werden.
Konvertiert einen String in einen Ein String. Die zugrundeliegende
InputStream
Implementierung benutzt einen
StringBuffer.
329
Programmieren in Java
FileInputStream
Dient zum Lesen aus einer Datei
PipedInputStream
Produziert
Daten
PipedOutputStream
das „Pipe“-Konzept.
SequenceInputStream
Konvertiert zwei oder mehrere
„InputStream“-Objekte in einen
einzelnen InputStream.
für
den
(implentiert
Als Datenquelle. Verbunden mit
einem
FiIeInputStream-Objekt
kann daraus ein nützliches
Interface gestaltet werden.
Ein String der den Dateinamen
enthält oder ein File- bzw.
FileDescriptor-Objekt
Als Datenquelle. Verbunden mit
einem FilterInputStream-Objekt
kann daraus ein nützliches
Interface gestaltet werden.
PipedOutputStream.
Als
Datenquelle
im
Multithreading. Verbunden mit
einem FilterInputStream-Objekt
kann daraus ein nützliches
Interface gestaltet werden.
Zwei InputStream-Objekte oder
eine Enumeration für einen
Container
von
InputStreamObjekten.
Als Datenquelle. Verbunden mit
einem FilterInputStream-Objekt
kann daraus ein nützliches
Interface gestaltet werden.
Abb.: Eingabestrom-Typen
Quellen zu einem Eingabestrom können sein:
1. Ein Array von Bytes
2. Ein Zeichenketten-Objekt
3. Eine Datei
4. Eine „Pipe“
5. Eine Folge von Strömen, die zu einem umfassenden, einzelnen Strom gesammelt werden können.
6. Anders beschaffene Quellen, z.B. eine Internet-Verbindung
Die „read“-Methoden
Zum Einlesen von Datenströmen gibt es die „read“-Methode. Die einfachste Form ist:
public abstract int read() throws IOException. Ein einzelnes Byte
wird aus dem Eingabestrom gelesen und ausgegeben. Falls der Bytestrom das Ende
erreicht hat, wird „-1“228 ausgageben. Die Methode führt ein blockierendes Lesen aus,
d.h.: Es wird auf Daten gewartet, falls sie nicht unmittelbar zur Verfügung stehen.
Die Methode public int read(byte[] bytes) throws IOException füllt
ein Datenfeld mit Bytes, die aus einem Strom gelesen wurden und gibt die Anzahl Bytes
zurück. Bei Bedarf (z.B. im Datenstrom sind nicht genügend Bytes vorhanden) werden
weniger Bytes gelesen, als das Datenfeld aufnehmen kann.
public int read(byte[] bytes, int offset, int length) throws
IOException füllt ein Datenfeld ab Position „offset“ mit bis zu length Bytes aus
dem Strom. Es wird entweder die Anzahl der gelesenen Bytes oder –1 für das
Dateiende ausgegeben.
228 Rückgabe „-1“ bedeutet zum Unterschied von C nicht, daß ein Fehler aufgetreten ist.
330
Programmieren in Java
Weitere nützliche Methoden
public int available throws IOException
Allen „read“-Methoden ist gemeinsam: Sie warten auf das Ende aller angeforderten
Eingaben. Es kann dabei zu längeren Blockaden beim Einlesen von Daten kommen.
Die Methode „available“ gibt die Anzahl Bytes zurück, die ohne Blockieren gelesen
werden kann.
public long skip(long n) throws IOException
Die „skip“-Methode benutzt „read“ zum Überspringen und blockiert deshalb unter den
gleichen Bedingungen wie „read“. Die gibt die Anzahl Bytes zurück, die sie
übersprungen hat, oder „-1“, falls das Ende des Stroms erreicht wurde
public boolean markSupported()
Die Methode gibt „true“ zurück, falls der Sttrom Markierungen unterstützt.
public void mark(int readLimit)
Die Methode markiert die aktuelle Position im Strom für eine spätere Rückkehr.
public void reset() throws IOException
Mit dieser Methode kann der Strom auf die Markierung zurücksetzen.
public void close() throws IOException
Diese Methode schließt die Verarbeitung von einem Strom. Die meisten Ströme
werden automatisch bei der Garbage Collection geschlossen.
331
Programmieren in Java
7.1.2 OutputStream
Aufgabenbeschreibung
Der Ausgabestrom ist ein Empfänger von Daten. Man findet Ausgabeströme fast nur in
Verbindung mit Eingabeströmen. Die Aufgabe der Klasse OutputStream ist die
Repräsentation von Klassen zur Festlegung der Datensenke. Das kann ein „Array von
Bytes“, eine Datei oder eine „Pipe“ sein.
Klasse
Funktion
ByteArrayOutputStream
Kreiert einen Arbeits-speicherPuffer.
Alle
vom
Strom
gesendeten Daten werden hier
plaziert.
FileOutputStream
Zum Schreiben einer Datei
PipedOutputStream
Information zum Schreiben ist
Eingabe für den assoziierten
PipedInputStream
Argumente für den Konstruktur
Nutzungsmöglichkeit
Optional: zu initialisierende
Puffergröße
Zum Bestimmen des Datenziels.
Verbunden
mit
einem
FilterOutputStream-Objekt kann
daraus ein nützliches Interface
gestaltet werden.
Eine Zeichenkette, die den
Namen der Datei repräsentiert ,
bzw. ein File- oder FileDescriptorObjekt.
Zum
Bestimmen
eines
Datenziels. Verbunden mit einem
FileOutputStream-Objekt
kann
daraus ein nützliches Interface
gestaltet werden.
PipedInputStream
Zum Bestimmen des datenziels
für „Multithreading“. Verbunden
mit einem FilterOutputStreamObjekt
kann
daraus
ein
nützliches Interface gestaltet
werden.
Abb.: Ausgabestrom-Typen
Die „write“-Methoden
public abstract void write(int b) throws IOException
Die Methode schreibt ein einzelnes Byte in den Ausgabestrom.
public void write(byte[] bytes) throws IOException
Die Methode schreibt den Inhalt des Datenfelds bytes in den Ausgabestrom.
public void write(byte[] bytes, int offset, int length)
throws IOException
Die Methode schreibt „length“ Bytes ab Position „offset“ aus dem Datenfeld
bytes.
public void flush() throws IOException
Diese Methode leert einen Ausgabestrom
public void close() throws IOException
Auch Ausgabeströme sollten am Ende einer Verarbeitung geschlossen werden.
332
Programmieren in Java
7.2 Gefilterte Ströme
Die Klassen FilterInputStream und FilterOutputStream zum Verknüpfen
von Strömen.
Diese Klassen ermöglichen das Verknüpfen von Strömen, keine neuen Methoden. Der
große Vorteil liegt in der Verbindung mit einem anderen Strom. Die Konstruktoren für
den FilterInputStream und den FilterOutputStream haben deshalb
InputStream- und OutputStream-Objekte als Parameter:
protected FilterInputStream(InputStream ein)
public FilterOutpuStream(OutputStream aus)
Nützliche Subklassen von FilterInputStream sind:
Klasse
Funktion
DataInputStream
Ermöglicht das
primitiven Typen
BufferedInputStream
Gepuffertes Einlesen
LineNumberInputStream
Verfolgt die zeilennummer im
Eingabestrom.
Die
Methode
getLineNumber()
und
setLineNumber(int)
können
verwendet werden
Hat
einen
Byte
großen InputStream
„Pushback“-Puffer, das letzte Braucht der Java-Compiler
gelesene Zeichen kann in den
strom zurückgelegt werden.
PushbackInputStream
Einlesen
von
Konstruktor-Argumente
Nutzungsmöglichkeit
InputStream
Enthält ein Interface für das lesen
primitiver Typen
InputStream
mit
optionale
Angabe der Puffergröße
InputStream
Fügt eine Zeilennummerierung
hinzu
Abb.: „FilterInputStream“-Typen
Nützliche Subklassen von FilterOutputStream sind:
Klasse
DataOutputStream
PrintStream
BufferedOutputStream
Funktion
Konstruktor-Argumente
Nutzungsmöglichkeit
Ausgabe primitiver Typen
OutputStream
Erlaubt das Schreiben primitiver
Typen
Produziert formatierte Ausgabe.
OutputStream
Formatierte Ausgabe
Gepufferte Ausgabe, der Puffer OutputStream
kann mit flush() geleert werden
Gepufferte Ausgabe
Abb.: „FilterOutputStream“-Typen
333
Programmieren in Java
Gepufferte Ströme
Gepufferte Ströme werden mit der Klasse BufferedInputStream für die Eingabe und
BufferedOutputStream für die Ausgabe realisiert:
public BufferedInputStream(InputStream ein)
public BufferedInputStream(InputStream ein, int puffergroesse)
public BufferedOutputStream(OutputStream aus)
public BufferedOutputStream(OutputStream aus, int puffergroesse)
Die Klasse BufferedInputStream implementiert die vollen Fähigkeiten der
InputStream-Methoden, besitzt aber zusätzlich einen gepufferten Byte-Array
(Cache für weitere Leseoperationen).
Die Klasse BufferedOutputStream implementiert die vollen Fähigkeiten der
OutputStream-Methoden, besitzt aber zusätzlich einen gepufferten Byte-Array.
Die BufferedInputStream-Klasse versucht mit einem einzigen read-Aufruf soviel
Daten wie möglich in ihre Puffer einzulesen. Die BufferedOutputStream-Klasse
ruft nur dann die write-Methode auf, wenn ihr Puffer voll ist oder wenn flush aufgerufen
wird.
Datenströme
Die Filter DataInputStream und DataOutputStream sind nützliche Filter des
java.io-Pakets. Sie ermöglichen Schreiben und Lesen primitiver Typen in Java auf
maschinenunabhängige Weise.
DataInputStream implementiert eine DataInput-Schnittstelle. Diese Schnittstelle
definiert Methoden zum Lesen von primitiven Datentypen in Java (sowie noch ein paar
weitere Methoden).
DataOutputStream implementiert eine DataOutput-Schnittstelle mit AusgabeMethoden, die den Eingabe-Methoden der DataInput-Schnittstelle entsprechen.
Die Konstruktoren zu den DataInputStream- und DataOutputStream-Klassen
sind Stromfilter-Konstruktoren, die den zu filternden Strom als Parameter verwenden:
public DataInputStream (InputStream ein)
public DataOutputStream(OutputStream aus)
Die Schnittstelle DataInput. Sie enthält Methoden zum Lesen primitiver Typen:
public
public
public
public
public
public
public
public
public
public
boolean readBoolean() throws EOFException, IOException
byte readByte() throws EOFException, IOException
int readUnsignedByte() throws EOFException, IOException
char readChar() throws EOFException, IOException
short readShort() throws EOFException, IOException
int readUnsignedShort() throws EOFException, IOException
int readInt() throws EOFException, IOException
long readLong() throws EOFException, IOException
float readFloat() throws EOFException, IOException
double readDouble() throws EOFException, IOException
334
Programmieren in Java
public String readUTF() throws EOFException, IOException229 ,
UTFDataFormatException
Ausgeworfene Ausnahmen sind vom Typ IOException oder EOFException.
EOFException wird ausgeworfen, wenn das Ende des Stroms erreicht wird. Die
Ausnahme läßt sich überall einsetzen, wo bisher auf „-1“ überprüft wurde.
Zum Lesen einer durch Zeilenumschaltung (\r, \n) begrenzten Zeile aus einer
Textdateigibt es die Methode: public String readLine() throws
IOException. „\r, \n, \r\n“ werden entfernt, bevor die Zeile als Zeichenkette
wiedergegeben wird.
Bsp.: Einlesen von der Standardeingabe und Ausgabe
Mit der Methode public int skipBytes(int anzahlBytes) wird eine Anzahl
Bytes übersprungen.
Die Schnittstelle DataOutput. Duch diese Schnittstelle sind folgende Methoden zur
Ausgabe primitiver Typen definiert:
public
public
public
public
public
public
public
public
void
void
void
void
void
void
void
void
writeBoolean(boolean b) throws IOException
writeByte(int b) throws IOException
writeChar(int c) throws IOException
writeShort(int c) throws IOException
writeInt(int i) throws IOException
writeFloat(float f) throws IOException
writeDouble(double d) throws IOException
writeUTF(String s) throws IOException
Mit den Methoden
public void writeBytes(String s) throws IOException
public void writeChars(String s) throws IOException
kann eine Zeichenkette als eine Reihe von Bytes oder Zeichen abgelegt werden.
Die PrintStream-Klasse
Der „System.out“-Strom mit den Methoden „System.out.print“ bzw.
„System.out.println“ ist eine Instanz von PrintStream. „System.in“ ist ein
InputStream.
Erstellen eines „PrintStream“-Objekts. Das PrintStream-Objekt muß in einen
existierenden Ausgabestrom angehangen werden. Über einen optionalen Parameter
kann automatisch die Methode flush() aufgerufen werden:
public PrintStream(OutputStream aus)
public PrintStream(OutputStream aus, boolean autoFlush)
229 UTF (Unicode Transmission Format) ist ein spezielles Format zum Kodieren von 16-Bit-Unicode-Werten
335
Programmieren in Java
Methode für die Ausgabe von Objekten.
public
public
public
public
public
public
public
public
public
public
public
public
public
public
public
public
public
public
public
public
void
void
void
void
void
void
void
void
void
void
void
void
void
void
void
void
void
void
void
void
flush()
close()
write(int b)
print(Object o)
print(String s)
print(boolean c)
print(char[] puffer)
print(char c)
print(int i)
print(float f)
print(double d)
println(Object f)
println(String s)
println(boolean c)
println(char[] puffer)
println(char c)
println(int i)
println(float f)
println(double d)
println()
336
Programmieren in Java
7.3 Die Klasse File
Ein File-Objekt kann sich auf eine Datei oder ein Verzeichnis beziehen und läßt sich
auf verschiedene Arten erstellen.
public File(String pfadname)
erstellt eine File-Instanz, die in pfadname angegeben wurde.
public File(String pfadname, String dateiname)
erstellt eine File-Instanz, die sich aus dem in dateiname angegebenen Dateinamen und dem in
pfadnamen aungegebenen Pfad zusammensetzt.
public File(File verzeichnis, String dateiname)
erstellt eine File-Instanz, die sich aus dem in dateiname angegebenen Dateinamen und dem in
verzeichnis angegebenen Verzeichnis zusammensetzt.
Die Klasse File (class java.io.File implements Serializable,
Comparable) umfaßt Methoden zum Bearbeiten von Dateien bzw. Verzeichnissen.
Nachdem ein File-Objekt erzeugt wurde, können Methoden zum Zugriff auf die einzelnen
Bestandteile des Dateinamens aufgerufen werden:
public String getName();
// liefert den Namen der Datei
public String getPath();
// liefert den kompletten Namen (Datei oder Verzeichnis)
public String getAbsolutePath();
// liefert den absoluten Pfadnamen für das File-Objekt
public String getParent();
Die Methoden
public boolean isFile()
public boolean isDirectory()
überprüfen, ob eine Datei oder ein Verzeichnis vorliegt. Mit
public boolean canRead()
public boolean canWrite()
wird Lese- bzw. Schreiberlaubnis überprüft. Die Methode
public long lastModified()
zeigt an, wann die Datei bzw. das Verzeichnis geändert wurde. Mit
public boolean exists()
wird die physikalische Existenz einer Datei oder eines Verzeichnisses überprüft. Über
public String getName()
wird der Name einer Datei bzw. eines Verzeichnisses ermittelt.
Wurde ein File-Objekt für ein Verzeichnis konstruiert, stehen folgende Methoden zur
Verfügung:
public String[] list();
337
Programmieren in Java
liefert einen Array von Strings, der für jeden gefundenen Verzeichniseintrag ein Element
enthält. Die Liste enthält den Namen aller Dateien und Unterverzeichnisse mit
Ausnahme von „.“ und „..“. Die einfache Funktion list() liefert nur relative Pfade
(Dateiname oder Verzeichnisname). Komplette File-Objekte beschafft die Methode
listFiles), die ihre vollständige Pfadangabe kennen. Der vollständige Pfad kann dann mit
getName() erfragt werden.
In einer Variante von list() kann die Auswahl der Verzeichniseinträge eingeschränkt
werden. Es muß aber dann ein Objekt übergeben werden, das das Interface
FilenameFilter implementiert. Dieses besitzt die Methode accept230, die für jede
gefundene Datei aufgerufen wird und entscheidet, ob sie in die Liste aufgenommen
werden soll oder nicht. Zusätzlich gibt es die statische Methode listRoots, mit der
eine Liste der Wurzeln verfügbarer Dateisysteme beschafft werden kann.
public static File[] listRoots()
liefert die verfügbaren Wurzeln der Dateisysteme oder null, falls diese nicht festgestellt
werden konnten.
Neben dem Zugriff auf Verzeichniseinträge gibt es Methoden231 für Löschen,
Umbenennen von Dateien bzw. Verzeichnissen und für das Neuanlegen von
Verzeichnissen.
public boolean mkdir()
// Anlegen des spezifizierten Verzeichnisses
public boolean mkdirs()
// auch "Vater"-Verzeichnisse werden automatisch angelegt.
public boolean renameTo(File ziel)
public boolean delete();
// löscht die durch das File-Objekt bezeichnete Datei
Da es bei URL-Objekten häufig vorkommt, daß eine Datei die Basis ist, wurde ab
Version 1.2 von Java die Methode toURL() aufgenommen:
public URL toURL() throws MalformedURLException
// liefert ein URL-Objekt vom File-Objekt
Es muß ein File-Objekt erzeugt werden. Anschließend erzeugt toURL() ein URLObjekt, das das Protokoll „file“ trägt und danach eine absolute Pfadangabe zur Datei
bzw. zum Verzeichnis enthält.
230 public boolean accept(File verzeichnis, String dateiName)
231 Alle Methoden geben true zurück, wenn sie ihre Aufgabe erfolgreich ausführen konnten; anderenfalls geben
sie false zurück
338
Programmieren in Java
7.4 Die RandomAccessFile-Klasse
Für den Zugriff auf Random-Access-Dateien stellt das Paket java.io die Klasse
RandomAccessFile zur Verfügung. Es kann nur auf Dateien zugegriffen werden,
auch das durch die „Streams“ realisierte Filter-Konzept gibt es in Random-AccessDateien nicht.
Öffnen, Neuanlegen und Schließen. Das Öffnen von Random-Access-Dateien erfolgt mit
den Konstruktoren:
public RandomAccessFile(File datei, String mode) throws
FileNotFoundException
Bei der Übergabe des File-Objekts wird die durch dieses Objekt spezifizierte Datei
geöffnet.
public RandomAccessFile(String name, String mode) throws
FileNotFoundException
Bei der Übergabe des String-Parametres „name“ wird die Datei mit diesem Namen
geöffnet. Der zweite Parameter mode bestimmt die Art des Zugriffs:
- „r“: Öffnen nur zum Lesen
- „rw“: Öffnen zum Schreiben und Lesen. Ein reiner Schreibmodus wird nicht unterstützt.
Es gibt in der Klasse RandomAccessFile keine explizite Differenzierung zwischen
Öffnen der Datei und Neuanlegen. Implizit gilt: Eine Datei wird neu angelegt, wenn beim
Öffnen im Modus „w“ nicht vorhanden ist. Existiert die Datei bereits, wird sie unverändert
geöffnet, und es gibt keine Möglichkeit, ihren Inhalt zu löschen oder die Dateilänge auf
einen bestimmten Wert zusetzen.
Das Schliessen erfolgt durch Aufruf der parameterlosen Methode close.
Positionieren des Dateizeigers. Jeder Schreib- und Lesezugriff erfolgt an der Position,
die durch den aktuellen Inhalt des Satzzeigers bestimmt wird und positioniert den Zeiger
um die Anzahl gelesener bzw. geschriebener Bytes weiter. Die Klasse
RandomAccessFile stellt eine Reihe von Methoden zum Zugriff auf den Satzzeiger
zur Verfügung.
Die Methode seek. Die RandomAccessFile-Klasse besitzt alle Methoden, die in
den Schnittstellen DataInput und DataOutput verfügbar sind. Mit public void
seek(long dateiPosition) throws IOException kann man an jede
beliebige Position der Datei springen. Mit der Methode public long
getFilePointer() throws IOException kann die derzeitige Dateiposition232
bestimmt werden.
Weiterhin stehen lesende und schreibende Zugriffsmethoden zur Verfügung.
232 Der Dateipositionswert in den Methoden seek und getFilePointer() ist die Anzahl der Bytes vom
Anfang bis zum Ende der Datei.
339
Programmieren in Java
RandomAccessFile
<<Konstruktoren>>
public RandomAccessFile(File datei, String zugriffsmodus) throws FileNotFoundException;
// erzeugt einen Random-Access-Datei-Strom vom File-Objekt.
// Ob aus der datei gelesen oder geschrieben wird, bestimmt
// ein String, der den Modus angibt: „r“, „w“ oder „rw“.
public RandomAccessFile(String datei, String zugriffsmodus) throws FileNotFoundException;
<< Methoden >>
public long getFilePointer() throws IOException;
publc void seek(long pos) throws IOException;
// setzt den Dateizeiger – bezogen auf den Dateianfang – auf eine
// bestimmte Stelle, an der der nächste Lese- und Schreibzugriff
// stattfindet
public int skipBytes(int n) throws IOException;
public long length() throws IOException;
public final boolean readBoolean() throws IOException;
public final byte readByte() throws IOException;
public final char readChar() throws IOException;
public final double readDouble() throws IOException;
public final float readFloat() throws IOException;
public final int readInt() throws IOException;
public final long readLong() throws IOException;
public final short readShort() throws IOException;
public final String readUTF() throws IOException;
public final void readFully(byte b[]) throws IOException;
public final void readFully(byte[], int off, int laenge) throws IOException;
public final String readLine() throws IOException;
public final int readUnsignedByte() throws IOException;
public final int readUnsignedShort() throws IOException;
public int read() throws IOException;
public int read(byte b[]) throws IOException;
public int read(byte b[], int off, int laenge) throws IOException;
public final void writeBoolean(boolean w) throws IOException;
public final void writeByte(int w) throws IOException;
public final void writeBytes(String s) throws IOException;
public final void writeChar(int w) throws IOException;
public final void writeChars(String s) throws IOException;
public final void writeDouble(double w) throws IOException;
public final void writeFloat(float w) throws IOException;
public final void writeInt(int w) throws IOException;
public final void writeLong(long w) throws IOException;
public final void writeShort(int w) throws IOException;
public final void writeUTF(String str) throws IOException;
public void write(int b) throws IOException;
public void write(byte b[]) throws IOException;
public void write(byte b[], int off, int laenge) throws IOException;
Abb.: Die Klasse RandomAccessFile
340
Programmieren in Java
7.5 Die Klasse StreamTokenizer
Die Klasse StreamTokenizer arbeitet im Gegensatz zum StringTokenizer aus
dem Paket java.util auf einem Datenstrom, genauer auf einem Reader. Sie
beachtet keine Unicode-Eingabe, sondern nur Zeichen aus dem Bereich von \u0000
bid \u00FF. Wahrend des Parsens werden bestimmte Merkmale aus dem Text
erkannt, so u. a. Identifier (etwa Schlüsselworte), Zahlen, Strings in Anführungszeichen
und verschiedene Kommentararten (C-Stil oder C++-Stil). Der Erkennungsvorgang wird
anhand einer Syntaxtabelle überprüft. Diese Tabelle enthält bspw. die Zeichen, die ein
Schlüsselwort identifizieren oder die Zeichen, die Trennzeichen sind. Jedes gelesene
Zeichen wird dann keinem, einem oder mehreren Attributen zugeordnet. Diese Attribute
fallen in die Kategorie Trennzeichen, alphanumerische Zeichen, Zahlen, Hochkomma
bzw. Anführungszeichen.
Zur Benutzung der Klasse wird zunächst ein StreamTokenizer-Objekt erzeugt und
dann die Syntaxtabellen initialisiert. Das Überlesen von Kommentarzeilen wird durch
st.slashSlashComments(true);
st.slashStarComments(true);
// Kommentar
/* Kommentart */
gesteuert. Die erste Methode überliest im Eingabestrom alle Zeichen bis zum Return.
Die zweite Methode überliest alles bis zum Stern / Slash.
Beim Lesen des Datenstroms mit nextToken() kann über bestimmte Flags erfragt
werden, ob im Stream ein Wort (TT_WORD) eine Zahl (TT_NUMBER), das Ende der
Datei (TT_EOF) oder das Ende der Zeile (TT_EOL) vorliegt. Wichtig ist
eolIsSignificant(true) zu setzen, da andernfalls der StreamTokenizer nie ein
TT_EOL findet. Wurde ein Wort erkannt, dann werden alle Zeichen in Kleinbuchstaben
konvertiert.
341
Programmieren in Java
class java.io.StreamTokenizer
<< Konstruktor >>
StreamTokenizer(Reader r)
<< Methoden >>
public void resetSyntax
// Reinitialisiert die Syntaxtabelle des Tokenizers, so dass kein Zeichen eine Soderstellung
// geniesst.
public void wordChars(int low, int hi)
// Zeichen im Bereich von low bis hi werden als Trennzeichen erkannt
public void whitespaceChars(int low, int hi)
// Zeichen im Bereich von low bis hi werden als Trennzeichen erkannt.
public void ordinaryChars(int low, int hi)
// Zeichen im Bereich von low bis hi geniessen keine Sonderbehandlung, werden als
// normale zeichen behandelt.
public void ordinaryChar(int ch)
// Das Zeichen besitzt keine zusätzliche Funktion, ist bspw. kein Kommentarzeichen,
// trennsymbol oder Nummernzeichen
public void parseNumbers()
// Nummern sollen vom Tokenizer erkannt werden. In der Syntaxtabelle gelten die 12
// Zeichen 0, 1, 2, 3, 4, 5, 6., 7, 8, 9, ., - als numerisch. Liegt ein Gleipunktzahl an,
// so wird der Vorkommateil in nval abgelegt und das Token ergibt im Attribut ttype
// den Wert TT_NUMBER
public void commentChar(int ch)
// Gibt das Zeichen an, das einen einzeiligen Kommentar einleitet
public void slashStarComments(boolean flag)
// Der Tokenizer soll Kommentare im C-Stil (/* .. */) erkennen oder nicht
public void slashSlashComments(boolean flag)
// Der Tokenizer soll Kommentare im C++-Stil (// ..) erkennen oder nicht
public void lowerCaseMode(boolean fl)
// liegt in ttype ein Token vom Typ TT_WORD vor, so wird das automatisch in
// Kleinschreibweise konvertiert, falls fi gleich true ist
public int nextToken() throws IOException
// liefert das nächste Token im Strom. Der Typ des Token wird im Attribut ttype hinterlegt.
// Zusätzliche Informationen befinden sich im Attribut nval (Nummer) oder sval (Zeichenkette).
// In der Regel wird solange geparst, bis das Token TT_EOF zurückgegeben wird.
public void pushBack()
// Der Aufruf von nextToken() liefert den aktuellen wert im Attribut ttype und ändert nval oder sval
// nicht
public int lineno()
// liefert die Zeilennummer
Abb.: Die Klasse StreamTokenizer
342
Programmieren in Java
7.6 Klassen für spezielle nützliche Ströme
Die LineInputStream-Klasse
Die SequenceInputStream-Klasse
Die PushbackInputStream-Klasse
343
Programmieren in Java
7.7 Java 1.1 IO-Ströme
7.7.1 Grundlagen
Bis zur Version 1.0 des JDK gab es nur Byte-Streams in Java. Das ergab
Schwierigkeiten bei der Umwandlung zwischen Bytes und 16 Bit langen UnicodeZeichen, die innerhalb von Java benutzt werden. Im JDK 1.1. wurden daher „Character“Streams auf der Basis von 16 Bit langen Unicode-Zeichen eingeführt. Die Kompatibilität
wurde durch Brückenklassen gewährleistet, die „Character“-Streams in „Byte“-Streams
überführen (und umgekehrt).
Quellen und Senken: „Java 1.0“-Klassen
InputStream
OutputStream
FileInputStream
FileOutputStream
StringBufferInputStream
ByteArrayInputStream
ByteArrayOutputStream
PipedInputStream
PipedOutputStream
Korrespondierende Klassen in Java 1.1
Reader
Konverter: InputStreamReader
Writer
Konverter: OutputStreamWriter
FileReader
FileWriter
StringReader
StringWriter
CharArrayReader
CharArrayWriter
PipedReader
PipedWriter
Abb. Wichtige Klassen zur Eingabe
In Filterklassen kann nur eine etwas gröbere Zuordnung von Klassen aus Java 1.0 zu
Java 1.1 angegeben werden. So ist „BufferedOutputStream“ eine Subklasse von
FilterOutputStream, BufferedWriter ist dagegen keine Subklasse von
FilterWriter.
Filter: Klassen in Java 1.0
FilterInputStream
FilterOutputStream
BufferedInputStream
BufferedOutputStream
DataInputStream
PrintStream
LineNumberInputStream
StreamTokenizer
PushbackInputStream
Korrespondierende Klassen in Java 1.1
FilterReader
FilterWriter (abstrakte Klasse ohne Subklassen)
BufferedReader (mit readLine)
BufferedWriter
DataInputStream, bei readLine() BufferedReader
verwenden
PrintWriter
LineNumberReader
StreamTokenizer
PushbackReader
Abb. Wichtige Klassen zur Ausgabe
Einige Klassen bleiben von der Umstellung unberührt: DataOutputStream, File,
RandomAccessFile, SequenceInputStream.
344
Programmieren in Java
7.7.2 Die abstrakte Klassen Reader und ihre Ableitungen
Basis aller sequentiellen Eingaben ist die abstrakte Klasse Reader, die eine
Schnittstelle für streambasierte Eingaben zur Verfügung stellt:
public abstract class java.io.Reader
{
protected Reader();
public abstract void close() throws IOException;
// schliesst den Eingabestrom
public void mark(int readAheadlimit) throws IOException;
// markiert eine bestimmte Position innerhalb eines Eingabestroms
public boolean markSupported();
// überprüft, ob Markieren unterstützt wird
public int read() throws IOException;
// liest das nächste Zeichen aus dem Eingabestrom und liefert es als
// "int". Der Rückgabewert –1 kennzeichnet das Ende des Eingabestroms
public int read(char cbuf[]) throws IOException;
// übernimmt eine Reihe von Zeichen in den als Parameter angegebenen
// Array
public abstract int read(char cbuf[], int off, int len) throws IOException;
// übernimmt eine Reihe von zeichen in einen Bereich des Puffers, der
// durch Versatz (Offset) und die gewünschte Länge gekennzeichnet ist
public long skip(long n) throws IOException;
// überliest Zeichen im Eingabestrom
public boolean ready() throws IOException;
// liefert True, falls der nächste Aufruf von read erfolgen kann, ohne
// daß die Eingabe beendet ist
public void reset() throws IOException;
// setzt den Lesezeiger an die markierte Stelle zurück
}
Von der abstrakten Basisklasse Reader können keine Instanzen abgeleitet werden. Es
gibt eine Reihe aus Reader abgeleitete Klassen, die die Verbindung zur konkreten
Eingabe bzw. zum konkreten Eingabegerät herstellen.
Klasse
InputStreamReader
FileReader
PushbackReader
BufferedReader
LineNumberReader
StringReader
CharArrayReader
PipedReader
Bedeutung
Abstrakte Klasse für alle Reader, die einen ByteStrom in einen Character-Strom umwandeln
Konkrete Ableitung von InputStreamReader zum
Einlesen einer Datei
Eingabefilter mit der Möglichkeit zur Rückgabe von
Zeichen
Reader zur Eingabepufferung und zum Lesen
kompletter Zeilen
Ableitung von BufferedReader mit der Fähigkeit
zum Zählen von Zeilen
Reader zum Einlesen von Zeichen aus einem String
Reader zum Einlesen von Zeichen aus einem
Zeichen-Array
Reader zum Einlesen von Zeichen aus einem
PipedWriter
Abb.: Von Reader abgeleitete Klassen
345
Programmieren in Java
InputStreamReader
Diese Klasse konvertiert Byte- in Character-Streams.
FileReader
Sie ermöglicht die Eingabe einer Datei. Die Klasse FileReader geht direkt aus einem
InputStreamReader hervor.
Konstruktoren. Mit ihnen ermöglicht FileReader das Öffnen von Dateien:
public FileReader(String dateiName) throws FileNotFoundException;
Bei Übergabe der Zeichenkette dateiName wird die Datei mit dem angegebenen
Namen zum Lesen geöffnet. Falls sie nicht vorhanden ist, kommt es zur Ausnahme des
Typs FileNotFoundException
public FileReader(File datei) throws FileNotFoundException;
erwartet ein File-Objekt zur Spezifikation einer zu öffnenden Datei.
public FileReader(FileDescriptor fd);
erwartet ein File-Deskriptor-Objekt, das eine bereits geöffnete Datei angibt.
StringReader
Diese Klasse erlaubt das Lesen von Zeichen aus einem String.
CharArrayReader
Diese Klasse erlaubt das Lesen von Zeichen aus einen „Zeichen-Array“
BufferedReader
Diese Klasse dient zur Pufferung von Eingaben. BufferedReader implementiert die
vollen Fähigkeiten der Methoden von Reader. Diese Klasse verwendet dazu gepufferte
Zeichen-Arrays.
Konstruktoren: public BufferedReader(Reader ein)
public BufferedReader(Reader ein, int gr)
Der erste Parameter ist ein Reader-Objekt, auf das ein BufferedReader aufgesetzt
werden soll. Der optionale Parameter gr gibt die Größe des internen Puffer an. Fehlt er,
so wird eine für die meisten Situationen angemessene Standardeinstellung verwendet.
Zeilenweises Lesen: public String readLine throws IOException.
Rückgabewert ist ein String mit dem Zeicheninhalt (ohne Begrenzungszeichen) oder
„null“, falls das Ende von „Stream“ erreicht wurde.
LineNumberReader
Diese Klasse ist eine Ableitung von BufferedReader, die zusätzlich noch die Anzahl
der Eingabezeichen beim Einlesen zählen kann. Mit „public
int
getLineNumber()“ wird der aktuelle Stand des Zeilenzählers abgefragt. Mit
public void setLineNumber(int LineNumber) kann der aktuelle Stand
des Zeilenzählers verändert werden.
346
Programmieren in Java
7.7.3 Die abstrakte Klasse Writer und ihre Ableitungen
Basis aller sequentiellen Eingaben ist die Klasse Writer, die eine Schnittstelle für
„stream“-basierte Ausgaben zur Verügung stellt:
public abstract class java.io.Writer
{
protected Writer();
// Oeffnen und Vorbereiten des Ausgabestroms
public abstract void close() throws IOException;
// Schliessen des Ausgabestroms
public abstract void flush() throws IOException;
// Leeren der Ausgabe von einem gepufferten Cache
public void write(int c);
// Grundform mit einem einzigen Ausgabeparameter vom typ int,
// der als Byte in den Ausgabestrom geschrieben wird
public void write(char einPuffer[]) throws IOException;
// Varianten von write, die ein Array von Bytes oder ein String-Objekt
// erwarten und dieses durch Aufruf von write() ausgeben
public abstract void write(char einPuffer[], int off, int len) throws
IOException;
public void write(String str) throws IOException;
public void write(String str, int off, int len) throws IOException;
}
Von der abstrakten Basisklasse Writer können keine Instanzen abgeleitet werden. Es
gibt eine Reihe aus „Writer“ abgeleitete Klassen, die die Verbindung zur konkreten
Ausgabe(gerät) herstellen:
Klasse
OutputStreamWriter
FileWriter
FilterWriter
PrintWriter
BufferedWriter
StringWriter
CharArrayWriter
PipedWriter
Bedeutung
Abstrakte Basisklasse für alle Writer, die einen Character-Stream in
einen Byte-Stream umwandeln
Konkrete Ableitung von OutputStreamWriter zur Ausgabe einer Datei
Abstrakte Basisklasse für die Konstruktion von Ausgabefiltern
Ausgabe aller Basistypen im Textformat
Writer mit Ausgabepufferung
Writer zur Ausgabe in einem String
Writer zur Ausgabe im Zeichen-Array
Writer zur Ausgabe in einem PipedReader
Abb.: Aus Writer abgeleitete Klassen
Von den abgeleiteten Klassen wird erwartet: Überlagerung der Methoden flush(),
close() und write(char einPuffer[], int offset, int laenge).
Zun Schachteln von Ausgabe-Streams gibt es die aus Writer abgeleiteten Klassen:
BufferedWriter, PrintWriter und FilterWriter. Falls die "write"Methode eines derart geschachtelten "Writer" aufgerufen wird, gibt sie Daten nicht
mehr direkt an den internen "Writer", sondern führt Filterfunktionen aus.
OutputStreamWriter
Diese Klasse übernimmt eine Konvertierung zwischen Character- und Byte-Streams.
347
Programmieren in Java
FileWriter
ermöglicht die Ausgabe in eine Datei. Sie implementiert die abstrakten Eigenschaften
von Writer. Die Klasse FileWriter bietet 4 Konstruktoren für das Öffnen von Dateien
an:
public FileWriter(String dateiName) throws IOException
Falls dateiName eine bereits vorhandene Datei bezeichnet, wird sie geöffnet und ihr bisheriger Inhalt
gelöscht, anderenfalls wird eine neue Datei mit diesem Namen angelegt. Sukzessive Aufrufe von write
schreiben dann in diese Datei.
public FileWriter(String dateiName, boolean append) throws IOException
Wird der Parameter append mit dem Wert true an den Konstruktor übergeben, dann werden die
Ausgabezeichen an die Datei angehängt, falls sie bereits existiert.
public FileWriter (File datei) throws IOException
public FileWriter (FileDescriptor fd)
Parameter ist hier ein File-Objekt bzw. ein FileDescriptor-Objekt
Die Klasse StringWriter
Sie besitzt zwei Konstruktoren:
public StringWriter()
bestimmt einen StringWriter in der Standardgröße eines StringBuffer-Objekts. Der interne Puffer wächst
automatisch, falls fortgesetzte Aufrufe von „write“ das eroderlich machen
public StringWriter(int initialGroesse)
Zugriffe: Die schreibenden Zugriffe auf die Puffer erfolgen mit den von Writer
bekannten „write“-Methoden. Für den Zugriff auf den Inhalt des Puffers gibt es die
Methoden public StringBuffer getBuffer() und public String
toString().
CharArrayWriter
Sie schreibt Zeichen in ein Character-Array, das automatisch bei fortgesetzten Aufrufen
von „write“ erweitert wird. Die Klasse besitzt zwei Konstruktoren:
public CharArrayWriter()
public CharArrayWriter(int initialGroesse)
Zugriffe: Schreibende Zugriffe erfolgen mit den von „Writer“ bekannten „write“Methoden. Für den Zugriff auf das Array gibt es die Methoden public String
toString() und public char[] toCharArray(). Mit der Methode public
void reset() wird der interne Puffer geleert. Die Größe des Zeichen-Array wird
über public int size() ermittelt. Durch Aufruf von public void
writeTo(Writer aus) throws IOException kann der Inhalt des Array an
einen anderen Writer übergeben werden und so mit einem einzigen Funktionsaufruf
in eine Datei geschrieben werden.
BufferedWriter
Die Klasse puffert „Stream“-Ausgaben über einen internen Puffer, in dem die
Ausgaben von write zwischengespeichert werden. Falls der Puffer gefüllt ist oder die
348
Programmieren in Java
Methode flush aufgerufen wird, werden alle gepufferten Ausgaben in den „Stream“
geschrieben. Das Puffern der Ausgabe ist immer dann nützlich, wenn die Ausgabe in
eine Datei geschreiben werden soll. BufferedWriter besitzt zwei Konstruktoren:
public BufferedWriter(Writer aus)
public BufferedWriter(Writer aus, int groesse)
In beiden Fällen wird ein existierender Writer übergeben, an den alle gepufferten
Ausgaben weitergereicht werden. Ist die Größe des Puffers nicht explizit angegeben,
legt BufferedWriter einen Standardpuffer an.
Die „write“-Methoden von BufferedWriter entsprechen denen der Klasse Writer.
Zusätzlich gibt es eine Methode newLine mit der eine Zeileumschaltung in den Stream
geschrieben werden kann.
PrintWriter
Diese Klasse stellt Ausgabemethoden für alle primitiven Datentypen und für
Objektgruppen zur Verfügung.
Konstruktoren.
public PrintWriter(Writer aus)
instanziert ein PrintWriter-Objekt durch Übergabe eines Writer-Objekts, auf das die Ausgabe
umgelenkt werden soll.
public PrintWriter(Writer aus, boolean autoflush)
Mit dem Parameter autoflush wird angegeben, ob nach der Ausgabe „einer Zeilenschaltung“
automatisch die Methode flush() aufgerufen werden soll.
Ausgabe primitiver Typen (und Objekttypen): Sie wird realisiert über eine Reihe
überladener Methoden mit dem Namen „print“. Zusätzlich gibt es alle Methoden in der
Variante println, bei der automatisch an das Ende der Ausgabe ein Zeilenvorschub
(Zeilenumschaltung) angehängt wird. Println() existiert auch parameterlos zur
Ausgabe einer einzelnen Zeilenumschaltung.
public
public
public
public
public
public
public
public
public
void
void
void
void
void
void
void
void
void
print(boolean b)
print(char z)
print(char s[])
print(double d)
print(float f)
print(int i)
print(long l)
print(Object obj)
print(String s)
349
Programmieren in Java
7.7.4 Demonstrationsprogramm zur Ein-/ Ausgabe ab Java Version 1.1
Das Demonstrationsprogramm soll folgende Ein-/Ausgabe zeigen:
1. Zeilenweises Lesen
2. Eingabe vom Speicher
3. Formatierte Speicher-Eingabe
4. Zeilennummerierung und Dateiausgabe
5. Speichern und Wiedergewinnen von Daten
import java.io.*;
public class EinAusDemo
{
public static void main(String[] args)
{
try
{
// 1. Zeilenweises Lesen
BufferedReader ein1 = new BufferedReader(
new FileReader(args[0]));
String s1, s2 = new String();
while ((s1 = ein1.readLine()) != null)
s2 += s1 + '\n';
ein1.close();
// Lesen Standardeingabe
BufferedReader stdin = new BufferedReader(
new InputStreamReader(System.in));
System.out.print("Gib eine Zeile an: ");
System.out.println(stdin.readLine());
// 2. Eingabe vom Speicher
StringReader ein2 = new StringReader(s2);
int zch;
while ((zch=ein2.read()) != -1)
System.out.print((char) zch);
// 3. Formatierte Speicher-eingabe
try
{
DataInputStream ein3 = new DataInputStream(
new StringBufferInputStream(s2));
while (true)
System.out.print((char) ein3.readByte());
}
catch(EOFException e)
{
System.out.println("Ende des Stroms");
}
// 4. Zeilennummerierung und Datei-Ausgabe
try
{
LineNumberReader zn = new LineNumberReader(
new StringReader(s2));
// BufferedReader ein4 = new BufferedReader(zn);
PrintWriter aus1 = new PrintWriter(
new BufferedWriter(
new FileWriter("EinAusDemo.aus")));
while ((s1 = zn.readLine()) != null)
aus1.println("/* " + zn.getLineNumber() + " */" + s1);
350
Programmieren in Java
// System.out.println("/* " + zn.getLineNumber() + " */" + s1);
aus1.close();
}
catch(EOFException e)
{
System.out.println("Ende des Stroms");
}
// 5. Speichern und Wiedergewinnen von Daten
try
{
DataOutputStream aus2 = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream("Daten.txt")));
aus2.writeDouble(3.14159);
aus2.writeBytes("Das war Pi");
aus2.close();
DataInputStream ein5 = new DataInputStream(
new BufferedInputStream(
new FileInputStream("Daten.txt")));
BufferedReader ein5br = new BufferedReader(
new InputStreamReader(ein5));
System.out.println(ein5.readDouble());
System.out.println(ein5br.readLine());
}
catch(EOFException e)
{
System.out.println("Ende des Stroms");
}
}
catch(FileNotFoundException e)
{
System.out.println("Datei nicht gefunden: " + args[1]);
}
catch(IOException e)
{
System.out.println("IO Exception");
}
}
}
351
Programmieren in Java
8. Serialisierung
8.1 Grundlagen
Serialisierung233 ist die Fähigkeit ein Objekt, das im Hauptspeicher der Anwendung
existiert, in ein Format zu konvertieren, das es erlaubt, das Objekt in eine Datei zu
schreiben oder über eine Netzwerkverbindung zu transportieren. Auch der umgekehrte
Weg gehört dazu: Rekonstruktion eines in serialisierter Form vorliegenden Objekts in
das interne Format der laufenden Java-Maschine.
8.1.1 Das Interface Serializable
Serialisierbare Objekte können in Dateien gespeichert oder über Netzwerke übertragen
werden. Dazu müssen sie in ein Byteformat umgewandelt werden können. Das
Markierungsinterface Serializable kennzeichnet eine Klasse als serialisierbar.
Ähnlich wie bei Cloneable sind dazu keine Methoden zu implementieren, es müssen
aber auch alle referenzierenden Klassen serialisierbar sein. Falls dies nicht der Fall ist,
wird eine NotSerializableException erzeugt.
Voraussetzung für das Serialisieren ist die Implementierung der Schnittstelle
Serializable. Das Interface enthält keine Implementierung, sondern dient nur dazu,
durch die Implementierungs-Hierarchie die Fähigkeit zum Schreiben anzuzeigen.
233 häufig auch mit dem Begriff Persistenz gleichgesetzt. Persistenz bezeichnet das dauerhafte Speichern von
Daten auf einem externen Datenträger, so daß sie auch nach dem Beenden des Programms erhalten bleiben.
352
Programmieren in Java
8.1.2 Die Klasse ObjectOutputStream
Zur Serialisierung eines Objekts ( - oder allgemeiner Daten bzw. Primitive - ) benötigt
man einen OutputStream. Am besten eignet sich ein FileOutputStream dafür,
da meistens die Daten in einer Datei234 gesichert werden sollen. FileOutputStream
erweitert die Klasse OutputStream. Eine Verbindung zwischen Datei und ObjektStrom
wird
durch
die
Klasse
ObjectOutputStream
(class
java.io.ObjectOutputStream extends OutputStream implements
DataOutput,
ObjectOutput,
ObjectStreamConstants) geschaffen.
ObjectOutputStream implementiert das Interface ObjectOutput, z.B. die
Funktion writeObject() zum Schreiben von Objekten.
OutputStream
{ abstract }
ObjectOutputStream
<< Konstruktoren >
public ObjectOutputStream(OutputStream out) throws IOException
// erzeugt einen ObjectOutputStream, der in den angegebenen OutputStream
// schreibt
<< Methoden >>
public final void writeObject(Object obj) throws IOException
// schreibt das Objekt. Die implementierende Klasse weiß, wie
// das Objekt zu schreiben ist
public void write(int b) throws IOException
// Ein Bayte wird geschrieben
public void write(byte b[]) throws IOException
// schreibt ein Array von Bytes
...
public void writeBytes(String daten) throws IOException;
public void writeChars(String daten) throws IOException;
public void writeByte(int daten) throws IOException;
public void writeShort(int daten) throws IOException;
public void writeChar(int daten) throws IOException;
public void writeInt(int daten) throws IOException;
public void writeLong(long daten) throws IOException;
public void writeFloat(float daten) throws IOException;
public void writeDouble(double daten) throws IOException;
...
public void flush() throws IOException
// Noch gepufferte Daten werden geschrieben
public void close() throws IOException
// Stream wird geschlossen.
...
Abb.: Die Klasse OblectOutputStream einschl. einiger Methoden des Interface ObjectOutput
234 Der Dateiname wird häufig so gewählt, daß er mit dem Präfix „ser“ endet.
353
Programmieren in Java
Die Methode public final void writeObject(Object obj) throws
IOException schreibt folgende Daten in den OutputStream:
- Die Klasse des als Argument übergebenen Objekts
- Die Signatur der Klasse
- Alle nicht statischen, nicht transienten Membervariablen des übergebenen Objekts einschl. der aus
Elternklassen geerbten Membervariablen.
Bsp.: Serialisierung eines „Zeit-“ Objekts. Das Zeit-Objekt ist eine Instanz der Klasse
„Zeit“, die eine Uhrzeit (Stunde, Minute) kapselt.
import java.io.*;
public class Zeit implements Serializable
{
private int stunde;
private int minute;
public Zeit(int stunde, int minute)
{
this.stunde = stunde;
this.minute = minute;
}
public String toString()
{
return stunde + ":" + minute;
}
}
Mit Hilfe eines Objekts vom Typ ObjectOutputStream kann ein Time-Objekt
serialisiert werden.
import java.io.*;
import java.util.*;
public class Pr81100
{
public static void main(String args[])
{
try {
FileOutputStream fs = new FileOutputStream("test1.ser");
ObjectOutputStream os = new ObjectOutputStream(fs);
Zeit z = new Zeit(10,20);
os.writeObject(z);
os.close();
}
catch(IOException e)
{ System.err.println(e.toString()); }
}
}
Nach dem Schließen des Streams steht das serialisierte Objekt in „test1.ser“.
354
Programmieren in Java
8.1.3 Die Klasse ObjectInputStream
An einen Eingebestrom kommt man über einen InputStream. Da die Daten häufig
aus einer Datei kommen ist dies häufig ein FileInputStream, der mit einem
ObjectInputStream verknüpft wird. Die Methode readObject() liest das
Objekt, findet heraus, was für ein Typ es ist und holt, falls nötig, auch noch Objekte, auf
die verwiesen wird.
InputStream
{ abstract }
ObjectInputStream
<< Konstruktoren >
public ObjectInputStream(InputStream in) throws IOException,
StreamCorruptedException
<< Methoden >>
public final Object readObject() throws OptionalDataException
ClassNotFoundException, IOException
// Liest ein Objekt und gibt es zurück
public boolean readBoolean() throws IOException
public byte readByte(i) throws IOException
public short readShort() throws IOException
public char readChar() throws IOException
public int readInt() throws IOException
public long readLong() throws IOException
public float readFloat() throws IOException
public double readDouble() throws IOException
. ...
Abb.: Die Klasse ObjectInputStream
355
Programmieren in Java
Analog zum Interface ObjectOutput gibt es hier das Interface ObjectInput
(interface java.io.ObjectInput extends DataInput)
<< interface >>
ObjectInput
<< Methoden >>
public Object readObject() throws ClassNotFoundException, IOException
public int read() throws IOException
// liest ein byte aus dem datenstrom. Dieses ist –1, wenn das Ende erreicht ist
public int read(byte b[]) throws IOException
// liest ein Array in den Puffer. Das Ende wird durch –1 angezeigt
public int read(byte b[], int off, int laenge) throws IOException
// liest ein Array von Bytes in den Puffer b an die Stelle off genau len Bytes
public long skip (long n) throws IOException
// überspringt n Bytes im Eingabestrom.
public int available() throws IOException
// Gibt die Anzahl der Zeichen zurück, die ohne Blockade gelesen werden
public void close() throws IOException
// schließt den Eingabestrom
...
Abb.: Das Interface ObjectInput
Das Deserialisieren kann mann sich etwa so vorstellen:
1. Anlegen einen neuen Objekts des zu serialisierenden Typs, Vorbelegen der Membervariablen mit
Defaultwerten, Aufruf des Defaultkonstruktor der ersten nicht serialisierbaren Superklasse.
2. Lesen der serialsierten Daten und Zuweisen der Daten zu den entsprechenden Membervariablen des
angelegten Objekts.
Das durch Deserialisieren erzeugte Objekt hat anschließend dieselbe Struktur und
denselben Zustand, den das serialsierte Objekt hatte 235. Da der Rückgabewert von
readObject() vom Typ Object ist, muß das erzeugte Objekt in den tatsächlichen
Typ (oder eine seiner Oberklassen) umgewandelt werden.
Bsp.: Deserialisieren des im vorherigen Beispiel serialisierten und in die Datei
„test1.ser“ geschriebenen Zeit-Objekts.
import java.io.*;
import java.util.*;
public class Pr81101
{
public static void main(String args[])
{
try {
FileInputStream fs
= new FileInputStream("test1.ser");
ObjectInputStream is = new ObjectInputStream(fs);
Zeit z = (Zeit) is.readObject();
System.out.println(z.toString());
is.close();
}
catch(ClassNotFoundException e)
235 Abgesehen von den nicht serialsierten Membervariablen des Typs static oder transient.
356
Programmieren in Java
{ System.err.println(e.toString()); }
catch(IOException e)
{ System.err.println(e.toString()); }
}
}
Beim Deserialisieren von Objekten können einige Fehler passieren. Damit ein Aufruf
von readObject() erfolgreich ist, müssen mehrere Kriterien erfüllt sein:
- das nächste Element des Eingabestroms ist tatsächlich ein Objekt (kein primitiver Typ).
- Das Objekt muß vollständig und fehlerfrei aus der Eingabedatei lesen lasse.
- Es muß eine Konvertierung auf den gewünschten Typ erlauben, also entweder zu derselben oder einer
daraus abgeleiteten Klasse gehören.
- Der Bytecode für die Klasse des zu serialsierenden Objekts muß vorhanden sein. Er wird beim
Serialisieren nicht mitgespeichert, sondern muß dem Empfängerstrom wie üblich als kompilierter
Bytecode zur Verfügung stehen.
- Die Klasseninformation des serialisierten Objekts und die im deserialisierenden Programm als
Bytecode vorhandene Klasse müssen zueinander kompatibel sein.
8.2 Tiefe Objektkopien
Klassen könen die Methode clone() überschreiben und so eine Kopie der Werte
liefern. Die Standarimplementierung ist jedoch so festgelegt, daß die Kopie flach ist.
Das bedeutet: Referenzen auf Objekte, die vom klonenden Objekt ausgehen, werden
beibehalten und diese Objekte nicht extra kopiert.
Eine tiefe Kopie kann folgendermaßen erzeugt werden: Das zu klonende Objekt ist zu
serialisieren und dann wieder auszupacken. Die zu klonenden Objekte müssen nur das
Serializable-Interface implementieren.
357
Programmieren in Java
9. Netzwerkprogrammierung
Das Paket java.net enthält Klassen zur Programmierung von TCP/IP 236Netzwerkzugriffen. Dieses (Internet-) Protokoll stellt die Basis aller Java-Netzzugriffe
dar. Darauf aufbauend gibt es weiter entwickelte Netzwerkprotokolle wie RMI für
entfernte Methodenaufrufe und verteilte Anwendungen und Enterprise JavaBeans
für verteilte Objekte.
9.1 Adressen, Ressourcen und URLs
9.1.1 Die Klasse InetAddress
Zur Adressierung von Rechnern im Netz wird die Klasse InetAddress des Pakets
java.net verwendet. Eine InetAddress enthält sowohl eine IP-Adresse237 als auch
den symbolischen Namen des jeweiligen Rechners238. Die beiden Bestandteile können
mit den Methoden getHostName() und getHostAddress() abgefragt werden.
Mit Hilfe von getAddress() kann die IP-Adresse auch direkt als „byte“-Array (mit 4
Elementen) beschafft werden.
Zur Generierung eines InetAddress-Objekts stehen zur Verfügung:
public static InetAddress getByName(String host) throws
UnknownHostException
// erwartet einen String mit der IP-Adtesse oder dem Namen als Argument
public static InetAddress getLocalHost() throws UnknownHostException
// liefert ein InetAdress-Objekt für den eigenen Rechner
236 Als Protokoll bezeichnet man die Menge aller Regeln, die nötig sind, um einen kontrollierten und eindeutigen
Verbindungsaufbau, Datenaustausch und Verbindungsabbau gewährleisten zu können. Die derzeit in Java
verfügbareb Netzwerkfähigkeiten basieren auf den Internet-Protokollen TCP/IP (bzw. TCP/UDP)
237 IP steht für Internet Protocol. Die 32-Bit-lange IP-Adresse besteht aus einer Netzwerk-ID und einer Host-ID.
Die Host-ID gibt die Bezeichnung des Rechners innerhalb seines eigenen Netzwerks an, die Netwerk-ID liefert die
Bezeichnung des Rechners.
238 Anstelle der IP-Adresse können bei Anwendungsprotokollen symbolische Namen verwendet werden. Sie
werden mit Hilfe von Namen-Servern in die zugehörige IP-Adresse übersetzt , bevor die Verbindung aufgebaut
wird. Das Domain Name system (DNS) ordnet numerischen IP-Adressen sprechende Namen zu.
358
Programmieren in Java
9.1.2 Die Klasse URL
Im WWW werden Ressourcen über URLs (Universal Ressource Locator) identifiziert.
Eine URL besteht aus:
einem Protokollnamen, z.B. http239 (HTML), file (lokale Dteien), ftp (Dateitransfer), rmi (Remote Method
Invocation), inop(Inter ORB Protocol) oder jdbc (Java Database Connectivity), dem ein Doppelpunkt und
zwei
Schrägstriche
folgen.
Nach
dem
Doppel-Slash
kommt
die
Angabe:
//user240 :password241 @host242 :port243 /url-path244 . Einige Teile können bei einer URL
ausgelassen werden. So sind „user:password@“, „password“, „port“ und „/url-path“ optional.
Sind Benutzername und Paßwort gegeben, so folgt ein „At“-Zeichen @. Paßwort und Benutzername
durfen nicht Doppelpunkt, At-Zeichen oder Slash enthalten. Feht die Angabe des Rechners, wird der
aktuelle Rechner (localhost) benutzt, bei Ports bekannte Standardnummern. Schließlich folgt eine
Bezeichnung der Ressource, typischerweise unter Angabe eines Pfads.
Java implementiert das Konzept eines Uniform Resource Locator durch eine eigene
Klasse URL, die sich im Paket java.net befindet.
239 Mit dem Hypertext Tranfer Protocol wird auf Inhalte des Web zugegriffen. Die URL für Dienste im Web
beginnt mit http.
240 Optionaler Benutzername
241 optionales Paßwort, ein Paßwort ohne Benutzername kann nicht angegeben werden
242 Auf die angabe des Protokolls folgt in der regel der Name der Domäne oder die IP-Adresse des Servers. Name
und IP-Adresse sind in der Regel gleichwertig, da von einem besonderen Dienst der Name in eine IP-Adresse
umgesetzt wird.
243 Eine Verbindung zu einem Rechner geschieht immer durch eine Art Tür, die Port genannt wird. Die PortNummer läßt den Server die Dienste kategorisieren. Jeder Dienst bekommt eine andere Portnummer, damit sie sich
unterscheiden lassen. Normalerweise horcht der HTTP-Server auf Port 80.
244 Auf den Servernamen folgt die Angabe der Datei, auf die über HTTP oder FTP zugegriffen werden soll. Da sie
in einem Verzeichnis liegt, beschreibt url-path den Weg zur Datei. Ist keine Datei vorhanden und endet die
Angabe der URL mit einem Slash „/“, dann versucht der Web-Server auf eine der Dateien index.html bzw.
index.htm zuzugreifen.
359
Programmieren in Java
URL
<< Konstruktoren >>
public URL(String url) throws MalformedURLException
public URL(String protokoll, String host, String pfad) throws MalformedURLException
public URL(String protololl, String host, int portnummer, String pfad)
throws MalformedURLException
// erzeugt ein URL-Objekt mit gegebenen Protokoll, Hostnamen, Portnummer und Datei
// Ist die Portnummer –1, so wird der Standard-Port verwendet, z:B für WWW Port 80
<< Methoden >>
public final Object getContent() throws IOException
//
public final InputStream openStream() throws IOException
// öffnet eine Verbindung zum Server und liefert einen Inputstream zurück. Die Me// thode ist eine Ankürzung für openConnection.getInputStream()
public URLConnection openConnection() throws IOException
// liefert ein URLConnection-Objekt, das die Verbindung zum entfernten Objekt
// vertritt.. openConnection() wird vom Protokoll-Handler immer dann aufgerufen,
// wenn eine neue Verbindung geöffnet wird.
public String getProtocol()
// liefert das Protokoll der URL
public String getHost()
// liefert den Hostnamen der URL, falls das möglich ist.
// Für das Protokoll „file“ ist das ein leerer String
public int getPort()
// liefert die Portnummer. Ist sie nicht gesetzt, liefert die Methode eine –1.
public String getFile()
// gibt den Dateinamen der URL zurück
Abb.: Die Klasse java.net.URL
9.1.3 Die Klasse URLConnection und abgeleitete Klassen
Eine URL-Connection erhält man durch Aufruf der Methode openConnection()
eines URL-Objekts. Mit diesem Objekt lassen sich Inhalte der Ressource lesen sowie
Informationen über die Art des Objekts ermitteln. Je nach Art des Protokolls wird die
abstrakte Klasse URLConnection durch verschieden konkrete Klassen
implementiert, z.B. HttpURLConnection, AppletResourceConnection,
FileURLConnection oder FtpURLConnection.
URLConnection
{ abstract }
<< Konstruktor >>
protected URLConnection(URL url)
<< Methoden >>
public Object getContent() throws IOException, UnkownServiceException
// liefert den Inhalt, auf den die URL verweist.
360
Programmieren in Java
9.2 Kommunikation in Netzwerken
Das Common GatewayInterface (CGI)
CGI ist die Beschreibung einer Schnittstelle über die externe Programme mit
Informations-Servern, meistens Web-Servern, Daten austauschen. CGI-Programme
können in allen möglichen Programmiersprachen verfaßt sein. Häufig sind es Shelloder Perl-Skripte.
Die CGI-Programme werden von einem Browser durch eine URL angesprochen. Der
Browser baut eine Verbindung zum Server auf. Dieser erkennt an Hand des Pfads der
URL, ob es sich um eine normale Web-Seite oder um ein Skript handelt. Falls es ein
Skript ist, dann führt der Server das Skript aus, das eine HTML-Datei erzeugt. Diese
wird übertragen und im Browser dargestellt.
Sockets245
Als Socket bezeichnet man ein streambasierte Programmierschnittstelle zur
Kommunikation zweier Rechner in einem TCP/IP-Netz. Das Übertragen von Daten über
eine Socket-Verbindung besteht aus:
1. Verbindungsaufbau
2. Lesen bzw. Schreiben der Daten
3. Verbindungsabbau
245 Sockets wurden zu Beginn der achtziger Jahre für die Programmiersprache C entwickelt.
361
Herunterladen