Prof. Sauer - oth

Werbung
Programmieren in Java
Prof. Jürgen Sauer
Programmieren in Java
Skriptum zur Vorlesung im WS 1999/2000
1
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 einer Java-Anwendung
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
Programmieren in Java
2.
Hauptbestandteile der Sprache
2.1
2.1.1
2.1.2
2.1.3
Token
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 Opeartionen 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.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
3
Programmieren in Java
2.6.4
2.6.5
2.6.6
Rückgabewerte von Methoden
Methodenname und Parameterliste
Überladen und Überschreiben von Methoden
3.
Das Arbeiten mit Objekten
3.1
Ereignisbehandlung unter grafischen Benutzeroberflächen (Graphical User Interface)
1. Gestaltung von grafischen benutzeroberflächen mit Hilfe der AWT-Klassen
2. Ereignisbehandlung unter grafischen benutzroberflächen
3. Anwendung lokaler Klassen für die Ereignisbehandlung
4. Externe- und interne Darstellung numerischer Werte
5. Low-Level-Events
3.2
Kommunikation des Anwenders mit Programmen in GUI über Dialoge bzw. Menüs
mit vordefinierten Dialogelementen
3.3
Grundlagen der Applet-Erstellung
3.3.1 Die interne Arbeitsweise eines Applets
Applet-Methoden
Methoden zur Ereignisbehandlung in Applets
„Multithreading“-fähige Applets
3.3.2 Die Applet-Klasse
3.4
Kollektionen (Container)
Die Klasse Vector
Die Klasse Stack
Die Klasse Hashtabelle
Die Klasse Bitset
4.
Grafik und Animation
4.1
4.1.1
4.1.2
4.1.3
Allgemeine Zeichenvorgänge
Punkte, Linien, Kreise, Bögen
Farbangaben
Textausgabe über den Zeichenmodus
Die Klasse Font
Die Klasse FontMetrics
4.1.4 Die Java-Zeichenmodi
4.1.5 Das Zeichnen von Bildern
4.2
4.2.1
4.2.2
4.2.3
Animation
Aufbau eines Animationsrahmens
Abspielen einer Animation
Reduktion von Flimmereffekten in Animationen
4.3
Grafikoperationen mit Java 2D
4
Programmieren in Java
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
Die Layout-Manager
5.4.1
5.4.2
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
6.
Multithreading
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
Java 1.1 IO-Ströme
Grundlagen
5
Programmieren in Java
7.6.2
7.6.3
7.6.4
Die abstrakte Klasse Reader und ihre Ableitungen
Die abstrakte Klasse Writer und ihre Ableitungen
Demonstrationsprogramm zur Ein-/Ausgabe (ab Java Version 1.1)
8.
6
Programmieren in Java
0. Übersicht
0.1 Ziel der Vorlesung „Programmieren“
„Vermittlung grundlegender Kenntnisse, die zum Lösen von Problemen mit einer
Rechenanlage (Computer, Rechner) nötig sind.“
Die Rechenanlage ist „irgendeine“ (vom „Jumbo“ bis zum Personal Computer) und
muß
- eine Eingabemöglichkeit für Ziffern, Buchstaben und Sonderzeichen besitzen (Tastatur)
- programmierbar sein
- eine Ausgabemöglichkeit haben (Bildschirm)
Zum Erwerb grundlegender Kenntnisse für das Lösen von Problemen auf einem
Universalrechner ist
- zu zeigen, wie man von der Problemstellung zum Lösungsverfahren (Algorithmus) kommt. Alle
Probleme, für die ein Lösungweg genügend prä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 Überlegungungen (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 halten
permanent die aktuellsten Informationen im Internet bereit.
0.2 Java-Maschine und Programmiersystem
Die Java-Maschine 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).
7
Programmieren in Java
Eingabeeinheit
Hauptspeicher
Externer Speicher
Ausgabeeinheit
Steuerwerk
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“.
Java-Programm
Daten
Eingabe (Tastatur)
Externer Speicher
System,
Systemprogramme
Java-Maschine
Ausgabe (Bildschirm)
8
Externer Speicher
Java-Compiler
Java-Quellprogramme
Java-Laufzeitsystem
Java-BytecodeDateien
Programmieren in Java
0.3 Der erste Versuch
0.3.1 Das erste Programm
Quelltext:
import java.lang.*;
/* ErstesProgramm ist eine Applikation, die den einfachen
Gebrauch von Zeichenketten aufzeigt */
public class ErstesProgramm extends Object
{
// Beginn der Ausfuehrung vom Programm
public static void main(String args[])
{
// Ausgabe auf das Standard-Ausgabegeraet
System.out.println(
"Das erste Programm der Vorlesung Programmieren in Java.");
}
}
Das Programm umfaßt zwei Teile: eine Klassendefinition und einen
Programmabschnittt, der durch die Methode main() bestimmt ist.
Jede Java-Anwendung besteht aus mehreren Klassen. Die Klasse, die die
Ausgangsbais für Java-Programme ist, muß die main()-Methode benutzen:
public static void main(Strig 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:
/* 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.");
}
}
9
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 Ausgabe 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
public static void main(String args[])
{
String s;
// Deklaration der Variablen s zur Aufnahme von Zeichenketten
// Zuweisung
s = "Das erste Programm der Vorlesung Programmieren in Java."
// Aufruf der Methode println() der Klasse System, die sich im Paket
// java.lang befindet
System.out.println(s);
}
10
Programmieren in Java
Vorrang
1
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
Abb.: Java-Operatoren
11
Programmieren in Java
System.out.println(s); ist eine Anweisung. Anweisungen stehen für eine
einzelne Aktion, die in einem Java-Programm ausgeführt wird. Jede Anweisung wird
mit einem Strichpunkt „;“ abgeschlossen. Einige Anweisungen erzeugen einen Wert,
wie das bspw. beim Addieren zweier Zahlen der Fall ist. Derartige Anweisungen
werden als Ausdrücke bezeichnet.
Ein Ausdruck ist eine Anweisung, die als Ergebnis einen (Rückgabe-) Wert
produziert. Dieser Wert kann zur späteren Verwendung im Programm gespeichert,
direkt in einer anderen Anweisung verwendet oder überhaupt nicht beachtet werden.
Ausdrücke können Konstanten, Variablen, Operatoren beinhalten. Ausdrücke sind
vielfach mit unären und binären Operatoren verknüpft.
Trennzeichen: Das sind Symbole und Zusammenfassungen von Quellcode: ( ) {
} [ ] ;. ,.
„(“: wird sowohl zum Öffnen einer Parameterliste für eine Methode als auch zur
Festlegung der Priorität für Operationen in einem Ausdruck benutzt.
„)“: wird sowohl zum Schließen einer Parameterliste für eine Methode als auch zur
Festlegung der Priorität für Operationen in einem Ausdruck benutzt.
Bsp.: public static void main(String args[])
„{“: wird zu Beginn eines Blocks mit Anweisungen oder einer Initialisierungsliste
gesetzt.
„}“:wird an das Ende eines Blocks mit Anweisungen oder einer Initialisierungsliste
gesetzt.
Bsp.: Methoden werden in Java durch einen Anweisungsblock bestimmt. Ein Anweisungsblock besteht
in der Regel aus einer Reihe von Anweisungen, die von geschweiften Klammern umschlossen sind.
public static void main(String args[])
{
String s;
s = "Das erste Programm der Vorlesung Programmieren in Java.";
System.out.println(s);
}
„[“: steht für eine Ausdruck, der als Index für ein Datenfeld (Array) steht.
„]“: folgt einem Ausdruck, der als Index für ein Datenfeld steht.
Bsp.: String args[] = {“Juergen“, “Hubert“, “Josef“, “Liesel“, “Christian“};
definiert einen Array mit 5 Komponenten, die alle den gleichen Typ besitzen
Datenfelder (Arrays) dienen zum Speichern von Elementen, die alle denselben Typ aufweisen. Jedem
Element wird innerhalb des Datenfelds ein eigener Speicherplatz zugewiesen. Dieser Speicherplatz ist
für den leichten Zugriff durchnumeriert.
[0]
[1]
[2]
[3]
[4]
“Juergen“
“Hubert“
“Josef“
“Liesel“
“Christian“
Auf den Wert einer Komponenten kann über den Array-Namen (z.B. args), gefolgt von einem Index in
eckigen Klammern (z.B. [0]) zugegriffen werden. Mit
args[0] = “Roland“;
kann dem ersten Element des Array args ein neuer Wert zugewiesen werden.
„;“: dient zum Beenden einer Anweisung.
„,“: wird häufig als Begrenzer (z.B. in einer Parameterliste) benutzt.
„.“: wird als Dezimalpunkt als auch zum Trennen solcher Dinge wie Paketnamen von
Klassennamen oder Variablennamen benutzt (z.B. System.out.println(s);).
12
Programmieren in Java
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.
13
Programmieren in Java
Konsole:
Arbeitsspeicher:
javac ErstesProgramm.java
Externspeicher
ErstesProgramm.java
javac
ErstesProgramm.class
java ErstesProgramm
ErstesProgramm.class
Java (-Interpreter)
"Das erste Programm der Verlesung Programmieren in Java"
14
Programmieren in Java
0.3.2 Bestandteile eines Programms
Wesentliche Programmelemente in Java sind:
- Anweisungen
Anweisungen1 gehören zu den elementaren ausführbaren Programmelementen. Eine Anweisung
kann eine Deklaration enthalten, einen Ausdruck 2 auswerten oder den Programmablauf3 (Auswahl-,
Iterations-, Sprung-Anweisungen und return-, throw-Anweisung) steuern.
- Blöcke
Ein Block4 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
Methoden5 unterscheiden sich von Blöcken folgendermaßen:
-- Sie haben einen Namen und können von verschiedenen Stellen des Programms aufgerufen
werden.
-- Sie sind parametrisierbar
-- Sie können einen Rückgabewert besitzen.
Methoden werden in Java immer lokal zu einer Klasse definiert.
- Klassen
Sie6 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-Methoden7 auf.
1
vgl. 2.4
vgl. 2.4.5
3 vgl. 2.4.6
4 vgl. 2.4.1
5 vgl. 2.6
6 vgl. 2.5
7 CallBack-Methoden sind von der abgeleiteteten Klasse zur Verfügung gestellte Methoden, die vom Browser
bzw. Appletviewer aufgerufen werden
2
15
Programmieren in Java
1. Einführung in die Java-Programmierung
1.1 Übersicht zur Entwicklung der Programmiersprache Java
Java ist in den Entwicklungslaboren der amerikanischen Firma Sun Microsystems 8
entstanden. Man entschied sich bei Sun zur Realisierung eines im Jahre 1990
begonnenen Projekts9 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-Browser10 wurde zu einer Zielplattform der neuen
Programmiersprache11, 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 12 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 Workshop13, 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
8
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)
10 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.
11 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.
12 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)
13 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.
9
16
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 214 eingeführt.
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 ist15 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 Java16 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)17.
- 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
Systemen befinden. Objekte können mit Java-Programmen über URLs18 vom gesamten Web
14
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
15 Offizielle Definition von Sun Microsystems
16 vgl. 2.3
17 vgl. 2.4.
18 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).
17
Programmieren in Java
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-Laufzeitumgebung19 interpretiert wird.
Java-Compiler (Pentium)
Java-Interpreter (Pentium)
Java-Code
Java-Compiler (SPARC)
Java-Interpreter (SPARC)
Abb.:
- 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
Rechners20.
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ähren21.
- 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 Java22)
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
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.
19 Normalerweise durch einen Java-Browser
20 Ungeprüfte Zugriffe auf Speicherbereiche des Rechners ermöglicht C/C++ (Zeigerarithmetik, implizite
Deklaration)
21 Verschiedene Sicherheitslücken wurden aufgedeckt und wurden inzwischen beseitigt.
22 Die virtuelle Maschine wird auch als Java Interpreter oder Java Environment (Java-Laufzeitumgebung)
bezeichnet
18
Programmieren in Java
Programms können mehrere Dinge gleichzeitig geschehen, mehrere Faden / Threads eines
Programms können gleichzeitig verfolgt und abgearbeitet werden.
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 23
erhältlich.
Nach der Installation hat das Java-Verzeichnis24 folgenden Inhalt25:
Abb.: Inhalt des Java-Verzeichnisses
Innerhalb des Java-Verzeichnisses befinden sich Unterverzeichnisse26 mit
folgendem Inhalt:
Verzeichnis
\bin
\lib
\include
Inhalt
In diesem Verzeichnis befinden sich die JDK-Programme
In diesem Verzeichnis befinden sich defaultmäßig die Standard-Java-Klassen des
JDK27
In diesem Verzeichnis befinden sich diverse Header-Dateien für die gemeinsame
23
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.
24 Erzeugt von der Entpackungsroutine des JDK
25 Das Verzeichnis „meinepr“ dient zur Aufnahme von Projekten und zählt nicht standardmäßig zum Inhalt eines
Java-Verzeichnisses
26 unter Windows
27 Im wesentlichen ist das die Datei classes.zip
19
Programmieren in Java
\demo
\src
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.
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-Umgebungsvariable28
„autoexec.bat“ so vorliegen:
sollte
unter
Windows
in
der Datei
set classpath=c:\jdk1.1.6\lib\classes.zip;
1.3.2 Applets und Anwendungen
Java Programme werden in zwei Hauptanwendungs-Gruppen gegliedert: Applets
und Anwendungen.
Applets sind Java-Programme, die über das WWW heruntergeladen und von einem
Web-Browser auf dem Rechner des Anwenders ausgeführt werden. Applets können
nur auf einem javafähigen Browser ausgeführt werden bzw. mit einem Tool des JDK,
dem Appletviewer, gesichtet werden.
Java-Anwendungen sind allgemeine, in der Java-Sprache geschriebene Programme. Zum Ausführen von Java-Anwendungen ist kein Browser nötig.
1.3.2.1 Entwicklung von Java-Anwendungen
1. Aufgabe: Erstelle eine Anwendung, die den Text „Willkommen in der JavaWelt“ ausgibt.
Lösungsschritte:
1) Erstelle die folgende Datei mit dem Namen „Willkommen.java“ mit Hilfe eines
Dateiaufbereiters (Editor):
class Willkommen
{
public static void main(String args[])
{
System.out.println("Willkommen in der Java-Welt!");
}
}
28
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.
20
Programmieren in Java
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
Verzeichnis29.
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 erzeugt30. 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!.
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 31 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
}
29
Vgl. PR13210
Mit der Option „-d“ im javac-Aufruf kann die .class-Dateii an einem anderen Ort gespeichert werden.
31 Vgl. PR13210
30
21
Programmieren in Java
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).
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.
22
Programmieren in Java
args
“Juergen“
“Hubert“
“Josef“
“Liesel“
“Christian“
[0]
[1]
[2]
[3]
[4]
Nach 4 Vergleichen hat das Datenfeld args folgende Gestalt angenommen:
args
“Christian“
“Juergen“
“Josef“
“Liesel“
“Hubert“
[0]
[1]
[2]
[3]
[4]
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
23
Programmieren in Java
args[i] = args[j];
args[j] = temp;
}
}
}
Zeichenketten werden über die Methode „compareTo“ der Klasse String
verglichen. Generell stehen die Vergleichsoperatoren (== != < <= > >=)nur für
das Vergleichen von Zahlen zur Verfügung.
Die Vergleichsbedingung wird durch das Schlüsselwort if erzeugt:
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“.
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“
24
Programmieren in Java
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.
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 + - * /
25
Programmieren in Java
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 = " +
z = ++x;
System.out.println("Nach z = ++x: z = " + z + ", x = " +
System.out.println("x = " + x);
System.out.println("y = " + y);
z = ++x + y--;
System.out.println("nach z = ++x + y--: z = " + z + ", x
+ x + " y = " + y);
System.out.println("x = " + x);
System.out.println("y = " + y);
z = x + y * ++y;
System.out.println("nach z = x + y * ++y: z = " + z + ",
+ x + " y = " + y);
z = (float) (1.0f / 0.0f);
System.out.println("z = (float) (1.0f / 0.0f) = " + z);
x);
x);
= "
x = "
}
}
4) Der Aufruf java FloatDemo 17 4 führt dann zu folgenden Ausgabe:
x = 17.0
x = 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 = 10.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 = 24.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.
5. 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
26
Programmieren in Java
„try“-Block wird der normale Ablauf behandelt. Im „catch“-Block erfolgt die
Behandlung der Ausnahmen. Die Verzweigung in den „catch“-Block erfolgt beim
Auftreten einer Ausnahme automatisch, der „try“-Block bleibt dabei unberührt.
2) Der Quelltext nimmt nach dem Einfügen eines „try-catch“-Blocks folgende
Gestalt an:
public class FloatDemo
{
public static void main(String args[])
{
try {
// Konvertieren
................
}
catch(Exception e)
{
System.out.println("Fehler bei der Eingabe: java 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 demnach nötig, das Applet in einem
HTML-Dokument32 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);
}
}
32
eine entsprechende Referenz innerhalb einer HTML-Seite mit einem speziellen Tag, dem <APPLET>-Tag
erledigt die Einbettung in den Browser
27
Programmieren in Java
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.
Die folgende Abbildung zeigt die Modellierung33 des vorliegenden Quellcodes:
WillkommenApplet
paint()
g.drawString
(“Herzlich Willkommen in der Java-Welt!“,5,25)
Abb.: 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:
33
Die Modellierung erfolgt nach den Regeln der Unified Modelling Language (UML). Die UML ist eine
grafische, standardisierte Sprache zum Spezifizieren, Konstruieren, Visualisieren und Dokumentieren.
28
Programmieren in Java
Applet
WillkommenApplet
paint()
Graphics
Abb.: 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
Linien34, von Ellipsen bzw. Kreisen35, von Rechtecken36 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:
34
public void drawLine(int x1, int y1, int x2, int y2); (x1,y1) bestimmt den
Anfangspunkt, (x2,y2) bestimmt den Endpunkt der Linie.
35 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
36 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.
29
Programmieren in Java
Object
Component
ImageObserver
Container
Panel
Applet
WillkommenApplet
Abb.: Verrebungshierarchie von WillkommenApplet
Die Beziehung zwischen ImageObserver und Component ist eine Schnittstelle.
ImageObserver wird von Component implementiert.
WillkommenApplet arbeitet mit den Klassen Applet und Graphics unmittelbar
zusammen. Diese beiden Klassen bilden lediglich einen kleinen Ausschnitt aus der
Bibliothek mit vordefinierten Java-Klassen. Die Verwaltung dieser Klassen und
Schnittstellen organisiert Java in mehreren verschiedenen Paketen. Das
Wurzelpaket in der Java-Umgebung heißt java. In dieses Paket sind mehrere
weitere Pakete geschachtelt, die wiederum andere Pakete, Schnittstellen und
Klassen enthalten. Object existiert im Paket lang, Panel, Container,
Component existieren im Paket awt, und die Klasse Applet im Paket applet. Die
Schittstelle ImageObserver existiert im Paket image, das liegt wiederum im Paket
awt (qualifierter Name: java.awt.ImageObeserver). Die Paketstruktur kann über
ein Klassendiagramm visualisiert werden. Die Paketstruktur37 kann in einem
Klassendiagramm visualisiert werden:
java
WillkommenApplet
applet
awt
lang
Abb.: Paketstruktur im WillkommenApplet
37
Pakete werden in der UML als Akten mit Reitern dargestellt. Die gestrichelten Pfeile repräsentieren die
Abhängigkeiten zwischen den Paketen.
30
Programmieren in Java
2) Speichern der Datei mit dem Namen „WillkommenApplet.java“ unter einem
beliebigen Verzeichnis.
3) Aufruf des Java-Übersetzers über die folgende Befehlszeileneingabe: „javac
WillkommenApplet.java“. Der Compiler gibt bei erfolgreicher Übersetzung keine
Rückmeldung. Zwei Dateien müssen nach erfolgreicher Übersetzung vorliegen:
„WillkommenApplet.java“ und „WillkommenApplet.class“.
4) Erstellen der folgenden HTML-Datei „WillkommenApplet.html“, anschließend
Speichern dieser Datei.
<HTML>
<HEAD>
<TITLE>Seid gegruesst!</TITLE>
</HEAD>
<BODY>
<P>Mein Java Applet sagt:<BR>
<APPLET CODE=“WillkommenApplet.class“ WIDTH=200 HEIGHT=50>
</APPLET>
</BODY>
</HTML>
Der Bezugspunkt in HTML-Dateien auf ein Applet erfolgt mit dem <Applet>-Tag. Das
Code-Attribut dient zur Angabe von dem Namen der Klasse, die das Applet enthält.
Die Attribute WIDTH und HEIGHT dienen zum Bestimmen der Größe des Applets.
Der Browser benutzt diese Werte zur Zuteilung des Raums, der für das Applet auf
der Seite freigehalten werden muß. Hier wurde eine Box mit einer Breite von 200
und einer Höhe von 50 Pixeln definiert.
WillkommenApplet ist als Applet implementiert. Es kann nie isoliert stehen, sondern
ist normalerweise Teil einer Webseite.
WillkommenApplet.java
WillkommenApplet.class
WillkommenApplet.html
Abb. 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.: Darstellung des Fensters mit dem Appletviewer
31
Programmieren in Java
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("Times Roman",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 „Times Roman“, fett in „12-Punkt“
zugewiesen. Das neue Objekt wird anschließend der Instanzvariablen f zugewiesen
und ist so der Methode paint() zugänglich:
public void paint (Graphics g)
{
// Mitteilung: Die Schrift zur Anzeige von Text befindet sich in der
// Instanzvariablen f
g.setFont(f);
// Mitteilung: Die Farbe fuer die Ausgabe ist gelb
g.setColor(Color.yellow);
// Die glebe Farbe dient zum Fuellen eines Rahmens fuer den Text, der
// aus einem Rechteck mit abgerundeten Ecken besteht
g.fillRoundRect(0,0,225,30,10,10);
// Mitteilung: die Farbe fuer Textausgaben ist
// eine Instanz der Klasse Color fuer die Farbe rot
g.setColor(Color.red);
// Mitteilung: Text wird in festgelegter Schrift und Farbe bei den
// (x,y)-Koordinaten (5,25) ausgegeben.
g.drawString("Herzlich Willkommen in der Java Welt!",5,25);
}
Die Klassen Font und Color werden über die folgenden import-Anweisungen
bereitgestellt:
import java.awt.Graphics;
import java.awt.Font;
import java.awt.Color;
Dafür kann man auch „import java.awt.*;“38 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: Überwachen des Lebenszyklus eines Applet.
Ein Applet führt keine Aktionen aus eigener Initiative aus, sondern empfängt
38
Diese Anweisung stellt alle Klassen des Pakets java.awt zur Verfügung
32
Programmieren in Java
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.
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);
}
}
33
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 (Instanziierung 39)
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 erfordert40, erfolgt die Rückgabe von nicht mehr
benötigtem Speicher automatisch41.
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
39
Eine Instanz einer Klasse ist ein (tatsächliches) Objekt (konkrete Darstellung)
Ausnahmen: String-, Array-Literale
41 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
40
34
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 Regeln42 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 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)43
Rückgabetypausdruck: Jede Methode ist typisiert. Der Typ einer Methode bestimt den Typ des
Rückgabewerts. Dieser kann von einem beliebigen primitiven Typ44, 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“Anweisung45 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.
Ein praktisches Beispiel
1. Erstellen der Klasse Rechentafel zum Rechnen mit ganzen Zahlen
Die grundlegende Klassendefinition ist: class Rechentafel{ }
42
vgl. 2.1.2 Bezeichner und Namenskonventionen
vgl. 2.6.2
44 vgl. 2.2
45 vgl. 2.6.4
43
35
Programmieren in Java
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 JavaAnwendung46:
2. Die Klasse Rechentafeltest zum Überprüfen der Klasse Rechentafel
Die Klasse Rechentafeltest ist durch die Anwendung der Klasse Rechentafel
bestimmt. Sie enthält die main()-Methode. Im Mittelpunkt der Anwendung steht die
Anweisung:
Rechentafel einRechenobjekt = new Rechentafel();
Sie erzeugt eine Instanz der Klasse Rechentafel und speichert eine Referenz
darauf in der Variablen „einRechenobjekt“. In jeder objektorientierten
Programmiersprache lassen sich spezielle Methoden definieren, die bei der
Initialisierung eines Objekts aufgerufen werden. In Java werden Konstruktoren als
Methoden ohne Rückgabewert definiert, die den Namen der Klasse erhalten, zu der
46
vgl. PR14101
36
Programmieren in Java
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());
}
}
Einfache Typen48 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 Typen49 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 Datentypen50:
- vier Ganzzahltypen mit unterschiedlichen Wertebereichen
48
vgl. 2.2.1
vgl. 2.2.1
50 vgl. 2.2.1
49
37
Programmieren in Java
- 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 noch 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.
Konstruktoren
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.:
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
38
Programmieren in Java
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 den Parameter this aufgerufen werden kann, z.B.:
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
Zugriff nur innerhalb einer Klasse
Zugriff innerhalb eines Pakets
Zugriff innerhalb eines Pakets oder von Subklassen in einem anderen
39
Programmieren in Java
public
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.
Abhlife verspricht hier das Prinzip der Datenkapselung (data hiding, data
encapsulation). Instanzvariable werden als „private“ erklärt, d.h.: Zugriff auf diese
Instanzvariable nur innerhalb der Klasse. Von außerhalb kann nur indirekt über das
Aufrufen von Methoden, die als „public“ erklärt sind, auf die Instanzvariablen
zugegriffen werden. Deshalb sollte auch prinzipiell für jede Instanzvariable eine
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;
}
40
Programmieren in Java
public int subtrahiere()
{
return ersterOperand - zweiterOperand;
}
public int multipliziere()
{
return ersterOperand * zweiterOperand;
}
public int dividiere()
{
// ganzzahlige Division
if (zweiterOperand == 0)
{
System.out.println("Fehler: Division durch Null");
System.out.println("Divisor wird auf 1 gesetzt!");
zweiterOperand = 1;
}
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" 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.
41
Programmieren in Java
Das reservierte Wort final macht Variablen zu Größen, denen nur einmal bei der
Deklaration ein Wert zugewiesen werden kann, d.h. sie werden dadurch zu
Konstanten, z.B.: public static final double PI = 3.1415926535897932846;
Klassenmethoden wirken sich wie Klassenvariable auf die ganze Klasse, nicht auf
einzelne Instanzen aus. Klassenmethoden sind nützlich zum Zusammenfassen
allgemeiner Methoden an einer Stelle der Klasse. So umfaßt die Math-Klasse
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
Konstanten51 gesetzt werden.
Konstanten sind Speicherbereiche mit Werten, die sich nie ändern. In Java können
solche Konstanten ausschließlich für Instanz- und Klassenvariable erstellt werden.
Konstante bestehen aus einer Variablendeklaration, der das Schlüsselwort final
vorangestellt ist, und ein Anfangswert zugewiesen ist.
51
Das bedeutet: das Schlüsselwort final ist bei lokalen Variablen nicht erlaubt
42
Programmieren in Java
Ü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.
Überladen kann.
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 Inforamtion
ü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.
Überschreiben einer Oberklassen-Methode: 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.
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
43
Programmieren in Java
Methodennamen ihrer Klasse und deren Superklasse. Methodennamen werden
dynamisch beim Aufruf einer Methode gewählt. Das bedeutet: Java prüft zuerst die
Klasse des Objekts auf Definition der betreffenden Methode. Ist sie nicht in der
Klasse des Objekts definiert, sucht Java in der Superklasse dieser Klasse usw.
aufwärts in der Hierarchie bis die Definition der Methode gefunden wird.
Ist in einer Subklasse eine Methode mit gleichem Namen, gleicher Anzahl und
gleichem Typ der Argumente wie in der Superklasse definiert, dann wird die
Methodendefinition, die zuerst (von unten nach oben in der Hierarchie) gefunden
wird, ausgeführt. Methoden der abgeleiteten Klasse überdecken die Methoden der
Superklasse (Overriding beim Überschreiben / Überdefinieren).
Zum Ableiten einer Klasse aus einer bestehenden Klasse ist im Kopf der Klasse mit
Hilfe des Schlüsselworts „extends“ ein Verweis auf die Basisklasse anzugeben.
Dadurch wird bewirkt: Die abgeleitete Klasse erbt alle Eigenschaften der
Basisklasse, d.h. alle Variablen und alle Methoden. Durch Hinzufügen neuer
Elemente oder Überladen der vorhandenen kann die Funktionalität der abgeleiteten
Klasse erweitert werden.
Die Vererbung einer Klasse kann beliebig tief geschachtelt werden. Eine abgeleitete
Klasse erbt dabei jeweils die Eigenschaften der unmittelbaren „Superklasse“, die
ihrerseits die Eigenschaften ihrer unmittelbaren „Superklasse“ erbt.
Superklassen können abstrakte Klassen mit generischem Verhalten sein. Von einer
abstrakten Klasse wird nie eine direkte Instanz benötigt. Sie dient nur zu
Verweiszwecken. Abstrakte Klassen dürfen keine Implementierung einer Methode
enthalten und sind damit auch nicht instanziierbar. Java charakterisiert abstrakte
Klassen mit dem Schlüsselwort abstract. Abstrakte Klassen werden zum Aufbau
einer Klassenhierarchie verwendet, z.B. für die Zusammenfassung von zwei oder
mehr Klassen.
Auch eine abstrakte Methode ist zunächst einmal durch das reservierte Wort
"abstract" erklärt. Eine abstrakte Methode besteht nur aus dem Methodenkopf,
anstelle des Methodenrumpfs (der Methodendefinition) steht nur das "Semikolon",
z.B. public abstract String toString();.
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.
Das folgende Klassendiagramm zeigt die hierarchische Struktur von Klassen, die
"geometrische Objekte" beschreiben. All diesen geometrischen Objekten ist
gemeinsam: Sie werden surch einen Bezugspunkt (x,y) fixiert. DieGemeinsamkeiten
sind in der abstrakten Klasse "Geomobjekt" zusammengefaßt.
44
Programmieren in Java
GeomObjekt
private double x;
private double y;
public double holeX();
public void setzeX(double x);
public double holeY();
public void setzeY(double y);
Kreis
Rechteck
private double r;
private double breite;
private double hoehe;
public double holeR();
public void setzeR(double r);
public double umfang();
public double flaeche();
publicc String toString();
public double holeBreite();
public void setzeBreite(double breite)
public double holeHoehe(double hoehe);
public void setzeHoehe(double hoehe);
public double umfang();
public double flaeche();
public String toString();
Ausgangspunkt ist die abstrakte Klasse "GeomObjekt". Dort ist der allen Objekten
zugeordnete Bezugspunkt fixiert. Subklassen (z.B. Kreis und Rechteck) erben diesen
Bezugspunkt und die zum Zugriff auf ihn bereitgestellte Methoden. Außerdem führen
die Subklassen die nötigen Spezialisierungen auf das jeweilige geometrische Objekt
durch.
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();
45
Programmieren in Java
public abstract double flaeche();
}
import java.lang.*;
public class Kreis extends GeomObjekt
{
// Instanzvariable
private double r;
// Radius
// Konstruktoren
public Kreis()
{
this(0.0,0.0,1.0);
}
public Kreis(double r)
{
this(0.0,0.0,r);
}
public Kreis(double x, double y, double r)
{
super();
super.setzeX(x);
super.setzeY(y);
this.setzeR(r);
}
// Instanzmethoden
public double holeR()
{
return r;
}
public void setzeR(double r)
{
this.r = (r >= 0.0 ? r : -r);
}
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)
{
46
Programmieren in Java
super();
super.setzeX(x);
super.setzeY(y);
this.setzeBreite(breite);
this.setzeHoehe(hoehe);
}
// Instanzmethoden
public double holeBreite()
{
return breite;
}
public void setzeBreite(double breite)
{
this.breite = ( breite >= 0 ? breite : -breite);
}
public double holeHoehe()
{
return hoehe;
}
public void setzeHoehe(double hoehe)
{
this.hoehe = ( hoehe >= 0 ? hoehe : -hoehe);
}
public double umfang()
{
return 2 * breite + 2 * hoehe;
}
public double flaeche()
{
return breite * hoehe;
}
public String toString()
{
return "Rechteck: (" + holeX() + ", " + holeY()
+ ") " + "breite = " + breite +
" hoehe = " + hoehe;
}
}
Die Klasse "Test"
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);
47
Klassenhierarchie
auf
Programmieren in Java
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());
}
}
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
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 „==“ bestätigt: Es handelt sich jetzt (nach der Zuweisung) um das
gleiche Objekt.
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.
48
Programmieren in Java
1.4.1.4 Konvertieren von Objekten und Primitivtypen
„Casting“ ist ein Mechanismus zum Konvertieren des Werts eines Objekts oder eines
primitiven Typs in einen anderen Typ. „Casting“ ergibt ein neues Objekt oder einen
neuen Wert und wirkt sich nicht auf das ursprüngliche Objekt bzw. den
ursprünglichen Wert aus.
Konvertieren von Primitivtypen
Ist der angestrebte Typ „größer“ als der zu konvertierende Typ, ist häufig kein
explizites „Casting“ nötig. Ein „Byte“ oder ein Zeichen kann automatisch z.B. als
„int“, ein „int“ als „long“, ein „int“ als „float“ behandelt werden. Es gehen beim
Konvertieren des Werts keine Informationen verloren, weil der größere Typ mehr
Genauigkeit bietet als der kleinere.
Ist der angestrebte Typ „kleiner“ als der zu konvertierende Typ, ist „Casting“ nötig.
Explizites Casting sieht so aus: (typname) wert
„typname“ ist der Name des Typs, auf den konvertiert wird, „wert“ ist ein Ausdruck,
der den zu konvertierenden Wert ergibt, z.B.: „(int) x/y;“. Dieser Ausdruck teilt x
durch y und wandelt dann das Ergebnis in „int“ um. Da Casting eine höhere
Priorität als Arithmetik hat, müssen die Klammern angegeben werden.
Konvertieren von Objekten
Mit einer Einschränkung können auch Klasseninstanzen in Instanzen anderer
Klassen konvertiert werden. Die betroffenen Klassen müssen durch Vererbung
miteinander verbunden sein, d.h.: Ein Objekt kann nur in eine Instanz der Sub- oder
Superklassen konvertiert werden, nicht aber in eine beliebige Klasse.
Durch Konvertieren eines Objekts in eine Instanz der Superklasse gehen
Informationen, die die Subklasse bereitgestellt hat, verloren. Spezifisches Casting ist
erforderlich: (klassename) objekt.
„klassename“ ist der Name der Klasse, in die das Objekt konvertiert werden soll.
„objekt“ ist eine Referenz auf das zu konvertierende Objekt.
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();
49
Programmieren in Java
1.4.1.5 Manipulieren von Objekten
Vergleichen von Objekten
Die relationalen Operatoren funktionieren in der Regel nur mit Primitivtypen, nicht mit
Objekten. Die Ausnahme zu dieser Regel bilden die Operatoren „==“ und „!=“. Die
Operatoren können für Objekte benutzt werden und prüfen, ob sich zwei Operanden
auf genau das gleiche Objekt beziehen.
Java besitzt kein Konzept für das Überladen (Overloading) von Operatoren. Zum
Vergleichen von Instanzen eigendefinierter Klassen müssen Sondermethoden in die
Klasse einbezogen werden.
Bsp.: Gleichheitstest mit Objekten der „String“-Klasse.
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-Objekten
public class Rechentafel implements Cloneable
{
50
Programmieren in Java
// Instanzvariable
private int ersterOperand = 0;
private int zweiterOperand = 0;
private int nummer;
// Identifikationsnummer
// Klassenvariable
static int anzRechenobjekte = 0;
// Konstruktoren
Rechentafel()
{
this(0,1);
}
Rechentafel(int ersterOperand, int zweiterOperand)
{
super();
this.ersterOperand = ersterOperand;
this.zweiterOperand = zweiterOperand;
nummer = ++anzRechenobjekte;
}
// Kopierkonstruktor
Rechentafel(Rechentafel rechenObjekt)
{
this(rechenObjekt.ersterOperand,rechenObjekt.zweiterOperand);
}
// Operationen
public int holersterOperand()
{
return ersterOperand;
}
public void setzersterOperand(int ersterOperand)
{
this.ersterOperand = ersterOperand;
}
public int holzweiterOperand()
{
return zweiterOperand;
}
public void setzweiterOperand(int zweiterOperand)
{
this.zweiterOperand = zweiterOperand;
}
public int addiere()
{
return ersterOperand + zweiterOperand;
}
public int subtrahiere()
{
return ersterOperand - zweiterOperand;
}
public int multipliziere()
{
return ersterOperand * zweiterOperand;
}
public int dividiere()
{
// ganzzahlige Division
try {
/* Innerhalb des try-Blocks werden diejenigen
kritischen Aktionen durchgefuehrt, die
Ausnahmen erzeugen koennen
*/
return (ersterOperand / zweiterOperand);
}
catch(java.lang.ArithmeticException a)
{
System.out.println(" Fehler: Division durch Null");
System.out.println("Divisor wird auf 1 gesetzt!");
zweiterOperand = 1;
51
Programmieren in Java
return (ersterOperand / zweiterOperand);
}
}
public String toString()
{
return "Rechenobjekt# " + nummer + " Erster Operand: "
+ ersterOperand + " Zweiter Operand: " + zweiterOperand;
}
public Object clone()
{
Object o = null;
try {
o = super.clone();
}
catch(CloneNotSupportedException e)
{
System.out.println("Objekt kann nicht geklont werden");
}
return o;
}
}
Diese Methode kann dann über ein in der Klasse Rechentafeltest instanziertes
Objekt der Klasse Rechentafel aufgerufen werden:
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);
52
Programmieren in Java
System.out.println(erstesRechenobjekt.toString());
System.out.println(drittesRechenobjekt.toString());
// Kopierkonstruktor
System.out.println("Arbeiten mit einem Kopierkonstruktor");
Rechentafel viertesRechenobjekt = new Rechentafel(erstesRechenobjekt);
System.out.println(viertesRechenobjekt.toString());
}
}
Der Test zeigt: Das geklonte Objekt enthält die gleichen Werte.
Bestimmen der Klasse eines Objekts
Die Klasse Class stellt über die Methode getname() den Namen einer Klasse zur
Verfügung: public String getName()
Bsp.: Der folgende Aufruf
Rechentafel einRechenobjekt = new
Rechentafel(3,2);
..System.out.print("Eine Instanz der Klasse ");
einRechenobjekt.zeigeKlasse();
betimmt den Namen der Klasse von „einRechenobjekt“. Die Methode „zeigeKlasse“
enthält die Methode getName() der Klasse Class. Das Objekt, dessen
Klassenzugehörigkeit bestimmt werden soll ruft die Methode getClass() der Klasse
Object auf. Das Ergebnis dieser Methode ist ein Class-Objekt, das die Methode
getName() kennt. „getName()“ gibt die Zeichenkette aus.
void zeigeKlasse()
{
System.out.print(this.getClass().getName());
}
Ein anderen Test bietet der Operator „instanceof“. Er hat zwei Operanden: Ein
Objekt links und den Namen der Klasse rechts. Der Ausdruck gibt true oder false
aus, je nach dem, ob das Objekt eine Instanz der benannten Klasse ist, z.B.:
if (!(anderesObj instanceof Rechentafel)) return false;
der Operator instanceof kann auch als Schnittstelle benutzt werden. Falls ein
Objekt eine Schnittstelle implementiert, gibt instanceof mit dem
Schnittstellennamen auf der rechten Seite true aus.
1.4.1.6 Innere (lokale) und anonyme Klassen
Lokale Klassen
Seit JDK 1.1 kann man innerhalb einer bestehenden Klasse z.B. X eine neue Klasse
z.B. Y definieren52. 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.
52
Im JDK wird das Konzept als Inner Classes bezeichnet
53
Programmieren in Java
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 implemntieren und überlagert die benötigten Methoden. Alle
übrigen Methoden des Interfaces werden von der Basisklasse zur Verfügung gestellt.
Zu jedem der Low-Level-Ereignisempfänger stellt java.awt.event eine passende
Adapterklasse zur Verfügung.
1.4.1.7 Schnittstellen und Pakete
Schnittstellen
Definition
Eine Schnittstelle ist in Java eine Sammlung von Methodennamen ohne konkrete
Definition. Klassen haben die Möglichkeit zur Definition eines Objekts, Schnittstellen
können lediglich ein paar abstrakte Methoden und Konstanten (finale Daten)
definieren53.
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
{
53
Angaben zur Implementierung sind nicht möglich
54
Programmieren in Java
// Abstrakte Methoden
}
bzw.
public interface meineSpezialschnittstelle extend meineSchnittstelle
{
// Abstarkte 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 */
}
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.:
interface Kaempfer
{
void kaempfen();
}
interface Schwimmer
{
void schwimmen();
}
interface Flieger
{
void fliegen();
}
class Kampfhandlung
{
public void kaempfen() {}
}
class Held extends Kampfhandlung
implements Kaempfer, Schwimmer, Flieger
{
public void schwimmen() {}
public void fliegen() {}
}
public class Abenteuer
{
static void t(Kaempfer x) { x.kaempfen(); }
static void u(Schwimmer x) { x.schwimmen(); }
static void v(Flieger x) { x.fliegen(); }
55
Programmieren in Java
static void w(Kampfhandlung x) { x.kaempfen(); }
public static void main(String args[])
{
Held i = new Held();
t(i);
u(i);
v(i);
w(i);
}
}
Konstanten
Neben abstrakten Methoden können Interfaces auch Konstanten (Variablen mit
dem Attributen static und final) enthalten. Wenn eine Klasse ein solches
Interface implementiert, erbt es gleichzeitig auch alle seine Konstanten. Ein
Interface kann ausschließlich Konstanten enthalten.
Pakete
Definition
Pakete sind in Java Zusammenfassungen von Klassen und Schnittstellen. Sie
entsprechen in Java den Bibliotheken anderer Programmiersprachen. Pakete
ermöglichen, daß modulare Klassengruppen nur verfügbar sind, wenn sie gebraucht
werden. Potentielle Konflikte zwischen Klassenamen in unterschiedlichen
Klassengruppen können dadurch vermieden werden.
Für Methoden und Variablen innerhalb von Paketen besteht eine
Zugriffsschutzschablone. Jedes Paket ist gegenüber anderen, nicht zum Paket
zählenden Klassen abgeschirmt. Klassen, Methoden oder Variablen sind nur
sichtbar für Klassen im gleichen Paket. Klassen, die ohne „package“ Anweisung
definiert sind, werden vom Compiler in ein „Standardpaket“ gestellt. Voraussetzung
dafür ist: Die „.java“- und „.class“-Dateien dieses Pakets befinden sich im aktuellen
Verzeichnis (oder in einen darunter liegenden Klassenverzeichnis).
Die Java-Klassenbibliothek54 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
54
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.
Die Klassenbibliothek des JDK befindet sich in einem Paket mit dem namen „java“.
56
Programmieren in Java
Throwable
System
Runtime
Process
Math
Number
Character
Boolean
ClassLoader
SecurityManager
Compiler
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
Einthät 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
Ermöglicht der Laufzeit-Umgebung von Java neie 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.
Erö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 erweitert55:
Paket
java.applet
java.awt
java.awt.datatranfer
java.awt.event
java.awt.image
java.beans
java.io
java.lang
java.lang.reflect
55
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
Zusätzlich 15 weitere Packages, etwa 500 Klassen und Schnittstellen
57
Programmieren in Java
Debugger und Inspektoren
java.math
java.net
java.rmi
java.rmi.dgc
java.rmi.registry
java.rmi.server
java.security
java.security.aci
java.security.intefaces
java.sql
java.util
java.util.zip
Netzzugriffe
Remote Method Invocation, Zugriff auf Objekte in
anderen virtuellen maschinen
RMI Distributed Garbage Collection
Verwaltet datenbank, die RMI-Verbindungen
koordiniert
RMI-Server
Sicherheit durch digitale Signaturen, Schlüssel
Access Control Lists
Digital Signature Algorithm (DAS-Klassen)
Datenbankzugriff (JDBC)
Diverse Utilities, Datenstrukturen
JAR-Files, Kompression, Prüfsummen
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.*"56 können alle Klassen des angegebenen Pakets auf einmal importiert
werden.
Standardmäßig haben Java-Klassen Zugang zu den in java.lang befindlichen
Klassen. Klassen aus anderen Paketen müssen explizit über den Paketnamen
einbezogen oder in die Quelldatei importiert werden.
1.4.1.8 Polymorphismus und Binden
Polymorphismaus
Definition
56
type import on demand, d.h.: Die Klasse wird erst dann in dem angegebenen Paket gesucht, wenn das
Programm sie wirklich benötigt.
58
Programmieren in Java
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.
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 Veerbung 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“.
59
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.
Zeichenkettendarstellung von Objekten: public String toString()
Darüber kann ein Objekt an einen Ausgabestrom übergeben werden.
Klonen
von
Objekten:
protected
Object
clone()
throws
CloneNotSupportedException, OutOfMemoryError erzeugt ein exaktes
Duplikat eines Objekts. Ein Objekt kann nur geklont werden, falls es die CloneableSchnittstelle unterstützt.
1.4.2.2 Die Class-Klasse
Sie enthält Informationen, die eine Java-Klasse beschreiben. In Java hat jede Klasse
eine entsprechende Instanz von Class.
Konstruktor. Es gibt keinen öffentlichen Konstruktor für das Class-Objekt.
Class-Instanzen. Eine Class-Instanz kann auf drei unterschiedliche Arten erhalten
werden:
- public final native Class getClass() ermittelt die Class-Instanz für ein Objekt.
Bsp.: void ausgabeClassName(Object obj)
{
System.out.println(“Die Klasse von “ + obj + “: “
+ obj.getClass.getName());
}
public
static
Class
forName(String
className)
throws
ClassNotFoundException gibt die Instanz von Class wieder, die zu className gehört.
- Laden einer neuen Klasse durch Verwendung eines eigenen ClassLoader-Objekts
Informationen über eine Klasse können bezogen werden über
public String getName() gibt den Namen der Klasse zurück.
public boolean isInterface() gibt true aus, falls es sich um eine Schnittstelle handelt.
public Class getSuperClass() gibt die Superklasse der Klasse an.
public Class[] getInterfaces() gibt ein Datenfeld aus, das Class-Instanzen für jede
Schnittstelle enthält, die diese Klasse unterstützt.
public ClassLoader getClassLoader() gibt die Instanz von ClassLoader an, die dafür
verantwortlich ist, daß diese Klasse in die Laufzeit-Umgebung geladen wird.
60
Programmieren in Java
1.4.2.3 Die Klasse System
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.
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
61
Programmieren in Java
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
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
62
Programmieren in Java
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 String valueOf(boolean b)
static String valueOf(char z)
Die „valueOf“-Methoden sind als Klassenmethoden implementiert und können ohen String-Objekt aufgerufen
werden. Da sie in der Regel zur Erzeugung von Strings verwendet werden, ist das sinnvoll. Ein Aufruf von
„valueOf“ wandelt ein primitives Objekt mit Hilfe der Methode „toString()“, die von der zugehörigen WrapperKlassen bereitgestellt wird, in eine Zeichenkette um.
Weitere Eigenschaften:
Die Klasse String ist final. Von einer String-Klasse können keine neuen Klassen abgeleitet werden.
String-Objekte sind nicht dynamisch. Es werden durch die Klasse String keine dynamischen
Zeichenketten implementiert. Nach der Initialisierung eines String bleibt dessen Länge konstant, z.B.:
String s = “Hallo Welt“;
s = substring(0,5);
Die „Substring“-Methode erzeugt eine Kopie, die mit dem gewünschten Inhalt gefüllt
wird. Diese gibt sie an den Aufrufer zurück, der das Ergebnis erneut s zuweist und
damit die Originalinstanz für den Carbage Collector freigibt.
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.
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.
63
Programmieren in Java
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;
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.
Die Klasse Character
Konstruktor:
Ermitteln vom „char“-Wert: public char charValue
Klassifizieren von Zeichen:
Methode
isDigit
isLetter
isLetterOrDigit
Beschreibung
Eine numerische Zahl zwischen 0 und 9
Ein Buchstabe des Alphabets
Ein alphabetischer Buchstabe oder eine numerische Zahl
64
Programmieren in Java
isLowerCase
isUpperCase
isJavaLetter
isJavaLetterOrDigit
isSpace
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
isTitleCase
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“)57 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
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
57
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.
65
Programmieren in Java
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.
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.
66
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.58:
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
58
PR14103
67
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()59. 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();
}
}
}
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
{
.........
}
59
Sie ist in der Klasse Throwable definiert und daher allen Exception-Objekten zugänglich.
68
Programmieren in Java
}
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.
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
69
Programmieren in Java
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
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
{
70
Programmieren in Java
throw new Exception("Hier ist meine Ausnahme");
}
catch (Exception a)
{
System.out.println("Eingefangene Ausnahme");
System.out.println("a.getMessage(): " + a.getMessage());
System.out.println("a.toString(): " + a.toString());
System.out.println("a.printStackTrace(): ");
a.printStackTrace();
}
}
}
/* Ausgabe:
Eingefangene Ausnahme
a.getMessage(): Hier ist meine Ausnahme
a.toString(): java.lang.Exception: Hier ist meine Ausnahme
a.printStackTrace():
java.lang.Exception: Hier ist meine Ausnahme
at AusnahmeMethoden.main(AusnahmeMethoden.java)
*/
Die throw-Anweisung besitzt Merkmale einer Sprunganweisung. Sie unterbricht das
Programm und verzweigt unmittelbar zur umgebenden catch-Klausel. Gibt es keine
catch-Klausel, wird der Fehler weitergegeben. Tritt die Ausnahme innerhalb einer
try-catch-Anweisung mit einer finally-Klausel auf, wird diese noch vor der
Weitergabe ausgeführt.
Auch das „Wiederauswerfen“ einer Ausnahme ist möglich, insbesondere dann, falls
eine Ausnahme eingefangen wurde, sie aber in einem größeren Zusammenhang
behandelt werden soll
catch (Exception a)
{
System.out.println(“Eine Ausnahme wurde eingefangen“);
throw a;
}
Eigene, d.h. benutzerdefinierte Ausnahmen können direkt von der Klasse
Exception abgeleitet werden, z.B.60:
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();
}
60
Vgl. PR14140
71
Programmieren in Java
public static void g() throws MeineAusnahme
{
System.out.println("Auswurf von MeineAusnahme aus g()");
throw new MeineAusnahme("Ursprung in g()");
}
public static void h() throws MeineAusnahme
{
System.out.println("Auswurf von MeineAusnahme aus h()");
throw new MeineAusnahme("Ursprung in h()",13);
}
public static void main(String args[])
{
try {
f();
}
catch (MeineAusnahme a)
{
a.printStackTrace();
}
try { g(); }
catch (MeineAusnahme a)
{
a.printStackTrace();
}
try { h(); }
catch (MeineAusnahme a)
{
a.printStackTrace();
System.out.println("a.wert() = " + a.wert());
}
}
}
/* 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
*/
72
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 InputStream61 in
public static PrintStream62 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“63 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. 64:
"Einlesen von zwei ganzen Zahlen".
import java.io.*;
public class EingZweiZahlen
{
// Einlesen von zwei ganzen Zahlen
public static void main(String args[]) throws IOException
{
int a, b, c;
BufferedReader ein = new BufferedReader(new
InputStreamReader(System.in));
System.out.print("Bitta a eingeben: ");
a = Integer.parseInt(ein.readLine());
System.out.print("Bitte b eingeben: ");
61
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
63 Das grundlegende Konzept für Ein- und Ausgabe sind Datenströme, die im Paket „java.io“ versammelt
sind.
64 Vgl. PR14150
62
73
Programmieren in Java
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äts65 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. Writer66 abgeleitet. Allgemeine
Fehlanzeige im Paket java.io ist die Ausnahme IOException, die deshalb auch
von einer Vielzahl von Methoden ausgelöst werden kann und dabei sehr
unterschiedliche Bedeutung annehmen kann.
Bsp.: Das folgende Programm liest eine Zeichenfolge aus einem Eingabestrom und
schreibt die Zeichenfolge in einen Ausgabestrom
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);
65
66
Hauptspeicher, Festplatte, Drucker, Netzwerk
Vgl. 7.6
74
Programmieren in Java
}
}
}
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(...)
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)
75
Programmieren in Java
{
System.out.println("readLine() nicht erfolgreich");
s = "Fehlanzeige";
}
return s;
}
void bereinigen()
{
// Freigabe der Syntax-Resourcen
try {
ein.close();
}
catch(IOException a2)
{
System.out.println("ein.close() nicht erfolgreich");
}
}
}
Das folgende Programm bearbeitet die Eingabedatei.
import java.io.*;
public class BearbeiteEingabeDatei extends Object
{
public static void main(String args[])
{
try {
// Erzeugen einer Instanz
EingabeDatei ein =
new EingabeDatei("BearbeiteEingabeDatei.java");
String s;
int i = 1;
while ((s = ein.holeZeile()) != null)
System.out.println("" + i++ + ": " + s);
ein.bereinigen();
}
catch (Exception a)
{
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)
Bsp.: Demonstration der Umlenkung von Standardeingabe und Standardausgabe in
Java 1.1
import java.io.*;
class Umlenken
{
public static void main(String args[])
{
try
{
BufferedInputStream ein =
76
Programmieren in Java
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();
}
}
}
77
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
Leeraum67
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 Unicode68.
Bei der Kompilierung wird der Quellcode automatisch in eine Folge von Unicode67
Technisch gesehen ist ein Leeraum kein Token
Alle Literale (d.h. alle vorkommenden Zeichen (Zahlen, Buchstaben, Sonderzeichen, etc.) bestehen aus
Unicode.
68
78
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.
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.
79
Programmieren in Java
- Identifizierer von Klassen sollten mit einem Großbuchstaben beginnen und anschließend klein geschrieben
werden. Wenn sich ein Bezeichner aus mehreren Wörtern zusammensetzt, dürfen diese nicht getrennt werden.
Die jeweiligen Anfangsbuchstaben innerhalb des gesamten Bezeichners werden jedoch groß geschrieben.
- Die Identifizierer von Variablen, Methoden und Elementen beginnen mit Kleinbuchstaben und werden auch
anschließend klein geschrieben. Wenn ein Bezeichner sich aus mehreren Wörtern zusammensetzt, dürfen
diese nicht getrennt werden. Die jeweiligen Anfangsbuchstaben der folgenden Wörter können jedoch
innerhalb des Gesamtbezeichners groß geschrieben werden.
2.1.3 Literale
Literale sind Token, die für zu speichernde Werte der Datentypen byte, short,
int, long, float, double, boolean und char stehen. Literale werden auch zur
Darstellung von Werten benutzt, die in Zeichenketten gespeichert werden.
Es gibt: Boolesche Literale, Zeichenliterale, Zeichenkettenliterale, Gleitpunktliterale,
ganzzahlige Literale.
1 Ganzzahlige Literale
Die Datentypen „int“ und „long“ können dezimal, hexadezimal und oktal
beschrieben werden. Die Voreinstellung ist dezimal und gilt immer dann, wenn Werte
ohne weitere Änderung dargestellt werden. Hexadezimale Darstellung beginnt immer
mit der Sequenz 0x oder 0X69. Oktale Darstellungen beginnen mit einer führenden
Null. Negativen Ganzzahlen wird ein Minuszeichen vorangestellt. Ganzzahlige
Literale haben gemäß Voreinstellung den Typ „int“. Durch Anhängen vom „l“ bzw.
„L“ kann man jedoch explizit den Typ „long“ wählen.
2. Gleitpunktliterale
Gleitpunktliterale bestehen aus mehreren Teilen. Sie erscheinen in folgender
Reihenfolge:
Teil
Ganzzahliger Teil
Dezimalpunkt
Gebrochener Teil
Exponent
Typensuffix
Nötig?
Nicht, wenn der gebrochene Teil
vorhanden ist
Nicht, wenn ein Exponent vorhanden
ist. Muß vorliegen, wenn es einen
Dezimalpunkt gibt
Darf nicht vorhanden sein, wenn es
keinen Dezimalpunkt gibt. Muß
vorhanden sein, wenn es keinen
ganzzahligen Teil gibt
Nur, wenn es keinen Dezimalpunkt
gibt
Nein. Bei Abwesenheit eines
Typensuffix wird angenommen, daß
es sich um eine Zahl doppelter
Genauigkeit handelt.
Beispiele
0, 1, 2, ... , 9, 131140
0, 1, 1311, 41421, 9944,
718281828
E23, E-19, E6, e+307
f, F, d, D
Bsp.: Typensuffix für Ganzzahlen bzw. Gleitpunktzahlen
Die Zahlen 0 bis 15 werden durch die Buchstaben „A“ bis „F“ bzw. „a“ bis „f“ dargestellt (Groß- oder
Kleinschreibung ist hier nicht relevant.
69
80
Programmieren in Java
class Literale
{
char c = 0xffff;
// max. hex. Wert fuer char
byte b = 0x7f;
// max. hex. Wert fuer byte
short s = 0x7fff;
// max. hex. Wert fuer short
int i1 = 0x2f;
// hexadezimal in Kleinbuchstaben
int i2 = 0X2F;
// hexadezimal in Grossbuchstaben
// hexadeimale und oktale Werte mit long
long n1 = 200L;
// "long"-Suffix
long n2 = 200l;
// "long"-suffix
long n3 = 200;
// float- und double-Literale
float f1 = 1;
float f2 = 1F;
// "float"-Suffix
float f3 = 1f;
// "float"-Suffix
float f4 = 1e-45f;
// wissenschaftl. Schreibweise
float f5 = 1e+9f;
// "float"-Suffix
double d1 = 1d;
// "double"-Suffix
double d2 = 1D;
// "double"-Suffix;
double d3 = 47e47d;
// wissenschaftl. Schreibweise
}
3. Boolesche Literale
Es gibt zwei Boolesche Literale: true und false. Es gibt keinen Nullwert und kein
numerisches Äquivalent.
4. Zeichenliterale
Sie werden in einzelne, hochgestellte Anführungszeichen gesetzt. Als einzelne
Zeichen gelten alle druckbaren Zeichen mit Ausnahme des Bindestrichs „-“ und des
Backslash „\“. Bei Escape-Zeichenliteralen folgen den Anführungszeichen der
Backslash und dem Backslash eine der folgenden Zeichen:
- b, t, n, f, r, <<, ‚ oder \
- eine Serie von Oktalziffern (dreistellig)70
- 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
70
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.
81
Programmieren in Java
5. Zeichenkettenliterale
Zeichenkettenliterale sind aus mehreren Zeichenliteralen zusammengesetzte Ketten
(Strings). Bei Zeichenkettenliteralen werden null oder mehr Zeichen in Anführungszeichen (“)dargestellt. Java erzeugt Zeichenketten als Instanz der Klasse String.
Damit stehen alle Methoden der Klasse String zur Manipulation einer Zeichenkette
zur Verfügung.
Zeichenkettenliterale stehen zwischen zwei Anführungszeichen (“) und können
Steuerzeichen wie Tabulatoren, Zeilenvorschübe, nichtdruckbare Unicode-Zeichen
und druckbare Unicode-Spezialzeichen enthalten. Es kann sich auch um EscapeSequenzen handeln. Beide Anführungszeichen müssen in derselben Zeile des
Quellcodes stehen.
Jedes String-Literal ist eine Referenz auf ein Objekt der Klasse String. Falls der
Compiler beim Übergeben des Quelltextes ein string-Literal findet, erzeut er ein
neues String-Objekt und verwendet es anstelle des Literals.
In Java ist der Operator + auch auf Strings definiert. Auf zwei String-Objekte angewendet, liefert er die Verkettung beider Objekte.
2.1.4 Trennzeichen
Trennnzeichen sind Token, die aus einem einzigen Zeichen bestehen und andere
Token trennen. Java kennt 9 Trennzeichen:
(
)
{
}
[
]
;
,
.
Wird sowohl zum Öffnen einer Parameterliste für eine Methode als auch zur Festlegung eines
Vorrangs für Operationen in einem Ausdruck benutzt.
Wird sowohl zum Schließen einer Parameterliste für eine Methode als auch zur Festlegung eines
Vorrangs für Operationen in einem Ausdruck benutzt
Wird zu Beginn eines Blocks mit Anweisungen oder einer Initialisierungsliste gesetzt
Wird an das Ende eines Blocks mit Anweisungen oder einer Initialisierungsliste gesetzt
Steht vor einem Ausdruck, der als Index für ein Datenfeld (Array) steht
Folgt einem Ausdruck, der als Index für ein Datenfeld dient
Dient sowohl zum Beenden einer Ausdrucksanweisung als auch zum Trennen der Teile einer forAnweisung.
wird in vielen Zusammenhängen als Begrenzer verwendet
Wird sowohl als Dezimalpunkt als auch zum Trennen solcher Dinge wie Paketnamen von
Klassennamen oder Variablennamen benutzt.
Abb.: Die Java-Trennzeichen
82
Programmieren in Java
2.1.5 Operatoren
Operatoren geben an, welche Operation mit einem oder mehreren gegebenen
Operanden durchgeführt werden soll. Es gibt in Java 37 Zeichensequenzen, die
Token darstellen, welche als Operatoren benutzt werden.
Arithmetische Operatoren
Arithmetische Operatoren benutzen nur zwei Operanden. Diese sind entweder
ganzzahlige Werte oder Gleitpunktzahlen. Als Rückgabe einer arithmetischen
Operation erhält man einen neuen Wert, dessen Datentyp sich folgendermaßen
ergibt:
- Zwei ganzzahlige Datentypen (byte, short, int oder long) als Operanden ergeben immer einen
ganzzahligen Datentyp als Ergebnis. Dabei kann als Datentyp int oder long entstehen, byte und
short sind nicht möglich. Der Datentyp long entsteht nur, wenn einer der beiden Operanden
bereits vom Datentyp long war oder das Ergebnis von der Größe her nur als long dargestellt
werden kann.
- Zwei Gleitpunktzahlentypen als Operanden ergeben immer einen Gleitpunktzahlentyp als Ergebnis. Die Anzahl
der Stellen des Ergebnisses ist immer das Maximum der Stellenanzahl der beiden Operanden.
- Falls die Operanden ein ganzzahliger Typ und eine Gleitpunktzahlentyp sind, dann ist das Ergebnis immer ein
Gleitpunktzahlentyp.
Operator
+
*
/
%
Bedeutung
Additionsoperator
Subtraktionsoperator
Multiplikationsoperator
Divisionsoperator
Modulo-Operator
Beispiel
13 + 11
13 - 11
13 * 11
13 / 11
13 % 11
Abb.: Die arithmetischen Java-Operatoren
Einstellige arithmetische Operatoren
Es gibt zwei einstellige (d.h. mit nur einem Operanden) arithmetische Operatoren in
Java:
- Einstellige arithmetische Negierung: - Das Gegenteil der arithmetischen Negierung: +
Arithmetische Zuweisungsoperatoren
Neben dem direkten Zuweisungsoperator gibt es die arithmetischen
Zuweisungsoperatoren. Diese sind eigentlich nur eine Abkürzung für arithmetische
Operationen mit ganzen Zahlen und Gleitpunktzahlen. Das Ergebnis einer
Zuweisung über einen arithmetischen Zuweisungsoperator steht immer auf der
linken Seite.
Operator
+=
-=
*=
Bedeutung
Additions- und Zuweisungsoperator
Subtraktions- und Zuweisungsoperator
Multiplikations- und Zuweisungsoperator
83
Programmieren in Java
/=
%=
=
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
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 Manipulieren71
import java.util.*;
public class BitManipulation
{
public static void main(String[] args)
{
71
PR21502
84
Programmieren in Java
Random rand = new Random();
int i = rand.nextInt();
int j = rand.nextInt();
pBinInt("-1",-1);
pBinInt("+1",+1);
int maxpos = 2147483647;
pBinInt("maxpos", maxpos);
int maxneg = -2147483648;
pBinInt("maxneg",maxneg);
pBinInt("i",i);
pBinInt("~i",~i);
pBinInt("-i",-i);
pBinInt("j",j);
pBinInt("i & j",i & j);
pBinInt("i | j",i | j);
pBinInt("i ^ j",i ^ j);
pBinInt("i << 5",i << 5);
pBinInt("i >> 5",i >> 5);
pBinInt("(~i) >> 5",(~i) >> 5);
pBinInt("i >>> 5",i >>> 5);
pBinInt("(~i) >>> 5", (~i) >>> 5);
long l = rand.nextLong();
long m = rand.nextLong();
pBinLong("-1L",-1L);
pBinLong("+1L",+1L);
long ll = 9223372036854775807L;
pBinLong("maxpos", ll);
long lln = -9223372036854775807L;
pBinLong("maxneg",lln);
pBinLong("l",l);
pBinLong("~l",~l);
pBinLong("-l",-l);
pBinLong("m",m);
pBinLong("l & m",l & m);
pBinLong("l | m",l | m);
pBinLong("l ^ m",l ^ m);
pBinLong("l << 5",l << 5);
pBinLong("l >> 5",l >> 5);
pBinLong("(~l) >> 5",(~l) >> 5);
pBinLong("l >>> 5",l >>> 5);
pBinLong("(~l) >>> 5", (~l) >>> 5);
}
static void pBinInt(String s, int i)
{
System.out.println(s + ", int: " + i + ", binaer: ");
System.out.print("
");
for (int j = 31; j >= 0; j--)
if (((1 << j) & i) != 0)
System.out.print("1");
else
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();
}
}
85
Programmieren in Java
Die Ausgabe zu dieser Anwendung zeigt für den Bereich „int“ jeweils die interne
Zahlendarstellung:
-1, int: -1, binaer:
11111111111111111111111111111111
+1, int: 1, binaer:
00000000000000000000000000000001
maxpos, int: 2147483647, binaer:
01111111111111111111111111111111
maxneg, int: -2147483648, binaer:
10000000000000000000000000000000
i, int: -1465644750, binaer:
10101000101001000000100100110010
~i, int: 1465644749, binaer:
01010111010110111111011011001101
-i, int: 1465644750, binaer:
01010111010110111111011011001110
j, int: -1273964081, binaer:
10110100000100001101100111001111
i & j, int: -1610610430, binaer:
10100000000000000000100100000010
i | j, int: -1128998401, binaer:
10111100101101001101100111111111
i ^ j, int: 481612029, binaer:
00011100101101001101000011111101
i << 5, int: 344008256, binaer:
00010100100000010010011001000000
i >> 5, int: -45801399, binaer:
11111101010001010010000001001001
(~i) >> 5, int: 45801398, binaer:
00000010101110101101111110110110
i >>> 5, int: 88416329, binaer:
00000101010001010010000001001001
(~i) >>> 5, int: 45801398, binaer:
00000010101110101101111110110110
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
86
Programmieren in Java
Vergleichsoperatoren
Vergleichsoperatoren haben zwei Operanden und vergleichen diese (zwei
Ganzzahlen oder zwei Gleitpunktzahlen)72. Als Rückgabewert der Operation entsteht
ein boolescher Wert (true oder false).
Operator
==
!=
<
>
<=
>=
Bedeutung
Gleichheitsoperator
Ungleichheitsoperator
Kleiner-als-Operator
Größer-als-Operator
Kleiner-als-oder-gleich-Operator
Größer-als-oder-gleich-Operator
Abb.: Die Java-Vergleichsoperatoren
Die relationalen Operatoren „==“ und „!=“ arbeiten mit allen Objekten. Ihre
Anwendung zeigt häufig ein „verwirrendes Ergebnis“, z.B. 73:
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()74 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
72
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.
73 Vgl. PR21500
74 Viele in der Java-Bibliorhek bereitgestellte Klassen implementieren die Methode equals()
87
Programmieren in Java
!
Logischer NOT-Operator
Abb.: Logische Vergleichsoperatoren
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 beginnen mit /* und enden mit */. Sie können sich über mehrere Zeilen
erstrecken.
- Dokumentationskommentare beginnen mit /** und enden mit */. Sie können sich ebenfalls über
mehrere Zeilen erstrecken.
Dokumentationskommentare
Sie dienen dazu, Programme im Quelltext zu kommentieren.
88
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
89
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
90
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
Überlauf75 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 Unterlauf 76 gibt einen speziellen
75
d.h.: Größeres Ergebnis von einer Operation als durch den Wertebereich des jeweiligen Typs ausgedrückt
werden kann.
76 d.h.: kleineres Ergebnis (- außer Null -) von einer Operation als durch den Wertebereich des jeweiligen Typs
ausgedrückt werden kann.
91
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.
92
Programmieren in Java
2.2.3 Datenfelder (Arrays)
Ein Datenfeld (Array) ist eine Ansammlung von Objekten eines bestimmten Typs 77,
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 78. Arrays können jeden Wertetyp
(primitiver Typ oder Objekt) enthalten, jedoch können in einem Array
unterschiedliche Typen gespeichert werden.
Arrays in Java sind Objekte und unterscheiden sich von Datenfeldern in anderen
Programmiersprachen durch folgende Merkmale:
- Array-Variable sind Referenzen
- Arrays besitzen Methoden und Instanz-Variable
- Arrays werden zur Laufzeit erzeugt
2.2.3.1 Deklarieren, Erstellen von Array-Objekten
In Java umfaßt das Erstellen eines Datenfelds drei Schritte:
1. Deklaration einer Variablen zur Aufnahme eines Datenfelds.
Array-Variable zeigen den Objekttyp an, den das Datenfeld aufnimmt und den
namen des Arrays, gefolgt von leeren eckigen Klammern „[]“, z.B: int x[].
Alternativ dazu kann eine Array-Variable auch so (Klammern nach dem Typ)
festgelegt sein, z.B. int[] x.
2. Erstellen eines neuen Array-Objekts und Zuweisen einer Array-Variablen.
Das kann erfolgen
- mit new, z.B. String[] namen = new String[10];
Hier wird ein neues Datenfeld von Strings mit 10 Elementen erstellt. Beim Erstellen
eines Array-Objekts mit new ist anzugeben, wieviele Elemente das Array
aufnehmen soll. Alle Elemente des Array werden automatisch initialisiert (0 für
numerische Datenfelder, false für boolesche, ‘\0‘ für Zeichen-Arrays und
„null“ für alles andere.
Bsp.: Erzeugen von Datenfeldern mit „new“
a) Aufnahme von Werten primitiver Typen
import java.util.*;
public class FeldPrim
{
static Random rand = new Random();
static int zRand(int mod)
{
return Math.abs(rand.nextInt()) % mod;
}
public static void main(String[] args)
77
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.
78 Es gibt drei Arten von Referenzvariablen: Klassen, Schnittstellen und Datenfelder
93
Programmieren in Java
{
int[] a;
// Die Groesse des Array wird zur Laufzeit
// zufaellig bestimmt
a = new int[zRand(20)];
ausgabe("Laenge von a = " + a.length);
for (int i = 0; i < a.length; i++)
ausgabe("a[" + i + "] = " + a[i]);
}
static void ausgabe(String s)
{
System.out.println(s);
}
}
b) Aufnahme von Objekten
Ein Datenfeld, dessen Komponenten keine primitiven Typen aufnehmen
sollen, muß immer mit „new“ gefüllt werden.
import java.util.*;
public class FeldObj
{
static Random rand = new Random();
static int zRand(int mod)
{
return Math.abs(rand.nextInt()) % mod;
}
public static void main(String[] args)
{
Integer[] a;
// Die Groesse des Array wird zur Laufzeit
// zufaellig bestimmt
a = new Integer[zRand(20)];
ausgabe("Laenge von a = " + a.length);
for (int i = 0; i < a.length; i++)
{
a[i] = new Integer(zRand(500));
ausgabe("a[" + i + "] = " + a[i]);
}
}
static void ausgabe(String s)
{
System.out.println(s);
}
}
c) Variable Argumente
Das zweite Format in der Feldinitialisierung bestimmt eine bequeme
syntaktische Form für den „Aufruf von Methoden“, die den gleichen Effekt
besitzt wie die „Argumentenliste in C“. Über eine solche Argumentenliste
kann eine unbekannte Menge von Argumenten eines unbekannten Typs
behandelt werden. Da alle Klassen von der Klasse Object abstammen,
kann eine Methode mit einem Datenfeld als Argument verwendet werden,
das aus Objekten vom Typ Object besteht, z.B.
class A
{
int i;
}
public class VarArgs
{
static void f(Object[] x)
{
94
Programmieren in Java
for (int i = 0; i < x.length; i++)
System.out.println(x[i]);
}
public static void main(String[] args)
{
f(new Object[] {
new Integer(40), new VarArgs(),
new Float(3.14), new Double(11.11)
});
f(new Object[] { "eins", "zwei", "drei" });
f(new Object[] { new A(), new A(), new A() });
}
}
- durch direktes Initialisieren des Array-Inhalts, z.B.: String[] namen =
{“juergen“, “bernd“, “liesel“, “dieter“, “hans“, “vera“,
“christian“, “theo“, “emil“, “karl“};
Alle Elemente der in der geschweiften Klammer stehenden Elemente müssen vom
gleichen Typ sein
3. Speichern von Elementen im Array
Zur Speicherung eines Werts in einem Array, wird der Array-Ausdruck „subscript“
benutzt, z.B.: x[subscript] . „subscript“ ist die Stelle (Position) für den Zugriff
auf eine Datenfeld-Element. Das Array-Subskript muß vom Typ int sein. Wegen
der vom Compiler vorgenommenen automatischen Typenkonvertierungen sind auch
short, byte, char zulässig. Indexausdrücke werden vom Laufzeitsystem auf
Einhaltung der Array-Grenzen überprüft.
2.2.3.2 Zugriff auf Datenfeld-Elemente, Ändern von Datenfeld-Elementen
Datenfeld-Subskripte beginnen mit 0. Alle Array-Subskripte werden beim
Kompilieren geprüft, ob sie sich innerhalb der Grenzem des Array befinden (Größer
als 0, kleiner als die Länge des Datenfelds).
String ort[] = new String[10];
ort[10] = “Dinkesbuehl“;
ist falsch (Fehler beim Kompilieren). Das in „ort“ gespeicherte Array hat nur 10 ab 0
numerierte Elemente.
Wird das Array-Subskript zur Laufzeit berechnet und resultiert daraus ein Wert
außerhalb der Array-Grenzen, dann erzeugt der Java-Interpreter einen Fehler79. 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
79
Er weist auf eine „Ausnahme“ hin.
95
Programmieren in Java
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
b) binäre Suche
3. Sortieren80
80
PR22301
96
Programmieren in Java
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]
M[0][4]
M[1][4]
M[2][4]
M[3][4]
Zweidimensionale Datenfelder werden, wie das folgende Beispiel zeigt, auf gleiche
Weise erstellt und initialisiert wie eindimensionale Felder.
class Einheitsmatrix
{
publich static void main(String args[])
{
double[][] EM;
EM = new double[4][4];
for (int zeile = 0; zeile < 4; zeile++)
{
for (int spalte = 0; spalte < 4; spalte++)
{
if (zeile != spalte)
{
EM[zeile][spalte] = 0.0;
}
else {
EM[zeile][spalte] = 1.0;
}
}
}
}
}
Da mehrdimensionale Arrays als geschachtelte Arrays gespeichert werden, ist es
möglich, „nicht-rechteckige“ Arrays zu erzeugen, z.B.:
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++)
97
Programmieren in Java
{
for (int j = 0; j < a[i].length; j++)
{
System.out.print(a[i][j] + " ");
}
System.out.println();
}
}
}
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)81.
Bsp.: Mehrdimensionale Felder
import java.util.*;
public class MehrdimFeld
{
static Random rand = new Random();
static int zRand(int mod)
{
return Math.abs(rand.nextInt()) % mod;
}
public static void main(String args[])
{
// 1. Bsp: Erzeugen eines mehrdimensionalen Felds
// mit Werten primitiver Typen
int[][] a1 = {
{ 1, 2, 3, },
{ 4, 5, 6, },
};
for (int i = 0; i < a1.length; i++)
for (int j = 0; j < a1[i].length; j++)
ausgabe("a1[" + i + "][" + j + "] = " + a1[i][j]);
// 2. Bsp.: Dreidimensionales Feld mit fester Laenge
int[][][] a2 = new int[2][2][4];
for (int i = 0; i < a2.length; i++)
for (int j = 0; j < a2[i].length; j++)
for (int k = 0; k < a2[i][j].length;k++)
ausgabe("a2[" + i + "][" + j + "][" + k + "] = "
+ a2[i][j][k]);
// 3. Bsp.: Dreidimensionales Feld mit variablen Laengen
int[][][] a3 = new int[zRand(7)][][];
for (int i = 0; i < a3.length; i++)
{
a3[i] = new int[zRand(5)][];
for (int j = 0; j < a3[i].length; j++)
{
a3[i][j] = new int[zRand(5)];
}
}
for (int i = 0; i < a3.length; i++)
for (int j = 0; j < a3[i].length; j++)
for (int k = 0; k < a3[i][j].length;k++)
ausgabe("a3[" + i + "][" + j + "][" + k + "] = "
+ a3[i][j][k]);
// Mehrdimensionales Feld mit nicht primitiven Objekten
Integer[][] a4 = {
{ new Integer(1), new Integer(2) },
81
Im Java werden multidimensionale Arrays streng genommen nicht unterstützt
98
Programmieren in Java
{ 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);
}
}
99
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.
100
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
101
Programmieren in Java
Abb.: Casting-Operatoren
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.
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.
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.
102
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;
2.4.5 Ausdrucksanweisungen
In Java gibt es sieben verschiedene Arten von Ausdrucksanweisungen:
Ausdrucksanweisung
Zuordnung
Prä-Inkrement
Prae-Dekrement
Beispiel
x=13;
++wert;
--wert;
103
Programmieren in Java
Post-Inkrement
Post-Dekrement
Methodenaufruf
Zuweisungsausdruck
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 DekrementOperatoren82
// 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
*/
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)
anweisung83
if-else-Anweisungen
82
vgl. PR24500
„anweisung“ bedeutet: Eine einfache Anweisung, die nur von einem Semikolon abgeschlossen ist bzw. eine
zusammengesetzte Folge von anweisungen, die von { ... } umschlossen ist.
83
104
Programmieren in Java
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-Anweisung84
import java.lang.*;
public class IfDemo extends Object
{
public static void main(String[] args)
{
int i;
int abs;
// if - else
i = 13;
System.out.println("i = " + i);
if (i >= 0)
{
abs = i;
}
else {
abs = -i;
}
System.out.println("abs = " + abs);
i = -13;
System.out.println("i = " + i);
if (i >= 0)
{
abs = i;
}
else {
abs = -i;
}
System.out.println("abs = " + abs);
// if
i = 13;
abs = i;
System.out.println("i = " + i);
if (abs < 0)
{
abs = -abs;
}
System.out.println("abs = " + abs);
i = -13;
abs = i;
System.out.println("i = " + i);
if (abs < 0)
{
abs = -abs;
}
System.out.println("abs = " + abs);
// Konditionaloperator
i = -13;
System.out.println("i = " + i);
abs = ( i < 0 ? -i : i);
84
Vgl. PR24601
105
Programmieren in Java
System.out.println("abs = " + abs);
i = 13;
System.out.println("i = " + i);
abs = ( i < 0 ? -i : i);
System.out.println("abs = " + abs);
}
}
switch-Anweisungen
Eine switch-Anweisung ermöglicht die Weitergabe des Kontrollflusses an eine von
vielen Anweisungen in ihrem Block mit Unteranweisungen (ist abhängig vom Wert
des Ausdrucks in der switch-Anweisung).
switch (integraler_Selektor)
{
case integraler_Wert1: anweisung; break;
case integraler_Wert2: anweisung; break;
case integraler_Wert3: anweisung; break;
.....
default: anweisung;
}
Ein integraler_Selektor ist ein Ausdruck zur Produktion eines integralen Werts85.
„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-Anweisung86
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;
Integrale Werte sind bspw. vom Typ „int“, „char“. Nichtintegrale Typen. Z.B. float, String, müssen über eine
Serie von „if“-Anweisungen „switch“ simulieren
86 vgl. PR24601
85
106
Programmieren in Java
case 10:
System.out.print("A");
break;
case 11:
System.out.print("B");
break;
case 12:
System.out.print("C");
break;
case 13:
System.out.print("D");
break;
case 14:
System.out.print("E");
break;
case 15:
System.out.print("F");
break;
default:
System.out.print("Keine gueltige Hex-Ziffer");
break;
}
System.out.println();
}
}
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.:
public class WhileDemo extends Object
{
public static void main(String args[])
{
double a = 2.0;
int
n = 10;
System.out.println("a = " + a);
System.out.println("n = " + n);
int absn = (n < 0) ? -n : n;
double aHochn = 1.0;
int i = 1;
while (i <= absn)
{
aHochn *= a;
i++;
}
if ( n < 0)
{
aHochn = 1.0 / aHochn;
107
Programmieren in Java
}
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.:
public class DoDemo extends Object
{
public static void main(String args[])
{
double a = 2.0;
int
n = 10;
System.out.println("a = " + a);
System.out.println("n = " + n);
int absn = (n < 0) ? -n : n;
double aHochn = 1.0;
int i = 1;
do
{
if (n == 0)
{
break;
}
aHochn *= a;
i++;
} while (i <= absn);
if ( n < 0)
{
aHochn = 1.0 / aHochn;
}
System.out.println("aHochn = " + aHochn);
}
}
for-Anweisung
Sie besteht aus
- for, gefolgt von optionalem Leerraum, gefolgt von einer öffnenden Klammer.
- Initialisierungsteil. Er enthält eine durch Kommata getrente Reihe von
Deklarations- und Zuweisungsanweisungen, die durch ein Semikolon beendet
wird. Die Deklarationen haben nur Gültigkeit für den Bereich der for-Anweisung
und ihrer Unteranweisungen. Die Zuweisungen werden nur einmal vor der esrten
Wiederholung der Unteranweisung oder des Blocks gemacht.
108
Programmieren in Java
- 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);
}
}
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
109
Programmieren in Java
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.: „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);
}
}
}
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.
110
Programmieren in Java
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.: Marken im Zusammenhang mit „for“- bzw. „while“-Schleifen
1. Marken im Zusammenhang mit „for“
public class MarkiertesFor
{
public static void main(String[] args)
{
int i = 0;
aussen:
// hier soll keine Anweisung stehen
for (; true; )
// Endlos-Schleife
{
innen:
// hier soll keine Anweisung stehen
for (; i < 10; i++)
{
ausgabe("i = " + i);
if (i == 2)
{
ausgabe("continue");
continue;
}
if (i == 3)
{
ausgabe("break");
i++;
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
111
Programmieren in Java
continue innen
i = 5
continue innen
i = 6
continue innen
i = 7
continue innen
i = 8
break aussen
*/
2. Marken im Zusammenhang mit „while“
public class MarkiertesWhile
{
public static void main(String[] args)
{
int i = 0;
aussen:
while (true)
{
ausgabe("Aeussere While-Schleife");
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
112
Programmieren in Java
*/
return-Anweisung
Alle Funktionen haben einen eindeutigen Typ, der gleichzeitig Typ des
Rückgabewerts ist. Mögliche Typen für die Rückgabe sind primitive Typen,
Datenfelder (Arrays), Klassen, Schnittstellen. Es gibt einen speziellen Typ für eine
Funktion ohne Rückgabewert: „void“.
Der Rückgabewert ist der Wert einer return-Anweisung.
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.
113
Programmieren in Java
2.5 Klassen
Klassen definieren Zustand und Verhalten von Objekten. Jedes Java-Programm
besteht aus einer Sammlung von Klassen. Alle Klassen in Java haben eine
gemeinsame Oberklasse, die Klasse Object. Auch Java selbst (als
Entwicklungsplattform) ist aus Klassen aufgebaut, die mit dem JDK frei verfügbar
sind. Das eigentliche 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
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
114
Programmieren in Java
Veränderungen möglich sind. Die neue Klasse erbt alle Daten und Methoden, die in
der Originalklasse definiert wurden.
2.6 Methoden
2.6.1 Die Deklaration
Die Deklarationen von Methoden haben folgendes Aussehen87:
Zugriffsspezifizierer Modifizierer Returnwert NameMethode(Parameter)
throws Exceptionliste
Methodenunterschrift88. 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
87
88
Kursiv Geschriebenes ist optional
Oft wird für denselben Zusammenhang der Begriff „methodensignatur“ verwendet.
115
Programmieren in Java
dieser Methode überschreiben. Methoden, die auf keinem Fall geändert werden
sollen, sollten deshalb immer als final deklariert werden.
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 89 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.
89
Nicht nur primitive Datentypen, sondern auch komplexe Objekte.
116
Programmieren in Java
3. Das Arbeiten mit Objekten und Klassen
3.1 Ereignisbehandlung unter grafischen Benutzeroberflächen
Mit Hilfe einfacher Benutzerschnittstellen Graphical User Interface (GUI))
lassen sich Ein- und Ausgaben90 bequem gestalten. Generell sollte Ein-/Ausgabe in
Java über ein "GUI" in Abhängigkeit von den vom Betriebssystem registrierten
Ereignisarten und Zustandsänderungen erfolgen.
3.1.1 Gestaltung von GUI mit Hilfe der AWT-Klassen
Java enthält ein einfach zu bedienendes System für Gestaltung grafische
Benutzeroberflächen: das Abstract Windowing Toolkit (AWT). Die Fähigkeiten des
AWT umfassen:
- Grafische Operationen zum Zeichnen von Linien oder Füllen von Flächen und zur Ausgabe von Text
- Methode zur Steuerung des Programmablaufs auf der Basis von Nachrichten für Tatstatur-, Mausund Fensterereignisse.
- Dialogelemente zur Kommunikation mit dem Anwender und Funktion zum Design von Dialogboxen.
- Grafikfunktionen zur Darstellung von Bitmaps und Ausgabe von "Sound".
Zum Einbinden der Grafikfähigkeiten dient die Anweisung
import java.awt.*;
zu Beginn der Klassendefinition. Danach stehen alle Klassen aus dem Paket
java.awt zur Verfügung.
Zur Ausgabe von grafischen Elementen benötigt eine Anwendung ein Fenster, das
eine Applikation (im Gegensatz zu einem Applet) selbst erzeugen muß. Das AWT
enthält verschiedene Fensterklassen:
Panel
Component
Applet
Container
Window
Dialog
FileDialog
Frame
Component
Container
Panel
90
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
Standardein- und Standardausgabe spielen in Java nur im Rahmen des „Debugging“ eine Rolle.
117
Programmieren in Java
Applet
Window
Frame
Dialog
FileDialog
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
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.
Bsp.: 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 ineinem 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
118
Programms
zeigt
das
folgende
Programmieren in Java
Das vorliegende Beispiel zeigt noch einige Mängel:
- Zwar kann in das für die Eingabe vorgesehene Textfeld ein String eingegeben werden, das zweite
Textfeld ist allerdings nicht zugänglich. Es passiert somit nicht gerade viel. Es werden noch keine
Ereignisse aufgefangen und behandelt.
- Noch nicht einmal kann das Fenster geschlossen werden, und die Applikation muß (nach dem
Schließen des Frame) mit "CTRL-C" explizit abgebrochen werden.
3.1.2 Ereignisbehandlung unter grafischen Benutzeroberflächen
Im Mittelpunkt der Programmierung unter einer GUI steht die Kommunikation
zwischen System und Anwendungsprogramm. Die Anwendung wird über alle Arten
von Ereignissen und Zustandsänderungen vom System durch Versenden von
Nachrichten (z.B. über Mausklick, Tastatureingaben, Veränderungen an Größe und
Lage der Fenster) informiert. Die Reaktion auf die Nachrichten erfolgt in spezielllen
Ereignisempfängern (EventListeners), die das zum Ereignis passende EmpfängerInterface implementieren. Damit ein Ereignisempfänger Nachrichten einer
bestimmten Ereignisquelle erhält, muß er sich bei der Quelle registrieren lassen,
d.h.: Es muß eine EventListener-Klasse geschrieben, instanziert und bei der
Ereignisquelle registriert werden. Zum Empfang von Nachrichten muß ein Objekt
eine Reihe von Methoden implementieren, die von der Nachrichtenquelle, bei der es
sich registriert hat, aufgerufen werden können. Die Ereignisempfänger stellen diese
Methoden durch Implementierung von Interfaces bereit, die aus der Klasse
EventListener des Pakets java.util abgeleitet sind.
Ereignistypen. Im JDK 1.1 werden Ereignistypen durch eine Hierarchie von
Ereignisklassen repräsentiert, die alle aus der Klasse java.util.EventObject91
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
91
Speichert das Objekt, das die Nachricht ausgelöst hat und gibt durch Aufruf von "public Object getSource()"
das Objekt an.
119
Programmieren in Java
Ereignisklassen des AWT, die sich im Paket java.awt.event befinden. Dieses
Paket ist in jede Klasse einzubeziehen, die sich mit dem Event-Handling von AWTAnwendungen beschäftigt.
EventListener-Interface. Je Ereignisklasse gibt es ein EventListener-Interface. Es
definiert eine seperate Methode für jede Ereignisart der Ereignisklasse. So besitzt
bspw. das Interface MouseListener die Methoden mouseClicked,
mouseEntered, mouseExited, mousePressed und mouseReleased, die beim
Eintreffen des jeweiligen Ereignis aufgerufen werden.
EventListener
FocusListener
ActionListener
AdjustementListener ItemListener
KeyListener
MouseListener
MouseMotionListener
ComponentListener
ContainerListener
WindowListener
Abb.: Hierarchie der EventListener-Interfaces
Jede der Methoden eines Listener-Interface enthält als einziges Argument ein Objekt
vom zugehörigen Ereignistyp. Alle Methoden sind vom Typ void.
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.
120
Programmieren in Java
Darüber kann in das Eingabetextfeld eingegebener Text in das Ausgabefeld ausgegeben
werden. Da die "Methode actionPerformed" der anonymen inneren Klasse auf
"eingabeTextFeld" bzw. "ausgabeTextFeld" zugreift, ist außerhalb von main()
anzugeben:
private static TextField eingabeTextFeld = new TextField(20);
private static TextField ausgabeTextFeld = new TextField(20);
Zum Schließen des Fensters wird über eine anonyme innere Klasse ein
WindowAdapter (eine Art WindowListener) mit der Methode "public void
windowClosing(WindowEvent e)" definiert und an den "Frame" eingekettet.
Wird der Frame geschlossen, dann wird diese Methode aufgerufen, die über
System.exit(0) die Applikation beendet.
Eine Adapterklasse implementiert ein Interface mit mehreren Mehoden und erlaubt
es somit abgeleiteten Klassen, nur noch die Methoden zu überlagern, die tatsächlich
von Interesse sind. Passende Adapterklasssen stellt das Paket java.awt.event bereit,
z.B. FocusAdapter, KeyAdapter, MouseAdapter, MouseMotionAdapter,
ComponentAdapter, ContainerAdapter, WindowAdapter. Die Registrierung
erfolgt mit der Methode addWindowListener, die in den Klassen Dialog und
Frame zur Verfügung steht.
Bsp.:
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);
}
});
// 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);
121
und
Programmieren in Java
panel.add(ausgabeTextFeld);
// Frame fenster = new Frame("Echo");
fenster.add(panel);
fenster.pack();
fenster.setVisible(true);
}
}
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()
{
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();
122
Programmieren in Java
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
componentResized
Bedeutung
Eine Komponente wurde sichtbar
Eine Komponente wurde unsichtbar
Eine Komponente wurde verschoben
Die Größe der Komponente hat sich verändert
Abb.: Übersicht zu den Methoden von ComponentListener
Window-Events
Ein WindowEvent wird generiert, falls sich am Status eines Fensters eine Änderung
ergeben hat, die für das Anwenderprogramm interessant sein könnte. Ein Empfänger
für Window-Events muß das Interface WindowListener implementieren.
WindowEvent stellt neben getID, getSource die Methode public Window
123
Programmieren in Java
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.
Ereignismethode
mousePressed
mouseReleased
mouseClicked
mouseEntered
mouseExited
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.
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.
124
Programmieren in Java
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
92geerbten Methoden
public
public
public
public
boolean
boolean
boolean
boolean
isShiftDown();
isControlDown();
isMetaDown();
isAltDown();
zur Verfügung.
MouseMotionEvents
Sie geben Auskunft über die Bewegung des Mauszeigers. Ein Empfänger für
"MouseMotionEvents" muß das Interface MouseMotionListener implementieren.
Es wird mit public void addMouseMotionListener(MouseListener l)
registriert. Die Methode steht allen Objekten der Klasse Component oder daraus
abgeleiteteter Klassen zur Verfügung. Die Methoden von MouseMotionListener
bekommen Events des Typs MouseEvent übergeben. Damit stehen diesselben
Methoden wie bei MouseEvent zur Verfügung.
Das Interface MouseMotionListener definiert
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 void focusGained(FocusEvent e)
// Aufruf, wenn die Komponente den Fokus erhält
public abstract void focusLost(FocusEvent e)
92
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
125
Programmieren in Java
// Aufruf, wenn 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);
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.
keyTyped93
getKeyCode
Zeichentaste94: VK_UNDEFINED
Funktionstaste95: -
93
getKeyChar
Zeichentaste: Taste als char
Funktionstaste: -
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
94
126
Programmieren in Java
keyPressed96
Zeichentaste: VK_...
Funktionstaste: VK_...
Zeichentaste: Taste als char
Funktionstaste: CHAR_UNDEFINED
Rückgabecode bei Tastatur-Ereignissen
Zusätzlich stehen fogende aus InputEvent geerbten Methoden zur Verfügung:
public
public
public
public
boolean
boolean
boolean
boolean
isShiftDown();
isControlDown();
isMetaDown();
isAltDown();
Bsp.: Das folgende Programm 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());
}
}
class KL implements KeyListener
{
public void keyPressed(KeyEvent e)
{
95
96
Dazu gehören bspw. F1, F2, Pos 1 aber auch die Umschalttasten: STRG, ALT, UMSCHALT
zeigt das Verhalten beim Aufruf von keyPressed
127
Programmieren in Java
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());
}
}
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);
}
}
Die Lösung zu dieser Aufgabe kann auch über
public void processEvent(AWTEvent e)
128
Programmieren in Java
erhalten werden. 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.
Bsp.: Das folgende Programm schreibt alle möglichen Ereignisse zu Low-LevelEvents auf.
import java.awt.*;
import java.awt.event.*;
public class PR14167 extends Frame
{
Display ausgabe = new Display();
PR14167()
{
// 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)
{
PR14167 f = new PR14167();
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
129
Programmieren in Java
});
f.setSize(200,200);
f.setVisible(true);
}
}
130
Programmieren in Java
3.2 Kommunikation Anwender – Programm über Dialoge bzw.
Menüs mit vor- oder freidefinierten Dialogelementen
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 Dialoge97" 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 Icon 98
zuordnen.
Zuordnen eines Layoutmanagers. Es wird über die Methode "public void
setLayout(LayoutManager mgr)" der Klasse Container realisiert. Java stellt
fünf Layoutmanager99 bereit: FlowLayout BorderLayout, GridLayout
GridBagLayout und CardLayout.
Neben den Fähigkeiten eines Layoutmanagers bestimmt in der Regel die
Reihenfolge der Aufrufe von der "add"-Methode des Fensters die tatsächliche
Anordnung der Komponenten auf dem Bildschirm.
Schachteln von Layoutmanagern. Dazu wird an die Stelle, die das Sublayout
erhalten soll, einfach ein Objekt der Klasse Panel eingefügt, das einen eigenen
Layoutmanager erhält. Dieses Panel kann mit Dialogelementen bestückt werden, die
entsprechend dem zugeordneten Sublayout formatiert werden, z.B.:
import java.awt.*;
import java.awt.event.*;
public class PR14171 extends Frame
{
public PR14171()
97
Modale Dialaloge verhindern die Interaktion des Anwenders mit anderen Fenstern der Anwendung bis zum
Schließen des Dialogfensters.
98 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.
99 Vgl. 5.4.2
131
Programmieren in Java
{
addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
setVisible(false);
dispose();
System.exit(0);
}
});
// Layout festelegen und Komponenten hinzufuegen
int i = 0;
Panel p1 = new Panel();
p1.setLayout(new GridLayout(3,1));
p1.add(new Button("Schaltflaeche " + ++i));
p1.add(new Button("Schaltflaeche " + ++i));
p1.add(new Button("Schaltflaeche " + ++i));
Panel p2 = new Panel();
p2.setLayout(new BorderLayout());
p2.add("North",new Button("Schaltflaeche " + ++i));
p2.add("South",new Button("Schaltflaeche " + ++i));
p2.add("West",new Button("Schaltflaeche " + ++i));
p2.add("East",new Button("Schaltflaeche " + ++i));
p2.add("Center",new Button("Schaltflaeche " + ++i));
// Hauptfenster
setLayout(new GridLayout(1,2));
add(p1);
add(p2);
pack();
}
public static void main(String args[])
{
PR14171 f = new PR14171();
f.setVisible(true);
}
}
Das vorliegende Programm zeigt nach dem Aufruf das folgende Fenster:
Einfügen von Dialogelementen. Es erfolgt über
public Component add(Component komponente)
public Component add(Component komponente, int pos)
public Component add (String name, Component komponente)
// erwartet einen String-Parameter, der bei bestimmten Layout-Managern
// (z.B. BorderLayout) Informationen zur Positionierung der Elemente ("bei
// BorderLayout: "South", "East", "West", "North", "Center") angibt.
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.
132
Programmieren in Java
Modale Dialoge
Die Klasse Dialog100 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.
Dateidialog
Für den Zugriff auf das Dateisystem verwendet man zweckmäßigerweise das
Dateidialogfeld.
Bsp.: Laden und Speichern von Dateien mit Unterstützung durch ein Dateidialogfeld.
import java.awt.*;
import java.awt.event.*;
public class DateiDialog extends Frame
{
TextField dateiName = new TextField();
TextField verzeichnis = new TextField();
Button oeffnen = new Button("Oeffnen");
Button sichern = new Button("Sichern");
public DateiDialog()
{
setTitle("Dateidialog-Test");
Panel p = new Panel();
p.setLayout(new FlowLayout());
oeffnen.addActionListener(new OeffnenL());
p.add(oeffnen);
sichern.addActionListener(new SichernL());
p.add(sichern);
add(p,BorderLayout.SOUTH);
verzeichnis.setEditable(false);
dateiName.setEditable(false);
p = new Panel();
p.setLayout(new GridLayout(2,1));
p.add(dateiName);
p.add(verzeichnis);
add(p,BorderLayout.NORTH);
}
class OeffnenL implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
FileDialog d = new FileDialog(DateiDialog.this,
"Welche Datei soll geoeffnet werden?");
d.setFile("*.java");
d.setDirectory(".");
// Aktuelles Verzeichnis
d.show();
String datei = "*.*";
if ((datei = d.getFile()) != null)
{
dateiName.setText(datei);
verzeichnis.setText(d.getDirectory());
}
else {
dateiName.setText("Cancel wurde gedrueckt!");
verzeichnis.setText("");
}
100
Vgl. 5.3.4
133
Programmieren in Java
}
}
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);
}
}
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, besizen Größe und
Position und sind in der Lage, Nachrichten zu empfangen und zu bearbeiten.
134
Programmieren in Java
Component
Button
TextComponent
TextField
Container
TextArea
Menu
Panel
Applet
MenuComponent
MenuBar
Checkbox
MenuItem
Window
Frame
Dialog
Abb.: Komponenten des AWT
Vordefinierte Dialogelemente unter den Komponenten des AWT sind:
Labels101, Schaltflächen(Buttons)102, Kontrollkästchen und Optionsfelder103,
Auswahlmenüs104, Listenfelder105, Textbereiche und Textfelder106, Schieberegler107,
Das freidefinierte 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
Die in der Klasse Canvas definierte Methode paint(Graphics g) füllt die
Zeichenfläche lediglich mit Hintergrundfarbe. Subklassen, die wirklich zeichnen, sind
von Canvas abgeleitete Klassen, deren paint-Methode alle zu zeichnenden
101
vgl. 5.2.2
vgl. 5.2.1
103 vgl. 5.2.3
104 vgl. 5.2.4
105 vgl. 5.2.5
106 vgl. 5.2.6
107 vgl. 5.2.7
102
135
Programmieren in Java
Objekte zu jedem Zeitpunkt neu zeichnen können. Durch Überlagerung von public
void paint(Graphics g) sorgt eine Canvas-Komponente für die Darstellung auf
dem Bildschirm. Der Punkt (0,0) des übergebenen Graphics-Objekts entspricht
dabei der linken oberen Ecke des Ausgabebereichs.
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.
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 108.
108
vgl. 5.3.3
136
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
Darstellungssoftwre (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.
Das Grundgerüst einer HTML-Seite
Eine HTML-Seite wird immer in die Anweisung <html> am Anfang und </html> am
Ende eingeschlossen.
137
Programmieren in Java
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
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.
1. Das Erstellen eines Applets
Zum Erstellen eines Applet muß immer eine „Subklasse“ der Klasse Applet109
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
109
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.*
138
Programmieren in Java
überschieben werden. Die folgenden Methoden bestimmen den Lebenszyklus eines
Applet:
Rückkehr zur HTML-Seite
init()
start()
stop()
destroy()
Verlassen der HTML-Seite
Abb.: 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.
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-Klasse110 in den Code der Applet-Subklasse importiert111
wird.
Bsp.: „Aller Anfang ist schwer!. Dieser Spruch soll mit Hilfe eines Applet gezeigt
werden.. Die zugehörige Quellcodedatei „AllerAnfangApplet.java“ umfaßt 112:
110
Teil des Pakets java.awt
Normalerweise geschieht dies über: import java.awt.Graphics
112 vgl. PR32101
111
139
Programmieren in Java
import java.applet.*;
import java.awt.*;
public class AllerAnfangApplet extends Applet
{
Font f;
String spruch;
//
public void init()
{
f = new Font("Helvetica",Font.BOLD,24);
this.spruch = "Aller Anfang ist schwer!";
}
//
public void paint(Graphics g)
{
// Oval mit Farbe yellow
g.setColor(Color.yellow);
g.fillOval(10,10,330,100);
// Roter Rahmen; da Java keine Linienbreite kennt,
// wird die Linienbreite durrch 4 Ovale, (die sich
// um Pixelbreite unterscheiden,) simuliert
g.setColor(Color.red);
g.drawOval(10,10,330,100);
g.drawOval( 9, 9,332,102);
g.drawOval( 8, 8,334,104);
g.drawOval( 7, 7,336,106);
g.setColor(Color.black);
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:
140
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 gezeichnet113 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.
public void update(Graphics g)
Sie wird von repaint() aufgerufen. Die „update“-Methode löscht den vollständigen
Zeichenbereich114 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.
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 Methoden zur Verfügung. So
kann bspw. das Drücken einer Maustaste über die Methode public boolean
mouseDown(Event e, int x, int y) Aktionen auslösen. Allerdings muß dann
die Methode mouseDown() überschrieben werden. „Event“ ist die Ereignisklasse,
die Informationen über das Ereignis enthält. „x“ und „y“ sind die Koordinaten, bei
denen die Maustaste gedrückt wurde.
Bsp.115: Ein Applet zum Zeichnen von Punkten an den Stellen, an denen eine
Maustaste gedrückt wurde.
113
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.
114 Das ruft oft den unangenehmen Flimmereffekt bei schnellen Bildsequenzen hervor.
115 PR32305
141
Programmieren in Java
// Import der Pakete
import java.awt.*;
// Top-Level Klassen-Deklaration des Applets
public class MausDownPunktApplet extends java.applet.Applet
{
// Variablen-Deklarationen
private int mausX, mausY;
private boolean mausKlick = false;
// Methoden, die ueberschrieben werden
public void init()
{
setBackground(Color.yellow);
}
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);
}
wird der Anzeigebereich nicht mehr geleert.
// Import der Pakete
import java.awt.*;
// 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);
}
public boolean mouseDown(Event e, int x, int y)
{
mausX = x; mausY = y;
mausKlick = true;
repaint();
return true;
}
// Ausgabemethode
public void paint(Graphics g)
142
Programmieren in Java
{
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.
Mausaktionen: Es gibt sechs mögliche Mausaktionen:
- Die Maustaste wird gedrückt
- Die Maustaste wird wieder losgelassen
- Die Maus wird mit gedrückter Maustaste bewegt (Drag)
- Die Maus wird mit losgelassener Maustaste bewegt
- Der Mauszeiger verläßt den Bereich des Applets
- Der Mauszeiger kommt (wieder) in den Bereich des Applets
Für jedes Ereignis stellt Java eine Methode116 zur Verfügung, die zur Applet-Klasse
gehört:
public
public
public
public
public
public
boolean
boolean
boolean
boolean
boolean
boolean
mouseDown(Event e, int x, int y)
mouseUp(Event e, int x, int y)
mouseEnter(Event e, int x, int y)
mouseExit(Event e, int x, int y)
mouseDrag(Event e, int x, int y)
mouseMove(Event e, int x, int y)
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 aktualisiert117. Nach den
bisher vorliegenden Erkenntnissen müßte das zugehörige Applet folgende Gestalt haben:
import java.awt.Graphics;
import java.awt.Font;
Die hier angegebenen Methoden werden von Sun als „depricated“ (veraltet) bezeichnet, da sie aus dem
Eventmodell 1.0 stammen. Sie sind aberlange noch nicht überflüssig.
117 vgl. PR42001
116
143
Programmieren in Java
import java.util.Date;
//
// Top Level Deklaration des Applets
//
public class DigitalUhr extends java.applet.Applet
{
// Variablen-Deklaration
Font einFomt = new Font("Tmes Roman",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(einFomt); // Setzen des aktuellen Font
g.drawString(datum.toString(),10,50); // Ausgabe Datum
// Da paint() wiederholt mit jeweils dem aktuellen Wert von
// "datum" aufgerufen wird, wird die Zeichenkette jede Sekunde
//zur Ausgabe des neuen Datums aufgerufen
}
}
In der start()-Methode nimmt die while-Schleife alle Systemressourcen für sich in Anspruch
(einschl. der Anzeige am Bildschirm). Deshalb funktioniert die digitale Uhr nicht. Außerdem kann
das Applet nicht gestoppt werden, da die stop()-Methode nicht aufgerufen werden kann. Die
Lösung des Problems liegt im erneuten Schreiben des Applets mit Threads. Das Applet muß
dazu mit den vorgegebenen vier Arbeitsschritten erweitert werden.
import
import
import
//
// Top
//
public
java.awt.Graphics;
java.awt.Font;
java.util.Date;
Level Deklaration des Applets
class DigitalThreadUhr extends java.applet.Applet
implements Runnable
{
// Variablen-Deklaration
Font einFomt = new Font("Tmes Roman",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)
{
144
Programmieren in Java
faden.stop();
faden = null;
}
}
public void run()
{
// 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(einFomt); // 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
}
}
3.3.4 Eine Applet-Schablone
3.3.5 Die Applet-Klasse
Die Methoden der Applet-Klasse erlauben Applets
- die Handhabung von Parametern über die Methoden:
public String getParameter(String name)
public String[][]getParameterInfo()
- Das Importieren von Bildern
Dazu werden folgende Methoden aus dem Image/Graphics-Paket genutzt:
public Image getImage(URL url, String name)
Die Methode lädt Bilddateien in ein Applet.
- das Importieren und Abspielen von Sound-Clips
- die Interaktion des Applets mit der Umgebung (Browser, Appletviewer) und Verwaltung des
Lebenszyklus eines Applets
145
Programmieren in Java
3.4 Behälter (Container, Collection)
146
Programmieren in Java
4. Grafik und Animation
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 Methoden118 zum Neuzeichnen des
Applet:
public void paint(Graphicds g)
public void repaint()
public void update(Graphics g)
4.1 Allgemeine Zeichenvorgänge
4.1.1 Punkte, Linien, Kreise, Bögen
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.
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 Linien119
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
118
119
vgl. 3.2.2
vgl. PR42005
147
Programmieren in Java
public void init()
{
setBackground(Color.lightGray);
}
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
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).
Bsp.: Ein Applet mit zufällig verteilten Rechtecken120
// zeichne Rechtecke
import java.applet.*;
import java.awt.*;
public class RechteckeAppl3 extends Applet
120
vgl. PR41103
148
Programmieren in Java
{
// 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);
}
}
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.
149
Programmieren in Java
Zeichnen abgerundeter, gefüllter Rechtecke. Dafür existiert die Methode public
abstract void fillRoundRect(int x, int y, int width, int
height, int arcWidth, int arcHeight).
Zeichnen von Polygonen. Hierfür kann die Methode public abstract void
drawPolygon(int[]
xPunkte,
int[]
yPunkte,
int
nPunkte)
herangezogen werden. Es gibt zwei Möglichkeiten beim Zeichnen von Polygonen:
- Weitergabe der beiden Datenbereiche (Arrays) mit den x- und y-Koordinaten der Punkte, z.B.121:
import java.awt.*;
public class ZeichnePolyAppl1 extends java.applet.Applet
{
// Instanzvariable
// Definition des Felds mit den x-Koordinaten
int xKoord[] = {20,50,70,40,20,20};
// Definition des Felds mit den y-Koordinaten
int yKoord[] = {30,10,20,70,50,30};
// Methoden
public void init() { setBackground(Color.yellow); }
public void paint(Graphics g)
{
// Zeichne ein 5-Eck
g.setColor(Color.red);
g.drawPolygon(xKoord,yKoord,6);
}
}
- Weitergabe einer Instanz der Polygon-Klasse, z.B.122:
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.123:
import java.awt.*;
public class ZeichnePolyAppl3 extends java.applet.Applet
{
121
PR41105
PR41105
123 PR41105
122
150
Programmieren in Java
// 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);
}
}
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) vertikalen 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)
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
Abb.: Gebräuchliche Farbwerte
151
Blau-Anteil
255
0
127
0
0
255
0
255
255
Programmieren in Java
Die Color-Klasse. Auf drei Arten kann eine Farbe erzeugt werden:
public Color(int red, int green, int blue)
Damit wird eine Farbe mit Rot-, Grün- und Blau-Werten zwischen 0 und 255 erzeugt.
public Color(int rgb)
public Color(float red, float green, float blue)
Eine gängige Farbe kann schneller über die Standardfarbobjekte der in der Color-Klasse definierten
verschiedenen Klassenvariablen gewonnen werden, z.B.:
public
public
public
public
public
public
public
public
public
public
public
public
public
static
static
static
static
static
static
static
static
static
static
static
static
static
Color
Color
Color
Color
Color
Color
Color
Color
Color
Color
Color
Color
Color
white;
lightGray;
gray;
darkGray;
black;
red;
blue;
green;
yellow;
magenta;
cyan;
orange;
pink;
Ermitteln der RGB-Werte von einem bestimmten Farbobjekt. Es erfolgt über
public int getRed();
public int getGreen();
public int getBlue();
Setzen von Farben. Es wird möglich durch die Methode: public abstract
void setColor(Color c). Der Parameter bestimmt das gewünschte Farbobjekt.
Setzen von Hintergrundfarben. Normalerweise ist die Hintergrundfarbe eines Applets
weiß oder dunkelgrau (je nach Container). Individuell kann die Hintergrundfarbe
eines Applets gesetzt werden durch: public 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 FontKlasse124 und die Fontmetrics-Klasse125 beim Textzeichnen eine Rolle.
4.1.4 Die Java-Zeichenmodi
124
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.
125
152
Programmieren in Java
Die Graphics-Klasse verfügt über zwei verschiedene Modi zum Zeichnen: den Paint-Modus
und den XOR-Modus.
4.1.5 Das Zeichnen von Bildern
Es umfaßt die Schritte: Das Laden von Bildern und das Ausgeben von Bildern.
Das Laden von Bildern mit der Methode: public Image getImage(URL url). Die
Methode126 lädt ein Bild und erstellt automatisch eine Instanz der Image-Klasse.
Das Anzeigen von Bildern mit der drawImage()-Methode
126
Die Methode gehört zur Applet-Klasse
153
Programmieren in Java
4.2 Animation
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.
4.2.1 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
4.2.2 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;
// Methoden
public void init()
{
setBackground(Color.yellow);
}
public void start()
{
154
Programmieren in Java
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);
}
}
4.2.3 Reduktion von Flimmereffekten in Animationen
Überschreiben der update()-Methode
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;
155
Programmieren in Java
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;
}
}
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;
}
}
Double Buffering
Mit „double buffering“ wird eine zweite Oberfläche geschaffen, in der alles
vorgezeichnet und dann auf einmal in die Zeichnungsoberfläche des Applet
ausgegeben wird.
156
Programmieren in Java
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;
//
int xStart = 150, yStart = 20;
// Radius des Pendels
double r = (double) 200;
// Durchmesser des Balls
int d = 20;
Thread faden;
int xAlt, yAlt;
Image backgroundImage;
Graphics backgroundGraphics;
// Methoden
public void init()
{
setBackground(Color.white);
backgroundImage = createImage(this.size().width,
this.size().height);
backgroundGraphics = backgroundImage.getGraphics();
}
public void start()
{
if (faden == null)
{
faden = new Thread(this);
faden.start();
}
}
public void stop()
{
if (faden != null)
{
faden.stop();
faden = null;
}
}
public void run()
{
while (true)
{
x = xStart + (int)(r * Math.sin(theta));
y = yStart + (int)(r * Math.cos(theta));
if ((theta >= thetaMax) | (theta <= thetaMin))
wechsel = -wechsel;
theta += wechsel;
repaint();
try { Thread.sleep(10); }
catch(InterruptedException e) {}
}
}
public void update(Graphics g)
{
paint(g);
}
public void paint(Graphics g)
{
157
Programmieren in Java
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;
}
}
4.3 Das neue 2D API
158
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 Komponenten127 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
127
Java 1.2 und das Swing-Konzept erweitern diese Komponenten noch einmal um einen satz von neuen und
ergänzenden Komponenten.
159
Programmieren in Java
5.2.1 Schaltflächen (Buttons)
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 Applet128 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,
kann es in der Button-Klasse durch "public void setActionCommand(String
kommando)" geändert werden.
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)
128
Abgeleitet von der Panel-Klasse
160
Programmieren in Java
Mit dem instanceof-Operator kann die event.target-Variable überprüft
werden, ob die Aktion auch für das Objekt erfolgt, das gewünscht wird.
Bsp.: Setzen von Hintergrundfarben nach verschiedenen Buttonklicks 129
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()
129
vgl. PR52105
161
Programmieren in Java
// Ausrichten der Textzeiele
5.2.3 Kontrollkästchen und Optionsfelder
Erzeugen von Kontrollkästchen. Ein Kontrollkästchen (Checkbutton / Checkbox)
hat zwei Bestandteile: ein Label (Text, der neben dem Kontrollkästchen angelegt
wird) und einem Zustand (boolesche Variable, die angibt, ob die Box eingeschaltet
wurde oder nicht130). 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.
Bsp.:
import java.awt.*;
import java.awt.event.*;
public class PR52301 extends Frame
{
Checkbox
cb1 = new Checkbox("Checkbox 1"),
130
Standardmäßig ist die Box ausgeschaltet (Wert ist false oder „off“).
162
Programmieren in Java
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
public Checkbox getCurrent()
public void setCurrent(Checkbox box)
Bsp.:
import java.awt.*;
import java.awt.event.*;
public class PR52302 extends Frame
163
Programmieren in Java
{
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");
}
}
}
}
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.
164
Programmieren in Java
Bsp.131:
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;
}
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.
131
PR54305
165
Programmieren in Java
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.
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);
166
Programmieren in Java
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:
nahezu
dieselbe
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();
167
Programmieren in Java
}
public class ILL implements ItemListener
{
public void itemStateChanged(ItemEvent e)
{
List lst = (List) e.getItemSelectable();
String str = lst.getSelectedItem();
int pos = ((Integer) e.getItem()).intValue();
System.out.println("event.getSelectedItem: " + str + " " + pos);
}
}
class ALL implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
Object obj = e.getSource();
if (obj instanceof List)
{
System.out.println("Listenfeld-Aktion: "
+ e.getActionCommand());
}
}
}
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 greite)
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)
168
Programmieren in Java
Über den Parameter text kann eine Zeichenkette vorgegeben werden, die beim aufruf des Textfelds
vorgelegt wird.
public TextField(String text, int breite)
Methoden zum Zugriff bzw. Verändern von Textfeldern.
public String getText();
public void setText(String text)
Mit
public int Columns()
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.
Berabeiten 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,
169
Programmieren in Java
getSource. Typischerweise wird innerhalb von textValueChanged mit
getSource das zugehörige Textfeld beschafft und mit getText auf seinen Inhalt
zugegriffen.
Bsp.:
import java.awt.*;
import java.awt.event.*;
public class PR14170 extends Frame
{
Button
b1 = new Button("Hole Text"),
b2 = new Button("Setze Text");
TextField
t1 = new TextField(30),
t2 = new TextField(30),
t3 = new TextField(30);
String s = new String();
public PR14170()
{
setLayout(new FlowLayout());
b1.addActionListener(new B1A());
b2.addActionListener(new B2A());
t1.addTextListener(new T1());
t1.addActionListener(new T1A());
add(b1);
add(b2);
add(t1);
add(t2);
add(t3);
}
class T1 implements TextListener
{
public void textValueChanged(TextEvent e)
{
t2.setText(t1.getText());
}
}
class T1A implements ActionListener
{
private int zaehler = 0;
public void actionPerformed(ActionEvent e)
{
t3.setText("t1 ActionEvent " + zaehler++);
}
}
class B1A implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
s = t1.getSelectedText();
if (s.length() == 0) s = t1.getText();
t1.setEditable(true);
}
}
class B2A implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
t1.setText("Eingefuegt durch Button 2: " + s);
t1.setEditable(false);
}
}
public static void main(String args[])
{
PR14170 f = new PR14170();
170
Programmieren in Java
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.
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
171
Programmieren in Java
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
Scrollbar
sendet
Adjustment-Ereignisse
an
seine
Ereignisempfänger. Diese müssen das Interface AdjustmentListener
implementieren
und
sich
durch
Aufruf
von
public
void
addAdjustmentListener(AdjustmentListener
l)
registrieren.
Das
Adjustment-Ereignis führt im Ereignisempfänger zum Aufruf der Methode:
public abstract void adjustmentValueChanged(AdjustmentEvent e),
die ein AdjustmentEvent übergeben bekommt. Dieses besitzt die Metode
getAdjustable(), mit der der auslösende Scrollbar bestimmt werden kann.
Zusätzlich gibt es die Methode "getAdjustmentType", die Auskunft darüber gibt,
welche Benutzeraktion zur Auslösung des Ereignisses führte.
172
Programmieren in Java
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();
}
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:
173
Programmieren in Java
System.out.println("Adjustment.UNIT_INCREMENT");
break;
case AdjustmentEvent.UNIT_DECREMENT:
System.out.println("Adjustment.UNIT_DECREMENT");
break;
case AdjustmentEvent.BLOCK_INCREMENT:
System.out.println("Adjustment.BLOCK_INCREMENT");
break;
case AdjustmentEvent.BLOCK_DECREMENT:
System.out.println("Adjustment.BLOCK_DECREMENT");
break;
case AdjustmentEvent.TRACK:
System.out.println("Adjustment.TRACK");
break;
}
System.out.println("Wert: " + e.getValue());
}
}
public static void main(String args[])
{
PR14175 f = new PR14175();
f.addWindowListener(
new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
f.setBackground(Color.lightGray);
f.setVisible(true);
}
}
5.2.8 ScrollPane
Ein ScrollPane ist ein Container für automatisches horizontales und vertikales
Scrolling. ScrollPane unterscheidet sich durch zwei Eigenschaften von einem
gewöhnlichen Panel:
- Es kann genau ein Dialogelement aufnehmen und benötigt keinen eigenen Layoutmanager
- Es ist in der lage, eine virtuelle Ausgabefläche zu verwalten, die größer ist als die auf dem Bildschirm
zur Verfügung stehende Ausgabefläche.
Die von einem ScrollPane angezeigte Komponente arbeitet mit einer virtuellen
Ausgabefläche ( und merkt nichts von evtl. Größenbeschränkungen auf dem
Bildschirm). Falls die benötigte Ausgabefläche größer ist als die anzeigbare, blendet
ein ScrollPane automatisch die erforderlichen Schieberegister ein, um
Dialogelemente horizontal und vertikal verschieben zu können.
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
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
174
Programmieren in Java
ScrollPane.SCROLLBARS_NEVER
Die Schieberegler werden nie angezeigt, der
Bildschirm kann nur vom Programm aus verschoben
werden
5.2.9 Zeichenbereiche
Ein Canvas ist ein frei definiertes Dialogelement, das in der Grundversion praktisch
keine Funktionalität zur Verfügung stellt. Damit ein Canvas etwas Sinnvolles tun
kann, muß daraus eine eigene Klasse abgeleitet werden.
Konstruktor: public Canvas()
Die Methode paint(Graphics g). Durch Überlagerung von public void
paint(Graphics g) sorgt eine Canvas-Komponente für die Darstellung auf dem
Bildschirm. Die vom System bereitgestellte Version zeichnet nur die Ausgabefläche
in der aktuellen Hintergrundfarbe. Eine überlagerte Version kann hier natürlich ein
beliebig komplexes Darstellungsverhalten erzielen. Der Punkt (0,0) des übergebenen
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();
}
public void paint(Graphics g)
{
System.out.println(
"Methode paint wurde aufgerufen, zeichneObjekte = "
+ zeichneObjekte);
175
Programmieren in Java
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()
{
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);
176
Programmieren in Java
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();
}
}
177
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
gesetzt wird. Wird die Einstellung nicht geändert, werden für Vordergrund- und
Hintergrundfarbe die unter Windows eingestellten Standardfarben verwendet. Mit
178
Programmieren in Java
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()-Methode132. Mit public void hide()133 kann der Frame wieder
unsichtbar gemacht werden. Über public void dispose() wird der Frame
beendet. Mit public boolean isShowing() kann geprüft werden, ob das
Fenster bereits angezeigt ist.
Standardlayout für Fenster: Border-Layout.
Hinzufügen von Komponenten: Rahmen sind Container wie Panels, andere
Komponenten können mit der add()-Methode hinzugefügt werden.
5.3.3 Menüs
Jedes Fenster / Frame kann eine eigene Menüleiste besitzen. Jede Menüleiste kann
mehrere Menüs enthalten und jedes Menü beliebige Einträge. Java unterstützt die
Konstruktion von Menüs durch eine Reihe speziell dafür vorgesehenen Klassen:
Klasse
MenuBar
Menu
MenuItem
CheckboxmenuItem
Bedeutung
Beschreibt die Menüzeile (-leiste)eines Fensters
Beschreibet ein einzelnes, der in der Menüzeile enthaltenen Menüs
Bildet die vom Anwender auswählbaren Einträge innerhalb der Menüs
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)
132
Für show() aus dem Paket java.awt.Component gibt es als neue Variante die Methode
setVisible(boolean)
133 hide() ist in java.awt.Frame als „deprecated“ deklariert. Statt dessen steht setVisible(boolean) zur
Verfügung
179
Programmieren in Java
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)
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()
180
Programmieren in Java
Die Klasse CheckboxMenuItem. Sie ist aus MenuItem abgeleitet und besitzt
zusätzlich eine interne Zustandsvariable, die zwischen true und false umgeschaltet
werden kann. Die visuelle Darstellung der Zustandsvariablen erfolgt durch Anfügen
oder Entfernen eines „Häkchens“ neben dem Menüeintrag.
Die Instanzierung eines CkeckboxMenuItem erfogt wie bei einem MenuItem.
Zusätzlich stehen die beiden Methoden setState und getState zum Setzen und
Abfragen des Zustands zur Verfügung:
public void setState(boolean status)
public boolean getState()
Menüaktionen: Ein Menü macht nur Sinn, wenn damit eine Aktion ausgelöst werden
kann. Diese Aktion kann wie jede andere Aktion mit der Methode actionPerformed
behandelt werden.
Bsp.: Zusammenstellung der wesentlichen Elemente zur Gestaltung von Menüs
import java.awt.*;
import java.awt.event.*;
public class PR53331 extends Frame
{
// TextField t = new TextField("Keine Anzeige",30);
public PR53331()
{
// Menueleiste erzeugen
MenuBar mb = new MenuBar();
// Menueleiste an Rahmen verankern
setMenuBar(mb);
// Menuepunkt erzeugen
Menu m1 = new Menu("Farben"); // Menuepunkt 1
// Ein Untermenue erzeugen
// Menueeintraege in der Menuezeile plazieren
mb.add(m1);
// Eintraege in Menuepunkt 1
MenuItem rot = new MenuItem("Rot");
m1.add(rot);
rot.addActionListener(new RotL());
MenuItem blau = new MenuItem("Blau");
m1.add(blau);
blau.addActionListener(new BlauL());
MenuItem gruen = new MenuItem("Gruen");
m1.add(gruen);
gruen.addActionListener(new GruenL());
MenuItem gelb = new MenuItem("Gelb");
m1.add(gelb);
gelb.addActionListener(new GelbL());
// Untermenue erzeugen
Menu unterMenue = new Menu("Farbanpassung");
// 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"),
181
Programmieren in Java
// menu shortcut
new MenuItem("Exit",new MenuShortcut(KeyEvent.VK_E))
};
ML ml = new ML();
datei[0].setActionCommand("Open");
datei[0].addActionListener(ml);
datei[1].setActionCommand("Exit");
datei[1].addActionListener(ml);
for (int i = 0; i < datei.length; i++)
d.add(datei[i]);
mb.add(d);
// CheckboxMenuItem
Menu s = new Menu("Sicherheit");
CheckboxMenuItem[] sicherung =
{
new CheckboxMenuItem("Wache"),
new CheckboxMenuItem("Versteck")
};
CMIL cmil = new CMIL();
sicherung[0].setActionCommand("Wache");
sicherung[0].addItemListener(cmil);
sicherung[1].setActionCommand("Versteck");
sicherung[1].addItemListener(cmil);
for (int i = 0; i < sicherung.length; i++)
s.add(sicherung[i]);
d.add(s);
// Hilfsmenue
Menu meineHilfe = new Menu("Hilfe");
mb.setHelpMenu(meineHilfe);
// Eintraege im Hilfsmenue
meineHilfe.add(new MenuItem("Info"));
meineHilfe.add(new MenuItem("Hilfe"));
// Textfeld initialisieren
// t.setEditable(false);
// add(t,BorderLayout.CENTER);
}
class ML implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
MenuItem ziel
= (MenuItem) e.getSource();
String aktionsKommando = ziel.getActionCommand();
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());
}
182
Programmieren in Java
}
}
class RotL implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
setBackground(Color.red);
}
}
class BlauL implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
setBackground(Color.blue);
}
}
class GruenL implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
setBackground(Color.green);
}
}
class GelbL implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
setBackground(Color.yellow);
}
}
class FarbeL implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
System.out.println("Im Untermenue wurde Farbe ausgewaehlt");
}
}
class MonoL implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
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);
}
}
183
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.
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:
184
Programmieren in Java
- 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());
5.4.2 Die einzelnen Layout-Manager
Die FlowLayout-Klasse
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");
add(meinKnopf7);
}
}
mit den im Applet-Tag angegebenen Abmessungen
<HTML>
<HEAD>
<TITLE>FlowLayout-Test</TITLE>
</HEAD>
<BODY>
185
Programmieren in Java
<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
Die BorderLayout-Klasse unterteilt einen Container in fünf Bereiche: North, South,
East, West, Center. Falls einem Container Komponenten hinzugefügt werden,
geschieht das über ein spezielles Exemplar der add()-Methode ( mit einem dieser
fünf Bereichsnamen).
Bsp.: BorderLayout-Applet, das einem Applet einige Schaltfläche hinzufügt.
Der Quellcode ist
import java.awt.*;
import java.applet.*;
public class BorderLayoutTestApplet extends Applet
{
public void init()
{
186
Programmieren in Java
int i = 0;
setBackground(Color.yellow);
setLayout(new BorderLayout());
add("North",new Button("Schaltflaeche "
add("South",new Button("Schaltflaeche "
add("East",new Button("Schaltflaeche "
add("West",new Button("Schaltflaeche "
add("Center",new Button("Schaltflaeche "
+
+
+
+
+
i++));
i++));
i++));
i++));
i++));
}
}
Für das Applet wurde folgende Größe vereinbart:
<HTML>
<HEAD>
<TITLE>FlowLayout-Test</TITLE>
</HEAD>
<BODY>
<APPLET CODE="BorderLayoutTestApplet.class" WIDTH=500 HEIGHT=100>
</APPLET>
</BODY>
</HTML>
Das führt zu dem folgenden Layout:
Die GridLayout-Klasse
Diese Klasse teilt den Container in ein Gitter mit gleich großen Zellen ein.
Hinzugefügte Komponenten werden von links nach rechts angeordnet, begonnen
wird bei den linken, oberen Zellen.
Bsp.: Plazieren von Schaltflächen
Der folgende Quellcode
import java.awt.*;
import java.applet.*;
public class GridLayoutTestAppl extends Applet
{
public void init()
{
setLayout(new GridLayout(7,3));
for (int i = 0; i < 20; i++)
add(new Button("Schaltflaeche " + i));
}
}
führt über die folgende Größe im Applet-Tag
<HTML>
<HEAD>
<TITLE>FlowLayout-Test</TITLE>
</HEAD>
<BODY>
187
Programmieren in Java
<APPLET CODE="GridLayoutTestAppl.class" WIDTH=300 HEIGHT=200>
</APPLET>
</BODY>
</HTML>
zu der folgenden Darstellung
Die GridBagLayout-Klasse und die GridBagConstraint-Klasse
Im Gegensatz zur GridLayout-Klasse entscheidet hier der Layout-Manager über
die Anzahl der Zeilen und Spalten. 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.
Bsp.134: Plazieren von Schaltflächen
import java.awt.*;
import java.util.*;
import java.applet.Applet;
134vgl.
PR54205
188
Programmieren in Java
public class GridBagBsp extends Applet
{
protected void macheSchalter(String name,
GridBagLayout gridbag,
GridBagConstraints c)
{
Button schalter = new Button(name);
gridbag.setConstraints(schalter, c);
add(schalter);
}
public void init()
{
GridBagLayout gridbag = new GridBagLayout();
GridBagConstraints c = new GridBagConstraints();
setFont(new Font("Helvetica", Font.PLAIN, 14));
setLayout(gridbag);
c.fill = GridBagConstraints.BOTH;
c.weightx = 1.0;
macheSchalter("Schaltflaeche1", gridbag, c);
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();
}
189
Programmieren in Java
Die CardLayout-Klasse
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)
190
bestimmt
die
Anfangsposition
und
Programmieren in Java
5.5 Die Event-Modelle 1.0 und 1.1
5.5.1 Der AWT-Handler 1.0
Die Methode handleEvent
Die handleEvent()-Methode135 ist unter dem AWT-Event-Handler die
allgemeinste Art, wie das AWT auf irgendwelche Ereignisse eingeht, die auf der
Benutzeroberfläche stattfinden. Ereignisse werden innerhalb der handleEvent()Methode interpretiert und dann gezielt passende Methoden aufgerufen. Damit die
handleEvent()-Funktion nicht zu groß und unübersichtlich wird, ruft
handleEvent() mehrere Hilfsfunktionen (mouseEnter, keydown, action, ... )
auf, die die jeweils zugeordneten Events bearbeiten. Die Verarbeitung von
Ereignissen stützt sich auf die Klasse Event. Die Instanzvariable „id“ der Klasse
Event enthält die Art des Ereignisses und liefert alle notwendigen Informationen. Das
Attribut „target“ enthält die Information, welches Objekt das Ereignis ausgelöst hat.
Bsp.: Darstellung einer Datei im Textbereich eines Fensters (Frame)
import java.awt.*;
import java.io.*;
public class DateiDarsteller extends Frame
{
// Instanzvariable
String dateiName;
// Konstruktoren
public DateiDarsteller() throws IOException
{
super("Dateidarsteller ");
MenuBar menue = new MenuBar();
Menu m = new Menu("Datei");
m.add(new MenuItem("Oeffnen"));
m.add(new MenuItem("Schliessen"));
menue.add(m);
// Installiere diesen Menuebalken
setMenuBar(menue);
this.show();
}
// Methoden
public void dateiDarstellen() throws IOException
{
try {
File d = new File(dateiName);
int groesse = (int) d.length();
int anzZeich = 0;
FileInputStream ein = new FileInputStream(d);
byte[] daten = new byte[groesse];
while (anzZeich < groesse)
anzZeich += ein.read(daten,anzZeich,groesse);
String s = new String(daten,0);
TextArea textBereich = new TextArea(s,24,80);
textBereich.setFont(new Font("Helvetiva",Font.PLAIN,10));
textBereich.setEditable(false);
this.add("Center",textBereich);
135
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,
191
Programmieren in Java
}
catch (IOException e) { }
this.show();
}
public boolean handleEvent(Event e)
{
if ((e.id == Event.ACTION_EVENT) && (e.target instanceof
MenuItem))
{
if (((MenuItem) e.target).getLabel().equals("Oeffnen"))
{
FileDialog fd = new FileDialog(this,"Dateidialog");
fd.show();
dateiName = fd.getFile();
System.out.println("Dateiname: " + dateiName);
try { this.dateiDarstellen(); }
catch (IOException a ) { }
return true;
}
if (((MenuItem) e.target).getLabel().equals("Schliessen"))
{
this.hide();
this.dispose();
System.exit(0);
return true; }
}
return false;
}
public static void main(String argv[]) throws IOException
{
try {
DateiDarsteller DD = new DateiDarsteller();
}
catch (IOException e) { System.out.println(e); }
}
}
Behandlung von Aktionsereignissen
Aktionsereignisse werden über public boolean action(Event e, Object
arg) behandelt. Zuerst muß innerhalb der methode überprüft werden, welche
Komponente der Benutzeroberfläche die Aktion erzeugt hat. Zur vereinfachten
Bearbeitung beinhaltet das Event-Objekt, das beim Aufruf von action() erhalten
wird, eine "target"-Instanzvariable, die eine Referenz zu dem Objekt, das das
Ereignis aufgenommen hat, enthält. Mit dem instanceof-Operator kann dann
bestimmt werden, von welcher Komponente das Ereignis ausging. Das zweite
Argument dient zum Bestimmen des Labels136, der Elemente, des Inhalts der
Komponenten137. Anstatt des zusätzlichen Arguments kann auch die Methode 138
getLabel verwendet werden.
Behandlung von Fokus-Ereignissen
Für die Ereignisse "Fokuserhalt" und "Fokusverlust" können die Methoden
136
vgl. PR52105
Nicht vergessen: Das Argument in den richtigen Objekt-Typ zu casten
138 vgl. PR54305
137
192
Programmieren in Java
public boolean getFocus(Enet evt, Object arg)
bzw.
public boolean lostFocus(Event evt, Object arg)
verwendet werden.
Ereignisse von Listenfeldern
Listenfelder erzeugen drei verschiedene Ereignisarten: Auswahl bzw. Abwahl eines
Eintrags in der Liste bzw. Doppelklick auf einen Eintrag. Ein Doppelklick auf einen
Eintrag kann mit der Methode action bearbeitet werden. Auswahl und Abwahl
eines Listeneintrags kann mit "handleEvent" und Überprüfen auf die Ereignisse mit
LIST_SELECT und LIST_DESELECT erfolgen.
5.5.2 Das Event-Handling in Java 1.1 bzw. Java 1.2
Im neuen Ereignismodell
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.
193
Programmieren in Java
Event-Handling mit Hilfe lokaler Klasse
Für die Ereignisbehandlung werden lokale Klassen herangezogen. Im JDK 1.0
wurden Klassen nur auf Paketen definiert, eine Schachtelung war nicht möglich. Im
JDK 1.1 kann innerhalb bestehender Klassen eine neue Klasse definiert werden
(Konzept der „Inner Classes“), die nur innerhalb der bestehenden Klasse sichtbar ist.
Objektinstanzen der inneren Klasse können nur aus der umfassenden Klasse heraus
erzeugt werden. Allerdings kann die innere Klasse auf die Instanzvariablen der
äußeren Klasse zugreifen.
Mit Hilfe lokaler Klassen werden die benötigten EventListener implementiert. Dazu
wird in dem GUI-Objekt, das einen Event-Handler benötigt, eine lokale Klasse
definiert (und aus einer passenden AdapterKlasse abgeleitet).
Ein einführendes Bsp.: Event-Handling nach der Bearbeitung von Schaltflächen des
AWT in Version 1.0 und 1.1
// Einfangen von Klicks auf Schaltflaechen
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
public class
{
Button sch1
Button sch2
public void
{
SchalterAppl extends Applet
= new Button("Schaltflaeche 1");
= new Button("Schaltflaeche 2");
init()
sch1.addActionListener(new Sch1());
sch2.addActionListener(new Sch2());
add(sch1);
add(sch2);
}
class Sch1 implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
getAppletContext().showStatus("Schaltflaeche 1");
}
}
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;
}
*/
194
Programmieren in Java
}
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
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
und
„add“-
bzw. Komponenten, die dieses Ereignis unterstützen
Button, List, TextField, MenuItem und seine
abkömmlinge einschl. CheckboxmenuItem, menu
und PopupMenu
Scrollbar,
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
195
Programmieren in Java
TextListener
addTextListener()
removeTextListener()
und TextField abgeleitet ist
Abb.: Event- und Listener-Typen
Jeder Komponenten-Typ unterstützt nur bestimmte Ereignis-Typen:
Komponenten-Typ
Adjustable
Applet
Button
Canvas
Checkbox
CheckboxMenuItem
Choice
Component
Container
Dialog
FileDialog
Frame
Label
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
Methoden für die Ereignisbehandlung
196
Programmieren in Java
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.
Bsp.: Bearbeitung von zwei Schaltflächen und einem Textfeld139
// Eine Anwendung und ein Applet
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
public class SchalterApplet extends Applet
{
139
PR55202
197
Programmieren in Java
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);
}
}
198
Programmieren in Java
6. Multithreading
Beim Ablauf eines Programms wird mit der Ausführung seiner Initialisierungsprozedur begonnen, werden Methoden aufgerufen und mit der Ausführung und
Verarbeitung fortgefahren, bis das Ende des Programms erreicht ist. Das Programm
nutzt einen einzigen Thread. Ein Thread ist für das Programm ein Kontrollpunkt.
Multithreading ermöglicht die Ausführung mehrerer verschiedener Threads
gleichzeitig im gleichen Programm ohne gegenseitige Beeinträchtigung.
Multithreading bedeutet: Gleichzeitige parallele Ausführung von mehreren einzelnen
Programmschritten. Innerhalb eines Programms können mehrere Dinge gleichzeitig
geschehen, mehrere „Fäden“ eines Programms können gleichzeitig verfolgt und
abgearbeitet werden.
Java hat Threads direkt in die Sprache integriert. Threads werden durch die Klasse
Thread und das Interface Runnable implementiert. In beiden Fällen wird der parallel
auszuführende Code durch die überlagerte Methode „run“ zur Verfügung gestellt.
Die Kommunikation kann durch Zugriff auf Instanz- / Klassenvariablen oder durch
den Aufruf beliebiger Methoden, die innerhalb von „run“ sichtbar sind, erfolgen. Zur
Synchronisation stellt Java das aus der Betriebssystemtheorie bekannte Konzept
des Monitors zur Verfügung. Darüber können kritische Abschnitte innerhalb der
korrekt geklammerten Programmfragmente und Methoden gekapselt und so der
Zugriff auf gemeinsam benutzte Datenstrukturen koordiniert werden.
Java bietet Funktionen zur Verarbeitung von Threads an: Zusammenfassen von
Threads zu Gruppen, Priorisieren von Threads, Ermitteln von Eigenschaften zu
Threads. Das Scheduling kann dabei wahlweise140 unterbrechend oder nicht
unterbrechend implementiert sein.
140
Die Sprachspezifikation legt dies nicht eindeutig fest
199
Programmieren in Java
7. Ein- und Ausgabe
In Java werden Ein-, Ausgabeoperationen über sog. Datenströme 141 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.io142 , das bei jedem Programm mit Ein-/Ausgabeoperationen
importiert werden muß.
Einfache Strom-Methoden erlauben nur das Versenden von Bytes mit Hilfe von
Datenströmen143. 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.
142 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.
143 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.
141
200
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
BufferdOutputStream
DataOutputStream
PrintStream
EOFException
FileNotFoundException
InterruptedIOException
UTFDataFormatException
Abb.: java.io
7.1 Die abstrakten Klassen InputStream und OutputStream
7.1.1 InputStream
Aufgabenbeschreibung
Mit dieser Klasse können Leseoperationen eines Bytestroms verwirklicht werden.
Woher die Bytes kommen und wie sie befördert werden, spielt keine Rolle. Sie
müssen dem einlesenden Objekt nur zur Verfügung stehen. Die Aufgabe der Klasse
InputStream ist die Repräsentation von Klassen, die Eingaben aus verschiedenen
Quellen produzieren.
Klasse
ByteArrayInputStream
StringBufferInputStream
Funktion
Argument für den Konstruktor
Nutzungsmöglichkeit
Ein Puffer im arbeitsspeicher Der Puffer aus dem Bytes geholt
wird ald Eingabestrom benutzt.
werden.
Als Datenquelle. Verbunden mit
einem
FileInputStream-Objekt
kann daraus ein nützliches
Interface gestaltet werden.
Konvertiert einen String in einen Ein String. Die zugrundeliegende
InputStream
Implementierung benutzt einen
StringBuffer.
Als Datenquelle. Verbunden mit
einem
FiIenputStream-Objekt
201
Programmieren in Java
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.
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.
Abb.: Eingabestrom-Typen
Quellen zu einem Eingabestrom können sein:
1. Ein Array von Bytes
2. Ein Zeichenketten-Objekt
3. Eine Datei
4. Eine „Pipe“
5. Eine Folge von Strömen, die zu einem umfassenden, einzelnen Strom gesammelt werden können.
6. Anders beschaffene Quellen, z.B. eine Internet-Verbindung
Die „read“-Methoden
Zum Einlesen von Datenströmen gibt es die „read“-Methode. Die einfachste Form
ist: public abstract int read() throws IOException. Ein einzelnes Byte
wird aus dem Eingabestrom gelesen und ausgegeben. Falls der Bytestrom das Ende
erreicht hat, wird „-1“144 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
144
Rückgabe „-1“ bedeutet zum Unterschied von C nicht, daß ein Fehler aufgetreten ist.
202
Programmieren in Java
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
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
203
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.
Programmieren in Java
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
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
Abb.: „FilterInputStream“-Typen
204
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
Programmieren in Java
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
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:
205
Programmieren in Java
public
public
public
public
public
public
public
public
public
public
public
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 IOException145
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)
145
UTF (Unicode Transmission Format) ist ein spezielles Format zum Kodieren von 16-Bit-Unicode-Werten
206
Programmieren in Java
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
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)
void println(String s)
void println(char c)
void println(char[] puffer)
void println(char c)
void println(int i)
void println(float f)
void println(double d)
void 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 umfaßt Methoden zum Berabeiten von Dateien bzw. Verzeichnissen.
Die Methoden
public boolean isFile()
public boolean isDirectory
überprüfen, ob eine Datei oder ein Verzeichnis vorliegt. Mit
public boolean canRead()
public boolean canWrite()
wird Lese- bzw. Schreiberlaubnis überprüft. Die Methode
public long lastModified()
zeigt an, wann die Datei bzw. das Verzeichnis geändert wurde. Mit
public boolean exists
207
Programmieren in Java
wird die physikalische Existenz einer Datei oder eines Verzeichnisses überprüft.
Über
public String getName()
wird der name einer Datei bzw. eines Verzeichnisses ermittelt.
7.4 Die RandomAccessFile-Klasse
Für den Zugriff auf Random-Access-Dateien stellt das Paket java.io die Klasse
RandomAccessFile zur Verfügung. Es kann nur auf Dateien zugegriffen werden,
auch das durch die „Streams“ realisierte Filter-Konzept gibt es in Random-AccessDateien nicht.
Öffnen, Neuanlegen und Schließen. Das Öffnen von Random-Access-Dateien erfolgt
mit den Konstruktoren:
public
RandomAccessFile(File
datei,
String
mode)
throws
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 Dateiposition
bestimmt werden.
208
Programmieren in Java
7.5 Spezielle nützliche Ströme
Die LineInputStream-Klasse
Die SequenceInputStream-Klasse
Die PushBackInputStream-Klasse
Die StreamTokenizer-Klasse.
Tokenstrom146.
146
Sie
zerlegt
Token: einzelne Wörter bzw. Satzzeichen
209
einen
Zeichenstrom
in
einen
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.
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.
Einige Klassen bleiben von der Umstellung unberührt: DataOutputStream, File,
RandomAccessFile, SequenceInputStream.
210
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);
//
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
Die abstrakte Basisklasse konvertiert Byte- in Character-Streams.
FileReader
211
Programmieren in Java
Sie ermöglicht die Eingabe einer Datei.
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.
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.
LineNumerReader
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.
212
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 Konvertzierung zwischen Character- und ByteStreams.
FileWriter
213
Programmieren in Java
ermöglicht die Ausgabe in eine Datei. Sie implementiert die abstrakten
Eigenschaften von Writer. Die Klasse FileWriter bietet 4 Konstruktoren für das
Öffnen von Dateien an:
public FileWriter(String dateiName) throws IOException
Falls dateiName eine bereits vorhandene Datei bezeichnet, wird sie geöffnet und ihr bisheriger Inhalt
gelöscht, anderenfalls wird eine neue Datei mit diesem Namen angelegt. Sukzessive Aufrufe von write
schreiben dann in diese Datei.
public FileWriter(String dateiName, boolean append) throws IOException
Wird der Parameter append mit dem Wert true an den Konstruktor übergeben, dann werden die
Ausgabezeichen an die Datei angehängt, falls sie bereits existiert.
public FileWriter (File datei) throws IOException
public FileWriter (FileDescriptor fd)
Parameter ist hier ein File-Objekt bzw. ein FileDescriptor-Objekt
Die Klasse StringWriter
Sie besitzt zwei Konstruktoren:
public StringWriter()
bestimmt einen StringWriter in der Standardgröße eines StringBuffer-Objekts. Der interne Puffer
wächst automatisch, falls fortgesetzte Aufrufe von „write“ das eroderlich machen
public StringWriter(int initialGroesse)
Zugriffe: Die schreibenden Zugriffe auf die Puffer erfolgen mit den von Writer
bekannten „write“-Methoden. Für den Zugriff auf den Inhalt des Puffers gibt es die
Methoden public StringBuffer getBuffer() und public String
toString().
CharArrayWriter
Sie schreibt Zeichen in ein Character-Array, das automatisch bei fortgesetzten
Aufrufen von „write“ erweitert wird. Die Klasse besitzt zwei Konstruktoren:
public CharArrayWriter()
public CharArrayWriter(int initialGroesse)
Zugriffe: Schreibende Zugriffe erfolgen mit den von „Writer“ bekannten „write“Methoden. Für den Zugriff auf das Array gibt es die Methoden public String
toString() und public char[] toCharArray(). Mit der Methode public
void reset() wird der interne Puffer geleert. Die Größe des Zeichen-Array wird
über public int size() ermittelt. Durch Aufruf von public void
writeTo(Writer aus) throws IOException kann der Inhalt des Array an
einen anderen Writer übergeben werden und so mit einem einzigen
Funktionsaufruf in eine Datei geschrieben werden.
BufferedWriter
Die Klasse puffert „Stream“-Ausgaben über einen internen Puffer, in dem die
Ausgaben von write zwischengespeichert werden. Falls der Puffer gefüllt ist oder die
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.
214
Programmieren in Java
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)
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();
215
Programmieren in Java
// Lesen Standardeingabe
BufferedReader stdin = new BufferedReader(
new InputStreamReader(System.in));
System.out.print("Gib eine Zeile an: ");
System.out.println(stdin.readLine());
// 2. Eingabe vom Speicher
StringReader ein2 = new StringReader(s2);
int zch;
while ((zch=ein2.read()) != -1)
System.out.print((char) zch);
// 3. Formatierte Speicher-eingabe
try
{
DataInputStream ein3 = new DataInputStream(
new StringBufferInputStream(s2));
while (true)
System.out.print((char) ein3.readByte());
}
catch(EOFException e)
{
System.out.println("Ende des Stroms");
}
// 4. Zeilennummerierung und Datei-Ausgabe
try
{
LineNumberReader zn = new LineNumberReader(
new StringReader(s2));
// BufferedReader ein4 = new BufferedReader(zn);
PrintWriter aus1 = new PrintWriter(
new BufferedWriter(
new FileWriter("EinAusDemo.aus")));
while ((s1 = zn.readLine()) != null)
aus1.println("/* " + zn.getLineNumber() + " */" + s1);
// 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)
216
Programmieren in Java
{
System.out.println("IO Exception");
}
}
}
217
Herunterladen