DOC - oth-regensburg.de

Werbung
Programmieren in Java
Prof. Jürgen Sauer
Programmieren in Java
Skriptum zur Vorlesung im WS 2001/2002
1
Programmieren in Java
2
Programmieren in Java
Inhaltsverzeichnis
0.
Einführung in die Java-Programmierung
0.1
Ziel der Vorlesung "Programmieren"
0.2
Basis der Vorlesung: "Die Java-Maschine"
0.3
0.3.1
0.3.2
Der erste Versuch
Das erste Programm
Bestandteile eines Programms
1.
Einführung in die Java-Programmierung
1.1
Übersicht zur Entwicklung der Programmiersprache Java
1.2
Was ist Java?
1.3
Einstieg in die Java-Programmierung
1.3.1 Die Software für die Java-Programmierung
1.3.2 Applets und Anwendungen
1.3.2.1 Entwickeln von Java-Anwendungen
1.3.2.2 Entwickeln von Java-Applets
1.4
Die Objektorientierung von Java
1.4.1 Grundlegende Konzepte
1.4.1.1 Zustand und Verhalten von Objekten, Klassen, Instanz- und Klassenvariable bzw. methoden
1.4.1.2 Superklassen und Subklassen, Vererbung und Klassenhierarchie
1.4.1.3 Referenzen und Referenztypen
1.4.1.4 Konvertieren von Objekten und Primitivtypen
1.4.1.5 Manipulieren von Objekten
Vergleichen von Objekten
Kopieren von Objekten
1.4.1.6 Lokale, innere und anonyme Klassen
1.4.1.7 Schnittstellen und anonyme Klassen
1.4.1.8 Polymorphismus und Binden
1.4.2 Klassen des Pakets java.lang
1.4.3 Ausnahmen und Ausnahmenbehandlung
Ausnahmen
Globales Exception Handling
Die Fehlerklassen von Java
Auslösen von Ausnahmen
1.4.4 Einfache Ein- und Ausgaben
2.
Hauptbestandteile der Sprache
2.1
Token
3
Programmieren in Java
2.1.1
2.1.2
2.1.3
Schlüsselworte
Bezeichner und Namenskonventionen
Literale
1. Ganzzahlige Literale
2. Gleitpunktliterale
3. Boolesche Literale
4. Zeichenliterale
5. Zeichenkettenliterale
2.1.4 Trennzeichen
2.1.5 Operatoren
2.1.6 Kommentare, eingebettete Dokumentation
2.2
Typen
2.2.1 Primitive Datentypen
2.2.2 Operationen mit primitiven Datentypen
2.2.3 Datenfelder (Arrays)
2.2.3.1 Deklarieren, Erstellen von Array-Objekten
2.2.3.2 Zugriff auf Datenfeld-Elemente
2.2.3.3 Anwendungen mit eindimensionalen Feldern
2.2.3.4 Multidimensionale Datenfelder
2.2.3.5 Die Klasse Arrays
2.3
2.3.1
2.3.2
2.3.3
2.3.4
2.3.5
Ausdrücke
Arithmetische Ausdrücke
Bewertung von Ausdrücken
Typkonvertierungen
Vergleichsoperatoren
Logische Ausdrücke
2.4
2.4.1
2.4.2
2.4.3
2.4.4
2.4.5
2.4.6
2.4.7
2.4.8
2.4.9
2.4.10
2.4.11
Anweisungen
Blöcke und Anweisungen
Leere Anweisungen
Benannte Anweisungen
Deklarationen
Ausdrucksanweisungen
Auswahlanweisungen
Wiederholungsanweisungen
Sprunganweisungen
Synchronisationsanweisungen
Schutzanweisungen
Unerreichbare Anweisungen
2.5
Klassen
2.6
Methoden
2.6.1 Die Deklaration
2.6.2 Die Zugriffsspezifizierung
2.6.3 Die Methodenmodifizierer
2.6.4 Rückgabewerte von Methoden
2.6.5 Methodenname und Parameterliste
4
Programmieren in Java
2.6.6 Rekursion
2.6.6.1 Rekursive Funktionen
2.6.6.2 Rekursion und Iteration
3.
3.1
3.1.1
3.1.2
3.1.3
3.1.4
3.1.5
Grafische Benutzeroberflächen und Applets
Ereignisbehandlung unter grafischen Benutzeroberflächen (Graphical User Interface)
Gestaltung von grafischen Benutzeroberflächen mit Hilfe der AWT-Klassen
Ereignisbehandlung unter grafischen Benutzeroberflächen
Anwendung lokaler Klassen für die Ereignisbehandlung
Externe- und interne Darstellung numerischer Werte
Low-Level-Events
3.2
Kommunikation des Anwenders mit Programmen in GUI über Dialoge bzw. Menüs
mit vordefinierten Dialogelementen
3.2.1 Dialoge
3.2.2 Menüs
3.3
Grundlagen der Applet-Erstellung
3.3.1 HTML-Grundlagen
3.3.2 Die interne Arbeitsweise eines Applets
3.3.3 „Multithreading“-fähige Applets
3.3.4 Animation in Applets
3.3.5 Das Laden und Anzeigen von Bildern
3.3.6 Das Laden und Anzeigen von Sound
3.3.7 Die Klasse JApplet
3.3.8 Das Interface AppletContext
3.4
Grafische Benutzeroberflächen mit Observable-Observer
Die Klasse Observable
Die Schnittstelle Observable
Observable-Observer: Kopplung von Anwendung und GUI
3.5
Swing
3.6
JavaBeans
3.6.1 Wiederverwendbare Softwarekomponenten
3.6.2 Komponenten für JavaBeans
3.6.3 Enterprise JavaBeans
4.
Grafik und Bildverarbeitung
4.1
4.1.1
4.1.2
4.1.3
Allgemeine Zeichenvorgänge
Punkte, Linien, Kreise, Bögen, Polygone
Farbangaben
Textausgabe über den Zeichenmodus
Die Klasse Font
Die Klasse FontMetrics
4.1.4 Größenangaben
5
Programmieren in Java
4.1.5
Clipping-Operationen
4.2
Bildverarbeitung
4.2.1 Klassen zur Bildverarbeitung
4.2.2 Bildproduzenten und Bildkonsumenten
4.2.3 Bildfilter
4.2.4 Kopieren von Speicher in ein Bild und von Bildern in einen Speicher
4.2.4.1 Kopieren von Speicher in ein Bild
4.2.4.3 Kopieren von Bildern in einen Speicher
4.3
4.3.1
4.3.2
4.3.3
Grafikoperationen mit Java 2D
Das zeichnen unter Java 2D API
Eigenschaften geometrischer Objekte
Affine Transformation
5.
AWT
5.1
Bestandteile des AWT
5.2
5.2.1
5.2.2
5.2.3
5.2.4
5.2.5
5.2.6
5.2.7
5.2.8
Die AWT-Komponenten
Schaltflächen (Buttons)
Labels
Kontrollkästchen und Optionsfelder
Auswahlmenüs
Listenfelder
Textbereiche und Textfelder
Schieber und Bildlaufleisten
Zeichenbereiche
5.3
5.3.1
5.3.2
5.3.3
5.3.4
Container
Panels
Frames
Menüs
Dialoge
5.4
5.4.1
5.4.2
Die Layout-Manager
Layout-Regeln
Die einzelnen Layout-Manager
5.5
5.5.1
5.5.2
Die Event-Modelle 1.0 und 1.1
Der AWT-Handler 1.0
Das Event-Handling 1.1
5.6
Benutzerschnittstellen mit Swing
6.
Utilities
6.1
Collections
6
Programmieren in Java
6.1.1
6.1.2
6.1.3
6.1.4
Die Klasse Vector
Die Klasse Stack
Die Klasse Hashtabelle
Die Klasse Properties
6.2
6.2.1
6.2.2
6.2.3
6.2.4
6.2.5
Java 2 Collections
Die Interfaces Collection, Iterator, Comperator
Die Collections des Typs List
Die Collections des Typs Set
Die Collections des Typs Map
Die Klasse Collection
6.3
Die Klasse StringTokenizer
6.4
Die Klasse Random
6.5
Datumsberechnungen
6.5.1 Die Klassen Date, Calendar, GregorianCalendar
6.5.2 Formatieren der Datumsangaben
7.
Ein-/ Ausgabe
7.1
Die abstrakten Klassen InputStream und OutputStream
7.1.1 InputStream
7.1.2 OutputStream
7.2
Gefilterte Ströme
7.3
Die Klasse File
7.4
Die Klasse RandomAccessFile
7.5
Spezielle nützliche Ströme
7.6
7.6.1
7.6.2
7.6.3
7.6.4
Java 1.1 IO-Ströme
Grundlagen
Die abstrakte Klasse Reader und ihre Ableitungen
Die abstrakte Klasse Writer und ihre Ableitungen
Demonstrationsprogramm zur Ein-/Ausgabe (ab Java Version 1.1)
8.
Serialisierung
8.1
8.1.1
8.1.2
8.1.3
Grundlagen
Das Interface Serilizable
Die Klasse ObjectOutputStream
Die Klasse ObjectInputStream
8.2
Tiefe Objektkopien
7
Programmieren in Java
9.
Netzwerkprogrammierung
9.1
9.1.1
9.1.2
9.1.3
Adressen, Ressourcen und URLs
Die Klasse InetAdress
Die Klasse URL
Die Klasse URLConnection und abgeleitete Klassen
9.2
Kommunikation in Netzwerken
8
Programmieren in Java
Literaturverzeichnis
Alexander Newman u.a.: JAVA, Referenz und Anwendungen, 1996, Que, Haar bei München
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 ...
9
Programmieren in Java
10
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äzis 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 JavaSoft 1 halten
permanent die aktuellsten Informationen im Internet bereit.
0.2 Java-Maschine und Programmiersystem
Die Java-Maschine2 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
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.
2
11
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 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“.
JavaProgramm
Daten
Eingabe (Tastatur)
Externer Speicher
Speicher
Externer
Java-
Compiler
System,
QuellproSystemprogramme
Laufzeit-
Java -
Java-Maschine
gramme
Javasystem
Java-
BytecodeDateien
Ausgabe (Bildschirm)
12
Programmieren in Java
13
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 ... Die Methode ist für andere Klassen und Objekte verfügbar
static ... Es handelt sich um eine Klassenmethode
main() ... 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 JavaKlassen 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:
14
Programmieren in Java
/* 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
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 UnicodeZeichen. 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 JavaSprachdefinition 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), Gleipunktzahlen (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
15
Programmieren in Java
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);
}
Vorrang
1
Operator
++
-+, ~
!
2
3
4
(type)
*, /, %
+, +
<<
>>
>>>
5
<, <=
>, >=
6
instanceof
==
!=
==
!=
7
8
9
10
11
12
13
&
&
^
^
|
|
&&
||
?=
=
*=, /=,
+=, -=
<<=, >>=,
>>>=,
&=, ^=,
|=
Operandentyp
Arithmetisch
Assoziation Operation
R
Pre- oder Post-Inkrement
(unär)
Arithmetisch
R
Preoder
PostDekrement (unär)
Arithmetisch
R
Unäres Plus, unäres
Minus
Integral
R
Bitweises
Komplement
(unär)
boolean
R
Logisches Komplement
(unär)
irgendein
R
cast
arithmetisch
L
Multiplikation,
Division,
Rest
Arithmetisch
L
Addition, Subtraktion
String
L
Verkettung
Integral
L
Links-Shift
Integral
L
Rechts-Shift
mit
Vorzeichen
Integral
L
Rechts-Shift mit NullenNachziehen
Arithmetisch
L
Kleiner als, kleiner als
oder gleich
Arithmetisch
L
Größer als, größer als
oder gleich
Objekt, Typ
L
Objekt?, Instanz?
Primitiver Typ
L
Gleich (identische Werte)
Primitiver Typ
L
Ungleich (verschiedene
Werte)
Objekt
L
Gleich (Referenz auf das
gleiche Objekt)
Objekt
L
Ungleich (Referenz auf
verschiedene Objekte)
Integral
L
Bitweises Und
boolean
L
Logisches Und
Integral
L
Bitweises Oder
boolean
L
Logisches
Oder
(exklusiv)
Integral
L
Bitweises Oder
boolean
L
Logisches Oder (inklusiv)
boolean
L
Konditionelles Und
boolean
L
Konditionelles Oder
boolean, irgendein, R
Konditioneller (ternärer)
irgendein
Operator
Variable, irgendein R
Zuweisung
Variable, irgendein R
Zuweisung mit Operation
16
Programmieren in Java
Abb. 0.3-1: Java-Operatoren
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“
17
Programmieren in Java
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.
„.“: 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 Erstes.Programm.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.
18
Programmieren in Java
Konsole:
Arbeitsspeicher:
Externspeicher
javac ErstesProgramm.java
ErstesProgramm.java
javac
ErstesProgramm.class
java ErstesProgramm
ErstesProgramm.class
Java (Interpreter)
"Das erste Programm der Vorlesung Programmieren in Java"
Abb. 0.3-2: Ablaufplan ErstesProgramm
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 Ausdruck 4 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.
3
vgl. 2.4
vgl. 2.4.5
5 vgl. 2.4.6
6 vgl. 2.4.1
7 vgl. 2.6
4
19
Programmieren in Java
- 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, saß 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“
(Standard-Feature). 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.
8
vgl. 2.5
CallBack-Methoden sind von der abgeleiteteten Klasse zur Verfügung gestellte Methoden, die vom Browser
bzw. Appletviewer aufgerufen werden
9
20
Programmieren in Java
21
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 Microsystems 10
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
Programmiersprache13, 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
Java-fähige Browser und damit die Präsentationsform für die ersten Java-Applets.
Außerdem war dieser Browser eine wesentliche Ergänzung des ersten JavaEntwicklungstools 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.
Natürlich gab es im JDK noch diverse Kinderkrankheiten. Im zweiten Quartal 1997
fogte deshalb nach einigen Zwischenversionen die Version 1.1 des JDK. Parallel zur
10
Sun ist eine der führenden Hersteller von Workstations
Entwicklung eines vollkommen neuen, plattformunabhängigen Betriebssystems für den „Consumerbereich der
allgemeinen Elektronk (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 makierte 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,
Server-Programme 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 Loader (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.
11
22
Programmieren in Java
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 Entwicklungstolls 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
Hochleistungs-geschwindigkeits-Anwendungen 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 Java18 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 Klassem sind Teil des JDK, das darüber hinaus noch
Klassen besitzt, die Funtionen 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.
- 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
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.
23
Programmieren in Java
Systemen befinden. Objekte können mit Java-Programmen über URLs20 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 Object-Code-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:
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 Adreßanfragen 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
24
Programmieren in Java
- 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 Java-Programmen ist die eingebaute Begrenzung der Zugriffsmöglichkeiten auf den
Speicherbereich
des
Rechners22.
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). Die 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
25
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 Internet 25
erhältlich.
Nach der Installation hat das Java-Verzeichnis26 folgenden Inhalt27:
Abb. 1.3-1: Inhalt des Java-Verzeichnisses
Innerhalb des Java-Verzeichnisses
folgendem Inhalt:
Verzeichnis
\bin
\lib
\include
\demo
\src
befinden
sich
Unterverzeichnisse28
mit
Inhalt
In diesem Verzeichnis befinden sich die JDK-Programme
In diesem Verzeichnis befinden sich defaultmäßig die Standard-Java-Klassen des
JDK29
In diesem Verzeichnis befinden sich diverse Header-Dateien für die gemeinsame
Verwendung von Java und C/C++
Das Demo-Verzeichnis einthälz 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-Microsytem-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 SunFTP-Server (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
26
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:\ORAWIN95\BIN;c:\windows;c:\windows\command;c:\jdk1.1.6\bin;c:\jdk1.1.6\include;
Die CLASSPATH-Umgebungsvariable30
„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%
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.
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.
27
Programmieren in Java
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 richtig gelaufen ist, erscheint in der letzten (Ausgabe-) Zeile: Willkommen in
der Java-Welt!.
31
32
Vgl. PR13210
Mit der Option „-d“ im javac-Aufruf kann die .class-Dateii an einem anderen Ort gespeichert werden.
28
Programmieren in Java
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 Quellcode 33 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 Befehlszeileneben eingegeben wurden, ausgegeben werden, dann ist die
Liste komponentenweise mit einer Zählschleife abzuarbeiten. Die Zählschleife (forSchleife) sieht in Java so aus:
for (Initialisierung; Test; Inkrement)
{
// Anweisungen
}
Initialisierung ist ein Ausdruck, der den Beginn der Zählschleife einleitet, z.B.
Initialiserung 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 Ausdrucke, 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.
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]). ArrayIndizes 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).
33
Vgl. PR13210
29
Programmieren in Java
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
“Juergen“
“Hubert“
“Josef“
“Liesel“
“Christian“
[0]
[1]
[2]
[3]
[4]
Nach 4 Vergleichen hat das Datenfeld args folgende Gestalt angenommen:
args
[0]
[1]
[2]
[3]
[4]
“Christian“
“Juergen“
“Josef“
“Liesel“
“Hubert“
30
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
“Christian“
“Hubert“
“Juergen“
“Liesel“
“Josef“
[0]
[1]
[2]
[3]
[4]
„Christian“ und „Hubert“ sind jetzt richtig eingeordnet. Num 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
“Christian“
“Hubert“
“Josef“
“Liesel“
“Juergen“
[0]
[1]
[2]
[3]
[4]
Der nächste Durchgang führt dann zum sortierten Datenfeld:
args
[0]
[1]
[2]
[3]
[4]
“Christian“
“Hubert“
“Josef“
“Juergen“
“Liesel“
2) Der soeben beschrieben Sortieralgorithmus muß in Java-Programmcode
abgebildet werden. Es ist leicht erkennbar, daß hier zwei verschachtelte forSchleifen 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.
31
Programmieren in Java
Die Vergleichsbedingung wird durch das Schlüsselwort if erzeugt:
if (Ausdruck)
{
// Anweisung(en)
}
else {
// Anweisung(en)
}
Eine if-Bedingung verwendet einen booeleschen 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 bestimmete
Anweisung bzw. Anweisungen ausgeführt werden, wenn der boolesche 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 Programm mit Parametern führt
zu: „Herzlich Willkommen Christian Hubert Josef Juergen Liesel“.
32
Programmieren in Java
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 Variable
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 Java einfach, da er alle Variablentypen und Objektwerte
wie Strings behandelt. Sobald ein Teil einer Verkettung ein String oder ein StringLiteral
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
Escape-Sequenz \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. DekrementOperationen 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.
33
Programmieren in Java
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);
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 = " + x);
z = ++x;
System.out.println("Nach z = ++x: z = " + z + ", x = " + 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 = "
+ x + " y = " + y);
z = (float) (1.0f / 0.0f);
System.out.println("z = (float) (1.0f / 0.0f) = " + z);
}
}
34
Programmieren in Java
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.
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:
35
Programmieren in Java
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 Dateiaufbereites (Editor):
/* Das erste Java-Applet */
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 classDateien 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
Graphics-Klasse können Textzeichen auf den Bildschirm gemalt werden.
34
eine entsprechende Referenz innerhalb einer HTML-Seite mit einem speziellen Tag, dem <APPLET>-Tag
erledigt die Einbettung in den Browser
36
Programmieren in Java
Die folgende Abbildung zeigt die Modellierung35 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 JavaSystem verfügbar. Applets müssen „public“ deklariert werden.
Die folgende Abbildung zeigt die Beziehungen der Klasse WillkommenApplet zu
ihren unmittelbaren Nachbarn:
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
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.
37
Programmieren in Java
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:
Object
Component
ImageObserver
Container
Panel
Applet
WillkommenApplet
Abb. 1.3-4: Vererbungshierarchie von WillkommenApplet
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.
38
Programmieren in Java
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 Paketstruktur39 kann
in einem Klassendiagramm visualisiert werden:
java
WillkommenApplet
applet
awt
lang
Abb. 1.3-5: Paketstruktur im WillkommenApplet
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.
39
Pakete werden in der UML als Akten mit Reitern dargestellt. Die gestrichelten Pfeile repräsentieren die
Abhängigkeiten zwischen den Paketen.
39
Programmieren in Java
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:
Abb. 1.3-7: Darstellung des Fensters mit dem Appletviewer
In einem Browser würde zusätzlich der Text rund um das Applet („Mein Java
Applet sagt:“) gezeigt werden.
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 Font-Objekt 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:
40
Programmieren in Java
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;
Dafür kann man auch „import java.awt.*;“40 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: //** ... */
40
Diese Anweisung stellt alle Klassen des Pakets java.awt zur Verfügung
41
Programmieren in Java
Grundsätzlich steht der Dokumentationskommentar vor Klassen, (Interfaces,
Konstruktoren,) Methoden, z.B.41:
/* 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
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.:
41
PR13210
42
Programmieren in Java
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 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.
43
Programmieren in Java
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
44
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
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()
{
// ...
}
// 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.
45
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
Software-Objekte 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
Variablenund
Methodendefinitionen. Zum Anlegen eines Objekts einer Klasse (Instanziierung42)
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 Bestandteilen des Objekts enthaltenen Informationen und
42
Eine Instanz einer Klasse ist ein (tatsächliches) Objekt (konkrete Darstellung)
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
43
46
Programmieren in Java
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.
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.
45
vgl. 2.1.2 Bezeichner und Namenskonventionen
vgl. 2.6.2
47 vgl. 2.2
48 vgl. 2.6.4
46
47
Programmieren in Java
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 JavaAnwendung49:
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
49
vgl. PR14101
48
Programmieren in Java
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
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.
- 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.
51
vgl. 2.2.1
vgl. 2.2.1
53 vgl. 2.2.1
52
50
Programmieren in Java
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.:
54
PR14102
51
Programmieren in Java
public Rechentafel()
{
this(0,1);
}
„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 PaketKonzept 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
52
Programmieren in Java
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
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;
}
53
Programmieren in Java
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;
}
}
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;
55
PR14103
54
Programmieren in Java
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
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“ befindet sich in der Klasse
„Rechentafel“.
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
55
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:
57
PR14103
56
Programmieren in Java
public String toString()
{
return " Rechenobjekt# " + nummer + " Erster Operand: "
+ ersterOperand + " Zweiter Operand: " + zweiterOperand;
}
}
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
57
Programmieren in Java
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();.
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;
public double holeR();
public void setzeR(double r);
public double umfang();
public double flaeche();
public String toString();
private double breite;
private double hoehe;
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
58
vgl. PR14131
58
Programmieren in Java
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.
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);
}
59
Programmieren in Java
// Instanzmethoden
public double holeR()
{
return r;
}
public void setzeR(double r)
{
this.r = (r >= 0.0 ? r : -r);
}
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;
}
60
Programmieren in Java
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()
{
return 2 * breite + 2 * hoehe;
}
public double flaeche()
{
return breite * hoehe;
}
public String toString()
{
return "Rechteck: (" + holeX() + ", " + holeY()
+ ") " + "breite = " + breite +
" hoehe = " + hoehe;
}
}
61
Programmieren in Java
Die Klasse "Test"
Funktionsfähigkeit:
überprüft
die
vorliegende
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());
}
}
62
Klassenhierarchie
auf
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.
Java verfügt über ein automatisches Speichermanagement. Deshalb braucht der
Java-Programmierer 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 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
59
Der Operator „==“ überprüft die Gleichheit der Referenzen.
63
Programmieren in Java
„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.
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
64
Programmieren in Java
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();
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
false
false
true
false
//
//
//
//
//
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;
60
61
PR14110
PR14202
65
Programmieren in Java
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()
{
return zweiterOperand;
}
public void setzweiterOperand(int zweiterOperand)
{
this.zweiterOperand = zweiterOperand;
}
public int addiere()
{
return ersterOperand + zweiterOperand;
}
public int subtrahiere()
{
return ersterOperand - zweiterOperand;
}
66
Programmieren in Java
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;
}
}
67
Programmieren in Java
Diese Methode kann dann über ein in der Klasse Rechentafeltest instanziertes
Objekt der Klasse Rechentafel aufgerufen werden:
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.
68
Programmieren in Java
Bestimmen der Klasse eines Objekts
Die Klasse Class stellt über die Methode getname() den Namen einer Klasse zur
Verfügung: public String getName()
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.
69
Programmieren in Java
1.4.1.6 Innere (lokale) und anonyme Klassen
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. 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 Adapter-Klasse 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.
62
Im JDK wird das Konzept als Inner Classes bezeichnet
70
Programmieren in Java
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.
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 extend meineSchnittstelle
{
// Abstrakte Methoden
}
"Interfaces" können benutzt werden, indem sie in einer Klasse implementiert werden,
z.B.:
public class MeineKlasse extend 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 + Metoden aus meinerSchnittstelle1
und meinerSchnittstelle2 */
}
63
Angaben zur Implementierung sind nicht möglich
71
Programmieren in Java
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.64: Das folgende Interface65 mit dem Namen „Vergleich“ umfaßt Deklarationen
für Vergleichsfunktionen.
interface Vergleich
{
boolean kleinerAls(Object ls, Object rs);
boolean kleinerAlsoderGleich(Object ls, Object rs);
}
Die Vergleichsfunktionen
herangezogen werden:
sollen
zum
Vergleichen
aller
möglichen
Objekte
1. Verwendung des Interface „Vergleich“ zum Sortieren von Zeichenketten
public class StringSortTest
{
static class StringVergleich implements Vergleich
{
public boolean kleinerAls(Object l, Object r)
{
return((String)l).toLowerCase().compareTo(((String)r).toLowerCase()) <
0;
}
public boolean kleinerAlsoderGleich(Object l, Object r)
{
return((String)l).toLowerCase().compareTo(((String)r).toLowerCase()) <=
0;
}
}
// static StringVergleich sVergl = new StringVergleich();
public static void main(String args[])
{
String x[] = {
"Juergen","Christian","Bernd","Werner","Uwe",
"Erich","Kurt","Karl","Emil","Ernst"
};
Object o[] = new Object[x.length];
for (int i = 0; i < x.length; i++)
{
o[i] = (Object) x[i];
}
// Ausgabe des noch nicht sortierten x
System.out.println("Vor dem Sortieren:");
64
65
PR14171
gespeichert in: Vergleich.java
72
Programmieren in Java
for (int i = 0; i < x.length; i++)
{
System.out.println("x["+i+"] = " + x[i]);
}
// Aufruf der Sortieroutine
Sort s = new Sort(new StringVergleich());
s.sort(o,x.length-1);
// Gib das sortierte Feld x aus
for (int i = 0; i < x.length; i++)
{
x[i] = (String) o[i];
}
System.out.println();
System.out.println("Nach dem Sortieren:");
for (int i = 0; i < x.length; i++)
{
System.out.println("x["+i+"] = " + x[i]);
}
}
}
2. Verwendung des Interface „Vergleich“ zum Sortieren von Zahlen
public class ZahlenSortTest
{
static class ZahlenVergleich implements Vergleich
{
public boolean kleinerAls(Object l, Object r)
{
return (((Integer) l).intValue() < ((Integer) r).intValue());
}
public boolean kleinerAlsoderGleich(Object l, Object r)
{
return (((Integer) l).intValue() <= ((Integer) r).intValue());
}
}
public static void main(String args[])
{
int x[] = { 50, 70, 80, 10, 20, 30, 1, 2, 3, 99, 12, 11, 13};
Object o[] = new Object[x.length];
for (int i = 0; i < x.length; i++)
{
o[i] = new Integer(x[i]);
}
// Ausgabe des noch nicht sortierten x
System.out.println("Vor dem Sortieren:");
for (int i = 0; i < x.length; i++)
{
System.out.println("x["+i+"] = " + x[i]);
}
// Aufruf der Sortieroutine
Sort s = new Sort(new ZahlenVergleich());
s.sort(o,x.length-1);
73
Programmieren in Java
// Gib das sortierte Feld x aus
for (int i = 0; i < x.length; i++)
{
x[i] = ((Integer) o[i]).intValue();;
}
System.out.println();
System.out.println("Nach dem Sortieren:");
for (int i = 0; i < x.length; i++)
{
System.out.println("x["+i+"] = " + x[i]);
}
}
}
Die unter 1. und 2. angegebenen Klassen benutzen die Klasse Sort, die die
eigentliche Funktion für das Sortieren enthält:
public class Sort
{
private Vergleich vergleich;
public Sort(Vergleich vergl)
{
vergleich = vergl;
}
public void sort(Object x[],int n)
{
bubbleSort(x,n);
}
public void bubbleSort(Object x[], int n)
{
for (int i = 0; i < x.length; i++)
{
for (int j = i + 1; j < x.length; j++)
{
if (!vergleich.kleinerAls(x[i],x[j]))
{
Object temp = x[i];
x[i] = x[j];
x[j] = temp;
}
}
}
}
}
74
Programmieren in Java
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).
Die Java-Klassenbibliothek66 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
Class
String
StringBuffer
Thread
ThreadGroup
Throwable
System
Runtime
Process
Math
Number
Character
Boolean
66
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.
Für jede in java definierte Klasse gibt es eine Instanz von Class, die diese Klasse
beschreibt
Enthält Methoden zur Manipulation von Java-Zeichenketten
Dient zum Erstellen von Java-Zeichenketten
Stellt einen Ausführungs-Thread in einem Java-Programm dar. Jedes Programm kann
mehrere Threads laufen lassen
Ermöglicht die Verknüpfung von Threads untereinander. Einige Thread-Operationen
können nur von Threads aus der gleichen ThreadGroup ausgeführt werden.
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.
Stellt spezielle Utilities auf Systemebene zur Verfügung
Enthält eine Vielzahl gleicher Funktionen wie System, behandelt aber auch das Laufen
externer Programme
Stellt ein externes Programm dar, das von einem Runtime-Objekt gestartet wurde.
Stellt eine Reihe mathematischer Funktionen zur verfügung
Ist die Basisklasse für Double,Float, Integer und Long (Objeckt-Wrapper)
Ist ein Objekt-Wrapper für den datentyp char und enthält eine Reihe nützlicher
zeichenorientierter Operationen
Ist ein Objekt-Wrapper für den Datentyp boolean
Die Klassenbibliothek des JDK befindet sich in einem Paket mit dem namen „java“.
75
Programmieren in Java
ClassLoader
SecurityManager
Compiler
Ermöglicht der Laufzeit-Umgebung von Java neue Klassen hinzuzufügen
Legt die Sicherheits-Restriktionen der aktuellen Laufzeitumgebung fest. Viele JavaKlassen benutzen den Security-Manager zur Sicherstellung, daß eine Operation auch
tatsächlich genehmigt ist.
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.
- 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 erweitert67:
Paket
java.applet
java.awt
java.awt.datatranfer
java.awt.event
java.awt.image
java.beans
java.io
java.lang
java.lang.reflect
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
java.math
java.net
java.rmi
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)
java.rmi.dgc
java.rmi.registry
java.rmi.server
java.security
java.security.aci
java.security.interfaces
67
Zusätzlich 15 weitere Packages, etwa 500 Klassen und Schnittstellen
76
Programmieren in Java
java.sql
java.util
java.util.zip
Datenbankzugriff (JDBC)
Diverse Utilities, Datenstrukturen
JAR-Files, Kompression, Prüfsummen
Abb.: Klassenbibliothek der Java-Version 1.1
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 import-Anweisung
eingebunden, z.B.:
import java util.*;
......
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.*"68 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.
68
type import on demand, d.h.: Die Klasse wird erst dann in dem angegebenen Paket gesucht, wenn das
Programm sie wirklich benötigt.
77
Programmieren in Java
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.69: 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
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());
69
Vgl. PR14181
78
Programmieren in Java
/* (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 Verbidung übernimmt das Laufzeitprogramm der Programmiersprache.
Java unterstützt das Konzept des „Late Binding“.
79
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 ob)
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 Hashcode70: public native 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 71.
Klonen von Objekten: protected native 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 native Class
getClass()
Benachrichtungsmechanismus. Hierfür gibt es die Methoden: public final
native notify() bzw. public final native void notifyAll() und
public final void wait() throws InterruptedException bzw. public
final
native
void
wait(long
timeout)
throws
InterruptedException. Threads72 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 Dateinen 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.
1.4.2.2 Die Class-Klasse
70
Methoden, die mit dem Schlüsselwort native deklariert sind, sind nicht in Java (meistens iin C++) geschrieben.
Normalerweise werden soche Methoden geschrieben, um etwas zu tun, was Java noch nicht kann:
Zusammenarbeit mit einer besonderen Computer-Hardware oder einem Betriebssystem oder zur
Leistungssteigerung
71 Die Klasse java.util.Hashtable bietet eine soche Datenstruktur an.
72 vgl. 1.4.2.4
80
Programmieren in Java
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 Class-Objekte 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 einegebunden 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
Field[] getFields() throws SecurityException
// liefert ein Feld mit Field-Objekten73.
Method[] getmethods() throws SecurityException
// gibt ein Array von Method-Objekten74 zurück, die alle öffentlichen Methoden der Klasse/
// Schnittstelle beschreiben
...
Abb.: Die Klasse Class
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 bezeichnet75.
Das folgende Programmstück76 testet Objekte systematisch durch:
73
Objekten der Klasse java.lang.reflect.Field
Objekte der Klasse java.lang.reflect.Method
75 Die Klasse java.lang.Class ist Baisis für Methoden, die Objekte vom Typ Constructor, Method, Field usw.
zurückliefern. Die zugehörigen Klassen sind im Package java.lang.reflect definiert.
74
81
Programmieren in Java
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
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.
76
vgl. PR14220
82
Programmieren in Java
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 aout
public static Securitymanager getSecurityManager()
public static void setSecurityManager(SecurityManager s)
public static native long currentTimeMillis()
public static Properties getProperties()
public static void setProperties(Properties props)
public static String getProperty(String key)
public static String getenv(Strin name)
// deprecated
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
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();
83
Programmieren in Java
liefert das komplette Properties-Objekt mit den System-Properties.
Bsp.77: 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 setError(PrintStream err);
kann die Standardein- und Standardausgabe aus dem Programm heraus umgeleitet
werden.
public static void exit(int status)
Mit System.exit 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()
77
PR14231
84
Programmieren in Java
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.
85
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 native Thread currentThread()
public static native void yield()
public static native void sleep(long millis) throws InterruptedException
public native synchronized void start()
public void run()
public final void stop()
public final synchronized void stop(Throwable o)
public void interrupt()
public static boolean interrupted()
public boolean isInterrupted()
public void destroy()
public final native boolean isAlive()
public final void suspend()
public final void resume()
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.
86
Programmieren in Java
Bsp.: Einfacher Thread, der in einer Endlosschleife einen Thread hochzählt.
class MeinThread extends Thread
{
public void run()
{
int i = 0;
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äuft78,)
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)
sorgt dafüt, daß der aktuelle Prozeß für die (in Millisekunden) angegebene Zeit
unterbrochen wird.
public static void sleep(long millis, int nanos)
Die erzielbare Genauigkeit der Unterbrechungs-Wartezeit wird durch HardwareRestriktionen der Maschine begrent
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.
78
Beim Starten eines Java-Programms wird automatisch ein Thread für die Ausführung des Hauptprogramms
angelegt.
87
Programmieren in Java
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++);
}
}
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.
88
Programmieren in Java
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 implementieren79. 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 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
79
sogar die Klasse Thread selbst
89
Programmieren in Java
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 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. Der Normalwert wird durch die Konstante Thread.NORM_PRIORITY
festgelegt80.
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.
80
im aktuellen JDK 1, 5, 10
90
Programmieren in Java
1.4.2.5 Die Klassen String und StringBuffer
Die Klasse String
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 ZeichenArray.
Zeichenextraktion
char charAt(int index) throws
StringIndexOutOfBoundsException
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 „StringIndexOutOfBoundsException“
erzeugt
String
substring(int Liefert den Teilstring, der an Position anfang beginnt und an
anfang, int ende)
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
String trim()
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
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 StringObjekts. 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 „n-1“ liegen.
Vergleichen von Zeichenketten:
boolean
einObjekt)
equals(Object 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.
boolean
Testet, ob das String-Objekt mit der Zeichenkette s beginnt.
startsWith(String s)
Ist dies der Fall, dann gibt die Methode „true“ zurück,
anderenfalls „false“.
boolean endsWith(String
s)
int compareTo(String s) 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
91
Programmieren in Java
die Methode den Rückgabewert 0.
int
regionMatches(int
toffset, String other,
int ooffset, int len)
Suchen in Zeichenketten
int indexOf(String s)
int
IndexOf(String
vonIndex)
s,
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.
int 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.
Ersetzen von Zeichenketten
String toLowerCase()
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.
String toUpperCase() 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.
String
replace(char Diese Methode führt eine zeichenweise Konvertierung des
altesZeichen,
char aktuellen
String-Objekts
durch.
Jedes
Auftreten
von
neuesZeichen)
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
Wrapper-Klassen bereitgestellt wird, in eine Zeichenkette um.
92
Programmieren in Java
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: " +
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.
Konstruktoren:
StringBuffer()
StringtBuffer(String s)
Erzeugt einen leeren StringBuffer
Erzeugt ein neues StringBuffer-Objekt,
Zeichenkette s ist.
das
eine
Kopie
der
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 StringBufferObjekt. 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.
93
Programmieren in Java
Verändern von Elementen: „void setCharAt(int index, char c) throws
StringIndexOutOfBoundException“. Das an Position index stehende Zeichen
wird durch c ersetzt. Falls StringBuffer zu kurz ist, löst die Methode eine
Ausnahme des Typs StringIndexOutOfBoundException 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
StringBuffer-Objekts an, sondern liefert einen Zeiger auf den internen Zeichenpuffer.
Erst wenn der StringBuffer erneut verändert werden soll, wird tatsächlich eine Kopie
erzeugt.
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 synchronized 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
6. Trigonometrische Funktionen
public static double sin(double winkel)
public static double cos(double winkel)
Der Wert von winkel wird im Bogenmaß angegeben.
7. Mathematische Konstanten
public double final double E;
public static final double PI;
94
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 bouleanValue()
public static Boolean valueOf(String s)
public static boolean getBoolean(String name)
public char charValue()
public Character(char value)
public int haschCode()
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 char toLowerCase(char ch)
public static char toUpperCase(char ch)
public static int digit(char ch, int radix)
public static boolean isSpace(char ch)
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
95
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 zur Umwandlung von Zahlen in Zeichenketten und
Zeichenketten in Zahlen:
public static int digit(char zeichen, int radix)
gibt den numerischen Wert eines Zeichens der angegebenen Zahlendarstellung
(„radix“)81 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;
Die Klasse Number
81
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.
96
Programmieren in Java
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 static int parseInt(String s) throws NumberFormatException
public static int parseInt(String s, int radix) throws NumberFormatException
public static Integer valueOf(String s) throws NumberFormatException
public static Integer valueOf(String 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.
97
Programmieren in Java
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)
Überprüfen auf „Unendlich“ bzw. auf „keine Zahl“:
public static boolean is Infinite(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.
98
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 trycatch Block erreicht, z.B.82:
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 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
82
PR14103
99
Programmieren in Java
zahlreichen Standardausnahmen von Java stehen oder auch selbstdefinierte
Ausnahmen. Das Ausnahme- / Fehlerobjekt besitzt einige nützliche Methoden, z.B.
public String getMessage()83. 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();
}
}
}
83
Sie ist in der Klasse Throwable definiert und daher allen Exception-Objekten zugänglich.
100
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 JavaInterpreter 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 tryKlausel 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.
101
Programmieren in Java
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 müssen nicht extra
abgefangen werden. „Errors“ sind 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 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
102
Programmieren in Java
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 IO-Exception). Selbstdefinierte Ausnahmen benötigen eine Subklasse von
Exception. Das Auslösen dieser selbstdefinierten Ausnahmen erfolgt über die throwAnweisung: 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 aurgs[])
{
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
103
Programmieren in Java
catch (Exception a)
{
System.out.println(“Eine Ausnahme wurde eingefangen“);
throw a;
}
Eigene, d.h. benutzerdefinierte Ausnahmen können direkt von der Klasse
Exception abgeleitet werden, z.B.84:
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();
}
84
Vgl. PR14140
104
Programmieren in Java
try { h(); }
catch (MeineAusnahme a)
{
a.printStackTrace();
System.out.println("a.wert() = " + a.wert());
}
}
}
/* 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
*/
105
Programmieren in Java
1.4.4 Einfache Ein-, Ausgaben
Ein- und Ausgabeströme der Klasse System
Die Klasse System enthält drei Datenströme:
public static InputStream85 in
public static PrintStream86 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“87 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.88:
"Einlesen von zwei ganzen Zahlen".
85
Mit der Klasse InputStream können Leseoperationen eines Bytestroms verwirklicht werden, vgl. 7.1.1
Die Klasse PrintStream bezieht sich auf die I/O von Java 1.0
87 Das grundlegende Konzept für Ein- und Ausgabe sind Datenströme, die im Paket „java.io“ versammelt
sind.
88 Vgl. PR14150
86
106
Programmieren in Java
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));
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);
}
}
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äts89 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. Writer90 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.
89
90
Hauptspeicher, Festplatte, Drucker, Netzwerk
vgl. 7.6
107
Programmieren in Java
Bsp.: Das folgende Programm liest eine Zeichenfolge aus einem Eingabestrom und
schreibt die Zeichenfolge in einen Ausgabestrom 91
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)
{
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(...)
91
vgl. PR14150
108
Programmieren in Java
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.
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;
}
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;
}
109
Programmieren in Java
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)
{
System.out.println("Eingefangen in main(), a.printStackTrace()");
a.printStackTrace();
}
}
}
Umlenken von Standardeingabe, Standardausgabe und Standardfehlerausgabe
In Java 1.1 ermöglichen die folgenden Methoden das Umlenken:
- setIn(InputStream)
- setOut(PrintStream)
- setErr(PrintStream)
110
Programmieren in Java
Bsp.: Demonstration der Umlenkung von Standardeingabe und Standardausgabe in
Java 1.192
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")));
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();
}
}
}
92
vgl. PR14150
111
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
Leeraum93
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
Java-Sprachdefinition 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
JavaDokumentations-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 Unicode94.
Bei der Kompilierung wird der Quellcode automatisch in eine Folge von Unicode93
Technisch gesehen ist ein Leeraum kein Token
Alle Literale (d.h. alle vorkommenden Zeichen (Zahlen, Buchstaben, Sonderzeichen, etc.) bestehen aus
Unicode.
94
112
Programmieren in Java
Zeichen transformiert. Eine ASCII-Kodierung wird einfach mit Voranstellen der
Zeichenfolge „\u00“ und folgender Hexadezimalzahl in das passende UnicodeZeichen übersetzt. Dies definiert in Java eine Escape-Sequenz, mit der alle UnicodeZeichen 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 JavaSchlü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.
113
Programmieren in Java
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.
- 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 0X95. 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.
Die Zahlen 0 bis 15 werden durch die Buchstaben „A“ bis „F“ bzw. „a“ bis „f“ dargestellt (Groß- oder
Kleinschreibung ist hier nicht relevant.
95
114
Programmieren in Java
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
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.
115
Programmieren in Java
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)96
- 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
\u0027
\u005c
Oktal-Sequenz
\010
\011
\012
\014
\015
\042
\047
\134
Bedeutung
Backspace
Tab
Neue Zeile
Formularvorschub
Return
Doppeltes Anführungszeichen
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.
96
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.
116
Programmieren in Java
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
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
117
Programmieren in Java
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
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
118
Programmieren in Java
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
Bsp.: Bitweises Manipulieren97
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);
97
PR21502
119
Programmieren in Java
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
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
120
Programmieren in Java
i >> 5, int: -45801399, binaer:
11111101010001010010000001001001
(~i) >> 5, int: 45801398, binaer:
00000010101110101101111110110110
i >>> 5, int: 88416329, binaer:
00000101010001010010000001001001
(~i) >>> 5, int: 45801398, binaer:
00000010101110101101111110110110
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)98. 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
98
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 16Bit-Zahlen entsprechend ihrer Unicode-Codierung behandelt.
121
Programmieren in Java
Die relationalen Operatoren „==“ und „!=“ arbeiten mit allen Objekten. Ihre
Anwendung zeigt häufig ein „verwirrendes Ergebnis“, z.B. 99:
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()100 bereit, z.B.:
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
99
Vgl. PR21500
Viele in der Java-Bibliorhek bereitgestellte Klassen implementieren die Methode equals()
100
122
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 Datei101 oder des Pakets erzeugt.
Tag
@see [Klassenname]
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
@see
Dieses Tag erzeugt einen Verweis („See also“-Link) in der HTML[Klassenname]#[Methodenname] Datei zu der Methode (innerhalb der mit Klassenname
spezifizierten Klasse), die von Methodenname spezifiziert wird
@param
[Parametername] Dieses Tag dient zur Dokumentation der Parameter.
Parametername ist selbsterklärend, und die Beschreibung
[Beschreibung]
spezifizeirt den Parameter. Geht die Beschreibung über eine Zeile
hinaus, so wird dies in der nächsten Zeile fortgeführt.
@version [Text]
Mit diesem Tag kann die Version des Programms spezifiziert
werden.
@author [Name]
Diese Tag fügt den Namen des Autors in die HTML-Datei ein
@return [Beschreibung]
Mit diesem Tag kann der wert beschrieben werden, der von einer
Methode zurückgegeben wird
@exception [Klassenname]
Dieses Tag erzeugt einen Link auf Ausnahmen, die von der Klasse,
die durch Klassenname spezifiziert ist, erzeugt werden.
@deprecated [Ausdruck]
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,
@since [Text]
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 Paketverzeichnis102 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.
Option
-version
101
102
Beschreibung
Diese Option veranlaßt javadoc, die version des JDK anzuzeigen.
vgl. 1.3.2.2, 3. Aufgabe
als Paketname zu verstehen, nicht als physikalisches Verzeichnis
123
Programmieren in Java
-classpath
[Verzeichnis]
-v
-verbose
-d [Verzeichnis]
-public
-protected
-private
-package
-Jflag
-notree
-doencoding [Name]
-sourcepath [Pfad]
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
124
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 ASCII-Zeichensatz 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
125
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 Kontrollstruktur. 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 Kontrollstruktur. 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
++,--
Binäre Arithmetik
Bedeutung
Einer Zeichenvariablen wird ein Wert zugewiesen
Durchführung eines Vergleichs innerhalb einer Kontrollstruktur. 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 Kontrollstruktur. 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
Inkrement- und Dekrement-Operatoren für den Unicode-Wert
von Zeichenvariablen
126
Programmieren in Java
<<,>>,>>>
<<=,>>=,
>>>=
~
&
|
^
&=,|=,^=
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
OR
Resultat 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 Kontrollstruktur. Rückgabe ist true, wenn beide Operanden
denselben
Wert
haben.
Anderenfalls
wird
false
zurückgegeben
Durchführung eines Vergleichs innerhalb einer Kontrollstruktur. 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-,
DivisionsOperatoren
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
Überlauf103 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 Null“ ergibt den Wert „NaN“ (keine Zahl). Ein Unterlauf104 gibt einen speziellen
103
d.h.: Größeres Ergebnis von einer Operation als durch den Wertebereich des jeweiligen Typs ausgedrückt
werden kann.
104 d.h.: kleineres Ergebnis (- außer Null -) von einer Operation als durch den Wertebereich des jeweiligen Typs
ausgedrückt werden kann.
127
Programmieren in Java
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 Kontrollstruktur. Rückgabe ist true, wenn beide Operanden
denselben
Wert
haben.
Anderenfalls
wird
false
zurückgegeben
Ungleichheit
Durchführung eines Vergleichs innerhalb einer Kontrollstruktur. 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-,
DivisionsOperatoren
Zuweisung
Additions-,
Subtraktions-,
Multiplikations-,
DivisionsZuweisungen
Binäre Arithmetik
Additions-,
Subtraktions-,
Multiplikations-,
DivisionsOperatoren
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.
128
Programmieren in Java
2.2.3 Datenfelder (Arrays)
Ein Datenfeld (Array) ist eine Ansammlung von Objekten eines bestimmten Typs105,
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 Referenzvariablen 106. 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.
105
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.
106 Es gibt drei Arten von Referenzvariablen: Klassen, Schnittstellen und Datenfelder
129
Programmieren in Java
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)
{
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
130
Programmieren in Java
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.
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.
131
Programmieren in Java
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 Fehler107. Die
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.
107
Er weist auf eine „Ausnahme“ hin.
132
Programmieren in Java
3. Sortieren108
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.
Bsp.:
1
37 22 18 9 9
22 37 37 37 37
18 18 22 22 22
9
9 9 18 18
25 25 25 25 25
2
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:
108
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;
PR22301
133
Programmieren in Java
// 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]);
}
}
}
2.2.3.4 Mehrdimensionale Datenfelder
Zweidimensionale Datenfelder
Ein zweidimensionale Feld entspricht einer zweidimensionalen Wertetabelle, z.B.:
Z0
Z1
Z2
Z3
Z4
S0 S1 S2 S3
1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4
Das vorliegende Datenfeld umfaßt 4 Zeilen und 3 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]
134
M[0][4]
M[1][4]
M[2][4]
M[3][4]
Programmieren in Java
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.:
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();
}
}
}
135
Programmieren in Java
Bsp.: Das 8-Damen-Problem109
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
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
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.
1
0
0
1
0
0
1
0
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
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
9
1
1
1
1
1
1
9
1
Vorschlag zur Implementierung:
import java.util.*;
109
vgl. PR22305
136
Programmieren in Java
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()
{
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;
137
Programmieren in Java
// 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++)
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();
}
138
Programmieren in Java
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();
}
}
Multdimensionale 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)110.
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[])
{
110
Im Java werden multidimensionale Arrays streng genommen nicht unterstützt
139
Programmieren in Java
// 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) },
};
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 um Paket java.util mit nützlichen
Methoden zum Zugriff auf Arrays:
140
Programmieren in Java
public static void fill(int[] a, int wert)
public static void 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.
141
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.
142
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)
(int)
(long)
(float)
(double)
(char)
(boolean)
(short) x
(int) (x/y)
(long) x
(float) x
(double) x
(char) x
(boolean) 0
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
Abb.: Casting-Operatoren
143
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.111: 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);
}
}
111
vgl. PR23301
144
Programmieren in Java
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 seine Superklasse festgelegt werden. Spezifische Informationen der Subklasse
gehen dabei verloren. Das Konvertieren erfolgt immer nach folgender Form:
(Klassenname) Objekt.
Bsp.112: 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);
112
PR23302
145
Programmieren in Java
if (Math.round(x1*appletBreite) ==
Math.round(x2*appletBreite)) break;
}
}
public void schneideausSegment()
{
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.
146
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;
147
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 DekrementOperatoren113
// 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
*/
113
vgl. PR24500
148
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)
anweisung114
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-Anweisung115
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);
„anweisung“ bedeutet: Eine einfache Anweisung, die nur von einem Semikolon abgeschlossen ist bzw.
eine zusammengesetzte Folge von anweisungen, die von { ... } umschlossen ist.
115 Vgl. PR24601
114
149
Programmieren in Java
if (i >= 0)
{
abs = i;
}
else {
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);
}
}
150
Programmieren in Java
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 Werts116.
„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
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-Anweisung117
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;
Integrale Werte sind bspw. vom Typ „int“, „char“. Nichtintegrale Typen. Z.B. float, String, müssen über eine
Serie von „if“-Anweisungen „switch“ simulieren
117 vgl. PR24601
116
151
Programmieren in Java
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();
}
}
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.118:
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;
118
PR24700
152
Programmieren in Java
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)
Bsp.119:
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.
119
PR24700
153
Programmieren in Java
- 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.
Jeder der drei Ausdrücke kann entfallen.
for (initialisierung; boolescher_Ausdruck; 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);
}
}
154
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) Wiederholungsund 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.120: „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);
}
}
}
120
PR24800
155
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.121: 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;
}
121
PR24800
156
Programmieren in Java
if (i == 3)
{
ausgabe("break");
i++;
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
*/
157
Programmieren in Java
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");
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“.
158
Programmieren in Java
Der Rückgabewert ist der Wert einer return-Anweisung.
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.
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 RUNTIME-Modul 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
159
Programmieren in Java
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.
Der Klassenname
Jede Klasse benötigt einen Namen.
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.
160
Programmieren in Java
2.6 Methoden
2.6.1 Die Deklaration
Die Deklarationen von Methoden haben folgendes Aussehen 122:
Zugriffsspezifizierer Modifizierer Returnwert NameMethode(Parameter)
throws Exceptionliste
Methodenunterschrift123. 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.
122
123
Kursiv Geschriebenes ist optional
Oft wird für denselben Zusammenhang der Begriff „methodensignatur“ verwendet.
161
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 124 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.
124
Nicht nur primitive Datentypen, sondern auch komplexe Objekte.
162
Programmieren in Java
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..
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üu die Rekursion spricht:
1. Es gibt bestimmte rekursiv formulierte Algorithmen, die schneller oder wenigstens gleich schnell
arbeiten als vergleichbare iterative.
2. 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.
163
Programmieren in Java
3. Grafische Benutzeroberflächen und Applets
3.1 Ereignisbehandlung unter grafischen Benutzeroberflächen
Mit Hilfe einfacher Benutzerschnittstellen des Graphical User Interface (GUI))
lassen sich Ein- und Ausgaben125 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
125
Standardein- und Standardausgabe spielen in Java nur im Rahmen des „Debugging“ eine Rolle.
164
Programmieren in Java
Component
Container
Panel
Applet
Window
Frame
Dialog
FileDialog
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 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.
Container
{ 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 synchronized void detFont(Font f)
public Point getLocation()
public Point getLocationOnScreen()
public void setLoaction(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 Dimension getPreferredSize()
public Dimension getMinimumSize()
165
Programmieren in Java
public Dimension getMaximumSize()
public void invalidate()
public Graphics getGraphics()
public FontMetrics getFontMetrics(Font font)
public void paint(Graphics g)
public void update(Graphics g)
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)
public boolean prepareImage(Image bild, int breite, int hoehe, ImageObserver obs)
public int checkImage(Image bild,ImageObeserver obs)
public int checkImage(Image bild, int breite, int hoehe, ImageObserver obs)
public synchronized void addComponentListener(ComponentListener l)
public synchronized void removeComponentListener(ComponentListener l)
public synchronized void addFocusListener(FocusListener l)
public synchronized void removeFocusListener(FocusListener l)
public synchronized void addKeyListener(KeyListener l)
public synchronized void removeKeyListener(KeyListener l)
public synchronized void addMouseListener(MouseListener l)
public synchronized void removeMouseListener(MouseListener l)
public synchronized void addMouseMotionListener(MouseMotionListener l)
public synchronized void removeMouseMotionListener(MouseMotionListener l)
public final void enableEvents(long eventsToEnable)
public final void disableEvents(long eventsToDisable)
public void processEvent(AWTEvent e)
public 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 void addNotify()
public void removeNotify()
public boolean gotFocus(Event evt)
public boolean lostFocus(Event evt, Object was)
public void requestFocus()
public synchronized void add(PopupMenu popup)
public synchronized void remove(MenuComponent popup)
public String toString()
Abb.: Die Klasse Component
166
Programmieren in Java
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 Insets insets()
public Component add(Component comp)
public void remove(Component comp)
public void removeAll()
public LayoutManager getLayout()
public void setLayout(LayoutManager mgr)
...
Abb.: Die Klasse Container
167
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 synchronized 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 synchronized void setResizable(boolen b)
public void remove(MenuComponent m)
public void dispose()
Abb.: Die Klassen Frame und Dialog
168
<< 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 synchronized void setResizable(
boolean b)
Programmieren in Java
Bsp.126: 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[])
{
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
Ausgabefenster:
des
vorliegenden
Programms
zeigt
das
folgende
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.
126
PR14160
169
Programmieren in Java
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.
Ereignistypen. Im JDK 1.1 werden Ereignistypen durch eine Hierarchie von
Ereignisklassen repräsentiert, die alle aus der Klasse java.util.EventObject127
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.
127
Speichert das Objekt, das die Nachricht ausgelöst hat und gibt durch Aufruf von "public Object getSource()"
das Objekt an.
170
Programmieren in Java
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.
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);
171
Programmieren in Java
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.128:
Textfeldbearbeitung
WindowAdapter
mit
"ActionListener-Interface"
import java.lang.*;
import java.awt.*;
import java.awt.event.*;
public class EchoMitBeno 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);
}
});
// 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);
}
});
128
PR14160
172
und
Programmieren in Java
// 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();
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);
}
}
173
Programmieren in Java
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
Zweckmäßigerweise ist dann die Anwendung eine Subklasse von Frame.
werden.
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)
{
System.out.println(ae.getActionCommand());
String s = eingabeTextFeld.getText();
ausgabeTextFeld.setText(s);
}
174
Programmieren in Java
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-catch-Anweisung entsprechend behandelt werden sollte.
Bsp.: 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);
}
});
// TextField eingabeTextFeld = new TextField(20);
Label eingabeTextFeldLabel = new Label("Zahleneingabe:");
eingabeTextFeld.setEditable(true);
eingabeTextFeld.addActionListener(new ActionListener()
{
175
Programmieren in Java
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.
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
Bedeutung
Eine Komponente wurde sichtbar
Eine Komponente wurde unsichtbar
Eine Komponente wurde verschoben
176
Programmieren in Java
componentResized
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 addMouseListener(MouseListener l), die in allen Klassen
zur Verfügung steht, die aus Component abgeleitet sind.
177
Programmieren in Java
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 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
129geerbten 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.
129
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
178
Programmieren in Java
Das Interface MouseMotionListener definiert
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 Komonente 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);
179
Programmieren in Java
Die Taste, die gedrückt wurde, erhält man über die folgenden Methoden der Klasse
KeyEvemt 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.
Symbolischer Name
VK_0..VK_9
VK_A..VK_Z
VK_ENTER
VK_SPACE
VK_TAB
VK_ESCAPE
VK_BACK_SPACE
VK_F1..VK_F!"
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
Zeichentaste131: VK_UNDEFINED
Funktionstaste132: 133
Zeichentaste: VK_...
keyPressed
Funktionstaste: VK_...
keyTyped130
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();
130
zeigt das Verhalten beim Aufruf der Listener-Methode keyTyped
Tasten, mit denen Buchstaben, Ziffern oder sonst. Unicode-Zeichen eingegeben werden, wie z.B. a, A, 1, 2,
% aber auch ESC, SPACE, TAB
132 Dazu gehören bspw. F1, F2, Pos 1 aber auch die Umschalttasten: STRG, ALT, UMSCHALT
133 zeigt das Verhalten beim Aufruf von keyPressed
131
180
Programmieren in Java
Bsp.: Das folgende Programm134 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());
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());
}
}
134
vgl. PR14160
181
Programmieren in Java
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)
{
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());
}
}
182
Programmieren in Java
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);
}
}
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:
public void processComponentEvent(ComponentEvent e)
public void processFocusEvent(FocusEvent e)
public void processKeyEvent(KeyEvent e)
public void processMouseEvent(MouseEvent e)
public void processMouseMoveEvent(MouseEvent e)
public 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
183
Programmieren in Java
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.
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 ComponetEvent durchlaufen wird.
Bsp.: Das folgende Programm schreibt alle möglichen Ereignisse zu Low-LevelEvents 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);
}
}
184
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 Dialoge135" 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 Icon136 zuordnen.
Zuordnen eines Layoutmanagers. Es wird über die Methode "public void
setLayout(LayoutManager mgr)" der Klasse Container realisiert. Java stellt
fünf Layoutmanager137 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.:
135
Modale Dialaloge verhindern die Interaktion des Anwenders mit anderen Fenstern der Anwendung bis zum
Schließen des Dialogfensters.
136 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.
137 Vgl. 5.4.2
185
Programmieren in Java
import java.awt.*;
import java.awt.event.*;
public class PR14171 extends Frame
{
public PR14171()
{
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:
186
Programmieren in Java
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.
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 Dialog138 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.139:
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);
}
});
138
139
vgl. 5.3.4
vgl. PR14170
187
Programmieren in Java
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)
{
// 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.
188
Programmieren in Java
Dialog
FileDialog
public static final int LOAD
public static final int SAVE
<< Konstruktor >>
public FileDialog(Frame eltern)
public FileDialog(Frame eltern, String titel)
public FileDialog(Frame eltern, String titel, int mode)
<< Methoden >>
public String getDirectory()
public synchronized void setDirectory(String dir)
public String getFile()
public synchronized void setFile(String file)
public FilenameFilter getFilenameFilter()
public synchronized void setFilenameFilter(FilenameFilter filter)
Abb.: Die Klasse FileDialog
Bsp.: Laden und Speichern von Dateien mit Unterstützung durch ein Dateidialogfeld140.
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);
}
140
PR14160
189
Programmieren in Java
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,
"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);
}
}
190
Programmieren in Java
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.
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:
Labels141, Schaltflächen(Buttons)142, Kontrollkästchen und Optionsfelder143,
Auswahlmenüs144, Listenfelder145, Textbereiche und Textfelder146, Schieberegler147.
141
vgl. 5.2.2
vgl. 5.2.1
143 vgl. 5.2.3
144 vgl. 5.2.4
145 vgl. 5.2.5
146 vgl. 5.2.6
147 vgl. 5.2.7
142
191
Programmieren in Java
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
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.148:
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.
148
PR52900
192
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äge 149.
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.
149
vgl. 5.3.3
193
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(Stiing label, MenuShortcut s)
<< Methoden >>
public void addNotify()
public String getLabel()
public synchronized void setLabel(String label)
public boolean isEnabled()
public synchronized void setEnabled(boolean b)
public MenuShortcut getShortcut()
public void setShortcut(MenuShortcut s)
public final void enableEvents(long eventsToEnable)
protected final void disableEvents(long eventsToEnable)
public void setActionCommand(String kommando)
public String getActionCommand()
public synchronized void
addActionListener(ActionListener l)
public synchronized void
removeActionListener(ActionListener l)
proteced 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 countMenus()
public Menu getMenu(int i)
public synchronized 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)
Menu
194
Programmieren in Java
PopupMenu
<< Konstruktor >>
public PopupMenu()
public PopupMenu(String label)
<< Methoden >>
public void addNotify()
public void show(Component origin, int x, int y)
Abb.: Die AWT-Menüklassen
195
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 Dokumentationsbeschreibungssprache 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.
196
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 AppletTag (</applet>) können benötigte Parameter oder beliebiger Text eingegeben
werden. Die einfachste Form der Applet-Referenz. Ohne irgendwelche Parameter
wird ein Java-Applet 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 Verzeichnis150 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.
150
Pfadname, in dem sich die Klassen befinden
197
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
198
Programmieren in Java
1. Das Erstellen eines Applets
Zum Erstellen eines Applet muß immer eine „Subklasse“ der Klasse Applet151
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 WebBrowser.
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.
151
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.*
199
Programmieren in Java
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
Applet-Fenster, 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-Klasse152 in den Code der Applet-Subklasse importiert153
wird.
Bsp.: „Aller Anfang ist schwer!. Dieser Spruch soll mit Hilfe eines Applet gezeigt
werden.. Die zugehörige Quellcodedatei „AllerAnfangApplet.java“ umfaßt 154:
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);
152
Teil des Pakets java.awt
Normalerweise geschieht dies über: import java.awt.Graphics
154 vgl. PR32101
153
200
Programmieren in Java
// 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);
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:
201
Programmieren in Java
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 gezeichnet155 werden muß. Das in der „paint“-Methode abgebene GraphicsObjekt 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.
public void update(Graphics g)
Sie wird von repaint() aufgerufen. Die „update“-Methode löscht den vollständigen
Zeichenbereich156 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.157: 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;
155
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.
156 Das ruft oft den unangenehmen Flimmereffekt bei schnellen Bildsequenzen hervor.
157 PR32305
202
Programmieren in Java
// 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;
}
*/
// 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.
203
Programmieren in Java
// 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;
}
*/
// 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.
204
Programmieren in Java
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
Multithreading-fä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
aktualisiert158. 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()
{
// 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
}
}
158
vgl. PR42001
205
Programmieren in Java
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;
}
}
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) {}
}
}
206
Programmieren in Java
// 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()
{
// ...
}
// Optional: die Ausgabemethode
public void paint(Graphics g)
{
// ..
}
// Bei Multithreading: Verwendung der run-Methode
public void run()
{
// ...
}
}
207
Programmieren in Java
3.3.4 Animation in Applets
Animationsschritte
Eine Animation umfaßt in Java zwei Schritte:
1.Aufbau und Ausgabe eines Animationsrahmens (-fenster).
2. 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;
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;
208
Programmieren in Java
// 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);
}
}
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 JavaApplets 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()-Methode159
update() löscht den Bildschirm durch Füllen mit der aktuellen Hintergrundfarbe,
setzt die aktuellen Farbe auf die Vordergrundfarbe und ruft anschließend paint()
159
vgl. PR32212
209
Programmieren in Java
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();
}
}
public void stop()
{
if (faden != null)
{
faden.stop();
faden = null;
}
}
210
Programmieren in Java
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“160 wird eine zweite Oberfläche geschaffen, in der alles
vorgezeichnet und dann auf einmal in die Zeichnungsoberfläche des Applet
ausgegeben wird.
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;
// Die initiale Position vom Pendel
double theta = (double) 0.;
//
double wechsel = (double) 0.01;
160
211
Programmieren in Java
//
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);
}
212
Programmieren in Java
public void paint(Graphics g)
{
backgroundGraphics.setColor(Color.white);
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 URL161)
- 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);
}
161
Adressen im World Wide Web werden durch URL-Objekte repräsentiert. Die Klasse URL (Uniform
Resource Locator) ist Teil des Pakets java.net
213
Programmieren in Java
Bsp.162:
import java.awt.*;
import java.applet.*;
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 163 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
Verzeichnis-URL 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
vgl. PR33501
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).
163
214
Programmieren in Java
Bsp.164:
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());
}
}
}
}
164
Vgl. PR33602, Aufruf z.B.: javac PR33602 file:///c:/winnt\media\ringin.wav
215
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.Applet
<< Konstruktor >>
public JPanel()
<< 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
216
Programmieren in Java
3.3.8 Das Interface AppletContext
Die Referenz vom Typ des Interface AppletContext dient vorwiegend zur
Kommunikation zwischen Applet und dem Webbrowser bzw. anderen Applets.
<< 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.
217
Programmieren in Java
3.4 Grafische Benutzeroberfläche mit Observable-Observer
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
{
public Observable();
public void addObserver(Observer o);
public void deleteObserver(Observer o);
public void deleteObservers();
public int countObservers);
public void setChanged();
public 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.
public synchronized 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 synchronized 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 synchronized boolean hasChanged()
stellt fest, ob ein Objekt von Interesse modifiziert wurde.
public synchronized void addObserver(Observer obs)
Mit dieser Methode können Observer ihr Interesse an einem beliebigen Observable bekunden.
public synchronized void deleteObserver (Observer obs)
218
Programmieren in Java
Mit dieser Methode kann ein Observer die Überwachung der entsprechenden Observables beenden
public synchronized void deleteObservers()
löscht die Liste aller Observer
public synchronized 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 Observable165 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.:166
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();
165
Das Konzept für diese Klasse ist eine Leihgabe von Smalltalk und unter dem namen Model-View-Controller
(MVC) bekannt.
166 PR34100
219
Programmieren in Java
public BoxObserver(int grid)
{
setTitle("Demonstration Observer");
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();
}
}
220
Programmieren in Java
private final boolean nextTo(OCBox b)
{
return Math.abs(x - b.x) <= 1 && Math.abs(y - b.y) <= 1;
}
}
221
Programmieren in Java
3.5 Swing
Swing167 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.
3.6 Java Beans
3.6.1 Wiederverwendbare Softwarekomponenten
Unter einer Software-Komponente168 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
167
vgl. 5.6
Die Definition wurde zitiert nach: Griffel, Frank: Componentenware – Konzepte und Techniken eines
Softwareparadigma, dpunkt-Verlag, 1998
168
222
Programmieren in Java
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
(„Design-Pattern“169) 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.
3.6.3 Enterprise Java Beans
„Java Beans“ wurde als ein lokales Komponentenmodell170 entworfen. Das JavaBeans-Modell 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
-
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.
170D.h.: Die Kommunikation zwischen Komponenten findet auf einer maschine statt.
223
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 Methoden171 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 setXOR;ode(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 abtsract void clearRect(int x, int y, int width, int height)
public abstract void drwaRoundRect(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, intx, int y)
public abstract boolean drawImage(Image bild, int x, int y, ImageObserver observer)
....
Abb.: Die Klasse Graphics
171
vgl. 3.2.2
224
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 Linien172
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();
}
}
172
vgl. PR42005
225
Programmieren in Java
public void stop()
{
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 abstract 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 void setColor(Color c).
226
Programmieren in Java
Bsp.: Ein Applet mit zufällig verteilten Rechtecken173
// 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;
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);
}
}
173
vgl. PR41103
227
Programmieren in Java
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.
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.174:
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);
}
}
174
PR41105
228
Programmieren in Java
- Weitergabe einer Instanz der Polygon-Klasse, z.B.175:
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),
z.B.176:
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 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 PolygonObjekt 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
175
176
PR41105
PR41105
229
Programmieren in Java
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 bollean contains(int x, int y)
...
public Rectange getBounds();
...
public boolean inside(int x, int y)
...
public void translate(int deltaX, int deltaY)
Abb.: Die Klasse Polygon
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)
230
Programmieren in Java
4.1.2 Farbangaben
Java setzt Farben aus sog. Primärfarben (Rot, Grün, Blau) des Lichts zusammen
(RGB-Modell). 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ß
Schwarz
Grau
Rot
Grün
Blau
Yellow
Magenta
Cyan
Rot-Anteil
255
0
127
255
0
0
255
255
0
Grün-Anteil
255
0
127
0
255
0
255
0
255
Blau-Anteil
255
0
127
0
0
255
0
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.
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.
231
Programmieren in Java
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 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.
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 FontKlasse177 und die Fontmetrics-Klasse178 beim Textzeichnen eine Rolle.
Font
public static final int PLAIN
public static final int BOLD
public static final int ITALIC
public String name
public int style
public int size
<< 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 decode(String str)
public static Font getFont(String nm, Font font)
public int hashCode()
public boolean equals(Object obj)
public String toString()
FontMetrics
{ abstract }
public Font getFont()
public int getLaeding()
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()
Abb.: Die Klassen Font und FontMetrics
177
Die Font-Klasse stellt bestimmte Fonts dar (Name, Stil, Fontgröße)
Die Fontmetrics-Klasse enthält Informationen über den Font wie tatsächliche Höhe und Breite eines
bestimmten Zeichens.
178
232
Programmieren in Java
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-TypeFonts“ „Arial“, „TimesNewRoman“ und „CourierNew“ abgebildet.
style: Auswahl der Ausprägung (fett, kursiv)
Name
Font.PLAIN
Font.BOLD
Font.ITALIC
Wert
0
1
2
Bedeutung
Standard-Font
fett179
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 void 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.: 180
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]);
}
}
BOLD und ITALIC können auch gemeinsam verwendet werden, indem beide Konstanten mit „+“
zusammengefügt werden.
180 PR41300
179
233
Programmieren in Java
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()
getLeading()
getHeight()
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)
Gibt den Abstand zwischen dem Überstand einer Zeile und dem Überstand der
nächsten Zeile aus
gibt die Gesamthöhe der Schrift aus, d.h. die Summe von Überstand, Unterstand und
Zeilenabstand
Abb.: Fontmetrics-Methoden
Bsp.181:
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;
}
//
181
PR41301
234
Programmieren in Java
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);
}
}
235
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 Rectange(Rectangle r)
public Rectange (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 Rectange(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.
236
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.
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-Bilder182
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, ImgaObserver obs)
public Image getScaledInstance(int vertikal, int horizontal, int modus)
<< interface >>
182
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).
237
Programmieren in Java
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)
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 synchronized void addImage(Image bild, int id)
public synchronized 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.ImageObeserver 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.
238
Programmieren in Java
Bsp.: Ein Bildbetrachter183
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);
// 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);
183
PR42300
239
Programmieren in Java
„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.waitForIdD0). 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 Aufruf
meinTracker.statusAll(true). Die Methoden statusID() bzw. statusAll() geben eine
ganze Zahl zurück, die durch folgende Flags symboloisiert 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 verwenden184. 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.
184
typischerweise einfache Zeichenroutinen, die das Bild auf dem Bildschirm anzeigen.
240
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. BufferdImageOp ist eine
Schnittstelle, die von AffineTransformOp, ConvolveOp, BandCombineOp, 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.:185 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);
}
}
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.
185
PR42300
241
Programmieren in Java
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);
}
}
Abb.: Darstellung von SpeicherBild
242
Programmieren in Java
Die PixelGrabber-Klasse nimmt ein Bild und macht aus diesem ein Datenfeld mit
ganzen Zahlen.
Der PixelGrabber ist für Modifikationen bereits existierender Bilder nützlich.
4.2.4.2 Kopieren von Bildern in einen Speicher
Die Klasse PixelGrabber nimmt ein Bild und macht aus diesem ein Datenfeld von
Ganzzahlen.
243
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 }
public Stroke stroke
// Definition von Linienstiften
public Paint paint
// Erweiterung des Farbmodells
public AffineTransform transform
// erlaubt Veränderungen der Form
public Composite composite
// gibt an, wie Objekte verschiedener Farben überlagert werden sollen,
// erlaubt weiche Farbübergänge (Anti-Aliasing)
....
<< Konstruktor >>
protected Grahics2D()
<< 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, ImageObsercer obs)
public abstract void fill(Shape s)
public abstract GraphicsConfiguration getDeviceConfiguration()
public abstract FontrenderContext(getRenderContext()
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(Color c)
public abstract setPaint(Paint paint)
public abstract void setRenederingHint(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
244
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
Graphics2D-Objekt 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);
}
}
Zur Definition des Rechtecks wird die neue Java 2D-API-Klasse GeneralPath
(Bestandteil des geom-Pakets186) herangezogen. Das Erstellen von Standardformen
186
Graphics2D ist Bestandteil des Pakets java.awt
245
Programmieren in Java
(z.B. Rechtecke Ellipsen, Bögen, Kurven) kann einfacher über diverse Subklassen
der Klasse Shape (in Verbindung mit GeneralPath) erfolgen.
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 2-D-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
3. Festlegen vom Aussehen der Form, des Textstrings oder des Bildes über
passende Graphics2D-Methoden.
246
Programmieren in Java
Java 2D API-Klassen187
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
CubicCurve2D188. 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_BUT,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
Besonders bei breiten Linie ist es interessant, wie die Linie endet. Hier läßt sich aus
CAP-BUT (keine Abschlußpunkte), CAP_ROUND (Anzeige von Kreisen an beiden
Enden) und CAP_SQUARE auswählen.
187
PR43210
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.
188
247
Programmieren in Java
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).
Das Objekt RenderingHints
4.3.3 Affine Transformation
Eine affine Transformation eines Objekts ist eine Translation (Verschiebung),
Rotation bzw. Scherung189. 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
189
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.
248
Programmieren in Java
249
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 Komponenten190 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
190
Java 1.2 und das Swing-Konzept erweitern diese Komponenten noch einmal um einen satz von neuen und
ergänzenden Komponenten.
250
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 Applet191 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
getActionCommand aufrufen kann, mit der die Beschriftung des Button abgefragt
werden kann. Falls das Action-Kommando nicht mit der Beschriftung identisch ist,
191
Abgeleitet von der Panel-Klasse
251
Programmieren in Java
kann es in der Button-Klasse durch „public void setActionCommand(String
kommando)“ geändert werden.
Bsp.: Setzen von Hintergrundfarben nach verschiedenen Buttonklicks192
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);
}
});
192
PR52105
252
Programmieren in Java
this.setLayout(new BorderLayout(15,15));
Panel p = new Panel();
p.setLayout(new FlowLayout(FlowLayout.CENTER,15,15));
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, Objekct 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.
253
Programmieren in Java
Bsp.: Setzen von Hintergrundfarben nach verschiedenen Buttonklicks193
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
if (knopfName.equals(“Rot“))
setBackground(Color.red);
else if (knopfName.equals(“Blau“))
setBackground(Color.blue);
else if (knopfName.equals(“Gruen“))
setBackground(Color.green);
else if (knopfName.equals(“Gelb“))
setBackground(Color.yellow);
else if (knopfName.equals(“Schwarz“)) setBackground(Color.black);
else;
}
}
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
193
vgl. PR52105
254
Programmieren in Java
wird) und einem Zustand (boolesche Variable, die angibt, ob die Box eingeschaltet
wurde oder nicht194). 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)
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.
194
Standardmäßig ist die Box ausgeschaltet (Wert ist false oder „off“).
255
Programmieren in Java
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()
{
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
Checkbox-Gruppe, 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
256
Programmieren in Java
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())
{ 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“); }
}
}
}
257
Programmieren in Java
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.195:
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;
}
// 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;
}
195
PR54305
258
Programmieren in Java
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 getSelectionIndex()
liefert den Index des ausgwä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 select(int)
// Auswahl über den Index
public 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. Zusätzlich gibt es public Object getItem(), die das ausgewählte
Element als String zur Verfügung stellt.
259
Programmieren in Java
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
Funktionalität wie Choice an. Zusätzlich stehen zur Verfügung:
260
nahezu
dieselbe
Programmieren in Java
public int[] getSelectedIndexes()
// liefert einen Array mit den Indexpositionen der ausgewählten
// 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 ActionEreignisse. 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);
}
}
261
Programmieren in Java
class ALL implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
Object obj = e.getSource();
if (obj instanceof List)
{
System.out.println(“Listenfeld-Aktion: “ + e.getActionCommand());
}
}
}
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 breite)
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 breite)
262
Programmieren in Java
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 spalten)
verändert werden.
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 getSelectEnd()
kann die aktuelle Auswahl begefragt 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 mit die Veränderung von Text verhindern. Mit
public void setEchoCharacter(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 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 textValueChanged mit
getSource das zugehörige Textfeld beschafft und mit getText auf seinen Inhalt
263
Programmieren in Java
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();
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);
}
}
264
Programmieren in Java
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);
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 scroll)
erzeugt ein TextArea-Objekt, in dem über scroll 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.
265
Programmieren in Java
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 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,
int 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
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
Ereignisempfänger.
Scrollbar
sendet
Adjustment-Ereignisse
an
seine
Diese müssen das Interface AdjustmentListener
266
Programmieren in Java
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,
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
Bsp.:
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
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();
}
267
Programmieren in Java
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());
}
}
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());
}
}
268
Programmieren in Java
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.
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
269
Programmieren in Java
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
Graphics-Objekts 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-,
Fokusund
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);
}
// Instanzmethoden
public void bereinigen()
{
zeichneObjekte = false;
repaint();
}
public void zeichnen()
{
zeichneObjekte = true;
repaint();
}
270
Programmieren in Java
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;
// Konstruktoren
public ZeichneGUI()
{
this(200,200);
}
public ZeichneGUI(int breite,int hoehe)
{
super(); setTitle(„Demonstration Zeichenflaeche“);
addWindowListener(new WindowAdapter()
{
271
Programmieren in Java
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();
}
}
272
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.
Einstellen von Vorder- Und Hintergrundfarbe. Die Vordergrundfarbe dient zur
Ausgabe von Grafik- und Textobjekten, wenn im Grafik-Kontext keine andere Farbe
273
Programmieren in Java
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()-Methode196. Mit public void hide()197 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.
196
Für show() aus dem Paket java.awt.Component gibt es als neue Variante die Methode
setVisible(boolean)
197 hide() ist in java.awt.Frame als „deprecated“ deklariert. Statt dessen steht setVisible(boolean) zur
Verfügung
274
Programmieren in Java
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
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)
Zugrff 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)
275
Programmieren in Java
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)
Seperatoren für Menüeinträge. Ein Seperator wird als waagrechter Strich zur
Trennung der Menüeinträge angezeigt:
public void addSeperator()
// fügt den Seperator hinter dem zuletzt eingefügten Menüeintrag ein
public void insertSeperator(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);
276
Programmieren in Java
// 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“);
// 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);
277
Programmieren in Java
// 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();
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
{
278
Programmieren in Java
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)
{
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);
}
}
279
Programmieren in Java
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(Frane
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 rezisable)
public boolean isResizable()
geschehen bzw. überprüft werden.
280
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
Layout-Manager 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.
2. Die Reihenfolge, in der die AWT-Komponenten in einen Container eingefügt
werden.
3. Die Art des Layout-Managers. Das AWT umfaßt 5 verschiedene Typen von
Layout-Managern, die die Oberfläche unterschiedlich aufgliedern:
-
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());
281
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)
<< Methoden >>
public void addLayoutComponent(String name, Component comp)
public int getAlignment()
public void setAlignment(int align)
....
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“);
add(meinKnopf2);
Button meinKnopf3 = new Button(“Gruen“);
add(meinKnopf3);
Button meinKnopf4 = new Button(“Pink“);
add(meinKnopf4);
Button meinKnopf5 = new Button(“Rosa“);
add(meinKnopf5);
Button meinKnopf6 = new Button(“Gelb“);
add(meinKnopf6);
Button meinKnopf7 = new Button(“Cyan“);
282
Programmieren in Java
add(meinKnopf7);
}
}
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>
Die BorderLayout-Klasse
<< interface >>
LayoutManager
283
Programmieren in Java
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).
284
Programmieren in Java
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());
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:
285
Programmieren in Java
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>
<BODY>
<APPLET CODE=“GridLayoutTestAppl.class“ WIDTH=300 HEIGHT=200>
</APPLET>
</BODY>
</HTML>
zu der folgenden Darstellung
286
Programmieren in Java
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
die Variablen gridwidth und gridheight bestimmen, wie viele Zellen hoch und breit eine
Komponente sein sollte (Standardwert ist 1). Der Wert GridBagConstraint.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 VERTICAL. „GridBagConstraint.BOTH“ sorgt für eine Streckung der Komponenten in
beiden Richtungen so, daß sie den Anzeigebereich voll ausfüllen.
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) 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 isapdx an. Wieviel obeb und unten hinzukommen, speichert isapdy (isapdx, isapdy
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
198vgl.
PR54205
287
Programmieren in Java
{
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);
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();
}
288
Programmieren in Java
Die CardLayout-Klasse
CardLayout199 plaziert die Elemente auf sich gegenseitig verdeckende Karten.
Jeweils eine dieser Karten kann sichtbar gemacht werden.
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)
199
bestimmt
die
Anfangsposition
Das CardLayout ist als Notizbuchersatz durch die Einführung der Klasse JTabbedPane in Swing fast
überflüssig geworden
289
und
Programmieren in Java
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);
}
}
}
290
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();
}
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,
291
Programmieren in Java
// 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);
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); }
}
}
292
Programmieren in Java
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 Labels201, der Elemente, des Inhalts der
Komponenten202. Anstatt des zusätzlichen Arguments kann auch die Methode 203
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.
201
vgl. PR52105
Nicht vergessen: Das Argument in den richtigen Objekt-Typ zu casten
203 vgl. PR54305
202
293
Programmieren in Java
5.5.2 Das Event-Handling in Java 1.1 bzw. Java 1.2
Im neuen Ereignismodell
java.util.EventObject.
java.awt.AWTEvent.
erben die Ereignisklassen von der
AWT-Events
erben
von
der
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
FocusListener
ItemListener
KeyListener
MouseListener
MouseMotionListener
WindowListener
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)
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
294
Programmieren in Java
// 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");
}
}
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-EventHandling
„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“
„remove“-Methoden
ActionEvent
ActionListener
addActionListener()
removeActionListener()
AdjustmentEvent
und
„add“-
bzw. Komponenten, die dieses Ereignis unterstützen
Button, List, TextField, MenuItem und seine
abkömmlinge einschl. CheckboxmenuItem, menu
und PopupMenu
Scrollbar,
295
Programmieren in Java
AdjustmentListener
addAdjustmentListener()
removeAdjustmentListener()
ComponenEvent
ComponenListener
addComponenListener()
removeComponentListener()
ContainerEvent
ContainerListener
addContainerListener()
removeContainerListener()
FocusEvent
FocusListener
addFocusListener()
removeFocusListener()
KeyEvent
KeyListener
addKeyListener()
removeKeyListener()
MouseEvent
MouseListener
addMouseListener()
removeMouseListener()
MouseEvent
MouseMotionListener
addMouseMotionListener()
removeMouseMotionListener()
WindowEvent
WindowListener
addWindowListener()
removeWindowListener()
ItemEvent
ItemListener
addItemListener()
removeItemListener()
TextEvent
TextListener
addTextListener()
removeTextListener()
alles was mit der Erzeugung vom „Adjustable“Interface zu tun hat
Component und seine Abkömmlinge, einschl.
Button, Canvas, Checkbox, Choice, Container,
Panel, Applet, ScrollPane, Window, Dialog,
FileDialog, Frame, Label, List, Scrollbar, TextArea,
TextField
Container und seine Abkömmlinge, einschl. Panel,
Applet, ScrollPane, Window, Dialog, FileDialog,
Frame
Component und seine Abkömmlinge einschl.
Button, Canvas, Container, Panel, Applet,
ScrollPane,
Window,
Dialog,
FileDialog,
FrameLabel, 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
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
296
Programmieren in Java
Jeder Komponenten-Typ unterstützt nur bestimmte Ereignis-Typen:
Komponenten-Typ
Adjustable
Applet
Button
Canvas
Checkbox
CheckboxMenuItem
Choice
Component
Container
Dialog
FileDialog
Frame
Label
List
Menu
MenuItem
Panel
PopupMenu
Scrollbar
ScrollPane
TextArea
TextComponent
TextField
Window
Ereignis, das durch die Komponente unterstützt
wird
AdjustmentEvent
ContainerEvent,
FocusEvent,
KeyEvent,
MouseEvent, ComponentEvent
ActionEvent,
FocusEvent,
KeyEvent,
MouseEvent,
ComponentEvent
ItemEvent, FocusEvent, KexEvent, 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
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
297
Programmieren in Java
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
ItemListener
TextListener
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)
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.
298
Programmieren in Java
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)
{ 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);
}
}
204
PR55202
299
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
Model-View-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
Menüs
- ComboBoxModel
behandlung
Elemente
- ListModel
- TreeModel
- ListDataEvent
- JComponent - JFrame
- JMenuBar
- TableModel
- ListSelectionEvent
- JButton
- JDialog
- JMenuItem
- TableColumnModel - TableModelEvent
- JCheckBox
- JPanel
- JCheckbox- BoundesRangeModel - TreeModelEvent
- JRadioButton - JApplet
MenuItem
- Renderer
- TreeExpansionEvent - JLabel
- JFileChooser
- JRadioBox- ListcellRenderer
- TreeSelectionEvent - JTextField
- JColorChooser
MenuItem
- TableCellRenderer
- CaretEvent
- JPasswordField - JdesktopPane - JMenu
- TableCellEditor
- MenuEvent
- JTextArea
- JInternalPane
- JPopupMenu
- TreeCellRenderer
- PopupMenuEvent
- JComboBox
- JOptionPane
- JToolBar
- TreeCellEditor
- JList
- JSrollPane
- JSeparator
- ComboBoxEditor
- JScrollBar
- JTabbedPane
- TreeNode
- JSlider
- MutableTreeNode
- JProgressbar
- Border
- JTree
- Scrollable
- JTable
- MenuElement
- Document
- Caret
ModelBorder
Bilder
Sonstiges
- Style
implementierung
- StyleDocument
- DesktopManager
- AbstractlistModel - BorderFactory - ......
- ImageIcon
- Icon
- DefaultListmodel
- TitleBorder
- Buttongroup
- SwingConstants
- AbstractTableModel - LineBorder
- KeyStroke
- *Listener
- ...
- ...
- ....
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.
300
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 (Grphics 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()
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
301
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()
{
302
Programmieren in Java
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);
}
}
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:
setDefauftCloseOperation(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.
303
Programmieren in Java
6. Utilities
6.1 Collections
Collections sind Datenstrukturen zur Aufnahme und Verarbeitung von Datenmengen.
Typische Collections sind Stacks, Queues, Deques, 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 Die Klasse Vector
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.
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.
Anlegen eines neuen Vektors (Konstruktor): public Vector()
Einfügen von Elementen: public void addElement(Object obj)
// Anhängen an des Ende der bisher vorliegenden Liste von Elementen
Eigenschaften: public final boolean isEmpty()
// Prüfen, ob der Vektor leer ist
public final int size()
// bestimmt die Anzahl der Elemente
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.
304
Programmieren in Java
Vector
<< Konstruktoren >>
public Vector()
public Vector(int startKapazitaet)
public Vector(int startKapazitaet, int kapazitaetsSchrittweite)
<< Methoden >>
public final synchronized Object(elentAt(int index)
public final int size()
public final synchronized Object firstElement()
public final synchronized Object lastElement();
public final synchronized void insertElementAt(Object obj, int index)
public final synchronized void setElementAt(Object obj, int index)
public final synchronized copyInto(Objrct einArray[])
public final boolean contains(Object obj)
public final int indexOf(Object obj)
public final int lastIndexOf(Object obj)
public final synchronized boolean removeElement(Object obj)
public final synchronized void removeElementAt(int index)
public final synchronized void removeAllElements()
public final int capacity()
// gibt an, wieviel Elemente im Vektor Patz haben
// (, ohne daßautomatische Größenanpassung erfolgt)
public synchronized Object clone()
public final synchronized String toString()
Abb.: Die Klasse Vector
Zugriff auf Elemente: Für den sequentiellen Zugriff steht ein Iterator zur Verfügung.
Wahlfreier Zugriff erfolgt über:
public Object firstElement() throws ArrayIndexOutOfBoundException;
public Object lastElement() throws ArrayIndexOutOfBoundException;
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. Ein Iterator wird in Java durch das Interface Enumeration
bereitgestellt206.
Das Interface Enumeration.
public Interface Enumeration
{
public boolean hasMoreElements();
public Object nextElement() throws NoSuchElementException;
// setzt den internen Zeiger auf das nächste Element
}
In der Klasse Vector liefert die Methode public Enumeration elements()
einen Enumerator (Iterator) für alle Elemente, die sich in Vector befinden.
206
und wird deshalb in der Java-Welt Enumerator genannt.
305
Programmieren in Java
6.1.2 Die Klasse Stack
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
Test: public boolean empty()
// bestimmt, ob der Stack leer ist
Vector
Stack
public Object push(Object obj)
public synchronized Object pop()
public synchronized Object peek()
public synchronized int search(Object obj)
public boolean empty()
Abb.: Die Klasse Stack
Anwendung: „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:
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
306
Programmieren in Java
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  444 10
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 Testprogramm207 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());
}
System.out.println();
}
}
207
PR61210
307
Programmieren in Java
6.1.3 Die Klasse Hashtable
Dictionary
{abstract}
public abstract Object put (Object key, Object value)
publicc abstract Object get(Object key)
public abstract Enumeration elements()
public abstract Enumeration keys()
public abstract int size()
public absztract 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 synchronized boolean contains(Object wert)
public synchronized boolean containsKey(Object key)
public synchronized void clear()
public synchronized Object clone()
protected void rehash()
public synchronized 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 synchronized void load(InputStream in) throws IOException
// Hier muß ein InputStream übergeben werden, der die daten der
// Property-Liste zur Verfügung stellt.
public synchronizred void save(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
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
308
Programmieren in Java
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.
Konstruktor. public Hashtable()
Einfügen von Elementen. public Object put(Object key, Object wert)
Der Auruf von „put“ fügt das Schlüssel-Werte-Paar (key,wert) in die Hashtable
ein. Weder key noch wert dürfen dabei null sein. Falls bereits ein Wertepaar mit
dem Schlüssel key vorliegt, wird der bisherige Wert gegen den neuen ausgetauscht,
und put liefert in diesem Fall den Wert zurück, der bisher dem Schlüssel zugeordnet
war
Zugriff auf Elemente. public Object get(Object key)
// get() erwartet ein Schlüsselobjekt
// und liefert den dazu passenden Wert
Zusätzlich gibt es noch:
public boolean contains(Object wert)
// Test auf einen bestimmten Wert
public boolean containsKey(Object key)
// Test auf einen bestimmten Schlüssel
Iteratoren. In der Klasse HahTable gibt es zwei Iteratoren:
public Enumeration elements()
// Iterator für die Auflistung aller Werte der Hashtabelle.
public Enumeration keys()
// Iterator für die Auflistung aller Schlüssel
Wie üblic liefern beide Iteratoren ein Objekt, welches das Interface Enumeration
implementiert.
Der
Zugriff
erfolgt
daher
mit
Hilfe
der
Methoden
hasMoreElements() und nextElement().
Bsp.: Hashtabelle zum Test der Zufälligkeit von Zufallszahlen der Klasse
Math.random.
import java.util.*;
class Zaehler
{
int i = 1;
public String toString()
{
return Integer.toString(i);
}
}
309
Programmieren in Java
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);
}
}
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.
6.1.4 Die Klasse Properties
Die Klasse Properties ist aus HashTable abgeleitet und repräsentiert ein auf
String-Paare spezialisiertes Dictionary, das es erlaubt, seinen Inhalt auf einem
externen Datenträger zu speichern oder von dort zu laden. Ein solches Objekt wird
auch als Property-Liste (oder Eigenschaftsliste) bezeichnet.
310
Programmieren in Java
6.1.5 Die Klasse BitSet
Sie umfaßt eine Menge boolescher Flags, die mit logischen Operationen wie „and()“,
„or()“, „xor()“ verknüpft werden können.
BitSet
public void and(BitSet bs)
public void or(BitSet bs)
public void xor(BitSet bs)
public void clear(int index)
public boolean get(int index)
public void set(int index)
public int size()
Abb.: Die Klasse BitSet
6.2 Java 2 Collections
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.
311
Programmieren in Java
6.2.1 Das Interface Collection, das Interface Iterator und das Interface
Comparator
Das Interface Collection
Das Interface Collection bildet die Basis aller Collection-Klasse und –Interfaces
des JDK 1.2.
<< interface >>
Collection
public void clear();
public boolean add(Object o);
public boolean addAll(Collection c);
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);
public boolean contains(Object o);
// Rückgabewert ist true, falls das vorgegebene Element gefunden werden konnte
public boolean containsAll(Collection c);
public boolean equals(Object o);
public boolean isEmpty();
public int size();
public boolean retainAll(Collection c);
public Iterator iterator();
publicObject [] toArray();
public Object [] toArray(Object [] a);
public int hashCode();
public String toString()
// Rückgabewert ist die Zeichenketten-Repräsentation der Kollektion.
Abb.: Das Interface Collection
312
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();
// das zuletzt gelöschte Element wird anschließend mit remove() gelöscht
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
6.2.2 Die Collections des Typs List
Collections des Typs List fassen eine Menge von Elementen zusammen, auf die
sequentiell oder über Index (-positionen) zugegriffen werden kann. Wie Vektoren der
Klasse Vector 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.
313
Programmieren in Java
Das Interface List
<< 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, CallsCastException 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();
public int hashCode();
public Iterator iterator();
public ListIterator listIterator():
public ListIterator listIterator(int startIndex);
public Object set(int index, Obeject element);
public List subList(int fromIndex, int toIndex);
public Object [] toArray();
public Object [] toArray(Object [] a);
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 UnsupportedException auslösen und diverse Methoden
abstract deklariert sind.
LinkedList
realisiert die doppelt verkettete, lineare Liste
ArrayList
implementiert die Liste als Array von Elementen.
314
Programmieren in Java
<< interface >>
Collection
<< interface >>
List
LinkedList
ArrayList
Vector
<< Konstruktor >>
<< Konstruktor
public LinkedList();
public ArrayList();
public LinkedList(Collection collection);
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 >>
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();
puplic 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();
315
Programmieren in Java
6.2.3 Die Collections 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).
316
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
public HashSet();
public HashSet(Collection collection);
public HashSet(int anfangskaazitaet);
public HashSet(int anfangskapazitaet,
int ladeFaktor);
<< interface >>
SortedSet
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();
public Object fisrt();
public Object last();
TreeSet208
public TreeSet()
public TreeSet(Collection collection);
public TreeSet(Comparator vergleich);
public TreeSet(SortedSet collection);
208
implementiert die sortierte Menge mit Hilfe der Klasse Treemap, verwendet einen Red-Black-Tree als
Datenstruktur
317
Programmieren in Java
6.2.4 Die Collections 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. StandardImplementierungem sind HashMap, HashTable und TreeMap.
Interface Map, SortedMap und implemetierend 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(Collection 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);
318
Programmieren in Java
6.2.5 Die Klasse Collections
Im Paket java.util gibt es die Klasse Collections mit einer großen Anzahl
statischer Methoden zur Manipulation und Verarbeitung von Collections209, z.B. zum
Durchsuchen, Sortieren, Kopieren und Synchronisieren von Collections.
Sortieren und Suchen
static void sort(List list);
static void sort(List list, Comparator c);
Mit Hilfe von sort() können beliebige Listen sortiert werden. Argumente sind vom
Typ List und optional vom Typ Comparator. Fehlt die Angabe von Comparator,
dann wird die Liste in ihrer natürlichen Ordnung sortiert. Alle Elemente müssen dann
das Comparable-Interface implementiert haben und ohne Typfehler paarweise
vergleichbar sein. Gemäß JDK-Dokumentation verwendet diese Methode ein
modifiziertes Mischsortieren (MergeSort), das auch im „Worst.Case“ eine Laufzeit
von N  log( N ) hat.
static int binarySearch(List list, Object key);
static int binarySearch(List list, Object key, Comparator c);
Für große Listen ist es sinnvoll, die Liste zu sortieren und anschließend „binäre
Suche“ zu verwenden. Das gewünschte Element wird durch Intervallschachtelung
mit fortgesetzter Teilung (Halbierung) der Intervallgröße immer weiter eingegrenzt,
und das gesuchte Element ist spätestens nach log(N) Schritten gefunden.
209
Ebenso gibt es eine Hilfsklasse Arrays für Operationen mit Arrays (Feldern), vgl. 2.2.3.3
319
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 Whitespaces210 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());
}
210
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.
320
Programmieren in Java
6.4 Die Klasse Random
Konstruktoren
Random()
// Erzeugt einen neuen Zufallszahlengenerator. Der Seed211 wird auf die aktuelle Zeit gesetzt.
Random(long seed)
// Erzeugt einen neuen Zufallszahlengenerator und benutzt den Parameter als Seed.
Methoden
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.0212.
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.
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
212 wird von Math.random() verwendet.
211
321
Programmieren in Java
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 übergebebe Objekt vom Typ Date, dann verhält sich die Funktion wie das zuvor
// angegebene compareTo(). Anderenfalls wirft die Methdoe ein ClassCastException
...
Abb.: Die Klasse Date
Bsp.213:
import java.util.Date;
class DatumundZeit
{
public static void main(String args[])
{
System.out.println(new Date() );
}
}
213
PR65100
322
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.
323
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 Datumsfelder214
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 ");
214
PR65100
324
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
System.out.println("Es ist die " +
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 staticTimeZone 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 Zeitzonen 215 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]);
}
}
215
Unter Windows mit dem JDK 1.3 sind dies 321 Zeitzonen, PR65100
325
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 final DateFormat getDateInstance()
public final DateFormat getTimeInstance()
public 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 lokal216)
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)
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.
Bsp. mit Sprachen217: Das folgende Programm erzeugt die Ausgabe für Deutschland
und anschließend für Italien.
216
Unter Locale sind für einige Sprachen Konstanten vordefiniert: ENGLISH, FRENCH, GERMAN, ITALIAN,
JAPANESE, KOREAN, CHINESE, SIMPLIFIED_CHINESE, TRADITIONAL_CHINESE
217 vgl. PR65200
326
Programmieren in Java
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 SiimpleDateFormat(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
java.text.SimpleDateFormat
(class
extends
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
Bedeutung
Ära
Jahr
Präsentation
Text
Nummer
327
Beispiel
AD
1998
Programmieren in Java
M
MM
MMM
MMMM
d
h
H
m
s
S
E
EEEE
D
F
w
W
a
k
K
z
‘
‘
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.
Wochr im Jahr
Woche im Monat
Am/pm-Txt
Stunde am Tg (1-24)
Stunde (0-11)
Zeitzone
Zeichen für Text
Einzelnes Hochkomma
Nummer
Nummer
Text
Text
Nummer
Nummer
Nummer
Nummer
Nummer
Nummer
Text
Text
Nummer
Nummer
Nummer
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
328
‘
Programmieren in Java
7. Ein- und Ausgabe
In Java werden Ein-, Ausgabeoperationen über sog. Datenströme218 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.io219 , das bei jedem Programm mit Ein-/Ausgabeoperationen
importiert werden muß.
Einfache Strom-Methoden erlauben nur das Versenden von Bytes mit Hilfe von
Datenströmen220. 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.
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.
219 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.
220 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.
218
329
Programmieren in Java
ByteArrayInputStream
FileInputStream
DataInput
BufferedInputSteam
DataInputStram
FilterInputStream
InputStream
Object
PipedInputStream
SequenceInputStream
StringBufferedInputStream
LineNumberInputSteam
PushbackInputStream
RandomAccessFile
ByteArrayOutputStream
FileOutputStram
OutputStream
FilterOutputStream
DataOutput
PipedOutputStream
Throwable
Exception
IOException
Abb.: java.io
330
BufferdOutputStream
DataOutputStream
PrintStream
EOFException
FileNotFoundException
InterruptedIOException
UTFDataFormatException
Programmieren in Java
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
Funktion
ByteArrayInputStream
Ein Puffer im arbeitsspeicher
wird ald Eingabestrom benutzt.
StringBufferInputStream
Konvertiert einen String in einen
InputStream
FileInputStream
Dient zum Lesen aus einer Datei
PipedInputStream
Produziert
Daten
für
den
PipedOutputStream (implentiert
das „Pipe“-Konzept.
SequenceInputStream
Konvertiert zwei oder mehrere
„InputStream“-Objekte in einen
einzelnen InputStream.
Abb.: Eingabestrom-Typen
331
Argument für den Konstruktor
Nutzungsmöglichkeit
Der Puffer aus dem Bytes geholt
werden.
Als Datenquelle. Verbunden mit
einem
FileInputStream-Objekt
kann daraus ein nützliches
Interface gestaltet werden.
Ein String. Die zugrundeliegende
Implementierung benutzt einen
StringBuffer.
Als Datenquelle. Verbunden mit
einem
FiIenputStream-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.
PipedOutputStram.
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.
Programmieren in Java
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“221 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)“ 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.
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)
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 synchronized void mark(int readLimit)
Die Methode markiert die aktuelle Position im Strom für eine spätere Rückkehr.
public synchronized 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.
7.1.2 OutputStream
221
Rückgabe „-1“ bedeutet zum Unterschied von C nicht, daß ein Fehler aufgetreten ist.
332
Programmieren in Java
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
Fileoder
FileDescriptor-Objekt.
Zum
Bestimmen
eines
Datenziels. Verbunden mit einem
FileOutputStream-Objekt kann
daraus ein nützliches Interface
gestaltet werden.
PipedInputStream
Zum Bestimmen des datenziels
für „Multuthreading“. 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)
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.
7.2 Gefilterte Ströme
333
Programmieren in Java
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:
public FilterInputStream(InpuStream ein)
public FilterOutpuStream(OutputStream aus)
Nützliche Subklassen von FilterInputStream sind:
Klasse
Funktion
DataInputStream
Ermöglicht das Einlesen von
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
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
334
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
public
222
boolean readBoolean() throws IOException
byte readByte() throws IOException
byte readUnsignedByte() throws IOException EOFException
byte readChar() throws IOException
byte readShort() throws IOException
byte readUnsignedShort() throws IOException
int readInt() throws IOException
long readLong() throws IOException
float readFloat() throws IOException
double readDouble() throws IOException
String readUTF() throws IOException222
UTF (Unicode Transmission Format) ist ein spezielles Format zum Kodieren von 16-Bit-Unicode-Werten
335
Programmieren in Java
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)
Methode für die Ausgabe von Objekten.
public
public
public
public
public
public
public
public
public
public
public
public
void flush()
void close()
abstract void write(int b)
void print(Object o)
void print(String s)
void print(char c)
void print(char[] puffer)
void print(char c)
void print(int i)
void print(float f)
void print(double d)
void println(Object f)
336
Programmieren in Java
public
public
public
public
public
public
public
public
void
void
void
void
void
void
void
void
println(String s)
println(char c)
println(char[] puffer)
println(char c)
println(int i)
println(float f)
println(double d)
println()
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()
337
Programmieren in Java
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();
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 „..“. 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 accept, 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.
Neben dem Zugriff auf Verzeichniseinträge gibt es Methoden 223 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.
7.4 Die RandomAccessFile-Klasse
223
Alle Methoden geben true zurück, wenn sie ihre Aufgabe erfolgreich ausführen konnten; anderenfalls geben
sie false zurück
338
Programmieren in Java
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
IOException
Bei der Übergabe des File-Objekts wird die durch dieses Objekt spezifizierte Datei
geöffnet.
public RandomAccessFile(String name, String mode) throws
IOException
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 Dateiposition224
bestimmt werden.
Weiterhin stehen lesende und schreibende Zugriffsmethoden zur Verfügung.
224
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);
// 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);
<< Methoden >>
public long getFilePointer()
publc void seek(long pos)
// setzt den Dateizeiger – bezogen auf den Dateianfang – auf eine
// bestimmte Stelle, an der der nächste Lese- und Schreibzugriff
// stattfindet
public void skipBytes(int n)
public long length()
public final boolean readBoolean();
public final byte readByte();
public final char readChar();
public final double readDouble();
public final float readFloat);
public read final readInt();
public final long readLong();
public final short readShort();
public final String readUTF();
public final void readFully(byte b[]);
public final void readFully(byte[], int off, int laenge);
public final String readLine();
public final int readUnsignedByte();
public final int readUnsignedShort();
public int read();
public int read(byte b[]);
public int read(byte b[], int off, int laenge);
public final void writeBoolean(boolean w);
public final void writeByte(int w);
public final void writeBytes(String s);
public final void writeBytes(String s);
public final void writeChar(int w);
public final void writeChar(String s);
public final void writeDouble(double w);
public final void writeFloat(float w);
public final void writeInt(int w);
public final void writeLong(int w);
public final void writeShort(int w);
public final void writeUTF(String str);
public void write(int b);
public void write(byte b[]);
public void write(byte b[], int off, int laenge);
Abb.: Die Klasse RandomAccessFile
340
Programmieren in Java
7.5 Die Klasse StreamTokenizer
Die Klasse StreamTokenizer abrbeitet 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 slahSlashComments(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.6 Java 1.1 IO-Ströme
7.6.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.6.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
{
public Reader();
publicc void close();
// schliesst den Eingabestrom
public void mark(int readAheadlimit);
// markiert eine bestimmte Position innerhalb eines Eingabestroms
public boolean markSupported();
// überprüft, ob Markieren unterstützt wird
public int read()
// 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[]);
// übernimmt eine Reihe von Zeichen in den als Parameter angegebenen
// Array
public int read(char cbuf[], int off, int len);
// ü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);
// überliest Zeichen im Eingabestrom
public boolean ready()
// liefert True, falls der nächste Aufruf von read erfolgen kann, ohne
// daß die Eingabe beendet ist
public void reset();
// 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
InputStreamReader
Diese Klasse konvertiert Byte- in Character-Streams.
345
Programmieren in Java
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.6.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 void close();
// Schliessen des Ausgabestroms
public void flush()
// 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[]);
// Varianten von write, die ein Array von Bytes oder ein String-Objekt
// erwarten und dieses durch Aufruf von write() ausgeben
abstact public void write(char einPuffer[], int off, int len);
public void write(String str);
public void write(String str, int off, int len);
}
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.
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:
347
Programmieren in Java
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.
348
Programmieren in Java
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
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 BufferdWriter(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.6.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");
}
350
Programmieren in Java
// 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);
// 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
Serialisierung225 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.
225
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 Datei226 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
ObjectOutput,
ObjectStreamConstants)
geschaffen.
ObjectOutputStream implementiert das Interface ObjectOutput, z.B. die
Funktion writeObject() zum Schreiben von Objekten.
OutputStream
{ abstract }
ObjectOutputStream
<< Konstruktoren >
public ObjetOutputStream(OutputStrean 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[]) trows 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
Die Methode public final void witeObject(Object obj) throws
IOException schreibt folgende Daten in den OutputStream:
226
Der Dateiname wird häufig so gewählt, daß er mit dem Präfix „ser“ endet.
353
Programmieren in Java
- 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 >
<< Methoden >>
public final Object readObject(Object obj) 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 ObjectInputStram
Analog zum Interface ObjectOutput gibt es hier das Interface ObjectInput
(interface java.io.ObjectInput extends DataInput)
355
Programmieren in Java
<< interface >>
ObjectInput
<< Methoden >>
public Object readObject() throws ClassNotFoubdException, IOException
public int read() throws IOException
// liest ein bayte 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 ObkectInput
Das Deralisieren 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 227. 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();
}
227
Abgesehen von den nicht serialsierten Membervariablen des Typs static oder transient.
356
Programmieren in Java
catch(ClassNotFoundException e)
{ 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/IP228Netzwerkzugriffen. 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 InetAdress
Zur Adressierung von Rechnern im Netz wird die Klasse InetAdress des Pakets
java.net verwendet. Eine InetAdress enthält sowohl eine IP-Adresse229 als auch
den symbolischen Namen des jeweiligen Rechners230. Die beiden Bestandteile
können mit den Methoden getHostName() und getHostAdress() abgefragt
werden. Mit Hilfe von getAdress() kann die IP-Adresse auch direkt als byte-Array
(mit 4 Elementen) beschafft werden.
Zur Generierung eines InetAdress-Objekts stehen zur Verfügung:
public static InetAdress getByname(String host) throws UnKnownHostException
// erwatet einen String mit der IP-Adtesse oder dem Namen als Argument
public static IntetAdress getLocalHost() throws UnKnownHostException
// liefert ein InetAdress-Objekt für den eigenen Rechner
228
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)
229 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.
230 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 Rtessourcen über URLs (Universal Ressource Locator)
identifiziert. Eine URL besteht aus:
einem Protokollnamen, z.B. http231 (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:
//user232:password233@host234:port235/url-path236. 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.
231
Mit dem Hypertext Tranfer Protocol wird auf Inhalte des Web zugegriffen. Die URL für Dienste im Web
beginnt mit http.
232 Optionaler Benutzername
233 optionales Paßwort, ein Paßwort ohne Benutzername kann nicht angegeben werden
234 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 IPAdresse umgesetzt wird.
235 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.
236 Auf den server-namen 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
//
public URLConnection openConnection() throws IOException
//
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 URL
360
Programmieren in Java
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, FtpURLConnection oder MailtoURLConnection.
URLConnection
{ abstract }
<< Methoden >>
public Object getContent() throws IOException, UnkownException
// liefert den Inhalt, auf den die URL verweist.
361
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.
Sockets237
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. Berbindungsaufbau
2. Lesen bzw. Schreiben der Daten
3. Verbindungsabbau
237
Sockets wirden zu Beginn der achtziger Jahre für die Programmiersprache C entwickelt.
362
Herunterladen