Einführung in Java

Werbung
Einführung in Java
/*
* The HelloWorldApp class implements an
* application that simply displays
* "Hello World!" to the standard output.
*/
class HelloWorldApp {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
Hochschule für Angewandte Wissenschaften Hamburg
Hamburg University of Applied Sciences
Department Informations- und Elektrotechnik
02.2008
Thomas Klinker
Inhalt
1
2
OOP I : Klassen und Objekte ______________________________________________ 5
1.1
Erste Java-Programme_____________________________________________________ 5
1.2
Einführung in Klassen und Objekte __________________________________________ 8
Grundelemente von Java_________________________________________________ 20
2.1
Datentypen, Namen, Felder ________________________________________________ 20
2.2
Die Collection ArrayList __________________________________________________ 28
2.3
Kontrollstrukturen _______________________________________________________ 32
2.4
Operatoren _____________________________________________________________ 33
2.5
Ausnahmebehandlung (Exception-Handling) _________________________________ 36
2.6
Einfache Ein- und Ausgabe ________________________________________________ 40
2.7
Parameterübergabe bei Methoden __________________________________________ 45
2.8
Zuweisung von Objekten __________________________________________________ 46
2.9
Vergleich von Objekten ___________________________________________________ 49
2.10
3
4
Übungsaufgaben _______________________________________________________ 51
OOP II : Vererbung_____________________________________________________ 53
3.1
Das Konzept der Vererbung _______________________________________________ 53
3.2
Die Basisklasse Object ____________________________________________________ 63
3.3
Typkonvertierung in Vererbungshierarchien _________________________________ 65
3.4
Verwendung des Operators instanceof____________________________________ 66
3.5
Pakete und Sichtbarkeit ___________________________________________________ 67
3.6
Übungsaufgaben ________________________________ Fehler! Textmarke nicht definiert.
Grafikprogramme, Maus und Tastatur _____________________________________ 73
4.1
Maus- und Tastaturereignisse ______________________________________________ 78
4.1.1
4.1.2
4.2
5
6
7
Maus ______________________________________________________________________ 78
Tastatur ____________________________________________________________________ 82
Übungsaufgaben _________________________________________________________ 85
Applets _______________________________________________________________ 86
5.1
Das Einbinden eines Applets _______________________________________________ 89
5.2
Übungsaufgaben _________________________________________________________ 92
OOP III : Polymorphie, Abstrakte Klassen und Interfaces ______________________ 94
6.1
Polymorphie und spätes Binden ____________________________________________ 94
6.2
Abstrakte Klassen und Interfaces ___________________________________________ 99
6.3
Das Interface Cloneable _______________________________________________ 101
6.4
Das generische Interface Comparable<T> _________________________________ 106
6.5
Ereignisbehandlung _____________________________________________________ 107
Timer und Threads ____________________________________________________ 112
7.1
Timer _________________________________________________________________ 112
Inhalt
8
7.2
Threads und das Interface Runnable ______________________________________ 115
7.3
Übungsaufgaben ________________________________________________________ 120
Swing-Komponenten ___________________________________________________ 121
8.1
Übersicht über die Swing-Komponenten ____________________________________ 121
8.2
JButton und JLabel _____________________________________________________ 123
8.3
Layout-Manager ________________________________________________________ 124
8.4
JDialog und JOptionPane ________________________________________________ 131
8.5
JCheckBox und JRadioButton ____________________________________________ 135
8.6
JTextField, JTextArea und JScrollPane_____________________________________ 136
8.7
JSlider und JProgressbar ________________________________________________ 139
8.8
JMenu und JPopupmenu _________________________________________________ 141
8.9
JFileChooser ___________________________________________________________ 144
8.10
9
3
Übungsaufgaben ______________________________________________________ 146
Anhang______________________________________________________________ 147
9.1
Schlüsselwörter (keywords) in Java ________________________________________ 147
9.2
Einige Java-Begriffe _____________________________________________________ 147
9.3
Package java.lang _______________________________________________________ 148
9.3.1
9.3.2
9.3.3
9.4
Class Summary _____________________________________________________________ 148
Class java.lang.Math _________________________________________________________ 149
Class java.lang.String ________________________________________________________ 150
Class java.awt.Graphics __________________________________________________ 153
Literatur
Bücher
[1] Krüger, G.: Handbuch der Java-Programmierung.
Addison-Wesley 2008.
(Download von www.gkrueger.com)
[2] Eckel, B.: Thinking in Java.
Prentice Hall 2006.
(Download von www.BruceEckel.com)
[3] Samaschke, K.: Java 6 – Einstieg für Anspruchsvolle.
Addison-Wesley / Pearson Studium 2007.
[4] Merker, E. und Merker, R: Programmieren lernen mit Java.
Vieweg 2006.
[5] Horstmann, C. und Cornell, G: Java, Band 1 – Grundlagen.
Addison-Wesley 2005.
[6] Louis, D. und Müller, P: Jetzt lerne ich Java 6.
Markt + Technik 2007.
[7] Flanagan, D.: Java in a Nutshell.
Deutsche Ausgabe für Java 1.2 und 1.3.
O'Reilly 2000
Tutorial
[8] Java Tutorial.
(java.sun.com/docs/books/tutorial/)
Entwicklungswerkzeuge
[9] Eclipse SDK 3.0.2.
(Download von www.eclipse.org/downloads/)
[10] Java Development Kit.
(Download von java.sun.com)
[11] Java Hilfe Dateien.
(Download von java.sun.com/docs/)
1 OOP I : Klassen und Objekte
Die objektorientierte Programmiersprache Java wurde beginnend Mitte der 90er Jahren bei
Sun Microsystems entworfen.
− Java greift in seinen grundlegenden Datentypen und Kontrollstrukturen auf C zurück.
− Die objektorientierten Konstrukte erinnern an C++. Im Detail unterscheidet sich Java aber
recht deutlich von C++.
− Applets sind Java-Pogramme, die im Fenster eines HTML-Browsers ausgeführt und dargestellt werden können.
1.1
Erste Java-Programme
Ein erstes Java-Programm
Datei "Hello.java"
/*
* The Hello class implements an application that simply
* displays "Hello World!" to the standard output
*/
public class Hello {
public static void main(String[] args) {
System.out.println("Hello World!"); //Display the string
}
}
Auf der Konsole
Hello World!
Das Java-Programm besteht aus der Klasse (class) "Hello", die hier lediglich die Methode
"main" enthält. Die Ausführung des Programms beginnt bei "main", so wie man es von C her
kennt. Die "main" vorausgehenden Schlüsselwörter haben die folgenden Bedeutungen:
− "public" bewirkt, dass die Methode "main" auch außerhalb der Klasse bekannt ist.
− "static" hat zur Folge, dass "main" aufgerufen werden kann, ohne dass eine Instanz der
Klasse angelegt werden muss (vgl. Abschnitt 1.2).
− "void" zeigt, dass "main" keinen Funktionswert zurückgibt.
Die Methode "main" übernimmt als einzigen Parameter immer ein Feld mit Elementen vom
Datentyp "String" (Zeichenkette). Jeder "String" im Feld enthält ein Kommandozeilenargument (vgl. Abschnitt 2). In "main" wird die Methode "println" aus der Klasse "System"
aufgerufen. Diese Klasse enthält unter anderem Methoden zur Ausgabe ("out").
Wichtig:
− Groß-/Kleinschreibung wird beachtet.
− Der Quelltext muss in einer Datei "<Name der Klasse>.java" gespeichert werden.
− Die Ausgabedatei des Compilers heißt dann "<Name der Klasse>.class", und enthält
Java-Bytecode und nicht etwa Maschinenbefehle für den jeweiligen Prozessor.
1 OOP I : Klassen und Objekte
6
JDK (Java Development Kit)
Im JDK [6] sind ein Compiler "javac", ein Interpreter "java", ein Appletbetrachter "appletviewer" und weitere Programme enthalten.
Übersetzung und Ausführung von "Hello.java" mit dem JDK
javac Hello.java
java Hello
(→ Java-Bytecode in "Hello.class")
(Und nicht "java Hello.class")
1995: JDK 1.0 (1.0.2)
Einfache Implementierung mit einer Reihe von Unzulänglichkeiten, grundlegende Änderungen waren notwendig. Folge: Viele nicht mehr aktuelle Methoden und Klassen (deprecated,
missbilligen, bemäkeln).
1997: JDK 1.1 (1.1.7)
Geändertes Ereignis-Modell für die Benutzeroberfläche (GUI, Graphical User Interface), viele
neue Klassenbibliotheken.
1999: JDK 1.2 (1.2.2)
Ab hier "Java 2 Plattform", neue GUI-Klassenbibliothek (Swing), neue Container-Klassen.
2000: JDK 1.3 (1.3.1)
Fehlerbereinigung, neuer Interpreter (Hotspot Virtual Machine).
2002: JDK 1.4 (1.4.2)
Verbesserte Performance, Unterstützung von Mausrad und "drag-and-drop".
2004: JDK 1.5
Zahlreiche Verbesserungren.
Die Java-Plattform
Bild 1.1 zeigt, dass die Java-Umgebung aus den Programmier- und Anwendungsschnittstellen
(Application Programming Interfaces, APIs) und der Java Virtuellen Maschine (JVM)
besteht.
Java Bytecode
Java APIs
Java Virtual Machine
PC (Windows/Linux/...)
Bild 1.1 Umgebung bei der Ausführung von Java-Bytecode
−
−
Java APIs sind Bibliotheken mit kompiliertem Code, die von Java-Programmen eingesetzt werden können. Das "Hello" Programm verwendet zum Beispiel ein Java API,
um den Text "Hello World!" auszugeben.
Java-Bytecode wird von der Java VM interpretiert. Java-Programme laufen also nicht
unmittelbar auf dem jeweiligen Prozessor.
Vorteil: "Cross Plattform Design", also bessere Portierbarkeit.
Nachteil: Java-Programme werden langsamer als kompilierte Programme ausgeführt.
1 OOP I : Klassen und Objekte
7
Ein erstes Java-Applet
Datei "HelloApplet.java"
// Provides the classes necessary to create an applet and
// the classes an applet uses to communicate with its applet
// context
import java.applet.*;
// Contains all of the classes for painting graphics and
// images (awt = abstract windows toolkit)
import java.awt.*;
public class HelloApplet extends Applet {
public void paint(Graphics g) {
g.drawString("Hello World!", 25, 50);
}
}
Mit "import" werden für das Applet benötigte Bibliotheken importiert. Das Applet besteht
aus der Klasse "HelloApplet", erweitert (extends) die Klasse "Applet" (vgl. Abschnitt 3.1)
und enthält hier lediglich die Methode "paint". Diese Methode wird von einem Appletviewer
oder von einem Web Browser aufgerufen und ist dazu gedacht, grafische Ausgaben zu
erzeugen, also z.B. Texte oder Linien zu zeichnen. Der Parameter "g" liefert den Bezug zum
grafischen Kontext der Umgebung und wird hier verwendet, um die Zeichenkette "Hello
World!" ab dem linken, unteren Anfangspunkt x = 25 und y = 50 auszugeben.
Die Datei "HelloApplet.java" muss kompiliert werden, und als Ergebnis entsteht die Datei
"HelloApplet.class", die Java Bytecode enthält.
Datei "HelloApplet.html"
<HTML>
<APPLET CODE="HelloApplet.class" WIDTH=400 HEIGHT=300>
</APPLET>
</HTML>
Um das Applet vom Appletviewer oder vom Web Browser
darstellen zu lassen, wird eine
kleine HTML Datei (Hyper Text
Markup Language) benötigt, die
denselben Namen wie das
Applet haben sollte.
In dieser Datei stellt das
sogenannte APPLET Tag
(Kennung, Markierung) einen
Bezug zum Applet her. Darüber
hinaus werden Breite und Höhe
des Ausgabefläche festgelegt.
Bild 1.2 Ausgabe im Web-Browser
1 OOP I : Klassen und Objekte
8
Aufgabe 1.1
Auf der Konsole soll der folgende Text ausgegeben werden.
Fb E/I
Hamburg
a) Schreiben Sie ein Java Programm "Display.java".
b) Schreiben Sie ein Applet "DisplayApplet.java" und eine geeignete HTML Datei
"DisplayApplet.html".
1.2
Einführung in Klassen und Objekte
Ein Java-Programm besteht aus einer Klasse oder aus mehreren miteinander verbundenen
Klassen.
Mit einer Klasse werden Eigenschaften und Fähigkeiten von Objekten beschrieben.
−
−
Beispiel, Circle1
public class Circle1 {
double radius;
double area() {return 3.14159 * radius * radius;}
public static void main(String[] args) {
//Or "Circle1 circle = new Circle1();"
Circle1 circle;
circle = new Circle1();
circle.radius = 1.5;
System.out.println("Circle area: " + circle.area());
}
}
Auf der Konsole
Circle area: 7.068577499999999
Die Definition der Klasse "Circle1" enthält eine Variable ("radius") und Methoden ("area",
"main"). Die Klasse "Circle1" stellt einen neuen Datentyp dar.
Circle1 circle;
circle
Bei der Ausführung der Anweisung wird eine Referenz auf eine
Instanz (ein Exemplar) der Klasse "Circle1" angelegt (Bild 1.3).
Bild 1.3 Referenz
circle = new Circle1();
Mit dem Schlüsselwort "new" wird ein Objekt zur Laufzeit des Programms dynamisch
erzeugt. Ein Objekt ist eine Instanz einer Klasse. Die Objektvariable "circle" verweist nun
also auf ein Objekt vom Datentyp "Circle1" (Bild 1.4).
1 OOP I : Klassen und Objekte
circle
9
Eine Instanz der Circle1-Klasse, also
ein Objekt vom Datentyp Circle1
Bild 1.4 Die Variable "circle" verweist nun auf ein Objekt vom Typ "Circle1"
circle.radius = 1.5;
Der Variablen "radius" des Objekts auf das "circle" verweist, oder kurz "radius" von "circle",
wird ein Wert zugewiesen.
System.out.println("Circle area: " + circle.area());
Die Methode "area" wird auf das Objekt "circle" angewendet (Objektorientierte Programmierung), es wird also die Fläche von "circle" berechnet. Der Rückgabewert wird hier
selbsttätig in eine Zeichenkette umgewandelt und danach mit dem Ausgabetext "Circle area: "
verkettet (Operator "+").
double area() {return 3.14159 * radius * radius;}
Das Objekt "circle", auf das die Methode hier angewendet wird, wird automatisch übergeben.
Mit "radius" ist dann also der Radius von "circle" gemeint. Es ist überflüssig und unzulässig
"circle.radius" zu schreiben.
Konstruktor
circle = new Circle1();
Das Klammernpaar in der Anweisung deutet auf den Aufruf einer Methode hin, und es wird
tatsächlich eine spezielle Methode aufgerufen, die Konstruktor genannt wird. Jede Klasse in
Java besitzt mindestens einen Konstruktor. Die Aufgabe eines Konstruktors ist es, ein neues
Objekt zu initialisieren. Da die Klasse "Circle1" keinen Konstruktor enthalten hat, ist von
Java automatisch ein leerer1 Standardkonstruktor angelegt worden.
Beispiel, Circle2
public class Circle2 {
double radius;
// Konstruktor
Circle2(double r) {radius = r;}
double area() {return 3.14159 * radius * radius;}
public static void main(String[] args) {
Circle2 circle = new Circle2(1.5);
System.out.println("Circle area: " + circle.area());
}
}
Auf der Konsole
Circle area: 7.068577499999999
1
Der Standardkonstruktor enthält genau genommen einen Aufruf des Konstruktors der Oberklasse (vgl.
Abschnitt 3.1).
1 OOP I : Klassen und Objekte
10
Ein Konstruktor hat immer den gleichen Namen wie seine Klasse, im vorausgehenden
Beispiel also "Circle2". Beim Anlegen des neuen Objekts "circle" in der Zeile
Circle2 circle = new Circle2(1.5);
wird automatisch der Konstruktor aufgerufen, und "radius" von "circle" wird hier mit dem
Wert "1.5" initialisiert.
Aufgabe 1.2
public class Cube {
int dim1, dim2, dim3;
...
...
public static void main(String[] args) {
Cube cube1 = new Cube(1, 2, 3);
System.out.println("Volume of cube1: " + cube1.volume());
Cube cube2 = new Cube(4, 5, 6);
System.out.println("Volume of cube2: " + cube2.volume());
}
}
Auf der Konsole
Volume of cube1: 6
Volume of cube2: 120
Vervollständigen Sie das Programm so, dass es die gegebenen Daten ausgibt.
Mehrere Konstruktoren
Beispiel, Circle3
public class Circle3 {
double radius;
Circle3(double r) {radius = r;}
// Standard constructor (empty)
Circle3() {}
double area() {return 3.14159 * radius * radius;}
public static void main(String[] args) {
Circle3 c1 = new Circle3(1.5);
Circle3 c2 = new Circle3();
// Copy object
c2 = c1;
System.out.println("Area of c2: " + c2.area());
}
}
1 OOP I : Klassen und Objekte
11
Auf der Konsole
Area of c2: 7.068577499999999
Eine Klasse kann mehrere Konstruktoren besitzen, die dann alle den gleichen Namen wie ihre
Klasse haben. Sie müssen sich aber in den Datentypen und/oder in der Reihefolge der Datentypen ihrer Parameter unterscheiden, damit der Compiler den jeweils aufzurufenden
Konstruktor eindeutig feststellen kann.
Methoden mit dem gleichen Namen aber sich unterscheidenden Parameterlisten zu definieren,
wird Überladen (Overloading) von Methoden genannt.
Datenkapselung
Im folgenden Beispiel ist eine Klasse Rect zur Beschreibung von Rechtecken implementiert.
Breite und Höhe können über die Methoden setWidth() und setHeight() geändert
werden. Die Fläche wird automatisch neu berechnet. Der direkte Zugriff auf die
Membervariablen width und height ist aber immer noch möglich. Dadurch kann eine
Instanz der Klasse Rect in einen Zustand gebracht werden, in dem die Membervariable
area nicht mehr den korrekten Wert enthält.
Beispiel, MyRectApp1
/* Klasse MyRectApp1
* Verstoss gegen Datenkapselung
*
*/
class Rect{
double width, height;
double area;
double getArea(){
return area;
}
void setWidth(double width){
this.width=width;
calculateArea();
}
void setHeight(double height){
this.height=height;
calculateArea();
}
void calculateArea(){
area = width*height;
}
}
public class MyRectApp1{
public static void main(String s[]){
Rect r = new Rect();
System.out.println("area: " + r.getArea());
r.setWidth(1.0);
1 OOP I : Klassen und Objekte
12
r.setHeight(3.0);
System.out.println("area: " + r.getArea());
r.width=0.5;
System.out.println("area: " + r.getArea());
}
}
Auf der Konsole
area: 0.0
area: 3.0
area: 3.0
Um die Klasse Rect als brauchbare Software nutzen zu können, werden die Membervariablen mit dem Modifikator private vor dem direkten Zugriff geschützt:
Beispiel, MyRectApp2
/* Klasse MyRectApp2
* Kein Verstoss mehr gegen Datenkapselung
*
*/
class Rect{
private double width, height;
private double area;
double getArea(){
return area;
}
void setWidth(double width){
this.width=width;
calculateArea();
}
void setHeight(double height){
this.height=height;
calculateArea();
}
void calculateArea(){
area = width*height;
}
}
public class MyRectApp2{
public static void main(String s[]){
Rect r = new Rect();
System.out.println("area: " + r.getArea());
r.setWidth(1.0);
r.setHeight(3.0);
System.out.println("area: " + r.getArea());
r.setWidth(0.5);
System.out.println("area: " + r.getArea());
}
}
1 OOP I : Klassen und Objekte
13
Auf der Konsole
area: 0.0
area: 3.0
area: 1.5
Klassenvariablen (static)
Beispiel, MyCircleApp4
/*
*
*
*/
MyCircleApp4
Verwendung von Klassenvariablen (static)
class Circle {
// Klassenvariable numberOfCircles
static int numberOfCircles = 0;
private double radius;
Circle(double r) {
radius = r;
numberOfCircles++;
}
double area() {return 3.14159 * radius * radius;}
}
public class MyCircleApp4 {
public static void main(String[] args)
{
System.out.println("Number of circles: "
+ Circle.numberOfCircles);
Circle c1 = new Circle(1.0);
System.out.println("Number of circles: "
+ Circle.numberOfCircles);
System.out.println(" Area of c1: " + c1.area());
Circle c2 = new Circle(2.0);
System.out.println("Number of circles: "
+ Circle.numberOfCircles);
System.out.println(" Area of c2: " + c2.area());
}
}
Auf der Konsole
Number
Number
Area
Number
Area
of
of
of
of
of
circles: 0
circles: 1
c1: 3.14159
circles: 2
c2: 12.56636
In der Klassendefinition gibt es die Instanzvariable radius. Jede Instanz der Klasse, also
jedes Objekt, hat eine eigene Kopie dieser Variablen. Neu ist die Klassenvariable
numberOfCircles.
1 OOP I : Klassen und Objekte
14
−
Klassenvariablen werden mit dem Schlüsselwort "static" vereinbart.
−
Es existiert nur ein Exemplar von Klassenvariablen, das schon beim Programmstart
angelegt wird.
Bild 1.5 verdeutlicht den Unterschied zwischen Klassen- und Instanzvariablen.
Klasse Circle4
numberOfCircles
Klassenvariable
Objekt c1
Objekt c2
radius
radius
Instanzvariablen
Bild 1.5 Klassen- und Instanzvariablen
Konstanten (static final)
Da in Java kein Präprozessor existiert, sind Vereinbarungen der Art "#define MAX 100"
nicht zulässig.
Beispiel, MyCircleApp5
/*
*
*
*/
MyCircleApp5
Verwendung von Konstanten (static final)
class Circle {
// Konstante MAXRADIUS
static final double MAXRADIUS = 2.0;
static int numberOfCircles = 0;
private double radius;
Circle(double r) {
radius = (r <= MAXRADIUS) ? r : MAXRADIUS;
numberOfCircles++;
}
double area() {return 3.14159 * radius * radius;}
}
public class MyCircleApp5 {
public static void main(String[] args)
{
System.out.println("Number of circles: "
+ Circle.numberOfCircles);
Circle c1 = new Circle(1.0);
System.out.println("Number of circles: "
1 OOP I : Klassen und Objekte
15
+ Circle.numberOfCircles);
System.out.println(" Area of c1: " + c1.area());
Circle c2 = new Circle(3.5);
System.out.println("Number of circles: "
+ Circle.numberOfCircles);
System.out.println(" Area of c2: " + c2.area());
}
}
Auf der Konsole
Number
Number
Area
Number
Area
of
of
of
of
of
circles: 0
circles: 1
c1: 3.14159
circles: 2
c2: 12.56636
Konstanten werden auf Klassenebene mit dem Schlüsselwort "final" vereinbart. Der Wert
wird bei der Vereinbarung zugewiesen und lässt sich danach nicht mehr verändern.
Klassenmethoden (static)
Beispiel, MyCircleApp6
/*
*
*/
MyCircleApp6
Verwendung von Klassenmethode (static)
class Circle {
private static int numberOfCircles = 0;
private double radius;
Circle(double r) {
radius = r;
numberOfCircles++;
}
// Klassenmethode getNumberOfCircles()
static int getNumberOfCircles() {
return numberOfCircles;
}
double area() {return 3.14159 * radius * radius;}
}
public class MyCircleApp6 {
public static void main(String[] args)
{
System.out.println("Number of circles: "
+ Circle.getNumberOfCircles());
Circle c1 = new Circle(1.0);
System.out.println("Number of circles: "
+ Circle.getNumberOfCircles());
System.out.println(" Area of c1: " + c1.area());
1 OOP I : Klassen und Objekte
16
Circle c2 = new Circle(2.0);
System.out.println("Number of circles: "
+ Circle.getNumberOfCircles());
System.out.println(" Area of c2: " + c2.area());
}
}
Auf der Konsole
Number
Number
Area
Number
Area
of
of
of
of
of
circles: 0
circles: 1
c1: 3.14159
circles: 2
c2: 12.56636
In der Klassendefinition gibt es wie in den vorausgehenden Beispielen die Instanzmethode
"area()". Instanzmethoden werden auf Objekte angewendet. Schreibweise:
<Objekt>.<Instanzmethode(...)>, z. B. c1.area()
Zusätzlich gibt es die Klassenmethode "getNumberOfCircle()". Klassenmethoden werden auf
die Klasse angewendet. Schreibweise:
<Klasse>.<Klassenmethode(...)>, z. B. Circle.getNumberOfCircles()
Zusammenfassend:
− Klassenmethoden werden mit dem Schlüsselwort "static" vereinbart.
− Da Klassenmethoden unabhängig von konkreten Instanzen der Klasse existieren, ist ein
Zugriff auf Instanzvariablen nicht möglich!
Die Klassenmethode "main()" wird beim Programmstart von der Java-Umgebung aufgerufen.
Mehrere Klassen
Wie schon in einigen Beispielen zuvor werden im folgenden Beispiel mehrere Klassen
verwendet. Sie können in einer Datei gespeichert werden. Dann darf nur eine dieser Klassen
als "public" deklariert werden, und der Dateinamen muss mit dem Namen dieser Klasse
übereinstimmen. Der Compiler erzeugt für jede Klasse eine Ausgabedatei <Klassennamen>.class. In der Regel sollte jede Klasse in einer eigenen Datei stehen.
Beispiel, Circle7
class MyMath {
static final double PI = 3.14159;
static double square(double value) {
return value * value;
}
}
public class Circle7 {
double radius;
Circle7(double r) {radius = r;}
1 OOP I : Klassen und Objekte
17
double area() {return MyMath.PI * MyMath.square(radius);}
public static void main(String[] args) {
Circle7 circle = new Circle7(1.5);
System.out.println("Area of circle: " + circle.area());
}
}
Auf der Konsole
Area of circle: 7.068577499999999
Das Beispiel zeigt noch einmal, dass Klassenvariablen und Klassenmethoden außerhalb ihrer
Klasse mit dem vorgestellten Klassennamen aufgerufen werden.
Die Klasse "Math"
Im Anhang ist ein Überblick über die Klasse "Math" aus dem Paket "java.lang" zu finden. Für
dieses grundlegende Paket ist keine Anweisung "import java.lang.*;" erforderlich.
Beispiel, Circle8
public class Circle8 {
double radius;
Circle8(double r) {radius = r;}
double area() {return Math.PI * Math.pow(radius, 2.0);}
public static void main(String[] args) {
Circle8 circle = new Circle8(1.5);
System.out.println("Area of circle: " + circle.area());
}
}
Auf der Konsole
Area of circle: 7.0685834705770345
Es werden "PI" und "pow" aus der Klasse "Math" eingesetzt, die in der Klasse als "static"
deklariert worden sind. In beiden Fällen wird deshalb der Klassenname "Math" angegeben.
Aufgabe 1.3
public class Circle {
double radius;
static double area(double r) {return Math.PI * Math.pow(r, 2.0);}
public static void main(String[] args) {
radius = 1.5;
System.out.println("Area of circle: " + area(radius));
}
}
1 OOP I : Klassen und Objekte
18
Fehlermeldungen des Compilers
Circle.java:7: non-static variable radius cannot be referenced from
a static context
radius = 1.5;
^
Circle.java:9: non-static variable radius cannot be referenced from
a static context
System.out.println("Area of circle: " + area(radius));
^
Warum gibt der Compiler die Fehlermeldungen aus? Korrigieren Sie das Programm.
Aufgabe 1.4
public class Tabel_a
{
public static void main(String[] args)
{
String angleStr, sinStr, cosStr, tanStr;
System.out.println("Angle
Sin
Cos
Tan
");
System.out.println("------------------------------");
for(int angle = 0; angle <= 60; angle += 10)
{
double rad = angle * Math.PI / 180.0;
angleStr = String.format("%5d", angle);
sinStr = String.format("%8.3f", Math.sin(rad));
cosStr = String.format("%8.3f", Math.cos(rad));
tanStr = String.format("%8.3f", Math.tan(rad));
System.out.println( angleStr + sinStr
+ cosStr + tanStr);
}
}
}
Was gibt das Programm aus?
Die Referenz "this"
Beim Aufruf einer Instanzmethode wird implizit eine Referenz "this" auf das Objekt übergeben, auf das die Methode angewendet wird. Das folgende Beispiel zeigt, wie man diese
Referenz im eigenen Programmcode einsetzen kann.
Beispiel, MyRectApp3
/* Klasse MyRectApp3
* Komplett mit getter und setter
*
*/
1 OOP I : Klassen und Objekte
19
class Rect{
private double width, height;
private double area;
public double getWidth() {
return width;
}
public double getHeight() {
return height;
}
public double getArea(){
return area;
}
public void setWidth(double width){
this.width=width;
calculateArea();
}
public void setHeight(double height){
this.height=height;
calculateArea();
}
private void calculateArea(){
area = width*height;
}
}
public class MyRectApp3{
public static void main(String s[]){
Rect r = new Rect();
System.out.println("area: " + r.getArea());
r.setWidth(1.0);
r.setHeight(3.0);
System.out.println("width: " + r.getWidth());
System.out.println("height: " + r.getHeight());
System.out.println("area: " + r.getArea());
}
}
Auf der Konsole
area: 0.0
width: 1.0
height: 3.0
area: 3.0
Hinweis: Instanzvariablen werden automatisch initialisiert: numerische Variablen auf 0,
boolesche Variablen auf false, Strings auf Null-String "", Objekte auf null. Lokale
Variablen von Methoden hingegen werden nicht automatisch initialisiert.
2 Grundelemente von Java
Kommentare
/**
*
*/
This class displays a text string at
the console
/* A C-style comment */
public class Hello1 {
// Another C-style comment
public static void main(String[] args){
System.out.println("Hello World!");
}
}
Java unterstützt drei Kommentarformen:
− Die von C bzw. C++ vertrauten "/* ... */" und "// ..." Kommentare.
− Dokumentationskommentare (doc comments) "/** ... */", die vom Programm "javadoc"
ausgewertet werden, um eine einfache Dokumentation zu erstellen.
2.1
Datentypen, Namen, Felder
Einfache Datentypen
Data Type
byte
short
int
long
float
double
char
boolean
Description
Integers
Byte-length integer
Short integer
Integer
Long integer
Real Numbers
Single-precision floating point
Double-precision floating point
Other Types
A single character
A boolean value
Size/Format
Range
8-bit two's compl.
16-bit two's compl.
32-bit two's compl.
64-bit two's compl.
-128 to +127
-32 768 to +32 767
9
9
-2.110 to +2.110
18
18
-9.210 to +9.210
32-bit IEEE 754
64-bit IEEE 754
±3.410
to ±3.410
-308
308
±1.710
to ±1.710
-38
16-bit Unicode char.
true or false
Vergleich mit C:
− Java kennt keine vorzeichenlosen Datentypen.
− Es gibt zusätzlich die Datentypen "byte" und "boolean".
− Die Länge von "int" ist auf 32 Bit festgelegt.
− Zeichen (character) werden nicht im 8-Bit ASCII Code sondern im 16-Bit Unicode
gespeichert.
− Es existieren keine Zeiger (z.B. "int*").
− Es gibt keine Strukturen ("struct"), keine Bitfelder und keine Unions ("union").
38
21
2 Grundelemente von Java
Beispiele für Konstanten
Literal
257
0x1AF
8864L
37.266
37.266D or 37.266d
87.363F or 87.363f
26.77e3
'c'
true
false
Data Type
int
int
long
double
double
float
double
char
boolean
boolean
Namen
Namen bestehen aus Buchstaben (Unicode, also auch Umlaute zulässig), Ziffern und den
Zeichen '_' (Unterstrich) und '$' (Dollar). Das erste Zeichen darf dabei keine Ziffer sein. Die
folgenden Konventionen machen Java-Programme lesbarer. Es ist gute Programmierpraxis
sich an diese Namenskonventionen zu halten.
−
Die Namen von Variablen und Methoden beginnen mit einem Kleinbuchstaben, neue
Wörter innerhalb des Namens mit einem Großbuchstaben.
Variablen: kontoNummer, teilnehmerName, index, temp
Methoden: main, paint, getValue, toString
−
Die Namen von Klassen beginnen mit einem Großbuchstaben, neue Wörter innerhalb des
Namens ebenfalls mit einem Großbuchstaben.
Beispiele: Hello, Echo, HelloApplet, AgeLimit
−
Die Namen von Konstanten bestehen aus Großbuchstaben, neue Wörter innerhalb des
Namens werden durch einen Unterstrich "_" eingeleitet.
Beispiele: PI, MAX_X_VALUE, HINTERGRUND_FARBE
Beispiel, AgeLimit
public class AgeLimit1 {
final static int LOWER_AGE_LIMIT = 12;
public static void main(String[] args) {
int customerAge = 11;
String tooYoung = "Too young";
String ageOk = "Age ok";
if(customerAge < LOWER_AGE_LIMIT)
System.out.println(tooYoung);
else
System.out.println(ageOk);
}
}
Auf der Konsole
Too young
// if ... else ...
2 Grundelemente von Java
22
Zeichenketten
Zeichenketten (Strings) sind in Java Objekte. Der Name eines Strings ist eine Referenzvariable, die auf den String verweist. Es ist zu beachten, dass durch die Klasse String keine
dynamischen Zeichenketten implementiert werden. Nach der Initialisierung eines StringObjektes bleiben dessen Länge und dessen Inhalt konstant. Die einzelnen Zeichen lassen sich
also nicht mehr ändern.
Es gibt drei Konstruktoren:
String()
Erzeugt ein leeres String-Objekt.
String(String value)
Erzeugt ein String-Objekt durch kopieren eines Vorhandenen.
String(char[] value)
Erzeugt ein String-Objekt aus einem vorhandenen Charakter-Array.
Wichtig ist, zu beachten, dass Variablen vom Typ String Referenzvariablen sind. Sie
verweisen auf ein Objekt, dass mit Hilfe des new-Operators auf dem Heap angelegt wird. Das
bedeutet, nach den folgenden Anweisungen
String name1 = new String("Anja");
String name2 = new String("Herbert");
name2 = name1;
zeigen beide Referenzen name1 und name2 auf die Zeichenkette "Anja". Beim
Vergleichen von Strings muss deshalb ebenfalls aufgepasst werden. Nach den Anweisungen
String s1 = new String("Hallo");
String s2 = new String("Hallo");
liefert die Anweisung s1 == s2 den Wert false, da die Referenzvariablen s1 und s2
auf verschiedene Objekte im Speicher zeigen, die zwar den gleichen Inhalt haben aber nicht
identisch sind. Die Anweisung s1.equals(s2) liefert jedoch, wie erwartet, den Wert
true, da sie die Objekte auf inhaltliche Gleichheit prüft.
Im Folgenden sind noch einige Methoden der String-Klasse aufgeführt. Dabei ist zu
beachten, dass jede Methode der Klasse String, die eine veränderte Zeichenkette erzeugt,
wie z.B. die Methode substring(), eine neue Instanz der Klasse String liefert und
nicht die Original-Instanz mit einem geänderten Inhalt. Denn es gilt, String-Instanzen können
nach ihrer Initialisierung nicht mehr geändert werden.
public int length()
Gibt die Anzahl der Zeichen einer Zeichenkette zurück.
public boolean equals(Object obj)
Vergleicht zwei String-Objekte miteinander und gibt true zurück, falls die String-Objekte den
gleichen Inhalt haben.
2 Grundelemente von Java
23
public String substring(int anfang, int ende)
Schneidet aus dem aufrufenden String-Objekt die Zeichen von der Position anfang bis zur
Position ende-1 heraus und gibt den herausgeschnittenen Teil als neues String-Objekt
zurück. Man beachte, dass das erste Zeichen den Index 0 hat.
Aufgabe 2.1
Erläutern Sie, warum es zu den gegebenen Konsolenausgaben kommt.
public class StringApp {
public static
String s1 =
String s2 =
String s3 =
String s4 =
void main(String[] args) {
new String("Test");
new String("Test");
"Te";
"st";
System.out.println("1:
System.out.println("2:
System.out.println("3:
System.out.println("4:
"
"
"
"
+
+
+
+
(s1 == "Test"));
(s1 == s2));
((s3+s4) == s1));
((s3+s4).equals(s1)));
String s5 = s2.substring(1, 3);
System.out.println("5: " + s5);
System.out.println("6: " + (s2.substring(1, 3) == s5));
System.out.println("7: " + (s2.substring(1, 3).equals(s5)));
}
}
Auf der Konsole
1:
2:
3:
4:
5:
6:
7:
false
false
false
true
es
false
true
Arrays für einfache Datentypen
Beispiel, IntArray1
public class IntArray1 {
public static void main(String[] args) {
int[] anArray;
anArray = new int[10];
// Step1: Declare the array
// Step2: Allocate storage
// Step3: Initialize the array
for (int i = 0; i < anArray.length; i++) {
anArray[i] = i;
System.out.print(anArray[i] + " ");
}
2 Grundelemente von Java
24
System.out.println();
}
}
Auf der Konsole
0 1 2 3 4 5 6 7 8 9
Die Länge eines Arrays wird festgelegt, wenn man zur Laufzeit des Programms mit "new"
Speicher für das Array anfordert. Danach hat das Array dann eine feste Länge.
Vorsicht:
− Die Länge eines Strings erhält man mit "<Name des Strings>.length()". Die Methode
"length()" aus der Klasse "String" wird dann also auf den String angewendet.
− Die Anzahl der Arrayelemente wird dagegen mit "<Name des Arrays>.length" (ohne
Klammernpaar!) ermittelt; "length" ist hier keine Methode sondern eine Instanzvariable
des jeweiligen Array-Objektes.
Wie in C reicht der zulässige Indexbereich immer von 0 bis "Arraylänge-1", für einen Array
der Länge 10 also zum Beispiel von 0 bis 9.
Beispiel, IntArray2
public class IntArray2 {
public static void main(String[] args) {
int[] anArray = {10, 11, 12, 13, 14};
for (int i = 0; i < anArray.length; i++)
System.out.print(anArray[i] + " ");
System.out.println();
}
}
Auf der Konsole
10 11 12 13 14
Das vorausgehende Beispiel zeigt, wie sich ein Feld initialisieren lässt. Die Feldlänge entspricht dann der Anzahl der Werte in der Initialisierungsliste.
Beispiel, Matrix
public class Matrix {
static final int ROWS = 4;
static final int COLS = 5;
public static void main(String[] args) {
double[][] aMatrix = new double[ROWS][COLS];
// or "...; i < ROWS; ..."
for (int i = 0; i < aMatrix.length; i++) {
// or "...; j < COLS; ..."
for (int j = 0; j < aMatrix[i].length; j++) {
aMatrix[i][j] = i+j/10.0;
25
2 Grundelemente von Java
System.out.print(aMatrix[i][j] + "
}
System.out.println();
");
}
}
}
Auf der Konsole
0.0
1.0
2.0
3.0
0.1
1.1
2.1
3.1
0.2
1.2
2.2
3.2
0.3
1.3
2.3
3.3
0.4
1.4
2.4
3.4
Man hätte mit der folgenden Anweisung die Matrix bei der Vereinbarung auch unmittelbar
initialisieren können.
double[][] aMatrix = {{0.0,
{1.0,
{2.0,
{3.0,
0.1,
1.1,
2.1,
3.1,
0.2,
1.2,
2.2,
3.2,
0.3,
1.3,
2.3,
3.3,
0.4},
1.4},
2.4},
3.4}};
Aufgabe 2.2
public class IntArray {
int[] myArray;
IntArray(int dim) {
myArray = new int[dim];
for(int i = 0; i < dim; i++)
// random() returns a double value with a positive sign,
// greater than or equal to 0.0 and less than 1.0
myArray[i] = (int) (100 * Math.random());
}
void sortArray() {
...
}
public static void main(String[] args) {
IntArray tab = new IntArray(8);
tab.sortArray();
for (int i = 0; i < tab.myArray.length; i++)
System.out.print(tab.myArray[i] + " ");
System.out.println();
}
}
Beispiel zur Ausgabe
16 19 21 22 22 57 59 85
Vervollständigen Sie die Methode "sortArray" so, dass sie das Feld "myArray" in aufsteigender Reihenfolge sortiert.
26
2 Grundelemente von Java
Arrays für Referenzdatentypen
Beispiel, StringArray
public class StringArray {
public static void main(String[] args) {
String[] anArray;
anArray = new String[3];
// Declare the array
// Allocate storage for references
// Do something
anArray[0] = "String 0";
anArray[1] = "String 1";
anArray[2] = anArray[1];
anArray[1] += " + new substring";
//
//
//
//
Line
Line
Line
Line
1
2
3
4
for (int i = 0; i < anArray.length; i++)
System.out.println(anArray[i]);
}
}
Auf der Konsole
String 0
String 1 + new substring
String 1
Die Anweisung "anArray = new String[3];" fordert Speicherplatz für 3 Referenzen an und
nicht etwa für drei Zeichenketten. Die Zuweisungen in "Line 1" bis "Line 3" führen dazu,
dass die Referenz (die Anfangsadresse) der jeweiligen Zeichenkette kopiert wird. In "Line 4"
wird eine neue Zeichenkette im dynamischen Speicher abgelegt und deren Referenz dann
kopiert.
Beispiel, Kommandozeilenargumente
public class Echo {
public static void main (String[] args) {
for (int i = 0; i < args.length-1; i++)
System.out.print(args[i]+" ");
System.out.println(args[args.length-1]);
}
}
Auf der Konsole
java Echo Klaus Waller Hamburg
Klaus Waller Hamburg
2 Grundelemente von Java
27
Beispiel, RefArray
class Circle {
double radius;
Circle(double r) {radius = r;}
double area() {return 3.14159 * radius * radius;}
}
public class RefArray {
public static void main(String[] args) {
// Declare the array and allocate storage for references
Circle[] anArray = new Circle[2];
// Initialize the array with objects of class Circle
anArray[0] = new Circle(1.0);
anArray[1] = new Circle(2.0);
for (int i = 0; i < anArray.length; i++)
System.out.println("Area " + anArray[i].area());
}
}
Auf der Konsole
Area
Area
3.14159
12.56636
Hier wird im ersten Schritt dynamischer Speicher für zwei Referenzen auf "Circle" angefordert. Danach werden zwei Instanzen von "Circle" angelegt und ihre Referenzen nach
"anArray" kopiert.
Aufgabe 2.3
class Point {
int x, y;
...
}
class Rectangle {
Point tl;
int width, height;
// top left
...
}
class ChessBoard {
static final int DIM = 32;
// Height and width of squares
Rectangle rct[] = new Rectangle[64];
...
}
public class Test {
28
2 Grundelemente von Java
public static void main(String[] args) {
Point p = new Point(225, 240);
System.out.println(p);
Rectangle r = new Rectangle(p, 122, 234);
System.out.println(r);
ChessBoard chb = new ChessBoard();
System.out.println(chb);
System.out.println("Point " + p
+ " in square " + chb.getRectInd(p));
}
}
Auf der Konsole:
x=225 y=240
topleft: x=225 y=240, width=122 height=234
ChessBoard:
[0]: topleft: x=0 y=0, width=32 height=32
b)[63]: topleft: x=224 y=224, width=32 height=32
Point x=225 y=240 in square 63
Vervollständigen Sie das Programm so, dass es die angegebenen Daten ausgibt. Insbesondere
ist in jeder Klasse eine geeignete toString()-Methode zu programmieren.
2.2
Die Collection ArrayList
Mit der Klasse "ArrayList" (ab JDK 1.2) kann man Referenzen auf Objekte dynamisch in eine
Liste einfügen und aus dieser Liste wieder löschen. Die Liste wird bei Bedarf selbsttätig
vergrößert.
Beispiel, ListDemo
import java.util.*;
public class ListDemo {
static void printList(ArrayList aList) {
for(int i = 0; i < aList.size(); i++)
System.out.println(aList.get(i));
System.out.println("- - - -");
}
// Get size
// Get object
public static void main(String[] args) {
String str;
ArrayList lst1 = new ArrayList();
for(int i = 0; i < 2; i++)
lst1.add("Element " + i);
lst1.add(0, "First");
lst1.add(lst1.size(), "Last");
printList(lst1);
// Append
// Insert at position
2 Grundelemente von Java
lst1.remove("Last");
lst1.remove(0);
lst1.set(1, "Set");
printList(lst1);
29
// Remove element
// Remove at position
// Replace at position
ArrayList lst2 = new ArrayList(lst1);
lst1.remove(0);
lst2.addAll(lst1);
// Append all
printList(lst2);
str = (String) lst2.get(1);
System.out.println(str);
// get with typecast
lst2.clear();
printList(lst2);
// Remove all
}
}
Auf der Konsole:
First
Element
Element
Last
- - - Element
Set
- - - Element
Set
Set
- - - Set
- - - -
0
1
0
0
Die Klasse ArrayList gibt es ab Java 5 in einer typsicheren Form. Diese Typsicherheit ist mit
Hilfe so genannter Generics implementiert worden. Es handelt sich dabei um CollectionKlassen, in die nicht allgemein Objekte vom Typ Object abgelegt werden können, sondern
die durch eine vorhergehende Typisierung sicherstellen, dass nur Objekte eines bestimmten
Typs (etwa Integer oder String) in die Liste eingefügt werden können. Der Typ der
Elemente wird dabei als Typparameter übergeben, z.B. ArrayList<String>. Im
folgenden sind einige Listenoperationen dargestellt:
Element Hinzufügen, add()
import java.util.ArrayList;
import java.util.Arrays;
public class ArrayList_Add {
public static void main(String args[]) {
2 Grundelemente von Java
ArrayList<String> list = new ArrayList<String>();
list.add("One");
list.add("Two");
list.add("Three");
// for-each-Schleife
for(String item : list) {
System.out.println(item);
}
}
}
Auf der Konsole:
One
Two
Three
Liste Hinzufügen, addAll()
import java.util.ArrayList;
import java.util.Arrays;
public class ArrayList_AddAll {
public static void main(String args[]) {
ArrayList<String> list = new ArrayList<String>();
list.add("One");
String[] data = { "Two", "Three", "Four", "Five" };
list.addAll(Arrays.asList(data));
for(String item : list) {
System.out.println(item);
}
}
}
Auf der Konsole:
One
Two
Three
Four
Five
Abrufen von Werten, get()
import java.util.ArrayList;
import java.util.Arrays;
30
2 Grundelemente von Java
31
public class ArrayList_For {
public static void main(String args[]) {
ArrayList<String> list = new ArrayList<String>();
list.add("One");
list.add("Two");
list.add("Three");
for(int i=0; i<list.size(); i++) {
System.out.println(String.format
("Element at Index #%d: \"%s\"", i, list.get(i)));
}
}
}
Auf der Konsole:
Element at Index #0: "One"
Element at Index #1: "Two"
Element at Index #2: "Three"
Ersetzen von Werten, set()
import java.util.ArrayList;
import java.util.Arrays;
public class ArrayList_Set {
public static void main(String args[]) {
String[] data = { "One", "Two", "Three"};
ArrayList<String> list =
new ArrayList<String>(Arrays.asList(data));
list.set(0, "Zero");
for(String item : list) {
System.out.println(String.format("%s", item));
}
}
}
Auf der Konsole:
Zero
Two
Three
2 Grundelemente von Java
2.3
32
Kontrollstrukturen
Schleifen
while (boolean expression) {
statement(s)
}
Die Bedingung (boolean expression) wird am Schleifenanfang geprüft. Die Anweisungen in
der Schleife werden gegebenenfalls nicht ein einziges Mal ausgeführt.
do {
statement(s)
} while (boolrean expression);
Die Bedingung (boolean expression) wird am Schleifenende geprüft. Die Anweisungen in der
Schleife werden immer mindestens ein Mal ausgeführt.
for (initialization; boolean expression; increment) {
statement(s)
}
Die Bedingung (boolean expression) wird am Schleifenanfang geprüft Die Anweisungen in
der Schleife werden gegebenenfalls nicht ein einziges Mal ausgeführt.
Bedingte Ausführung und Alternativen
if (boolean expression) {
statement(s)
}
Die Anweisungen (statement(s)) werden nur ausgeführt, wenn die Bedingung wahr ist.
if (boolean expression) {
statement(s) 1
} else {
statement(s) 2
}
Abhängig von der Bedingung werden alternativ nur die ersten Anweisungen (statement(s) 1)
oder die zweiten Anweisungen (statement(s) 2) ausgeführt.
if (boolean expression 1) {
statement(s) 1
} else if (boolean expression 2) {
statement(s) 2
} else if (boolean expression 3) {
statement(s) 3
} else {
statement(s) 4
}
2 Grundelemente von Java
33
Abhängig von den Bedingungen (logical expression 1 to 3) werden alternativ nur die ersten
Anweisungen (statement(s) 1), oder die zweiten Anweisungen (statement(s) 2), usw. ausgeführt.
switch (integer expression) {
case integer expression 1:
statement(s) 1
break;
case integer expression 2:
statement(s) 2
break;
...
...
default:
statement(s)
break;
}
Abhängig vom ganzzahligen Ausdruck (integer expression) wird zum zugehörigen Fall (case)
verzweigt. Die Anweisungen werden ab dieser Stelle bis zum "break" ausgeführt. Wenn
keiner der Fälle zutrifft, wird der default-Zweig gewählt.
Weitere Kontrollanweisungen
continue
Die Anweisungen unterbricht die innerste Schleife und führt dazu, dass deren Schleifenbedingung erneut ausgewertet wird.
break
Die Anweisung beendet die innerste "switch", "for", "while" oder "do while" Anweisung.
2.4
Operatoren
Arithmetische Operatoren
Operator
+
*
/
%
Use
op1
op1
op1
op1
op1
Operator
++
Use
op++
++
++op
+
*
/
%
op2
op2
op2
op2
op2
Description
Adds op1 and op2
Subtracts op2 from op1
Multiplies op1 by op2
Divides op1 by op2
Computes the remainder of dividing op1 by op2
Description
Increments op by 1; evaluates the value of op
before the increment operation
Increments op by 1; evaluates the value of op after
2 Grundelemente von Java
--
op--
--
--op
the increment operation
Decrements op by 1; evaluates the value of op
before the increment operation
Decrements op by 1; evaluates the value of op after
the increment operation
Vergleichsoperatoren
Operator
>
>=
<
<=
==
!=
Use
op1
op1
op1
op1
op1
op1
> op2
>= op2
< op2
<= op2
== op2
!= op2
Returns true if
op1 is greater than op2
op1 is greater than or equal to op2
op1 is less than op2
op1 is less than or equal to op2
op1 and op2 are equal
op1 and op2 are not equal
Logische Operatoren
Operator
&&
||
!
Use
op1 && op2
op1 || op2
!op
Returns true if
op1 and op2 are both true
either op1 or op2 is true
op is false
Schiebeoperatoren
Operator
>>
<<
>>>
34
Use
op1 >> op2
op1 << op2
op1 >>> op2
Operation
shift bits of op1 right by distance op2
shift bits of op1 left by distance op2
shift bits of op1 right by distance op2 (unsigned)
Aufgabe 2.4
public class Shift {
public static void main(String[] args) {
int val = 0xFFFFFFFF;
String hexStr1 = "0x" + Integer.toHexString(val >> 3);
String hexStr2 = "0x" + Integer.toHexString(val >>> 3);
System.out.println("val >> 3: " + hexStr1);
System.out.println("val >>> 3: " + hexStr2);
}
}
Was gibt das Programm aus?
2 Grundelemente von Java
35
Bitoperatoren
Operator
&
|
^
~
Use
op1 & op2
op1 | op2
op1 ^ op2
~op2
Operation
bitwise AND
bitwise OR
bitwise XOR
bitwise NOT
Zuweisungsoperatoren
Operator
=
+=
-=
*=
/=
%=
&=
|=
^=
<<=
>>=
>>>=
Use
op1
op1
op1
op1
op1
op1
op1
op1
op1
op1
op1
op1
Equivalent to
= op2
+= op2
-= op2
*= op2
/= op2
%= op2
&= op2
|= op2
^= op2
<<= op2
>>= op2
>>>= op2
op1
op1
op1
op1
op1
op1
op1
op1
op1
op1
op1
=
=
=
=
=
=
=
=
=
=
=
op1
op1
op1
op1
op1
op1
op1
op1
op1
op1
op1
+ op2
- op2
* op2
/ op2
% op2
& op2
| op2
^ op2
<< op2
>> op2
>>> op2
Weitere Operatoren
Operator
?:
Use
op1 ? op2 : op3
[]
type []
[]
type[op1]
[]
op1[op2]
.
()
op1.op2
op1(params)
(type)
(type) op1
new
new op1
instanceof op1 instanceof op2
Description
If op1 is true, returns op2. Otherwise, returns
op3.
Declares an array of unknown length, which
contains type elements.
Creates and array with op1 elements. Must be
used with the new operator.
Accesses the element at op2 index within the
array op1. Indices begin at 0 and extend
through the length of the array minus one.
Is a reference to the op2 member of op1.
Declares or calls the method named op1 with
the specified parameters.
Casts (converts) op1 to type. An exception
will be thrown if the type of op1 is
incompatible with type.
Creates a new object or array. op1 is either a
call to a constructor, or an array specification.
Returns true if op1 is an instance of op2.
2 Grundelemente von Java
36
Priorität der Operatoren
In der folgenden Tabelle sind die Operatoren in der Reihenfolge ihrer Priorität aufgelistet: je
höher in der Tabelle ein Operator aufgeführt ist, desto größer ist seine Priorität. Operatoren in
derselben Zeile haben die gleiche Priorität.
postfix operators
unary operators
creation or cast
multiplicative
additive
shift
relational
equality
bitwise AND
bitwise exclusive OR
bitwise inclusive OR
logical AND
logical OR
conditional
assignment
[] . (params) expr++ expr-++expr --expr +expr -expr ~ !
new (type)expr
* / %
+ << >> >>>
< > <= >= instanceof
== !=
&
^
|
&&
||
? :
= += -= *= /= %= &= ^= |= <<= >>= >>>=
Für Operatoren mit derselben Priorität gelten die folgenden Zusatzregeln:
− Wenn binäre Operatoren derselben Priorität mit Ausnahme von Zuweisungsoperatoren in
einem Ausdruck (expression) zu finden sind, wird der Ausdruck von links nach rechts
ausgewertet.
− Zuweisungsoperatoren werden von rechts nach links ausgewertet.
2.5
Ausnahmebehandlung (Exception-Handling)
Im Beispiel "Exception1" wird die Programmausführung mit einer Fehlermeldung beendet,
wenn es zu einer Division durch Null kommt.
Beispiel, Exception1
public class Exception1 {
public static void main(String[] args) {
for(int i = -2; i <= 2; i++)
System.out.println("100/" + i + " = " + 100/i);
}
}
Auf der Konsole
100/-2 = -50
100/-1 = -100
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Exception1.main(Exception1.java:5)
2 Grundelemente von Java
37
In Java gibt es die Möglichkeit, einen Programmabschnitt zu markieren ("try", versuchen), in
diesem Programmabschnitt auftretende Laufzeitfehler "abzufangen" ("catch") und auf sie
dann geeignet zu reagieren.
Beispiel, Exception2
public class Exception2 {
public static void main(String[] args) {
for(int i = -2; i <= 2; i++) {
try {
System.out.println("100/" + i + " = " + 100/i);
}
catch(ArithmeticException e) {
System.out.println("100/0 = ????
" + e);
}
}
}
}
Auf der Konsole
100/-2 = -50
100/-1 = -100
100/0 = ????
100/1 = 100
100/2 = 50
java.lang.ArithmeticException: / by zero
Wenn es im try-Block zu einer Ausnahmesituation kommt, unterbricht die Laufzeitumgebung
die normale Programmausführung. Das Programm wird dann mit den Anweisungen in der
catch-Klausel fortgesetzt, die zur jeweiligen Ausnahmesituation passt, im Beispiel also in der
catch-Klausel für eine arithmetische Ausnahmesituation (ArithmeticException). Gibt es keine
passende catch-Klausel, wird die Fehlerbehandlung von der Laufzeitumgebung übernommen.
Java-Programme haben grundsätzlich die Pflicht, den Compiler über möglicherweise auftretende Fehler zu informieren. Dabei gilt die sogenannte "catch-or-throw"-Regel. Diese
Regel besagt, dass jede Ausnahme entweder mit einer catch-Klausel abgefangen oder mit
"throws" weitergegeben werden muss. Das folgende Beispiel zeigt die Vorgehensweise bei
der Weitergabe.
Beispiel, Exception3
public class Exception3 {
public static void main(String[] args) throws ArithmeticException {
for(int i = -2; i <= 2; i++)
System.out.println("100/" + i + " = " + 100/i);
}
}
Auf der Konsole
100/-2 = -50
100/-1 = -100
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Exception3.main(Exception3.java:5)
2 Grundelemente von Java
38
Um den Aufwand durch die Fehlerbehandlung nicht zu groß werden zu lassen, ist es für viele
Laufzeitfehler zulässig, auf die explizite Weitergabe mit "throws" zu verzichten. Das Beispiel
"Exception1" zeigt, dass arithmetische Ausnahmesituationen zu diesen Laufzeitfehlern
gehören.
Treten in dem eigenen Programm Fehler auf, die in dem von Java zu Verfügung gestellten
Klassenbaum der Exceptions noch nicht vertreten sind, so muß man eine spezielle, eigene
Ausnahmeklasse vereinbaren. Da Exceptions nichts anderes als Klassen sind, leitet man sich
für seine Bedürfnisse einfach eine Klasse von der Klasse Exception oder einer Subklasse
ab. Dadurch können die Exceptions unterschieden und entsprechend angepasste
Fehlermeldungen ausgegeben werden. Das folgende Beispiel zeigt, wie man eine selbst
definierte Exception generieren, auswerfen und wieder fangen kann.
Beispiel, Ausnahme1
class MyException extends Exception
{
public MyException()
{
// Aufruf des Konstruktors der Klasse Exception.
// Ihm wird ein String mit dem Fehlertext übergeben.
super ("Fehler ist aufgetreten!");
}
}
public class MyClass
{
public static void main (String[] args)
{
// Dieser try-Block ist untypisch, da in ihm nur eine
// Exception zu Demonstrationszwecken geworfen wird.
try
{
MyException ex = new MyException();
throw ex;
// Anweisungen unterhalb einer throw-Anweisung in einem
// try-Block werden nie abgearbeitet.
}
catch (MyException e)
{
System.out.println (e.getMessage());
}
}
}
Auf der Konsole
Fehler ist aufgetreten!
Zur Erstellung einer eigenen Exception wird in dem Beispiel die Klasse MyException
von der Klasse Exception abgeleitet. Im parameterlosen Konstruktor der Klasse
MyException wird mit super() der Konstruktor der Klasse Exception
aufgerufen. An den Konstruktor von Exception wird ein String übergeben, der den
2 Grundelemente von Java
39
Fehlertext enthält. Dieser Fehlertext kann dann im Fehlerfall mit der Methode
getMessage() ausgelesen werden.
In obigem Beispiel wurde die Exception direkt im Hauptprogramm generiert und ausgeworfen
mit dem Befehl
throw ex;
Die Exception kann aber auch von einer Methode erzeugt werden, die an irgendeiner Stelle im
Hauptprogramm aufgerufen wird. Eine Exception, die eine Methode auslösen kann, muss
dabei zwingend in der Deklaration der Methode mit Hilfe der throws-Klausel angegeben
werden. Dadurch wird dem Aufrufer signalisiert, welche Ausnahmen von einer Methode
ausgelöst bzw. weitergereicht werden. Der Aufrufer muss die geworfene Exception dann
auffangen und sinnvoll behandeln. Im nachfolgenden Beispiel, das nur eine kleine
Abwandlung gegennüber dem Beispiel "Ausnahme1" darstellt, wird dies verdeutlicht.
Beispiel, Ausnahme2
class MyException extends Exception
{
public MyException()
{
// Aufruf des Konstruktors der Klasse Exception.
// Ihm wird ein String mit dem Fehlertext übergeben.
super ("Fehler ist aufgetreten!");
}
}
public class MyClass
{
static void method() throws MyException
{
MyException ex = new MyException();
throw ex;
}
public static void main (String[] args)
{
// Dieser try-Block ist untypisch, da in ihm nur eine
// Exception zu Demonstrationszwecken geworfen wird.
try
{
method();
// Anweisungen unterhalb einer throw-Anweisung in einem
// try-Block werden nie abgearbeitet.
}
catch (MyException e)
{
System.out.println (e.getMessage());
}
}
}
2 Grundelemente von Java
2.6
40
Einfache Ein- und Ausgabe
Beispiel, DisplayNumbers1
public class DisplayNumbers1 {
public static void main(String[] args) {
int iVar1 = 11,
int iVar2 = 1/2;
double dVar1 = 2.718,
double dVar2 = 1.0/3.0;
System.out.println("iVar1:
System.out.println("iVar2:
System.out.println("dVar1:
System.out.println("dVar2:
"
"
"
"
+
+
+
+
iVar1);
iVar2);
dVar1);
dVar2);
}
}
Auf der Konsole
iVar1:
iVar2:
dVar1:
dVar2:
11
0
2.718
0.3333333333333333
Im nächsten Beispiel wird die Ausgabe mit Hilfe der statischen Methode "printf()" formatiert:
public PrintStream printf(String format, Object... args)
Wobei der Formatstring den Aufbau hat:
% [Argument-Index$][Flags][Width][.Precision]Conversion
"Conversion" gibt den Datentyp an. Die wichtigsten Möglichkeiten sind:
b
c
d
o
x
X
f
e
E
g
G
t
s
- Boolescher Wert
- Einzelnes Zeichen
- Ganzzahl in Dezimaldarstellung
- Ganzzahl in Oktaldarstellung
- Ganzzahl in Hexadezimaldarstellung
- Dito, mit großen Buchstaben
- Flieskommazahl
- Flieskommazahl mit Exponent
- Dito, mit großem "E"
- Flieskommazahl in gemischter Schreibweise
- Dito, ggfs. mit großem "E"
- Prefix für Datums-/Zeitangaben
- Strings und andere Objekte
Anders als in C/C++ müssen die Argumente strikt typkonform übergeben werden, was
mitunter etwas lästig sein kann. Das Konvertierungszeichen "f" akzeptiert beispielsweise nur
Fliesskommazahlen, nicht aber Ganzzahlen; bei "d" ist es genau umgekehrt.
2 Grundelemente von Java
41
Einen Sonderstatus haben "%" und "n", die für das Prozentzeichen bzw. die Zeilenschaltung
stehen und kein zusätzliches Argument benötigen.
Mit den "Flags" können weitere Ausgabeoptionen abgerufen werden.
+
0
,
(
- Linksbündige Ausgabe
- Vorzeichen immer ausgegeben
Zahlen werden mit Nullen aufgefüllt
- Zahlen werden mit Tausenderpunkten ausgegeben
- Negative Zahlen werden in Klammern eingeschlossen
Sollen mit Hilfe des Konvertierungsprefixes "t" Datums-/Zeitwerte ausgegeben werden, so
muß mit einem zweiten Buchstaben direkt dahinter angegeben werden, welcher Teil des Dateoder Calendar-Objektes ausgegeben werden soll. Einige der vielen Möglichkeiten sind:
H
M
S
d
m
Y
F
c
- Stunde, zweistellig, im 24-Stunden-Format
- Minute, zweistellig
- Sekunde, zweistellig
- Tag, zweistellig
- Monat, zweistellig, 1..12
- Jahr, vierstellig
- Datum, formatiert nach ISO 8601 (YYYY-MM-DD)
- Kompletter Datums-/Zeitstring inkl. Zeitzone
Beispiel, MyPrintf1
import java.util.*;
public class MyPrintf1
{
public static void main(String[] args)
{
//Boolesche Werte
System.out.printf("%b %b %2$b %1$b%n", true, false);
//Ganzzahlen
System.out.printf("[%d]%n", -2517);
System.out.printf("[%7d]%n", -2517);
System.out.printf("[%-7d]%n", -2517);
System.out.printf("[%(7d]%n", -2517);
System.out.printf("[%07d]%n", -2517);
System.out.printf("[%,7d]%n", -2517);
System.out.printf("%1$d %<o %<x %<X%n", 127);
//Fliesskommazahlen
System.out.printf("%f%n", 0.000314);
System.out.printf("%1$6.2f %1$6.2e %1$6.2E %1$6.2G%n",
3.141592);
System.out.printf("%,8.2f%n", 31415.92);
System.out.printf(Locale.ENGLISH, "%,8.2f%n", 31415.92);
//Zeichen und Strings
42
2 Grundelemente von Java
System.out.printf("%c%c%c\n", 97, 64, 98);
System.out.printf("%s nein\n", "ja");
//Datum/Uhrzeit
Calendar now = Calendar.getInstance();
System.out.printf(
"%1$td.%1$tm.%1$tY %1$tH:%1$tM:%1$tS%n",
now
);
System.out.printf("%tF%n", now);
System.out.printf("%tc%n", now);
}
}
Auf der Konsole:
true false false true
[-2517]
[ -2517]
[-2517 ]
[ (2517)]
[-002517]
[ -2.517]
127 177 7f 7F
0,000314
3,14 3.14e+00 3.14E+00
3.1
31.415,92
31,415.92
a@b
ja nein
26.02.2008 12:28:51
2008-02-26
Di Feb 26 12:28:51 CET 2008
Die Trennzeichen "," und "." entsprechen der deutschen Standardeinstellung (default locale).
Aufgabe 2.5
Schreiben Sie ein Programm, das die folgende Tabelle der ASCII-Zeichen 32 bis 127 ausgibt.
32
38
44
50
56
62
68
74
80
86
92
98
104
110
116
122
0x20
0x26
0x2c
0x32
0x38
0x3e
0x44
0x4a
0x50
0x56
0x5c
0x62
0x68
0x6e
0x74
0x7a
&
,
2
8
>
D
J
P
V
\
b
h
n
t
z
33
39
45
51
57
63
69
75
81
87
93
99
105
111
117
123
0x21
0x27
0x2d
0x33
0x39
0x3f
0x45
0x4b
0x51
0x57
0x5d
0x63
0x69
0x6f
0x75
0x7b
!
'
3
9
?
E
K
Q
W
]
c
i
o
u
{
34
40
46
52
58
64
70
76
82
88
94
100
106
112
118
124
0x22
0x28
0x2e
0x34
0x3a
0x40
0x46
0x4c
0x52
0x58
0x5e
0x64
0x6a
0x70
0x76
0x7c
"
(
.
4
:
@
F
L
R
X
^
d
j
p
v
|
35
41
47
53
59
65
71
77
83
89
95
101
107
113
119
125
0x23
0x29
0x2f
0x35
0x3b
0x41
0x47
0x4d
0x53
0x59
0x5f
0x65
0x6b
0x71
0x77
0x7d
#
)
/
5
;
A
G
M
S
Y
_
e
k
q
w
}
36
42
48
54
60
66
72
78
84
90
96
102
108
114
120
126
0x24
0x2a
0x30
0x36
0x3c
0x42
0x48
0x4e
0x54
0x5a
0x60
0x66
0x6c
0x72
0x78
0x7e
$
*
0
6
<
B
H
N
T
Z
`
f
l
r
x
~
37
43
49
55
61
67
73
79
85
91
97
103
109
115
121
127
0x25
0x2b
0x31
0x37
0x3d
0x43
0x49
0x4f
0x55
0x5b
0x61
0x67
0x6d
0x73
0x79
0x7f
%
+
1
7
=
C
I
O
U
[
a
g
m
s
y
2 Grundelemente von Java
43
Beispiel, ReadString
import java.io.*;
public class ReadString {
public static void main(String[] args) throws IOException {
// BufferedReader reads text from a character-input stream,
// buffering characters so as to provide for the efficient
// reading of characters, arrays, and lines
// An InputStreamReader is a bridge from byte streams to
// character streams: It reads bytes and translates them into
// characters according to a specified character encoding
// System.in: the "standard" input stream
BufferedReader din = new BufferedReader(
new InputStreamReader(System.in));
System.out.print("String: ");
// Read a line of text
String str = din.readLine();
System.out.println(str);
}
}
Auf der Konsole
String: Ich bin ein String mit Leerzeichen
Ich bin ein String mit Leerzeichen
Wenn die Angabe von "throws IOException" fehlt, bricht der Compiler mit einer Fehlermeldung ab (vgl. Abschnitt 2.4).
Beispiel, ReadInt
import java.io.*;
public class ReadInt {
public static void main(String[] args) throws IOException {
BufferedReader din = new BufferedReader(
new InputStreamReader(System.in));
System.out.print("anInt: ");
// "parseInt" parses the string argument as a signed
// decimal integer
int anInt = Integer.parseInt(din.readLine());
System.out.println(anInt);
}
}
2 Grundelemente von Java
44
Auf der Konsole
anInt: -123
-123
Aufgabe 2.6
Schreiben Sie ein Programm, das ein Rechteck auf der Konsole ausgibt.
******
*
*
*
*
******
Breite (width) und Höhe (height) sollen eingelesen werden. Weisen Sie Eingaben zurück,
wenn nicht 1 ≤ Breite ≤ 79 und 1 ≤ Höhe ≤ 20 gilt. Beachten Sie auch den Sonderfall Breite =
Höhe = 1.
Im folgenden Beispiel wird eine Kopie einer Textdatei angelegt. Die Namen der beiden
Dateien sind dabei in der Kommandozeile zu übergeben.
Beispiel, CopyTextFile
import java.io.*;
public class CopyTextFile {
public static void main(String[] args) throws IOException {
// Line separator (Windows "\r\n", Unix "\n", Mac "\r")
final String L_SEP = System.getProperty("line.separator");
BufferedReader in;
BufferedWriter out;
String txt;
if(args.length < 2) {
System.out.println("Usage: CopyTextFile <inFile> <outFile>");
return;
}
try {
in = new BufferedReader(new FileReader(args[0]));
}
catch(FileNotFoundException e) {
System.out.println("Could not find file " + args[0]);
return;
}
try {
out = new BufferedWriter(new FileWriter(args[1]));
}
2 Grundelemente von Java
45
catch(FileNotFoundException e) {
System.out.println("Could not create file " + args[1]);
return;
}
while((txt = in.readLine()) != null)
out.write(txt + L_SEP);
// Copy text file
in.close();
out.close();
}
}
2.7
Parameterübergabe bei Methoden
In Java werden Methodenparameter generell per Wert übergeben. Das heißt, ein formaler
Parameter erhält beim Aufruf der Methode eine Kopie des entsprechenden aktuellen
Parameters (Übergabe per Wert, call by value). Dies hat aber völlig unterschiedliche
Auswirkungen, je nach dem ob der Übergabeparameter von einfachem Datentyp ist oder von
Referenztyp, also eine Objekt repräsentiert. Im folgenden Beispiel betrachten wir zunächst
den Fall, dass der Übergabeparameter von einfachem Datentyp ist.
Beispiel, Parameter1
/*
*
*/
Beispiel: Parameter1
public class Parameter1 {
public static void method1 (int par) {
par = 2;
}
public static void main(String[] args) {
int value = 1;
System.out.println("Vor dem Methodenaufruf");
System.out.println("value = " + value);
method1(value);
System.out.println("Nach dem Methodenaufruf");
System.out.println("value = " + value);
}
}
Auf der Konsole
Vor dem Methodenaufruf
value = 1
Nach dem Methodenaufruf
value = 1
2 Grundelemente von Java
46
Die Änderungen, welche die Methode methode1() an der Variablen par vornimmt, haben
keine Auswirkung auf den aktuellen Übergabeparameter value. Dies entspricht genau einem
call by value.
Im nächsten Beispiel ist der Übergabeparameter vom Referenztyp. Auch hier geschieht die
Übergabe des Parameters per call by value, diesmal aber wird eine Referenz kopiert. Damit
referenziert der formale Parameter par dasselbe Objekt wie der aktuelle Parameter ref.
Beide greifen also auf dasselbe Objekt zu. Damit kann das Originalobjekt aber in der Methode
verändert werden. Dieses entspricht vom Verhalten her einem call by reference.
Beispiel, Parameter2
/*
*
*/
Beispiel: Parameter2
class RefTyp {
int x;
}
public class Parameter2 {
public static void method2 (RefTyp par) {
par.x = 2;
}
public static void main(String[] args) {
RefTyp ref = new RefTyp();
ref.x = 1;
System.out.println("Vor dem Methodenaufruf");
System.out.println("ref.x = " + ref.x);
method2(ref);
System.out.println("Nach dem Methodenaufruf");
System.out.println("ref.x = " + ref.x);
}
}
Auf der Konsole
Vor dem Methodenaufruf
ref.x = 1
Nach dem Methodenaufruf
ref.x = 2
2.8
Zuweisung von Objekten
Beim Zuweisen von Objekten muss man Vorsicht walten lassen. Im folgenden Beispiel
werden mit der Zuweisung m2 = m1 lediglich die Referenzen kopiert. Man erhält dadurch
zwei Referenzen, die auf das gleiche Objekt verweisen. Eine Änderung an der Kopie m2
bewirkt dann auch eine Änderung am Original m1, was in der Regel unerwünscht ist.
2 Grundelemente von Java
Beispiel, MyCopy1
/*
*
*/
Beispiel: MyCopy1
class Mitarbeiter
{
private String name;
private int gehalt;
public Mitarbeiter() {
}
public Mitarbeiter(String name, int gehalt) {
this.name = name;
this.gehalt = gehalt;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getGehalt() {
return gehalt;
}
public void setGehalt(int gehalt) {
this.gehalt = gehalt;
}
}
public class MyCopy1
{
public static void main(String[] args)
{
Mitarbeiter m1 = new Mitarbeiter("Mueller", 2000);
Mitarbeiter m2;
m2 = m1;
// flache Kopie (shallow copy)!!!
m2.setName("Schulze");
m2.setGehalt(1500);
System.out.println("m2.name = " + m2.getName() +
", m2.gehalt = " + m2.getGehalt());
System.out.println("m1.name = " + m1.getName() +
", m1.gehalt = " + m1.getGehalt());
}
}
47
2 Grundelemente von Java
48
Auf der Konsole
m2.name = Schulze, m2.gehalt = 1500
m1.name = Schulze, m1.gehalt = 1500
Um wirklich eine echte Kopie der Originalobjektes zu erhalten, wird im folgenden Beispiel
der Klasse Mitarbeiter die Methode "copy()" hinzugefügt, die ein neues Objekt vom Typ
Mitarbeiter erzeugt und alle Daten des Originalobjekts kopiert. Hier werden also alle Daten
des Originalobjektes kopiert und nicht nur die Objektvariable, die lediglich eine Referenz auf
das Objekt enthält.
Beispiel, MyCopy2
/*
*
*/
Beispiel: MyCopy2
class Mitarbeiter
{
private String name;
private int gehalt;
public Mitarbeiter() {
}
public Mitarbeiter(String name, int gehalt) {
this.name = name;
this.gehalt = gehalt;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getGehalt() {
return gehalt;
}
public void setGehalt(int gehalt) {
this.gehalt = gehalt;
}
public Mitarbeiter copy() {
Mitarbeiter c = new Mitarbeiter();
c.name = this.name;
c.gehalt = this.gehalt;
return c;
}
}
2 Grundelemente von Java
49
public class MyCopy2
{
public static void main(String[] args)
{
Mitarbeiter m1 = new Mitarbeiter("Mueller", 2000);
Mitarbeiter m2;
m2 = m1.copy();
// tiefe Kopie (deep copy)!!!
m2.setName("Schulze");
m2.setGehalt(1500);
System.out.println("m2.name = " + m2.getName() +
", m2.gehalt = " + m2.getGehalt());
System.out.println("m1.name = " + m1.getName() +
", m1.gehalt = " + m1.getGehalt());
}
}
Auf der Konsole
m2.name = Schulze, m2.gehalt = 1500
m1.name = Mueller, m1.gehalt = 2000
Wenn in das zu kopierende Objekt wiederum Referenzvariablen eingebettet sind, unterscheidet man erneut zwischen flacher and tiefer Kopie.
− Flache Kopie (shallow copy): Man kopiert die eingebetteten Referenzvariablen. Anschließend verweisen dann also die eingebetteten Referenzvariablen von Original und
Kopie auf ein- und dieselben Daten.
− Tiefe Kopie (deep copy): Man kopiert auch die Daten, auf die die eingebetteten Referenzvariablen verweisen.
In einem späteren Kapitel werden wir sehen, dass man für das kopieren von Objekten in der
Regel die Methode clone() verwendet, die in der Basisklasse Object, von der alle Klassen
abgeleitet sind, vordefiniert ist. Man muss dazu das Interface Cloneable implementieren und
die Methode clone() in der eigenen Klasse überschreiben.
2.9
Vergleich von Objekten
Auch beim Vergleichen von Objekten, also Referenzvariablen, muss man ähnlich Vorsicht
walten lassen wie bei der Zuweisung von Objekten. Bei Referenztypen wird mit dem
Vergleich a == b getestet, ob die Referenzen gleich sind, das heißt, es wird getestet, ob die
Referenzvariablen auf dieselbe Instanz verweisen. Es wird aber nicht geprüft, ob zwei
unterschiedliche Instanzen den gleichen Inhalt haben. Will man diesen Test auf inhaltliche
Gleichheit bei unterschiedlichen Instanzen haben, so muss in der eigenen Klasse die Methode
equals(), die von der Klasse Object geerbt wird, überschrieben werden. Wir hatten dieses
Problem bei Zeichenketten (Strings) schon einmal angesprochen. In der Klasse String gibt es
bereits eine geeignete Methode equals(), die zwei verschiedene Strings auf Gleichheit prüft.
Im folgenden Beispiel wird das noch einmal an Hand einer eigenen Klasse dargestellt. Dort ist
die Klasse Book entworfen, um z.B. Bücher einer Bibliothek zu erfassen. Der Klasse Book
wurde eine geeignete Methode equals() hinzugefügt, die zwei verschiedene Objekte der
Klasse Book als gleich erkennt, wenn Titel und Autor identisch sind.
2 Grundelemente von Java
/*
* Beispiel: Vergleich1
*/
class Book {
private String title;
private String author;
public Book(String title, String author) {
this.title = title;
this.author = author;
}
public boolean equals(Object otherObject) {
// true, wenn die Objekte identisch sind
if (this == otherObject)
return true;
// false, wenn otherObject == null
if (otherObject == null)
return false;
// false, wenn die Klassen nicht gleich sind
if (getClass() != otherObject.getClass())
return false;
Book other = (Book) otherObject;
// true, wenn title und author gleich
return title.equals(other.title)
&& author.equals(other.author);
}
}
public class Vergleich1 {
public static void main(String[] args) {
Book b1 = new Book("Java 6", "Samaschke");
Book b2 = new Book("Java 6", "Samaschke");
System.out.println(b1 == b2);
System.out.println(b2.equals(b1));
}
}
Auf der Konsole
false
true
50
2 Grundelemente von Java
51
2.10 Übungsaufgaben
Übungsaufgabe 2.1
Schreiben Sie ein Programm, das eine Zeichenkette (String) einliest und diese Zeichenkette
dann in umgekehrter Reihenfolge wieder ausgibt.
Beispiel
String: Heute ist Montag
gatnoM tsi etueH
Hinweis: Im Anhang ist eine Kurzbeschreibung der Methoden der Klasse "java.lang.String"
zu finden.
Übungsaufgabe 2.2
Schreiben Sie ein Programm zur grafischen Ausgabe der folgenden Funktion:
y = a+b ⋅ x+c ⋅ x 2 +d ⋅ x 3 +e ⋅ x 4
Das untenstehende Beispiel zeigt, wie die Parameter eingegeben werden sollen und wie die
Grafik auszugeben ist. Die Grafik besteht aus 17 Zeilen und 71 Spalten und soll so beschriftet
werden, wie es das Beispiel zeigt.
Auf der Konsole
Geben Sie die Parameter ein:
a: -2
b: -2
c: 0
d: 1
e: .1
4| y
|
*
|
*
|
|
*
|
*
|
*
|
*
x
----------------------------------------------------------------*------2
0|
*
2
***************
|
**
***
***** |
*
***
*****
**
**
| *****
***
*
|
************
**
|
*
-4|
2 Grundelemente von Java
52
Übungsaufgabe 2.3
Vervollständigen Sie die Klasse "IntStack" für Daten vom Typ "int" so, dass sich "main"
ausführen lässt und die gegebene Konsolenausgabe liefert.
class IntStack {
int stack[], stackSize, index;
...
...
}
public class Test {
public static void main(String[] args) {
IntStack stack1 = new IntStack(2);
stack1.push(2);
stack1.push(5);
stack1.push(1);
// Stack ist voll
while(!stack1.empty())
System.out.println("Stack 1: " + stack1.pop());
stack1.pop();
// Stack ist leer
IntStack stack2 = new IntStack(3);
stack2.push(-3);
stack2.push(44);
stack2.push(-9);
while(!stack2.empty())
System.out.println("Stack 2: " + stack2.pop());
}
}
Auf der Konsole
Stack
Stack
Stack
Stack
Stack
Stack
Stack
ist voll!
1: 5
1: 2
ist leer!
2: -9
2: 44
2: -3
3 OOP II : Vererbung
Aus Abschnitt 1.2 sind die folgenden Begriffe schon bekannt:
− Klassen und Objekte;
− Instanz- und Klassenvariablen (static), sowie Konstanten (static final);
− Konstruktor, Standardkonstruktor, Methoden und Überladen von Methoden;
− Instanz- und Klassenmethoden (static),
− die Referenz "this".
3.1
Das Konzept der Vererbung
Eine Klasse kann ihre Daten und Methoden an eine andere Klasse "vererben" (Bild 3.1)
Oberklasse (superclass)
Transportmittel
"ist ein"-Beziehung
Landtransportmittel
Abgeleitete Klasse(subclass)
Bild 3.1 Oberklasse und abgeleitete Klasse
Die vererbende Klasse wird Oberklasse (superclass) genannt, die erbende Klasse bezeichnet
man als abgeleitete Klasse (subclass). Es ist üblich, dass der Vererbung eine "ist ein"Beziehung zugrunde liegt: ein Landtransportmittel ist ein Transportmittel. Wenn man den
Pfeil in diesem Sinn liest, ist seine Richtung sinnvoll.
Die Vererbung ist nicht auf zwei Klassen beschränkt. Bild 3.2 zeigt ein umfangreicheres
Beispiel.
− Die Klasse "Transportmittel" enthält Daten und Methoden, die allen Transportmitteln
gemeinsam sind.
− Von dieser Klasse wird "Landtransportmittel" abgeleitet, und es werden die für diese
Teilmenge der Transportmittel notwendigen Daten und Methoden hinzugefügt.
− Ein "Auto" ist ein "Landtransportmittel". Die Klasse "Landtransportmittel" stellt also aus
Sicht der Klasse "Auto" die Oberklasse dar.
− Die Klasse "Amphibienfahrzeug" erbt Daten und Methoden von "Auto" und von
"Wassertransportmittel". Ein Amphibienfahrzeug ist ein Auto, und ein Amphibienfahrzeug ist ein Wassertransportmittel. In Java ist Mehrfachvererbung nicht zulässig.
Eine Oberklasse stellt eine Generalisierung der von ihr abgeleiteten Klassen dar. "Landtransportmittel" enthält z.B. für "Rikscha", "Fahrrad" und "Auto" gemeinsame Daten und
Methoden. Jede abgeleitete Klasse fügt dann für sie typische Dinge hinzu oder legt das
geerbte Verhalten neu fest.
Eine abgeleitete Klasse ist eine Spezialisierung ihrer Oberklasse. "Rikscha", "Fahrrad" und
"Auto" sind z.B. spezielle "Landfahrzeuge". In der jeweiligen Klasse brauchen nur noch die
Abweichungen beschrieben zu werden, alles andere kann man wiederverwenden.
54
3 OOP II : Vererbung
Transportmittel
Klasse
breite
hoehe
Daten
Methoden
bewegen()
Rikscha
...
...
Landtransportmittel
Wassertransportmittel
radzahl
fahren()
schieben()
bruttoregistertonnen
anlegen()
ablegen()
Fahrrad
...
...
Auto
spritverbrauch
starten()
fahren()
tanken()
Amphibienfahrzeug
hersteller
schottenDichtmachen()
Bild 3.2 Beispiel zur Vererbung von Daten und Methoden
Im Programmcode kennzeichnet man die Vererbung in der Kopfzeile der abgeleiteten Klasse.
class Transportmittel
{ double breite, hoehe;
void bewegen() {...}
}
class Landtransportmittel extends Transportmittel
{ int radzahl;
void fahren() {...}
void schieben() {...}
}
class Auto extends Landtransportmittel
{ double spritverbrauch;
void starten() {...}
void fahren() {...}
void schieben() {...}
}
...
...
...
3 OOP II : Vererbung
55
Im folgenden Programmausschnitt wird als Beispiel ein Objekt "auto" vom Typ "Auto"
vereinbart.
public static void main(String[] args) {
Auto auto = new Auto();
...
}
Bild 3.3 veranschaulicht, dass "auto" je ein Objekt der Klassen "Landtransportmittel" und
"Transportmittel" enthält. In ein Objekt einer abgeleiteten Klasse ist also je ein anonymes
Objekt seiner Oberklasse(n) eingebettet. Zu "auto" gehören folglich die Daten "breite",
"hoehe", "radzahl" und "spritverbrauch", sowie die Methoden "bewegen()", "fahren()",
"schieben()", "starten()", "fahren()" und "tanken()".
Auto
Landtransportmittel
Transportmittel
breite
hoehe
bewegen()
radzahl
fahren()
schieben()
spritverbrauch
starten()
fahren()
tanken()
Bild 3.3 Ein Objekt der Klasse "Auto" enthält je ein Objekt der Klassen "Landtransportmittel" und
"Transportmittel"
Aufgabe 3.1
Warum lässt der Compiler die Zuweisung 1 zu, meldet dann aber für die Zuweisung 2 einen
Fehler?
...
...
...
(Programmcode zu Bild 3.3)
public class Test {
public static void main(String[] args) {
Landtransportmittel ltm = new Landtransportmittel();
Auto auto = new Auto();
ltm = auto;
auto = ltm;
}
}
// Zuweisung 1
// Zuweisung 2
3 OOP II : Vererbung
56
Ein Beispiel
Ein Unternehmen hat unterschiedliche Mitarbeiter: Arbeiter, Vertreter, Manager, usw. Die
Mitarbeiter unterscheiden sich z.B. in der Art, in der ihr Monatsgehalt berechnet wird.
• Arbeiter: Stundenlohn * Stunden,
• Vertreter: Stundenlohn * Stunden + Provision * Anzahl,
• Manager: Festes Gehalt,
• usw.
Für die Mitarbeiter sind Namen und gehaltsrelevante Daten zu speichern und die Namen und
das zugehörige Gehalt sind auszugeben.
In einem ersten Programm wird für jeden Mitarbeitertyp eine eigene Klasse mit allen
benötigten Daten vereinbart. Wir nutzen hier nicht das Konzept der Vererbung.
Beispiel, Employee1
class Worker //Arbeiter
{
String name;
// Name
double hourlyWage; // Stundenlohn
double hours;
// Stunden
Worker(String n, double hW, double h) {
name = new String(n);
hourlyWage = hW;
hours = h;
}
void display() {
System.out.println(name + " "
+ hourlyWage * hours + " EUR");
}
}
class SalesAgent //Vertreter
{
String name;
// Name
double hourlyWage; // Stundenlohn
double hours;
// Stunden
double commission; // Provision
double count;
// Anzahl
SalesAgent(String n, double hW, double h,
double c, double cn) {
name = new String(n);
hourlyWage = hW;
hours = h;
commission = c;
count = cn;
}
void display() {
System.out.println(name + " "
+ (hourlyWage * hours + commission * count)
3 OOP II : Vererbung
57
+ " EUR");
}
}
class Manager // Manager
{
String name;
// Name
double salary;
// Gehalt;
Manager(String n, double s) {
name = new String(n);
salary = s;
}
void display() {
System.out.println(name + "
+ salary + " EUR");
}
"
}
public class Employee1
{
public static void main(String[] args) {
Worker w = new Worker("Meyer, Klaus", 15.82, 151.00);
SalesAgent s = new SalesAgent("Hamer, Peter",
8.80, 150.0, 60.28, 22.0);
Manager m = new Manager("Kramer, Hans", 3501.27);
w.display();
s.display();
m.display();
}
}
Auf der Konsole
Meyer, Klaus
Hamer, Peter
Kramer, Hans
2388.82 EUR
2646.16 EUR
3501.27 EUR
Es liegt hier nah, zur Lösung eine Klassenhierarchie einzusetzen. Ein entscheidender Vorteil
liegt darin, dass bestehender Code wieder verwendet werden kann. Bereits bestehende
Konstruktoren und Methoden der Superklassen können wieder verwendet werden. Dort
durchgeführte Sicherheitsprüfungen und Plausibilitätschecks werden übernommen, und so
können robuste Programme geschrieben werden. Das Beispiel "Employee2" zeigt die
Vorgehensweise. Es wir dieselbe Aufgabenstellung programmiert wie in Beispiel
"Employee1" aber diesmal unter Verwendung von Vererbung. Von der Basisklasse Employee
wird die Klasse Worker abgeleitet und von dieser wiederum die Klasse SalesAgent. Von der
Klasse Employee wird außerdem die Klasse Manager abgeleitet (Bild 3.4). Mit "super(..)"
kann in einer Klasse jeweils der Konstruktor der Oberklasse, der "Vatersklasse", aufgerufen
werden. Dadurch ergibt sich in Klassenhierarchien eine Verkettung der Konstruktoren.
Methoden einer Superklasse, wie im nachfolgenden Beispiel die Methode display() der Klasse
Worker, können in einer Subklasse überschrieben werden. Beim Überschreiben einer
3 OOP II : Vererbung
58
Methode müssen aber die Signatur und der Rückgabewert der überschreibenden Methode
identische sein mit der Signatur und dem Rückgabewert der überschriebenen Methode.
Employee
Worker
Manager
SalesAgent
Bild 3.4 Klassenhierarchie
Beispiel, Employee2
class Employee
{
String name;
// Name
Employee(String n) {
name = new String(n);
}
void displayName() {
System.out.print(name + " " );
}
}
class Worker extends Employee
{
double hourlyWage; // Stundenlohn
double hours;
// Stunden
Worker(String n, double hW, double h) {
super(n);
hourlyWage = hW;
hours = h;
}
double workerSalary() {
return hourlyWage * hours;
}
void display() {
displayName();
System.out.println(workerSalary() + " EUR");
}
}
3 OOP II : Vererbung
class SalesAgent extends Worker
{
double commission; // Provision
double count;
// Anzahl
SalesAgent(String n, double hW, double h,
double c, double cn) {
super(n, hW, h);
commission = c;
count = cn;
}
void display() {
displayName();
System.out.println((workerSalary() + commission*count)
+ " EUR");
}
}
class Manager extends Employee
{
double salary; // Gehalt
Manager(String n, double s) {
super(n);
salary = s;
}
void display() {
displayName();
System.out.println(salary + " EUR");
}
}
public class Employee2
{
public static void main(String[] args)
{
Worker w = new Worker("Meyer, Klaus", 15.82, 151.00);
SalesAgent s = new SalesAgent("Hamer, Peter",
8.80, 150.0, 60.28, 22.0);
Manager m = new Manager("Kramer, Hans", 3501.27);
w.display();
s.display();
m.display();
}
}
Auf der Konsole
Meyer, Klaus
Hamer, Peter
Kramer, Hans
2388.82 EUR
2646.16 EUR
3501.27 EUR
59
3 OOP II : Vererbung
60
Mit "super(..)" wird jeweils der Konstruktor der Oberklasse, des "Vatersklasse", aufgerufen.
Unmittelbare Zugriffe auf den Konstruktor der "Großvaterklasse" sind in Java nicht möglich
In "SalesAgent" ist also z.B. kein Zugriff auf den Konstruktor von "Employee" durchführbar.
(Anweisungen wie "super.super(..)" sind nicht zulässig.)
Hinweis: Wenn ein Konstruktor die Anweisung "super(...)" enthält, muss sie die erste
Anweisung dieses Konstruktors sein!
Aufgabe 3.2
Im Beispiel "Empoyee2" wird in der Klasse "Employee" die Methode "displayName()" in
"display()" umbenannt.
class Employee {
String name;
Employee(String n) { name = new String(n); }
void display() { System.out.print(name + " " ); }
}
Erläutern Sie dadurch verursachte Programmänderungen und die dadurch entstehenden
Probleme.
Im nachfolgenden Programmbeispiel "Employee3" wird der Konstruktor der Oberklasse
"Employee" nicht mehr explizit mit "super(...)" aufgerufen. Die Konsolenausgabe zeigt, dass
der Compiler dann selbständig einen Aufruf des Standardkonstruktors der Oberklasse
generiert.
Beispiel, Employee3
class Employee
{
String name;
Employee() {
System.out.println("--- Employee-Konstruktor ---" );
}
void displayName() {
System.out.print(name + " " );
}
}
class Worker extends Employee
{
double hourlyWage;
double hours;
Worker(String n, double hW, double h) {
name = new String(n);
hourlyWage = hW;
hours = h;
}
3 OOP II : Vererbung
61
double workerSalary() {
return hourlyWage * hours;
}
void display() {
displayName();
System.out.println(workerSalary() + " EUR");
}
}
public class Employee3
{
public static void main(String[] args) {
Worker w = new Worker("Meyer, Klaus", 15.82, 151.00);
w.display();
}
}
Auf der Konsole
--- Employee-Konstruktor --Meyer, Klaus 2388.82 EUR
Hinweis: Wenn der Compiler im Konstruktor einer abgeleiteten Klasse als erste Anweisung
kein "super(..)" findet, erzeugt er selbständig einen Aufruf des Standardkonstruktors der
Oberklasse2.
Aufgabe 3.3
Im Beispiel "Empoyee3" wird in der Klasse "Employee" der Standardkonstruktor auskommentiert.
class Employee {
...
// Employee() { System.out.println("Employee-Konstruktor" ); }
...
Kommt es dann zu einer Fehlermeldung des Compilers?
Im nachfolgenden Beispiel werden noch einmal das Überschreiben von Methoden und das
Verdecken von Membervariablen in einer Vererbungshierarchie dargestellt. Die
überschriebene Instanzmethode methode() einer Vaterklasse kann mit super.methode()
innerhalb von Instanzmethoden der abgeleiteten Sohnklasse angesprochen werden. Der
Aufruf super.methode() wird dabei vom Compiler in ((Vaterklasse) this).methode()
umgesetzt. Vom Verdecken einer Membervariable spricht man, wenn es in der Sohnklasse
eine Membervariable mit gleichem Namen gibt, wie in der Vaterklasse. Der Zugriff auf eine
von der Vaterklasse ererbte, verdeckte Instanzvariable x erfolgt mit super.x. Der Zugriff
super.x wird dabei vom Compiler in den Zugriff ((Vaterklasse) this).x umgesetzt.
2
Es gibt eine Ausnahme zu dieser Regel, wenn ein Konstruktor einer Klasse einen anderen Konstruktor
derselben Klasse aufruft ([2], [3]).
3 OOP II : Vererbung
62
Beispiel, Ueberschreiben
//------------------------------------------------------------class A {
char c = 'A';
void f1() {System.out.println("f1 aus " + c);}
void f2() {System.out.println("f2 aus " + c);}
void f3() {System.out.println("f3 aus " + c);}
}
//------------------------------------------------------------class B extends A {
char c = 'B';
void f1() {System.out.println("f1 aus
"
void f2() {System.out.println("f2 aus
"
" + c +
extends " + super.c);}
" + c +
extends " + super.c);}
}
//------------------------------------------------------------class C extends B {
char c = 'C';
void f1() {
System.out.println("f1 aus " + c +
" extends " + super.c);
super.f1();
super.f2();
super.f3();
}
}
//------------------------------------------------------------public class SuperDemo {
public static void main(String[] args) {
A a = new A();
a.f1();
a.f2();
a.f3();
B b = new B();
b.f1();
b.f2();
b.f3();
C c = new C();
c.f1();
c.f2();
c.f3();
}
}
3 OOP II : Vererbung
63
Auf der Konsole
f1
f2
f3
f1
f2
f3
f1
f1
f2
f3
f2
f3
3.2
aus
aus
aus
aus
aus
aus
aus
aus
aus
aus
aus
aus
A
A
A
B
B
A
C
B
B
A
B
A
extends A
extends A
extends B
extends A
extends A
extends A
Die Basisklasse Object
In Java besitzt jede selbstdefinierte Klasse eine Superklasse. Wenn man diese Superklasse
nicht mit "extends" angibt, dann wird automatisch die Klasse "Object" zur Superklasse
(Bild 3.5).
Object
Employee
Worker
Manager
SalesAgent
Bild 3.5 Klassenhierarchie zum Beispiel "Employee2" mit Superklasse Object
Die Klasse "Object"
− ist die einzige Klasse, die keine Superklasse besitzt und
− ihre Methoden können in jedem Javaobjekt aufgerufen werden.
Im folgenden Beispiel werden die drei Methoden "toString", "hashCode" und "equals" eingesetzt, die von "Object" geerbt worden sind. Eine weitere Methode ("clone") folgt in einem
späteren Abschnitt.
Beispiel, Object1
package consolepack;
class Employee {
// extends Object
String name;
Employee(String n) { name = new String(n); }
}
3 OOP II : Vererbung
64
public class Object1 {
public static void main(String[] args) {
Employee e1 = new Employee("Meyer, Klaus");
Employee e2 = new Employee("Meyer, Klaus");
System.out.println(e1.toString());
System.out.println(e2);
System.out.println(Integer.toHexString(e1.hashCode()));
System.out.println(Integer.toHexString(e2.hashCode()));
System.out.println(e1.equals(e2));
System.out.println(e1 == e2);
}
}
Auf der Konsole
consolepack.Employee@1cde100
consolepack.Employee@16f0472
1cde100
16f0472
false
false
Die Methode "hashCode" berechnet einen numerischen Wert, der als Schlüssel zur Speicherung eines Objekts in einer Hash Tabelle eingesetzt werden kann. Weitergehende Informationen sind z.B. in [2] zu finden. Im folgenden Beispiel werden die geerbten Methoden
"toString" und "equals" durch eigene Methoden ersetzt ("überschrieben").
Beispiel, Object2
class Employee {
// extends Object
String name;
Employee(String n) {
name = new String(n);
}
public String toString() {
return name;
}
public boolean equals(Object obj) {
return name.equals(((Employee) obj).name);
}
}
public class Object2 {
public static void main(String[] args) {
Employee e1 = new Employee("Meyer, Klaus");
Employee e2 = new Employee("Meyer, Klaus");
System.out.println(e1.toString());
System.out.println(e2);
System.out.println(e1.equals(e2));
System.out.println(e1 == e2);
}
}
3 OOP II : Vererbung
65
Auf der Konsole
Meyer, Klaus
Meyer, Klaus
true
false
Ein anderes Beispiel für das Überschreiben der equals()-Methode hatten wir bereits in dem
Kapitel 2.9 gesehen. Dort war die equals()-Methode in geeigneter Weise für die Klasse Book
überschrieben worden. In der Klasse String ist bereits eine geeignete equals()-Methode
implementiert, die zwei Zeichenketten Buchstaben für Buchstaben auf inhaltliche Gleichheit
prüft.
3.3
Typkonvertierung in Vererbungshierarchien
Wir betrachten die folgende Klassenhierarchie bestehend aus der Vaterklasse Vater und
der davon abgeleiteten Sohnklasse Sohn.
class Vater {
int valueVater;
public void methodVater() {...}
}
class Sohn extends Vater {
int valueSohn;
public void methodSohn() {...}
}
Wir betrachten eine Zuweisung, bei der eine Referenzvariable vom Typ der Sohnklasse einer
Referenzvariablen vom Typ der Vaterklasse zugewiesen wird.
Sohn refSohn = new Sohn();
Vater refVater = refSohn;
Die Referenzvariable refVater vom Typ Vater zeigt damit auf ein Objekt der Klasse
Sohn. Dies ist zulässig, weil ein Sohnobjekt durch den Vererbungsmechanismus auch alle
Eigenschaften eines Vaterobjektes besitzt. Bei der Zuweisung findet eine implizite
Typkonvertierung auf den Typ Vater statt mittels des cast-Operators (Vater) angewandt
auf die Referenz refSohn. Allerdings sieht die Referenz refVater vom Typ Vater nur
die Vateranteile des Sohnobjekts, und die Anweisung refVater.methodeSohn() führt
natürlich zu einem Compilerfehler, da zur Compile-Zeit unter Umständen ja noch gar nicht
feststeht, ob die Referenz refVater auf ein Sohn- oder ein Vaterobjekt zeigt. Eine solche
implizite Typkonvertierung in eine Superklasse wird auch als Up-Cast bezeichnet.
Die umgekehrte Zuweisung,
Vater refVater = new Vater();
Sohn refSohn = refVater;
3 OOP II : Vererbung
66
bei der eine Referenzvariable vom Typ der Vaterklasse einer Referenzvariablen vom Typ der
Sohnklasse zugewiesen wird, ist unzulässig und führt zu einem Compilerfehler. Dies ist
einleuchtend, weil ein Vaterobjekt nicht alle Eigenschaften eines Sohnobjektes besitzt. Man
kann diese Zuweisung erzwingen durch eine explizite Typkonvertierung der Form
Sohn refSohn = (Sohn) refVater;
Eine solche explizite Typkonvertierung in eine Subklasse wird auch als Down-Cast
bezeichnet. Man muss aber beachten, dass die obige Zuweisung nur sinnvoll ist, wenn man
vorher sichergestellt hat, dass die Referenzvariable refVater tatsächlich auf ein Sohnobjekt
zeigt. Dies kann mit Hilfe des Operators instanceof geschehen, der nachfolgend noch genauer
beschrieben wird. Zeigt die Referenz refVater auf ein Objekt der Vaterklasse, so führt
obige Typkonvertierung zum Programmabsturz, und zwar wegen einer ClassCastException.
Mit dem Operator instanceof kann man ermitteln, zu welcher Klasse ein Objekt gehört.
Der Ausdruck "x instanceof y" liefert genau dann "true", wenn "x" eine Instanz der Klasse "y"
oder einer von ihr abgeleiteten Klasse ist. Dies kann man nutzen, um obige Zuweisung sicher
zu programmieren. Mit dem instanceof-Operator lässt sich prüfen, ob das referenzierte Objekt
tatsächlich von dem angenommenen Typ ist. Mit diesem Ergebnis kann die Referenz dann in
den entsprechenden Typ down-gecastet werden.
if (refVater instanceof Sohn)
Sohn refSohn = (Sohn) refVater;
3.4
Verwendung des Operators instanceof
Das Up-casten und das sichere Down-casten mit dem Operator instanceof wird in der Praxis
häufig genutzt, wenn in einer Collection oder auch einem Array Objekte aus einer
Vererbungshierarchie abgespeichert werden sollen. Die Elemente des Arrays sind dann
Referenzen vom Typ der Basisklasse. Diesen Referenzen können auch Instanzen vom Typ der
abgeleiteten Klassen zugewiesen werden. Auf diese Weise können Objekte der gesamten
Vererbungshierarchie in dem Array abgelegt werden. Mit dem instanceof-Operator kann man
nun feststellen, von welchem Klassentyp ein bestimmtes Array Element zur Laufzeit ist, und
dann die entsprechenden Methoden aufrufen. Dazu das folgende Beispiel.
Beispiel, OpInstanceof
class Shape {
int x, y;
Shape(int x, int y) {this.x = x; this.y = y;}
}
class Circle extends Shape {
int r;
Circle(int x, int y, int r) { super(x, y); this.r = r; }
}
class Text extends Shape {
String s;
Text(int x, int y, String s) { super(x, y); this.s = s; }
}
3 OOP II : Vererbung
67
class BoldText extends Text {
BoldText(int x, int y, String s) { super(x, y, s); }
}
//--------------------------------------------------------public class OpInstanceof {
public static void main(String[] args) {
Shape shape[] = new Shape[4];
shape[0]
shape[1]
shape[2]
shape[3]
=
=
=
=
new
new
new
new
Shape(10, 20);
Circle(10, 20, 200);
Text(10, 20, "Monday to Friday");
BoldText(10, 20, "Sunday");
for(int i = 0; i < shape.length; i++)
System.out.println(shape[i] instanceof Text);
for(int i = 0; i < shape.length; i++) {
if(shape[i] instanceof BoldText)
System.out.println("Bold: " + ((BoldText) shape[i]).s);
else if(shape[i] instanceof Text)
System.out.println(((Text) shape[i]).s);
}
}
}
Auf der Konsole
false
false
true
true
Monday to Friday
Bold: Sunday
3.5
Pakete und Sichtbarkeit
Pakete
Pakete (Packages) dienen dazu, die Software eines größeren Projektes in inhaltlich
zusammenhängende Bereiche einzuteilen. Jedes Paket entspricht dabei einer KlassenBibliothek. Java Klassen, die zu einem Paket gehören, lassen sich dann mit der schon
bekannten "import"-Anweisung in ein Programm einbinden. Pakete können aus
verschiedenen Quellcode-Dateien bestehen. Jede Quellcode-Datei, die zu einem Paket gehört,
muss mit derselben Paketdeklaration beginnen. Alle Programmeinheiten einer QuellcodeDatei gehören auf jeden Fall zum gleichen Paket. Im folgenden Beispiel "PackageDemo" wird
die folgende Verzeichnisstruktur benutzt:
<Pfad>
<Pfad>\demo
<Pfad>\demo\tools
// Pfad für PackageDemo
// Subdirectory mit Klassen A und B
// Subdirectory mit Klasse C
3 OOP II : Vererbung
Beispiel, PackageDemo (nach [1])
In Datei "<Pfad>\PackageDemo.java"
import demo.*;
import demo.tools.*;
public class PackageDemo {
public static void main (String args[]) {
A a = new A();
B b = new B();
C c = new C();
a.Hello();
b.Hello();
c.Hello();
}
}
In Datei "<Pfad>\demo\A.java"
package demo;
public class A {
public void Hello(){
System.out.println("--- A ---");
}
}
In Datei "<Pfad>\demo\B.java"
package demo;
public class B {
public void Hello(){
System.out.println("--- B ---");
}
}
In Datei "<Pfad>\demo\tools\C.java"
package demo.tools;
public class C {
public void Hello(){
System.out.println("--- C ---");
}
}
Auf der Konsole
--- A ----- B ----- C ---
68
69
3 OOP II : Vererbung
Beim Übersetzen von "PackageDemo" prüft der Compiler selbsttätig, ob die ".class"-Dateien
in den Packages "demo" und "demo.tools" noch aktuell sind. Andernfalls übersetzt er die
zugehörigen Quelldateien erneut. Es wird übersetzt, wenn zu einer ".java"-Datei noch keine
".class"-Datei existiert oder eine ".java"-Datei neuer als die zugehörige ".class"-Datei ist.
−
−
Wenn eine "package"-Anweisung verwendet wird, muss sie die erste Anweisung in der
Quelldatei sein.
Klassen ohne "package"-Anweisung gehören zu einem namenlosen Default-Paket
(default package).
Default-Pakete sind für kleinere Programmierprojekte gedacht, für die es sich nicht lohnt,
eigene Packages anzulegen. In aller Regel gehören alle in einem Verzeichnis oder in einem
Unterverzeichnis gespeicherte Klassen zu ein- und demselben Default-Paket.
Sichtbarkeit
Tabelle 3.1 gibt einen Überblick über die Sichtbarkeit von Daten und Methoden. Die
Sichtbarkeit lässt sich mit Modifikatoren verändern (private, <default>, protected, public).
Tabelle 3.1 Sichtbarkeit von Daten und Methoden
In der Klasse
In einer abgeleiteten Klasse
Im Paket (package)
Überall
private
X
<default>
X
X
protected
X
(X)
X
public
X
X
X
X
(X) = Mit Einschränkungen.
−
−
−
−
Mit "private" vereinbarte Daten und Methoden sind nur in der eigenen Klasse sichtbar.
Ohne Angabe eines Modifikators (<default>) vereinbarte Daten und Methoden sind nur
im eigenen Paket sichtbar.
Mit "protected" vereinbarte Daten und Methoden sind im eigenen Paket und mit Einschränkungen in abgeleiteten Klassen in anderen Paketen sichtbar.
Mit "public" vereinbarte Daten und Methoden sind überall sichtbar.
Im Folgenden wollen wir den Zugriffsschutz von Membervariablen und Methoden mittels
Modifikatoren an Hand eines Beispiels noch deutlicher machen. Für die weitere Diskussion
gehen wir davon aus, dass sich die Klassen A, B und C im Paket x befinden und die Klassen
D und E im Paket y. Die Klasse C im Paket x ist von der Klasse A abgeleitet. Ebenso ist die
Klasse E im Paket y von der Klasse A abgeleitet. Die Klasse A im Paket x hat die
Membervariable valueInA und die Methode methodInA(). Als Diskussionsgrundlage sollen
diese zunächst den Zugriffsschutz "private", dann "<default>", dann "protected" und
schließlich "public" haben.
Beispiel: ProtectedDemo
//--- package x ------------------package x;
3 OOP II : Vererbung
public class A {
private int valueInA = 0;
private void methodInA() {
System.out.println("Method in A");
}
}
package x;
public class B {
public void methodInB() {
A refA = new A();
refA.valueInA = 5;
// z1
refA.methodInA();
// z2
}
}
package x;
public class C extends A {
public void methodInC() {
valueInA = 2;
// z1
methodInA();
// z2
A refA = new A();
refA.valueInA = 5; // z3
refA.methodInA();
// z4
}
}
// --- package y ------------------package y;
import x.A;
public class D {
public void methodInD() {
A refA = new A();
refA.valueInA = 5; // z1
refA.methodInA();
// z2
}
}
package y;
import x.A;
public class E extends A {
public void methodInE() {
valueInA = 2;
// z1
methodInA();
// z2
A refA = new A();
refA.valueInA = 5; // z3
refA.methodInA();
// z4
}
}
70
71
3 OOP II : Vererbung
Zugriffsmodifikator "private":
Haben die Membervariablen und Methoden der Klasse A das Zugriffsrecht "private", so
kann aus keiner der Klassen B, C, D und E auf die privaten Membervariablen und
Methoden der Klasse A zugegriffen werden.
Zugriffsmodifikator "<default>":
Haben die Membervariablen und Methoden der Klasse A das Zugriffsrecht "<default>",
so kann aus Klassen heraus, die im gleichen Paket liegen, zugegriffen werden. Die
Zugriffe auf die Variable valueInA und die Methode methodInA() in den Klassen B und C
sind also zulässig. Die Klassen D und E haben keinen Zugriff auf die Variable valueInA
und die Methode methodInA() der Klasse A.
Zugriffsmodifikator "protected":
Haben die Membervariablen und Methoden der Klasse A das Zugriffsrecht "protected", so
besteht gegenüber dem Zugriffsrecht "<default>" ein etwas erweiterter Zugriff. Auf
Membervariablen und Methoden mit dem Zugriffsrecht "protected" kann aus Klassen im
gleichen Paket zugegriffen werden, und zusätzlich können auch Subklassen in anderen
Paketen auf die von der Vaterklasse geerbten Membervariablen und Methoden zugreifen.
Bedingung ist allerdings, dass nur auf die eigenen ererbten Membervariablen und
Methoden zugegriffen wird. Wird jedoch in der Klasse E ein neues Objekt der Klasse A
angelegt, so darf auf die protected geschützten Membervariablen und Methoden dieses
Objekts nicht zugegriffen werden. Die Zugriffe z1 und z2 in der Klasse E sind also
zulässig, die Zugriffe z3 und z4 sind jedoch nicht zulässig.
Zugriffsmodifikator "public":
Haben die Membervariablen und Methoden der Klasse A das Zugriffsrecht "public", so
kann aus allen Klassen B, C, D und E auf die Membervariablen und Methoden der Klasse
A zugegriffen werden.
3.6
Übungsaufgaben
Übungsaufgabe 3.1
Programmieren und testen Sie die in
Bild 3.6 dargestellten Klassen. Mit den
Klassen soll "main()" zu der gegebenen
Textausgabe auf der Konsole führen.
class Konto {
...
...
}
class PrivatGirokonto
extends Konto {
...
...
}
PrivatGirokonto
Konto
kontonummer
bankleitzahl
display()
kundenname
kundenadresse
zinssatz
kontostand
display()
Bild 3.6 Klassen "Konto" und "PrivatGirokonto"
3 OOP II : Vererbung
public class Test {
public static void main(String[] args) {
PrivatGirokonto pGiro = new PrivatGirokonto(
32082455L, 20040020L,"Meyer, Boris",
"Alphaeck 12, 22624 Hamburg", 3.20, 6123.25);
pGiro.display();
}
}
Auf der Konsole
Meyer, Boris
Alphaeck 12, 22624 Hamburg
Kontonr.
32082455
BLZ
20040020
Zinsen
3.2%
Kontostand 6123.25 DM
72
4 Grafikprogramme, Maus und Tastatur
Ab diesem Abschnitt werden Klassen und Methoden aus dem Swing Toolset eingesetzt.
Swing war der Name eines Projekts bei Sun Microsystems, in dem neue Komponenten und
das zugehörige API (Application Programming Interface) entwickelt wurden. Im Gegensatz
zum Vorläufer AWT (Abstract Windows Toolkit) benutzt Swing nur noch selten plattformspezifische GUI Ressourcen (Graphical User Interface). Wenn z.B. ein Button gezeichnet
werden soll, übernimmt Swing diese Aufgabe selbst und gibt den Auftrag nicht etwa an den
GUI Manager weiter.
− Als Vorteile fallen plattformspezifische Besonderheiten weg, und man ist nicht mehr auf
den kleinsten gemeinsamen Nenner der unterstützten Betriebssysteme angewiesen.
− Dem stehen als Nachteile langsamere Ausführung und höherer Ressourcenbedarf gegenüber. Außerdem werden Swing Applets von Browsern nur selten unterstützt.
Jedes Programm mit einem Swing GUI muss mindestens eine der folgenden Hauptkomponenten enthalten: JFrame, JWindow, JDialog oder JApplet. Die Hauptkomponenten stellen
anderen Swing Komponenten Methoden zum Zeichnen und zur Ereignisbehandlung zur
Verfügung.
− JFrame: Ein Hauptfenster mit Rahmen, Kopfleiste und Standardschaltflächen.
− JWindow: Ein rahmenloses Fenster.
− JDialog: Ein sekundäres Fenster, das von einem anderen Fenster abhängig ist.
− JApplet: Ein Fenster für einen Ausgabebereich in einem Browser.
Im folgenden ersten Swing Beispiel wird die Klasse "HelloWorld1" von JFrame abgeleitet.
Die Programmumgebung ruft "paint" auf, wenn das Fenster neu gezeichnet werden soll. Das
ist z.B. nach dem Programmstart der Fall oder wenn das Fenster verdeckt worden war und
dann wieder sichtbar wird.
Beispiel, HelloWorld1
import java.awt.*;
import javax.swing.*;
// For "Graphics"
// For "JFrame"
public class HelloWorld1 extends JFrame {
// Draw whenever necessary. Do some fancy graphics
public void paint(Graphics g) {
setBackground(Color.GREEN);
// The pink oval
g.setColor(Color.PINK);
g.fillOval(10, 10, 330, 100);
// The red outline. Java doesn't support wide lines, so a
// 4-pixel wide line is simulated by drawing four ovals
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);
// The text
g.setColor(Color.BLACK);
g.setFont(new Font("Arial", Font.BOLD, 46));
4 Grafikprogramme, Maus und Tastatur
74
g.drawString("Hello World!", 40, 75);
}
public static void main(String[] args) {
JFrame window = new HelloWorld1();
window.setTitle("Hello World 1");
window.setSize(360, 150);
window.setLocation(100, 100);
// Exit the application using the System exit method.
// Use this only in applications
window.setDefaultCloseOperation(EXIT_ON_CLOSE);
window.setVisible(true);
}
}
Bild 4.1 zeigt das Ausgabefenster. Der Koordinatenursprung liegt in der oberen linken Ecke
des Gesamtfensters. Die Kopfleiste überdeckt
deshalb hier noch einen Teil des Ovals. Der
Fehler wird in den beiden folgenden Programmen korrigiert werden.
Bild 4.1 Hello World 1
Das Swing-Hauptfenster JFrame und seine Komponenten
JFrame enthält eine Hauptkomponente Root Pane, die ihrerseits die in Bild 4.2 zu sehenden
weiteren Komponenten besitzt.
Bild 4.2 Frame, Root Pane und Komponenten (aus [4])
Layered Pane (Geschichtete Scheibe)
Die Layered Pane wird eingesetzt, um die zugehörigen Komponenten Content Pane und
Menu Bar (Menüleiste) zu positionieren. Sie kann zusätzliche übereinander geschichtete
Komponenten mit in z-Richtung frei wählbarer Reihenfolge enthalten.
Content Pane
Dieser Container nimmt in der Regel die sichtbaren Komponenten eines Programms mit
Ausnahme der Menüleiste auf.
Menu Bar (optional)
Die Menüleiste enthält die Menüs einer Anwendung
Glass Pane (Glasscheibe)
Die Glass Pane ist normalerweise durchsichtig und wird in der Regel nicht zur Ausgabe
benutzt. Sie kann eingesetzt werden, wenn man das gesamte Fenster ansprechen will, um
z.B. Mauseingaben abzufangen.
Im Programm "HelloWorld2" ist eine erste Vorgehensweise zur Grafikausgabe in der Content
Pane zu finden.
4 Grafikprogramme, Maus und Tastatur
75
Beispiel, HelloWorld2
import java.awt.*;
import javax.swing.*;
public class HelloWorld2 extends JFrame {
public static void main(String[] args) {
JFrame window = new HelloWorld2();
window.setTitle("Hello World 2");
window.setSize(360, 150);
window.setLocation(100, 100);
window.setDefaultCloseOperation(EXIT_ON_CLOSE);
JPanel mp = new MyPanel();
window.getContentPane().add(mp);
window.setVisible(true);
}
}
class MyPanel extends JPanel {
MyPanel() { setBackground(Color.GREEN); }
// Draw whenever necessary. Do some fancy graphics
public void paintComponent(Graphics g) {
super.paintComponent(g);
// The pink oval
// ... Same code as in HelloWorld1
}
}
Bild 4.3 zeigt das Ausgabefenster. Der Fehler des
Programms "HelloWorld1" ist also behoben.
JPanel mp = new MyPanel();
window.getContentPane().add(mp);
Die Methode "getContentPane" gibt eine Referenz auf die Content Pane zurück. In die Content
Pane wird dann eine Instanz von "MyPanel"
eingefügt.
Bild 4.3 Hello World 2
class MyPanel extends JPanel {
Die Klasse "MyPanel" wird von "JPanel" (Tafel, Platte) abgeleitet.
public void paintComponent(Graphics g) {
super.paintComponent(g);
Die Programmumgebung ruft "paintComponent" auf, wenn das Fenster neu gezeichnet
werden soll. Der Aufruf von "super.paintComponent" führt hier dazu, daas das Fenster mit
der Hintergrundfarbe gefüllt wird.
Das Programm "HelloWorld3" zeigt eine zweite Vorgehensweise zur Grafikausgabe in der
Content Pane. Man leitet das Programm von "JPanel" ab und instanziiert "JFrame" in "main".
4 Grafikprogramme, Maus und Tastatur
76
Beispiel, HelloWorld3
import java.awt.*;
import javax.swing.*;
public class HelloWorld3 extends JPanel {
HelloWorld3() { setBackground(Color.GREEN); }
// Draw whenever necessary. Do some fancy graphics
public void paintComponent(Graphics g) {
super.paintComponent(g);
// The pink oval
// ... Same code as in HelloWorld1
}
public static void main(String[] args) {
JFrame window = new JFrame("Hello World 3");
window.setSize(360, 150);
window.setLocation(100, 100);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.getContentPane().add(new HelloWorld3());
window.setVisible(true);
}
}
Das Ausgabefenster entspricht Bild 4.3, in der Kopfleiste wird aber "Hello World 3" ausgegeben. In den weiteren Beispielen dieses Abschnitts wird in der Regel die in "HelloWorld3" verwendete Programmstruktur eingesetzt.
Das Programm "RandomStrings" gibt 25 Zeichenketten aus. Farbe und Position der einzelnen
Kopien werden dabei zufällig bestimmt. Die Schriftart wird ebenfalls zufällig aus fünf
Vorgaben gewählt
Beispiel, RandomStrings
import java.awt.*;
import javax.swing.*;
public class RandomSrrings extends JPanel {
private Font font[] = new Font[5];
RandomSrrings () {
// The font name can be a logical font name or a font face
// name. A logical name must be either: Dialog, DialogInput,
// Monospaced, Serif, SansSerif, or Symbol. If name is null,
// the name of the new Font is set to Default
font[0] = new Font("Serif", Font.BOLD, 14);
font[1] = new Font("SansSerif", Font.BOLD + Font.ITALIC, 24);
font[2] = new Font("Monospaced", Font.PLAIN, 20);
font[3] = new Font("Dialog", Font.PLAIN, 30);
font[4] = new Font("Serif", Font.ITALIC, 36);
setBackground(Color.black);
}
4 Grafikprogramme, Maus und Tastatur
77
public void paintComponent(Graphics g) {
super.paintComponent(g);
int width = getSize().width;
int height = getSize().height;
// Get width
// and height
for (int i = 0; i < 25; i++) {
int fontNum = (int) (5 * Math.random());
g.setFont(font[fontNum]);
float hue = (float) Math.random(); // hue = Farbton
// getHSBColor(float hue, float saturation,
//
float brightness)
g.setColor(Color.getHSBColor(hue, 1.0F, 1.0F));
// Select the position of the string, at random
int x = -50 + (int) (Math.random() * (width + 40));
int y = (int) (Math.random() * (height + 20));
// Draw the message
g.drawString("Hello", x, y);
}
}
public static void main(String[] args) {
JFrame window = new JFrame("Random Srrings");
window.setSize(360, 150);
window.setLocation(100, 100);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.getContentPane().add(new RandomSrrings());
window.setVisible(true);
}
}
Vom Programm wird das Fenster in Bild
4.4 ausgegeben. Das Programm hat aber
noch Nachteile.
− Wenn der Benutzer die Größe des
Fensters
ändert,
wird
erneut
"paintComponent" aufgerufen. Es
werden dann neue Kopien von
"Hello" ausgegeben und folglich ein
verändertes Bild gezeichnet.
− Wenn ein ehemals verdeckter Teil
des Fensters wieder sichtbar wird,
wird nur dieser Bereich des Fensters
erneuert. In Bild 4.5 ist zum Beispiel
die rechte Bildhälfte neu gezeichnet
worden.
Bild 4.4 Random Strings
Bild 4.5 Nur die rechte Bildhälfte wurde neu gezeichnet
Aufgabe 4.1
Schreiben Sie ein Programm "RandomStrings1". Ändern Sie dazu "RamdomStrings" so, dass
das Bild beim Neuzeichnen unverändert bleibt.
4 Grafikprogramme, Maus und Tastatur
4.1
78
Maus- und Tastaturereignisse
Grafische Programme sind ereignisgesteuert. Sie warten und reagieren auf äußere Ereignisse
(Events), wie z.B. das Verändern eines Fensters, das Bewegen der Maus oder das Betätigen
einer Taste. Es gibt Ereignisquellen (Event sources) und Ereignisempfänger (Event listener).
In Tabelle 4.1 sind Listener Interfaces (Empfängerschnittstellen) für Maus und Tastatur und
die zugehörigen Methoden zu finden. Der Java Begriff "Interface" wird in einem späteren
Abschnitt genauer erläutert.
Tabelle 4.1 Listener Interface für Maus und Tastatur und zugehörige Methoden
Listener Interface
MouseListener
MouseMotionListener
KeyListener
Methoden
mouseClicked
mouseEntered
mouseExited
mousePressed
mouseReleased
mouseDragged
mouseMoved
keyPressed
keyReleased
keyTyped
Auslösende Aktion(en)
Eine Maustaste wurde gedrückt und wieder
losgelassen
Der Mauszeiger "betritt" die Komponente
Der Mauszeiger verlässt die Komponente
Eine Maustaste wurde gedrückt
Eine Maustaste wurde losgelassen
Die Maus wurde bei gedrückter Taste bewegt
Die Maus wurde bewegt, ohne dass eine Taste
gedrückt war
Eine Taste wurde gedrückt
Eine Taste wurde losgelassen
Eine Taste wurde gedrückt und wieder losgelassen
Für Mausereignisse gibt es zwei Event Interfaces. "MouseListener" ist für Mausklicks zuständig, und "MouseMotionListener" reagiert auf Mausbewegungen. Diese Trennung ist aus
Performancegründen vorgenommen worden. Wenn der Benutzer die Maustaste 1 gefolgt von
der Maustaste 2 drückt und sie in derselben Reihenfolge wieder loslässt, werden zum Beispiel
die folgenden Methoden in der gegebenen Reihenfolge aufgerufen:
1)
2)
3)
4)
5)
6)
mousePressed
mousePressed
mouseReleased
mouseClicked
mouseReleased
mouseClicked
für
für
für
für
für
für
Maustaste
Maustaste
Maustaste
Maustaste
Maustaste
Maustaste
1
2
1
1
2
2
4.1.1 Maus
Vom folgenden Programm wird das Fenster in Bild
4.6 ausgegeben. Der Kreis lässt sich bei gedrückter
linker oder rechter Maustaste über das Fenster
ziehen.
Beispiel, CircleAndMouse
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
Bild 4.6 CircleAndMouse
// For MouseEvent, MouseMotionAdapter
public class CircleAndMouse extends JPanel {
private static final int R = 40;
private static final Color BG_COLOR = Color.WHITE;
79
4 Grafikprogramme, Maus und Tastatur
private static final Color FG_COLOR = Color.BLACK;
private int x = 40, y = 40; // Center of circle
CircleAndMouse() {
// Add MyMouseMotionListener to receive mouse motion events
addMouseMotionListener(new MyMouseMotionListener());
setBackground(BG_COLOR);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
draw(g); }
void draw(Graphics g) { display(g, FG_COLOR); }
void erase(Graphics g) { display(g, BG_COLOR); }
void display(Graphics g, Color c) {
g.setColor(c); g.drawOval(x-R, y-R, R+R, R+R); }
void moveTo(Graphics g, int xx, int yy) {
erase(g); x = xx; y = yy; draw(g); }
public static void main(String[] args) {
JFrame window = new JFrame("Circle and Mouse");
window.setSize(360, 200);
window.setLocation(100, 100);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.getContentPane().add(new CircleAndMouse());
window.setVisible(true);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - class MyMouseMotionListener extends MouseMotionAdapter {
public void mouseDragged(MouseEvent me) {
moveTo(getGraphics(), me.getX(), me.getY());
}
}
}
addMouseMotionListener(new MyMouseMotionListener());
Mit dieser Anweisung wird die Klasse "MyMouseMotionListener" als Ereignisempfänger
angemeldet. Danach wird ihre Methode "mouseDragged" (und gegebenenfalls auch "mouseMoved") bei den zugehörigen Mausereignissen aufgerufen.
class MyMouseMotionListener extends MouseMotionAdapter {
public void mouseDragged(MouseEvent me) {
moveTo(getGraphics(), me.getX(), me.getY());
}
}
Die Klasse "MyMouseMotionListener" ist vollständig in die Klasse "CirleAndMouse" eingebettet. "MyMouseMotionListener" ist damit eine innere Klasse (inner class). Eine innere
Klasse hat Zugriff auf alle Daten und Methoden der umgebenden Klasse. Das gilt auch dann,
wenn diese Daten und Methoden als "private" vereinbart worden sind. Adapterklassen, wie
hier z.B. "MouseMotionAdapter" werden in einem späteren Abschnitt erläutert.
4 Grafikprogramme, Maus und Tastatur
80
Das folgende Programm MouseDemo1 enthält sowohl einen "MouseListener" als auch einen
"MouseMotionListener". Darüber hinaus zeigt es, wie man Doppelklicks feststellen kann, die
jeweilige Maustaste bestimmt und beim Mausklick zusätzlich gedrückte Tasten ermittelt.
Beispiel, MouseDemo1
import java.awt.event.*;
import javax.swing.*;
public class MouseDemo1 extends JPanel
{
private long previousTime = 0;
MouseDemo1()
{
addMouseMotionListener(new MyMouseMotionListener());
addMouseListener(new MyMouseListener());
}
void printMessageMousePressed(String s, MouseEvent me)
{
long time = me.getWhen(); // Timestamp in ms
if(time - previousTime < 300)
s += "Double ";
previousTime = time;
if(me.isShiftDown()) // Test Shift
s += "Shift ";
if(me.isControlDown()) // Test Control
s += "Control ";
int mods = me.getModifiers();
if((mods & InputEvent.BUTTON1_MASK) != 0)
System.out.println(s + "Button 1"); // Left mouse button
if((mods & InputEvent.BUTTON2_MASK) != 0)
System.out.println(s + "Button 2"); // Middle or wheel
if((mods & InputEvent.BUTTON3_MASK) != 0)
System.out.println(s + "Button 3"); // Right
}
void printMessageMouseDragged(String s, MouseEvent me)
{
if(me.isShiftDown()) // Test Shift
s += "Shift ";
if(me.isControlDown()) // Test Control
s += "Control ";
int mods = me.getModifiers();
if((mods & InputEvent.BUTTON1_MASK) != 0)
System.out.println(s + "Button 1"); // Left mouse button
if((mods & InputEvent.BUTTON2_MASK) != 0)
System.out.println(s + "Button 2"); // Middle or wheel
if((mods & InputEvent.BUTTON3_MASK) != 0)
System.out.println(s + "Button 3"); // Right
}
4 Grafikprogramme, Maus und Tastatur
81
public static void main(String[] args)
{
JFrame window = new JFrame("Mouse Demo 1");
window.setSize(360, 200);
window.setLocation(100, 100);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.getContentPane().add(new MouseDemo1());
window.setVisible(true);
}
// --------------------------------------------------------class MyMouseMotionListener extends MouseMotionAdapter
{
public void mouseDragged(MouseEvent me)
{ printMessageMouseDragged("mouseDragged ", me); }
}
class MyMouseListener extends MouseAdapter
{
public void mousePressed(MouseEvent me)
{ printMessageMousePressed("mousePressed ", me); }
}
}
Auf der Konsole
mousePressed
mousePressed
mousePressed
mousePressed
mousePressed
mousePressed
mouseDragged
mouseDragged
...
Button 1
Button 1
Double Button 1
Shift Button 3
Shift Control Button 3
Shift Button 1
Shift Button 1
Shift Button 1
// Beginn eines Doppelklicks
// Rechte Maustatse
// Shift + Maustaste gedrückt und Maus bewegt
Aufgabe 4.2
Schreiben Sie das Programm "Duke", mit dem
ein in der Datei "Duke.gif" gespeichertes Bild
geladen und dargestellt werden kann (Bild 4.7).
"Duke" soll an die Position eines Mausklicks
springen und sich bei gedrückter Maustaste
bewegen lassen. Der folgende Programmausschnitt zeigt, wie man eine Bilddatei lesen und
ein Bild zeichnen kann.
Bild 4.7 Duke
...
public class Duke extends JPanel {
private Image duke;
private int hDuke, wDuke;
// Height and width of Duke
...
Duke() {
addMouseListener(new MyMouseListener());
4 Grafikprogramme, Maus und Tastatur
82
addMouseMotionListener(new MyMouseMotionListener());
setBackground(BG_COLOR);
duke = getToolkit().getImage("Duke.gif");
MediaTracker mt = new MediaTracker(this);
mt.addImage(duke, 0); // Identifier id = 0
// Wait until image with identifier 0 has been loaded
try {
mt.waitForID(0);
} catch (InterruptedException e) {}
wDuke = duke.getWidth(this);
hDuke = duke.getHeight(this);
}
...
// x, y is the upper left corner of the picture
void draw(Graphics g) { g.drawImage(duke,x-wDuke,y-hDuke,this); }
...
Die Methode "getToolkit" gibt eine Referenz auf einen "Werkzeugkasten" für die aktuelle
Umgebung zurück. Zu diesem Werkzeugkasten gehört die Methode "getImage", die den Lesevorgang für die als Parameter übergebene Bilddatei anstößt. (Wenn nicht der vollständige
Pfad, sondern nur ein Dateiname angegeben wird, erwartet Eclipse die Datei im Projektverzeichnis, also im Verzeichnis, in dem auch die Datei ".project" gespeichert ist.)
Die Klasse "MediaTracker" ermöglicht es, auf den Abschluss des Lesevorgangs zu warten.
Mit "addImage" wird "duke" in die Liste der zu verfolgenden Lesevorgänge aufgenommen,
und es wird der Identifier 0 zugewiesen. Die Methode "waitForID" kehrt erst dann in das
aufrufende Programm zurück, wenn die Datei mit dem angegebenen Identifier vollständig
gelesen worden ist.
4.1.2 Tastatur
Im folgenden Programm "CircleAndKeys" lässt sich ein Kreis mit den Pfeiltasten der Tastatur
bewegen. Dazu enthält "CircleAndKeys" einen "KeyListener". Das vom Programm ausgegebene Fenster stimmt Bild 4.6 überein.
Beispiel, CircleAndKeys
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class CircleAndKeys extends JPanel {
private static final int R = 40;
private static final Color BG_COLOR = Color.WHITE;
private static final Color FG_COLOR = Color.BLACK;
private int x = 40, y = 40; // Center of circle
CircleAndKeys() {
setBackground(BG_COLOR);
addKeyListener(new MyKeyListener());
// Request that this component gets the input focus
83
4 Grafikprogramme, Maus und Tastatur
setFocusable(true);
requestFocus();
}
public void paintComponent(Graphics g) {
super.paintComponent(g); draw(g); }
void draw(Graphics g) { display(g, FG_COLOR); }
void erase(Graphics g) { display(g, BG_COLOR); }
void display(Graphics g, Color c) {
g.setColor(c); g.drawOval(x-R, y-R, R+R, R+R);
}
void moveTo(Graphics g, int xx, int yy) {
erase(g); x = xx; y = yy; draw(g); }
public static void main(String[] args) {
JFrame window = new JFrame("Circle and Keys");
window.setSize(360, 200);
window.setLocation(100, 100);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.getContentPane().add(new CircleAndKeys());
window.setVisible(true);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - class MyKeyListener extends KeyAdapter {
public void keyPressed(KeyEvent ke) {
switch(ke.getKeyCode()) {
case KeyEvent.VK_DOWN:
// Valid key arrow down
moveTo(getGraphics(), x, y+5); break;
case KeyEvent.VK_UP:
// Arrow up
moveTo(getGraphics(), x, y-5); break;
case KeyEvent.VK_LEFT:
// Arrow left
moveTo(getGraphics(), x-5, y); break;
case KeyEvent.VK_RIGHT: // Arrow right
moveTo(getGraphics(), x+5, y); break;
}
}
}
}
Das folgende Programm "KeyDemo" zeigt, wie man auf wieder losgelassene Tasten reagieren
kann. Im Gegensatz zu gedrückten Tasten gibt es in diesem Fall keine Selbstwiederholung
(auto repeat). Zusätzlich werden die Funktionstasten "Shift" und "Ctrl" abgefragt.
Beispiel, KeyDemo
import java.awt.event.*;
import javax.swing.*;
public class KeyDemo extends JPanel {
KeyDemo() {
addKeyListener(new MyKeyListener());
setFocusable(true);
requestFocus();
}
4 Grafikprogramme, Maus und Tastatur
public static void main(String[] args) {
JFrame window = new JFrame("Key Demo");
window.setSize(360, 200);
window.setLocation(100, 100);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.getContentPane().add(new KeyDemo());
window.setVisible(true);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - class MyKeyListener extends KeyAdapter {
public void keyReleased(KeyEvent ke) {
String s = "KeyReleased ";
if(ke.isShiftDown())
s += "Umschalt ";
if(ke.isControlDown())
s += "Strg ";
switch(ke.getKeyCode()) {
// Two examples for valid key constants
case KeyEvent.VK_F1:
s += "Key F1"; break;
case KeyEvent.VK_F2:
s += "Key F2"; break;
default:
// Methods in class KeyEvent
s += KeyEvent.getKeyText(ke.getKeyCode());
}
System.out.println(s);
}
}
}
Auf der Konsole
KeyReleased
KeyReleased
KeyReleased
KeyReleased
KeyReleased
KeyReleased
KeyReleased
...
Rechts
Links
Key F1
Umschalt Key F1
Umschalt
Strg Key F2
Strg
//
//
//
//
//
//
//
Pfeiltaste ->
Pfeiltaste <Funktionstaste F1
Shift + F1
Shift
Strg + F2
Strg
84
4 Grafikprogramme, Maus und Tastatur
85
Aufgabe 4.3
Schreiben Sie ein Programm "Ellipse" zur grafischen Ausgabe der Funktion, wie es Bild 4.8 zeigt. Die folgende
Gleichung beschreibt eine Ellipse mit den beiden Halbachsen a und b.
x2 y2
+
=1
a 2 b2
−
Mit der Taste F1 lässt sich die Farbe von Achsenkreuz, Beschriftung und Ellipse ändern.
−
Wenn der Benutzer die Fenstergröße ändert, soll sich
das Diagramm der neuen Größe anpassen.
4.2
Bild 4.8 Ellipse
Übungsaufgaben
Übungsaufgabe 4.1 Schreiben Sie ein Programm "RandomStrings2". Ändern Sie dazu das in
Aufgabe 4.1 geschriebene Programm "RandomString1" so, dass die Anzahl der Strings proportional zur Fenstergröße anwächst, die "Stringdichte" also näherungsweise gleich bleibt.
Übungsaufgabe 4.2 Schreiben Sie ein Programm "StringsAndMouse". Ändern Sie dazu das
in Aufgabe 4.1 geschriebene Programm "RandomString1" so, dass neue Strings erzeugt und
ausgegeben werden, wenn der Benutzer eine Maustaste drückt.
Übungsaufgabe 4.3 Schreiben Sie ein Programm "MouseAndKeys". Ändern Sie dazu das
Programm "CircleAndMouse" aus dem vorigen Abschnitt so, dass
− mit den Funktionstasten F1 bis F4 unterschiedliche Farben für den Kreis ausgewählt
werden können und
− sich der Kreis sowohl mit der Maus als auch mit den Pfeiltasten der Tastatur bewegen
lässt.
5 Applets
Ein erstes Applet ist schon im Abschnitt 1.1 zu finden. Dort ist das Applet von der AWT
Klasse "Applet" abgeleitet worden. In den folgenden Beispielen wird stattdessen die Swing
Klasse "JApplet" eingesetzt. Vorteile und Nachteile sind schon im vorausgehenden Abschnitt
erläutert worden.
Beispiel, HelloWorldApplet
import java.awt.*;
import javax.swing.*;
public class HelloWorldApplet extends JApplet {
public void init () { getContentPane().add(new MyPanel()); }
}
class MyPanel extends JPanel {
private Font font;
MyPanel() {
font = new Font("Arial", Font.BOLD, 46);
setBackground (Color.GREEN);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.PINK);
g.fillOval(10, 10, 330, 100);
g.setColor(Color.RED);
g.drawOval(10,10, 330,
g.drawOval( 9, 9, 332,
g.drawOval( 8, 8, 334,
g.drawOval( 7, 7, 336,
100);
102);
104);
106);
g.setColor(Color.BLACK);
g.setFont(font);
g.drawString("Hello World!", 40, 75);
}
}
Datei "HelloWorldApplet.html"
<HTML>
<APPLET CODE="HelloWorldApplet.class" WIDTH=352 HEIGHT=120>
</APPLET>
</HTML>
Vom Appletviewer wird das Fenster in Bild 5.1
ausgegeben.
Bild 5.1 HelloWorldApplet
5 Applets
87
Bevor ein Applet aktiv wird, instanziiert der Browser (oder der Appletviewer) zunächst ein
Objekt der von "JApplet" abgeleiteten Klasse, im vorausgehenden Beispiel also von
"HelloWorldApplet".
Einige Methoden, die vom Browser aufgerufen werden
−
public void init()
Nach der Instanziierung ruft der Browser "init" auf, um dem Applet die Möglichkeit zu
geben, Initialisierungen vorzunehmen. Die Methode "init" wird während der Lebensdauer
eines Applets nur einmal aufgerufen.
−
public void start()
Der Browser ruft "start" immer dann auf, wenn das Applet sichtbar wird. Im Gegensatz
zur Initialisierung kann das Starten eines Applets also mehrfach erfolgen. Die Methode
kann z.B. eine Animation starten.
−
public void paintComponent(Graphics)
Durch Aufruf von "paintComponent" fordert der Browser das Applet auf, sich auf dem
Bildschirm darzustellen. Die Methode wird z.B. aufgerufen, wenn das Browserfenster
verdeckt war und dann wieder neu gezeichnet werden muss.
−
public void stop()
Der Browser ruft "stop" auf, wenn ein Applet vorübergehend unsichtbar wird, weil der
Benutzer es beispielsweise vom Bildschirm gescrollt hat. Die Methode kann z.B. eine
Animation anhalten.
−
public void destroy()
Durch Aufruf von "destroy" teilt der Browser dem Applet mit, dass es nicht mehr
benötigt wird. Das ist z.B. der Fall, wenn der Benutzer den Browser beendet. Die
Methode kann z.B. einen Thread (Programmfaden) "zerstören", der vom Applet angelegt
worden ist.
Darüber hinaus gibt es weitere Methoden. Einige werden verwendet, um auf äußere
Ereignisse (events) zu reagieren, wie z.B. auf Tastatur- oder Mausereignisse.
Unterschiede zwischen Applikationen (Programmen) und Applets
−
−
−
Eine Applikation wird vom Java-Interpreter durch Aufruf von "main" gestartet. Zum
Starten eines Applets instanziieren Web-Browser oder Appletviewer die von Applet
abgeleitete Klasse und rufen danach die Methoden "init" usw. auf.
Im Gegensatz zu Applikationen darf ein Applet in der Regel aus Sicherheitsgründen nicht
auf Dateien des Rechners zugreifen.
Ein Applet arbeitet immer im Grafikmodus, eine Applikation kann sich dagegen auf
Konsolenausgabe beschränken.
Das folgende Applet "OneLine" zeigt, dass sich die schon bekannte Vorgehensweise zur
Ereignisbehandlung unverändert auf Applets übertragen lässt. Zum Zeichnen einer Linie
drückt man eine Mautaste und zieht den Mauscursor bei gedrückt gehaltener Maustaste. Die
Linie folgt dem Cursor, bis die Maustaste wieder losgelassen wird. Mit einer beliebigen Taste
der Tastatur kann man die Linienfarbe verändern.
Beispiel, OneLine
import java.awt.*;
import java.awt.event.*;
5 Applets
88
import javax.swing.*;
public class OneLine extends JApplet {
public void init() { getContentPane().add(new OneLinePanel()); }
}
class OneLinePanel extends JPanel {
private final static Color COLOR[] = { Color.BLACK, Color.WHITE };
private Point from = new Point(-1, -1), to = new Point(-1, -1);
private int colInd = 0;
OneLinePanel() {
addMouseListener(new MyMouseListener());
addMouseMotionListener(new MyMouseMotionListener());
addKeyListener(new MyKeyListener());
setFocusable(true);
requestFocus();
setBackground (Color.LIGHT_GRAY);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(COLOR[colInd]);
g.drawLine(from.x, from.y, to.x, to.y);
g.setColor(Color.BLACK);
g.drawString("Line from (" + from.x + "," + from.y +
") to (" + to.x + "," + to.y + ")", 0, 10);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - class MyMouseListener extends MouseAdapter {
public void mousePressed(MouseEvent me){
from = to = me.getPoint(); repaint(); }
}
class MyMouseMotionListener extends MouseMotionAdapter {
public void mouseDragged(MouseEvent me) {
to = me.getPoint(); repaint(); }
}
class MyKeyListener extends KeyAdapter {
public void keyPressed(KeyEvent ke) {
colInd = ++colInd % COLOR.length; repaint(); }
}
}
Bild 5.2 zeigt ein Beispiel zum Fenster,
das vom Appletviewer ausgegeben wird.
Aufgabe 5.1
Schreiben Sie das Applet "OneLIne" so
um, dass man es wahlweise als Applet
oder als Programm (Application) starten
kann. Verwenden Sie dabei eine Vorgehensweise, die sich ohne größere Änderungen auf andere Applets übertragen
lässt.
Bild 5.2 Applet OneLine
5 Applets
5.1
89
Das Einbinden eines Applets
Ein Applet wird mit einem Applet-Tag (Kennung, Markierung) in ein HTML Dokument
eingebunden3. Die "Minimalform" eines Applet-Tags ist schon bekannt:
<APPLET CODE="HelloWorld.class" WIDTH=350 HEIGHT=120>
</APPLET>
Die Datei "HelloWorld.class" wird in diesem Fall im selben Verzeichnis wie das HTMLDokument erwartet. In Tabelle 5.1 sind einige weitere Angaben zu finden, die in einem
Applet-Tag zulässig sind.
Tabelle 5.1 Einige im Applet-Tag zulässige Angaben
Angabe
CODEBASE
ALIGN
HSPACE
VSPACE
Bedeutung
Verzeichnis, in dem die ".class"-Dateien gesucht werden (beliebige URL)
Ausrichtung des Fensters (z.B. left oder right)
Rand rechts und links vom Applet
Rand über und unter dem Applet
In der folgenden HTML Datei "AppletAndText" wird das Applet "OneLIne" in Text eingebettet. Die Angaben im Applet Tag führen dazu, dass der Browser
− das Applet im Unterverzeichnis "classes" zum Verzeichnis der HTML Datei sucht,
− das Applet-Fenster rechtsbündig ausrichtet und
− die vorgegebenen Ränder einhält.
Datei "AppletAndText.html"
<HTML>
<p>An array is an important data structure in any programming
language. An array is a fixed-length structure that stores
<APPLET CODEBASE="classes"
CODE="OneLine.class"
WIDTH=200
HEIGHT=160
ALIGN=right
HSPACE=25
VSPACE=15>
</APPLET>
multiple values of the same type. You can group values of the same
type within arrays. Arrays are supported directly by the Java
programming language; there is no array class. Arrays are implicit
extensions of the Object class, so you can assign an array to a
variable whose type is declared as Object. The Java platform groups
its classes into functional packages. Instead of writing your own
classes, you can use one provided by the platform. Most of the
classes discussed in this chapter are members of the java.lang
package. All the classes in the java.lang package are available to
your programs automatically.</p>
</HTML>
3
Die Einbindung eines Applets mit Embed- und Object-Tags wird hier nicht erläutert.
5 Applets
90
Vom Browser wird Bild 5.3
ausgegeben. Bei gedrückter
Maustaste lässt sich eine Linie
im Appletfenster zeichnen.
Bild 5.3 Das Appletfenster wird in den Text eingebettet
Center Tag
Mit dem HTML Zentrierungs-Tag <CENTER> ... </CENTER> lässt sich ein Applet-Fenster
zentriert ausgeben.
...
An array is a fixed-length structure that stores
<CENTER><APPLET CODEBASE="classes"
CODE="CircleAndMouse.class"
WIDTH=200
HEIGHT=160>
</APPLET></CENTER>
multiple values of the same type. You can group values of the same
...
Param Tag
Man kann in einem Applet Parameter aus der zugehörigen HTML-Datei lesen. Dazu werden
Param-Tags verwendet, die in das Applet-Tag eingebettet sind.
In der Datei "TestParamTag.html" sind als Beispiel zwei Parameter mit den Namen (name)
"initialX" bzw. "initialY" und ihren Werten (value) "10" bzw. "12" zu finden.
Datei "TestParamTag.html"
<APPLET CODE="TestParamTag.class" WIDTH=350 HEIGHT=120>
<PARAM name="initialX" value=10>
<PARAM name="initialY" value=12>
</APPLET>
ImApplet können die Paramter mit der Methode
public String getParameter(String name)
5 Applets
91
als String gelesen werden. Das folgenden Programm "TestParamTag.java" zeigt die Vorgehensweise.
Datei "TestParamTag.java"
import java.awt.*;
import javax.swing.*;
public class TestAppletTag extends JApplet {
private int startX, startY;
public void init() {
try {
startX = Integer.parseInt(getParameter("initialX"));
startY = Integer.parseInt(getParameter("initialY"));
} catch (NumberFormatException e) {
startX = 0;
startY = 0;
}
}
public void paint(Graphics g) {
System.out.println("startX = " + startX);
System.out.println("startY = " + startY);
}
}
Auf der Konsole
startX = 10
startY = 12
Aufgabe 5.2
Schreiben Sie ein Applet "Funktion" zur grafischen Ausgabe der folgenden Funktion:
y = a0 + a1 ⋅ x + a2 ⋅ sin( π ⋅ x ) + a3 ⋅ sin( 3 ⋅ π ⋅ x ) + a4 ⋅ sin(5 ⋅ π ⋅ x )
Bild 5.4 zeigt, wie die Funktion ausgegeben
werden sollen.
− Lesen Sie die benötigen Koeffizienten a0
bis a4 aus der HTML-Datei ein.
−
Wenn das Applet mit einem Appletviewer ausgeführt wird und der Benutzer
die Fenstergröße ändert, soll sich die
Größe des Diagramms der neuen
Fenstergröße anpassen.
Bild 5.4 a0 = a1 = 0, a2 = 5, a3 = 1.667, a4 = 1
5 Applets
5.2
92
Übungsaufgaben
Übungsaufgabe 5.1
a) Schreiben Sie ein Applet "Rechteck" zum Zeichnen eines Rechtecks.
Man drückt eine Mautaste und legt damit den ersten Eckpunkt fest.
Bei gedrückt gehaltener Maustaste zieht man den Mauscursor auf den zweiten Eckpunkt.
Während der Mauscursor bewegt wird, gibt das Applet jeweils das derzeit aktuelle
Rechteck aus.
Erster und zweiter Eckpunkt sollen beliebig zueinander positioniert werden können.
b) Sinngemäß wie a), es soll aber ein Kreis ausgegeben werden.
Übungsaufgabe 5.2
Das gegebene Programm "Klausur" gibt auf der Konsole eine
einfache Übersicht über das
Ergebnis einer Klausur aus (Bild
5.5).
Wandeln Sie das Programm in
ein Applet um, das eine ähnliche
Übersicht im Appletfenster ausgibt.
0..4
5..6
7..9
10..12 13..15
-----------------------------------------*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
Participants: 19
Bild 5.5 Übersicht über ein Klasusurergebnis
public class Klausur {
private final String RANGE[] = {" 0..4 ", " 5..6 ",
" 7..9 ", "10..12","13..15"};
private final String STAR = "
*
", BLANKS = "
";
private int numStud[] = new int[RANGE.length], sum = 0, max = 0;
Klausur() {
numStud[0]
numStud[1]
numStud[2]
numStud[3]
numStud[4]
}
=
=
=
=
=
3;
3;
6;
5;
2;
public void diagram() {
for(int i = 0; i < RANGE.length; i++)
System.out.print(" " + RANGE[i]);
System.out.println();
for(int i = 0; i < 2 + 8*RANGE.length; i++)
System.out.print('-');
System.out.println();
5 Applets
for(int i = 0; i < numStud.length; i++) {
sum += numStud[i];
max = Math.max(max, numStud[i]);
}
for(int i = 0; i < max; i++) {
String s = "";
for(int j = 0; j < RANGE.length; j++)
s += (i < numStud[j])? STAR: BLANKS;
System.out.println(s);
}
System.out.println("\nParticipants: " + sum);
}
public static void main(String[] args) {
Klausur klausur = new Klausur();
klausur.diagram();
}
}
93
6 OOP III : Polymorphie, Abstrakte Klassen und Interfaces
6.1
Polymorphie und spätes Binden
Frühes Binden (Statisches Binden, Early Binding) bedeutet, dass Compiler, Linker und
Loader schon vor der Ausführung eines Programms die tatsächliche physikalische Adresse
einer Methode (eines Unterprogramms) in den Maschinencode einfügen. Frühes Binden wird
z.B. in Assembler- und in C-Programmen verwendet.
In dem nachfolgenden Beispiel "EmployeeApp" werden einer Referenz auf die Basisklasse
"Employee" nacheinander Instanzen der abgeleiteten Klassen "Worker", "SalesAgent" und
"Manager" zugewiesen. Die Konsolenausgabe zeigt, dass immer die displaySalary()-Methode
der Klasse aufgerufen wird, auf die die Basisklassenreferenz aktuell zeigt. Dies ist eine
Ausprägung der Polymorphie, welche ein wichtiges Konzept des objektorientierten
Programmierens ist. Die Polymorphie von Objekten erlaubt es, wieder verwendbaren Code zu
schreiben, der nur Referenzen auf Objekte der Basisklasse enthält, wie im nachfolgenden
Beispiel der array Employee[] empl. Trotzdem können Objekte der gesamten
Vererbungshierarchie in ihm abgelegt werden, und es wird stets die "passende" Methode
displaySalary() der entsprechenden abgeleiteten Klasse aufgerufen.
Beispiel, EmployeeApp
class Employee
{
private String name;
Employee(String n) { name = new String(n); }
void displayName() { System.out.print(name + " " ); }
void displaySalary() {} // For "empl[i].displaySalary();"
}
//---------------------------------------------------------------class Worker extends Employee
{
protected double hourlyWage, hours;
Worker(String n, double hW, double h) {
super(n);
hourlyWage = hW;
hours = h;
}
void displaySalary() {
System.out.println(hourlyWage * hours + " EUR");
}
}
//---------------------------------------------------------------class SalesAgent extends Worker
{
protected double commission, count;
SalesAgent(String n, double hW, double h, double c, double cn) {
super(n, hW, h);
commission = c;
count = cn;
6 OOP II : Polymorphie, Abstrakte Klassen und Interfaces
95
}
void displaySalary() {
System.out.println(hourlyWage * hours
+ commission*count
+ " EUR");
}
}
//---------------------------------------------------------------class Manager extends Employee
{
private double salary;
Manager(String n, double s) {super(n); salary = s; }
void displaySalary() {
System.out.println(salary + " EUR");
}
}
//---------------------------------------------------------------public class EmployeeApp
{
public static void main(String[] args) throws IOException
{
int sel;
Employee[] empl = new Employee[3];
BufferedReader din = new BufferedReader(
new InputStreamReader(System.in));
for (int i= 0; i < empl.length; i++) {
do {
System.out.println("Was soll erzeugt werden?");
System.out.println("- Worker
(1)\n" +
"- SalesAgent (2)\n" +
"- Manager
(3)");
System.out.print("Eingabe: ");
sel = Integer.parseInt(din.readLine());
switch(sel) {
case 1:
empl[i] = new Worker("Meyer, Klaus", 15.82, 151.00);
break;
case 2:
empl[i] = new SalesAgent("Hamer, Peter", 8.80, 150.0,
60.28, 22.0);
break;
case 3:
empl[i] = new Manager("Kramer, Hans", 3501.27);
break;
default:
System.out.println("Ungültige Eingabe");
}
} while (sel < 1 || sel > 3);
}
for (int i= 0; i < empl.length; i++) {
empl[i].displayName();
6 OOP II : Polymorphie, Abstrakte Klassen und Interfaces
96
empl[i].displaySalary();
}
}
}
Auf der Konsole:
Was soll erzeugt werden?
- Worker
(1)
- SalesAgent (2)
- Manager
(3)
Eingabe: 1
Was soll erzeugt werden?
- Worker
(1)
- SalesAgent (2)
- Manager
(3)
Eingabe: 2
Was soll erzeugt werden?
- Worker
(1)
- SalesAgent (2)
- Manager
(3)
Eingabe: 3
Meyer, Klaus 2388.82 EUR
Hamer, Peter 2646.16 EUR
Kramer, Hans 3501.27 EUR
Wenn in einer abgeleitete Klasse eine Methode definiert wird, die den gleichen Namen, den
gleichen Rückgabetyp und die gleichen Parameter wie eine Methode der Oberklasse besitzt,
dann überschreibt diese Methode die gleichnamige Methode der Oberklasse (Overriding). Im
vorausgehenden Programm ist das für displaySalary() der Fall.
Für überschriebene Methoden gilt, dass das zur Programmlaufzeit zugewiesene Objekt die
Methode bestimmen. Die Entscheidung, welche Version von displaySalary() jeweils
angesprochen wird, findet also erst zur Ausführungszeit des Programms statt und wird als
Spätes Binden (Dynamisches Binden, Late binding) bezeichnet. Beachten Sie, dass die
Methode displaySalary() in der Basisklasse Employee noch einen leeren Methodenrumpf hat,
da man für einen allgemeinen Angestellten noch kein konkretes Gehalt berechnen kann.
Das Auswählen der "richtigen" Methode zur Laufzeit des Programms ist in Java zwar
effizient implementiert, der Aufwand ist aber dennoch höher als beim Frühen Binden. Wenn
das Späte Binden aus Laufzeitgründen verhindert werden soll, lässt sich das folgendermaßen
erreichen:
− Private Methoden ("private") sind in einer abgeleiteten Klasse nicht sichtbar und können
folglich nicht überschrieben werden.
− Mit dem zusätzlichen Schlüsselwort "final" lässt sich festlegen, dass eine Methode in
abgeleiteten Klassen nicht überschrieben werden kann.
− Klassenmethoden ("static") können nicht überschrieben werden.
Das nachfolgende Applet ist ebenfalls ein Beispiel zur Polymorphie. Wenn das Applet
"ShapeAndMouse" ausgeführt wird, kann man bei gedrückter linker Maustaste wahlweise
einen Kreis oder ein Rechteck über das Appletfenster ziehen. Mit der rechten Maustaste lässt
sich dabei zwischen Kreis zum Rechteck umschalten. Hier wird die Methode display() der
Basisklsse Shape in den abgeleiteten Klassen Circle und Rect überschrieben. Die Aufgabe der
Methode display() ist es, das jeweilige Objekt auf der Zeichenebene zu zeichen.
6 OOP II : Polymorphie, Abstrakte Klassen und Interfaces
97
Beispiel, ShapeAndMouse
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ShapeAndMouse extends JApplet {
public void init() { getContentPane().add(new SMPanel()); }
}
class SMPanel extends JPanel {
private Circle circle = new Circle(100, 100, 40);
private Rect rect = new Rect(100, 100, 70, 70);
private Shape shape = circle;
SMPanel() {
addMouseMotionListener(new MyMouseMotionListener());
addMouseListener(new MyMouseListener());
setBackground(Shape.BG_COLOR);
setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
}
public void paintComponent(Graphics g) {
super.paintComponent(g); shape.draw(g); }
class MyMouseMotionListener extends MouseMotionAdapter {
public void mouseDragged(MouseEvent me) {
if(!me.isMetaDown())
// Left button pressed?
shape.moveTo(getGraphics(), me.getX(), me.getY());
}
}
class MyMouseListener extends MouseAdapter {
public void mousePressed(MouseEvent me) {
if(me.isMetaDown()) {
// Right button pressed?
shape.erase(getGraphics());
if(shape == circle)
shape = rect;
else
shape = circle;
shape.moveTo(getGraphics(), me.getX(), me.getY());
}
}
}
}
//--------------------------------------------------------------class Shape {
static final Color BG_COLOR = Color.LIGHT_GRAY;
static final Color FG_COLOR = Color.BLACK;
private int x, y;
// Reference point
Shape(int x, int y) {this.x = x; this.y = y;}
6 OOP II : Polymorphie, Abstrakte Klassen und Interfaces
98
int getX() { return x; }
int getY() { return y; }
void display(Graphics g, Color c) {}
void draw(Graphics g) {display(g, FG_COLOR);}
void erase(Graphics g) {display(g, BG_COLOR);}
void moveTo(Graphics g, int x, int y) {
erase(g); this.x = x; this.y = y; draw(g); }
}
//--------------------------------------------------------------class Circle extends Shape {
int r;
Circle(int x, int y, int r) { super(x, y); this.r = r; }
void display(Graphics g, Color c) {
g.setColor(c);
g.drawOval(getX(), getY(), r+r, r+r);
}
}
//--------------------------------------------------------------class Rect extends Shape {
int width, height;
Rect(int x, int y, int w, int h) {
super(x, y); width = w; height = h; }
void display(Graphics g, Color c) {
g.setColor(c);
g.drawRect(getX(), getY(), width, height);
}
}
Vom Appletviewer wird Bild 6.1 ausgegeben.
Aufgabe 6.1
a) Erweitern Sie das Programm so, dass sich zusätzlich zu Kreis und Rechteck auch ein Kreisbogen über das Appletfenster ziehen lässt. Mit
der rechten Maustaste soll zyklisch zwischen
Kreis, Rechteck und Kreisbogen umgeschaltet
werden können.
b) Was geschieht, wenn man das Programm um
eine weitere Klasse, wie z.B. "Arc", erweitert
und dabei die zugehörige Funktion "display"
vergisst?
Bild 6.1 Applet ShapeAndMouse
6 OOP II : Polymorphie, Abstrakte Klassen und Interfaces
6.2
99
Abstrakte Klassen und Interfaces
Abstrakte Klassen und abstrakte Methoden
Eine Klasse wird durch das Schlüsselwort "abstract" zur abstrakten Klasse. Abstrakte Klassen
können nur als Oberklassen für andere Klassen verwendet werden. Es ist nicht möglich, ein
Objekt einer abstrakten Klasse zu vereinbaren, d.h. die Klasse zu instanziieren.
abstract class Shape {
...
// Abstakte Klasse
Abstrakte Klassen enthalten häufig abstrakte Methoden. Eine abstrakte Methode beginnt
ebenfalls mit "abstract". Zusätzlich wird der "Methodenkörper" einschließlich des geschweiften Klammernpaars durch ein Semikolon ersetzt. Wenn nur eine einzige Methode einer Klasse
abstrakt ist, so ist die Klasse selber zwangsläufig abstrakt, und ist damit mit dem
Schlüsselwort "abstract" zu deklarieren.
abstract class Shape {
// Abstarkte Klasse
...
abstract void display(Graphics g, Color c); // Abstrakte Methode
...
Eine von einer abstrakten Klasse abgeleitete Klasse erbt natürlich auch die abstrakten
Methoden.
− Nur wenn die abgeleitete Klasse für alle geerbten abstrakten Methoden konkrete Implementierungen enthält, ist sie nicht mehr abstrakt, und es lassen sich Objekte vereinbaren.
− Wenn die abgeleitete Klasse auch nur für eine der abstrakten Methoden keine konkrete
Implementierungen bereitstellt, ist sie ebenfalls wieder abstrakt. Sie muss dann mit dem
Schlüsselwort "abstract" beginnen.
Beispiel, "Shape" als abstrakte Klasse
abstract class Shape {
// Abstarkte Klasse
static final Color BG_COLOR = Color.LIGHT_GRAY;
static final Color FG_COLOR = Color.BLACK;
private int x, y;
Shape(int x, int y) {this.x = x; this.y = y;}
int getX() { return x; }
int getY() { return y; }
abstract void display(Graphics g, Color c);
// Abstrakte Methode
void draw(Graphics g) {display(g, FG_COLOR);}
void erase(Graphics g) {display(g, BG_COLOR);}
void moveTo(Graphics g, int x, int y) {
erase(g); this.x = x; this.y = y; draw(g); }
}
6 OOP II : Polymorphie, Abstrakte Klassen und Interfaces
100
Das Beispiel zeigt eine sinnvolle Anwendung. Die Klasse "Shape" aus dem Programm
"ShapeAndMouse" ist nun abstrakt. Als Folge kann man von Klassen, die von ihr abgeleitet
werden, nur dann Objekte vereinbaren, wenn sie eine konkrete Implementierung der Methode
"display()" enthalten. Daher kann nach dieser Änderung der Fehler aus Unterpunkt c) der
Aufgabe 6.1 nicht mehr auftreten!
Interfaces
Ein Interface (Schnittstelle) ist eine spezielle Klasse die ausschließlich abstrakte Methoden
und Konstanten enthält. Statt "class" wird das Schlüsselwort "interface" verwendet.
Beispiel, Interface "Drawable"
Datei "Drawable.java"
import java.awt.*;
public interface Drawable {
public static final Color BG_COLOR = Color.lightGray;
public static final Color FG_COLOR = Color.black;
public abstract void display(Graphics g, Color c);
}
Alle abstrakten Methoden eines Interface sind durch Voreinstellung "public" und "abstract".
Man sollte das Schlüsselwort "public" aber hinzufügen, man muss es aber nicht. Andere
Modifizierer, wie z.B. "private", sind nicht zulässig. Bei der Definition von Konstanten
besteht bezüglich der Angabe der Modifikatoren "public", "static" und "final" völlige Freiheit.
Sie können angegeben werden, sie können aber auch weggelassen werden.
Man kann bekanntlich eine Klasse von einer Oberklasse mit "extends" ableiten, ein Interface
wird dagegen mit dem Schlüsselwort "implements" implementiert.
Beispiel, "implements"
Datei "Shape.java"
import java.awt.*;
//-------------------------------------------------------------abstract class Shape implements Drawable {
private int x, y;
int getX() { return x; }
int getY() { return y; }
Shape(int x, int y) {this.x = x; this.y = y;}
// public void display(Graphics g, Color c)
void draw(Graphics g) {display(g, FG_COLOR);}
void erase(Graphics g) {display(g, BG_COLOR);}
...
}
6 OOP II : Polymorphie, Abstrakte Klassen und Interfaces
101
//-------------------------------------------------------------class Circle extends Shape {
...
public void display(Graphics g, Color c) { ... }
}
//-------------------------------------------------------------class Rect extends Shape {
...
public void display(Graphics g, Color c) { ... }
}
Eine Klasse, die ein Interface implementiert, muss alle abstrakten Methoden des Interface
überschreiben. Andernfalls wird sie wie "Shape" zur abstrakten Klasse ("abstract") oder
der Compiler meldet einen Fehler.
Eine Klasse kann nur von einer Oberklasse abgeleitet werden. Aber eine Klasse kann
nicht nur eine, sondern beliebig viele Interfaces implementieren. Auf der Ebene von
Interfaces ist also Mehrfachvererbung möglich.
Interfaces bieten ein elegantes Mittel zur Prüfung, ob der Anwender bei
Parameterübergaben den richtigen Typ übergeben hat.
−
−
−
In der Java-Klassenbibliothek werden eine Reihe von Interfaces definiert und implementiert.
Im folgenden Abschnitt wird z.B. das Interface "Cloneable" zum "Klonen" von Objekten eingesetzt. Weitergehende Erläuterungen zu Schnittstellen sind z.B. in [1] zu finden.
6.3
Das Interface Cloneable
Klonen bedeutet nichts anderes, als eine exakte Kopie von etwas schon Existierendem zu
erstellen. Wenn ein Objekt geklont wird, erwartet man, dass man eine Referenz auf ein neues
Objekt bekommt, dessen Datenfelder die gleichen Werte haben, wie das Originalobjekt. Die
bloße Zuweisung von Referenzen c2 = c1 leistet dies nicht. Um eine echte Kopie von
Objekten einer Klasse zu bekommen muss man zwei Dinge tun:
−
−
Das Interface Cloneable implementiert.
Die Methode clone() der Klasse Object überschreiben.
Diese Teile sind im nachfolgenden Beispiel "Clone 1" durch Fettdruck hervorgehoben. Die
Klasse Circle implementiert das Interface Cloneable und enthält eine public-Methode clone().
In clone() wird der Aufruf an die Oberklasse, also an die Klasse Object weitergeleitet. Da die
Klasse Circle das Interface Cloneable implementiert, sollte die CloneNotSupportedException
nie auftreten. Die Aufgabe der Methode clone() der Klasse Object besteht darin, eine Eins-zuEins-Kopie des Objektes zu erstellen, für das sie aufgerufen wird.
Beispiel, Clone 1
class
{
int
int
int
Circle implements Cloneable
x = 0;
y = 0;
r = 0;
Circle(int x, int y, int r) {
6 OOP II : Polymorphie, Abstrakte Klassen und Interfaces
102
this.x = x;
this.y = y;
this.r = r;
}
void display() {
System.out.println("x=" + x + ", y=" + y + ", r=" + r);
}
public Object clone() throws CloneNotSupportedException {
// mit super.clone() wird die clone()-Methode
// der Klasse Object aufgerufen
return super.clone();
}
}
public class Clone1
{
public static void main(String[] args)
throws CloneNotSupportedException {
Circle c1 = new Circle(10,10,10);
Circle c2 = c1;
c2.x = 20;
c2.y = 20;
c2.r = 20;
c1.display();
c2.display();
c1 = new Circle(10,10,10);
Circle c3 = (Circle) c1.clone();
c3.x = 30;
c3.y = 30;
c3.r = 30;
c1.display();
c3.display();
}
}
Auf der Konsole:
x=20,
x=20,
x=10,
x=30,
y=20,
y=20,
y=10,
y=30,
r=20
r=20
r=10
r=30
Man erkennt an der Ausgabe, dass nach der bloßen Zuweisung c2 = c1 Änderungen an der
Kopie c2 auch das Original c1 verändern, während nach c3 = (Circle) c1.clone()
Änderungen an der Kopie c3 das Original c1 unverändert lassen. Es ist noch Folgendes zu
bemerken. Man könnte vermuten, dass die Deklaration der clone()-Methode in dem Interface
Cloneable enthalten ist. Das ist aber nicht der Fall, das Interface Cloneable hat einen leeren
Rumpf:
public interface Cloneable
{
}
6 OOP II : Polymorphie, Abstrakte Klassen und Interfaces
103
Was gewinnt man dann durch das Implementieren des Interface Cloneable? Das Kompilieren
der Klasse Circle wäre auch möglich, wenn die clone()-Methode der Klasse Object in der
Klasse Circle nicht überschrieben würde. Aber die clone()-Methode der Klasse Object hat nur
den Zugriffsmodifikator protected, so dass der Aufruf c1.clone() in main() nicht möglich
ist. Durch das Überschreiben in der Klasse Circle wird die Sichtbarkeit der clone()-Methode
auf public angehoben. Das Implementieren des Interface Cloneable ist aber ebenfalls
notwendig. Lässt man es weg, so lässt sich das Programm zwar compilieren, der Aufruf
super.clone() wirft dann aber zur Laufzeit eine CloneNotSupportedException aus. Das
Interface Cloneable ist ein so genanntes Marker-Interface. Die Klasse gibt damit an, dass
ihre Objekte kopierbar sind. Man kann auf diese Weise verhindern, dass Objekte von Klassen
kopiert werden, für die das gar nicht vorgesehen ist.
Im vorausgegangenen Programm "Clone 1" war es ausreichend, in der Methode clone() der
Klasse Circle einfach die Methode clone() der Klasse Object aufzurufen. Die Methode clone()
der Klasse Object erzeugt dann eine Eins-zu-Eins-Kopie von allen Datenfeldern. Sobald die
Datenfelder des Objekts nicht mehr nur aus einfachen Datentypen bestehen, sondern auch
Referenzen umfassen, reicht obige Vorgehensweise aber nicht mehr aus. Denn die Methode
clone() der Klasse Objekt erzeugt dann nur eine Kopie der jeweiligen Referenz, also lediglich
eine flache Kopie. Im nachfolgenden Beispielprogramm "Clone 2" ist das deutlich zu sehen.
Eine Membervariable der Klasse Circle ist jetzt von dem Referenztyp Point, welcher in dem
Paket java.awt deklariert ist, und Punkte in der Ebene beschreibt.
Beispiel, Clone 2
import java.awt.Point;
class Circle implements Cloneable
{
Point p;
int r;
Circle(int x, int y, int r) {
this.p = new Point(x,y);
this.r = r;
}
void display() {
System.out.println("x=" + p.x + ", y=" + p.y + ", r=" + r);
}
public Object clone ()
throws CloneNotSupportedException
{
// mit super.clone() wird die clone()-Methode
// der Klasse Object aufgerufen. Das erzeugt hier
// zunächst lediglich eine flache Kopie
return super.clone();
}
}
public class Clone2
{
public static void main(String[] args)
6 OOP II : Polymorphie, Abstrakte Klassen und Interfaces
104
throws CloneNotSupportedException
{
Circle c1 = new Circle(10,10,10);
Circle c2 = (Circle) c1.clone();
c2.p.x = 30;
c2.p.y = 30;
c2.r = 30;
c1.display();
c2.display();
}
}
Auf der Konsole:
x=30, y=30, r=10
x=30, y=30, r=30
Änderungen an der Instanzvariable p des Objektes c2 verändern auch das Original c1
an der entsprechenden Stelle. Man muss die clone()-Methode der Klasse Circle so erweitern,
dass das in der Classe Cirlcle enthaltenen Teilobjekt des Typs Point echt mit kopiert (geklont)
wird. Auf diese Weise erhält man dann eine tiefe Kopie des Circle-Objekts.
public Object clone ()
throws CloneNotSupportedException
{
// mit super.clone() wird die clone()-Methode
// der Klasse Object aufgerufen. Das erzeugt
// zunächst eine flache Kopie
Circle temp = (Circle) super.clone();
// dann Erweiterung zur tiefe Kopie
temp.p = (Point) p.clone();
return temp;
}
Mit der so geänderten clone()-Methode in der Klasse Circle liefert das Programm "Clone 2"
auf der Konsole:
x=10, y=10, r=10
x=30, y=30, r=30
Das nachfolgende Beispiel "Clone 3" zeigt Beispiel schließlich noch, wie Objekte einer
Vererbungshierarchie zu klonen sind. Man hat lediglich in jeder Stufe der Hierarchie zu
entscheiden, ob eine tiefe Kopie notwendig ist. Dies ist immer dann erforderlich, wenn die
entsprechende Unterklasse Membervariablen hat, die von Referenztyp sind. In diesem Fall
müssen die entsprechenden Membervariablen tief kopiert werden.
Beispiel, Clone 3
import java.awt.Point;
class OnePoint implements Cloneable
{
Point p1;
OnePoint(int x, int y) {
6 OOP II : Polymorphie, Abstrakte Klassen und Interfaces
this.p1 = new Point(x,y);
}
void display() {
System.out.println("x=" + p1.x + ", y=" + p1.y);
}
public Object clone ()
throws CloneNotSupportedException
{
OnePoint temp = (OnePoint) super.clone();
temp.p1 = (Point) p1.clone();
return temp;
}
}
class TwoPoints extends OnePoint
{
Point p2;
TwoPoints(int x1, int y1, int x2, int y2) {
super(x1,y1);
p2 = new Point(x2,y2);
}
void display() {
System.out.println("x1=" + p1.x + ", y1=" + p1.y
+ ", x2=" + p2.x + ", y2=" + p2.y);
}
public Object clone ()
throws CloneNotSupportedException
{
TwoPoints temp = (TwoPoints) super.clone();
temp.p2 = (Point) p2.clone();
return temp;
}
}
public class Clone3
{
public static void main(String[] args)
throws CloneNotSupportedException
{
TwoPoints t1 = new TwoPoints(10,10,10,10);
TwoPoints t2 = (TwoPoints) t1.clone();
t2.p1.x = 30;
t2.p1.y = 30;
t2.p2.x = 30;
t2.p2.y = 30;
t1.display();
t2.display();
}
}
105
6 OOP II : Polymorphie, Abstrakte Klassen und Interfaces
106
Auf der Konsole:
x1=10, y1=10, x2=10, y2=10
x1=30, y1=30, x2=30, y2=30
6.4
Das generische Interface Comparable<T>
Als Beispiel für ein generisches Interface betrachten wir das Interface Comparable<T>.
Dieses Interface kann von jeder Klasse implementiert werden und ermöglicht dann, dass
Arrays, bestehend aus Referenzen auf Objekte dieser Klasse durch die Klassenmethode
public static void sort (Object[] a)
der Klasse Arrays aus dem Packet java.util sortiert werden können. Die Implementierung
des generischen Interface Comparable<T> stellt dabei sicher, dass die Objekte eine
Vergleichsoperation unterstützen. Das generische Interface Comparable<T> hat den Aufbau
public interface Comparable<T>
{
public int compareTo (T o);
}
Dabei werden bei dem Aufruf a.compareTo(b) folgende Rückgabewerte erwartet:
−
−
−
kleiner Null (z.B. -1), wenn a < b,
0, wenn a == b,
größer Null (z.B. +1), wenn a > b ist.
Nachfolgend ein Beispiel hierzu, bei dem mechanische Artikel erfasst und sortiert werden.
Die Klasse Artikle hat zwei Membervariablen artNr und name und implementiert das
Interface Comparable<T>.
Beispiel, Vergleich
import java.util.*;
class Artikel implements Comparable<Artikel>
{
private Integer artNr;
private String name;
public Artikel (Integer artNr, String name)
{
this.artNr = artNr;
this.name = name;
}
public int compareTo (Artikel artikel)
{
// Die Methode compareToIgnoreCase() ist in der Klasse
// String implementiert und liefert einen lexikographischen
// Vergleich beider Strings ohne Brücksichtigung von
6 OOP II : Polymorphie, Abstrakte Klassen und Interfaces
107
// Groß- und Kleinschreibung
return name.compareToIgnoreCase (artikel.name);
}
// Überschreiben der toString()-Methode, um die String// repräsentation des Objektes ausgeben zu können.
public String toString()
{
return (artNr + " " + name);
}
}
public class Vergleich
{
public static void main (String[] args)
{
Artikel[] arr = new Artikel [4];
arr [0] = new Artikel (1000, "Mutter");
arr [1] = new Artikel (1001, "Dichtungsring");
arr [2] = new Artikel (1002, "Abstreifring");
arr [3] = new Artikel (1003, "montierbarer Zackenring");
Arrays.sort (arr);
for (Artikel artikel : arr)
System.out.println (artikel);
}
}
Auf der Konsole
1002
1001
1003
1000
6.5
Abstreifring
Dichtungsring
montierbarer Zackenring
Mutter
Ereignisbehandlung
Die Ereignisbehandlung basiert auf dem Konzept der Ereignisempfänger (Event Listener).
Jedes Objekt, das über Ereignisse benachrichtigt werden kann, ist ein Ereignisempfänger.
Eine Ereignisquelle (Event Source) verwaltet eine Liste von Empfängern, die über Ereignisse
zu benachrichtigen sind und stellt Methoden zur Verfügung, mit denen sich Empfänger selbst
in diese Liste aufnehmen oder sich wieder aus ihr löschen können. Beispiele:
addMouseListener(MouseListener);
removeMouseListener(MouseListener);
Wenn dann ein Ereignis eintritt, benachrichtigt die Quelle alle Empfänger, die sich in ihre
Liste eingetragen haben. Ein Empfänger wird durch Aufruf einer dem jeweiligen Ereignis
zugeordneten Methode benachrichtigt. Als Folge dieser Vorgehensweise müssen Ereignisempfänger alle zugehörigen Methoden implementieren, ein "MouseListener" zum Beispiel
alle fünf in Tabelle 6.1 gegebenen Methoden. Das wird dadurch erzwungen, dass im Interface
"MouseListener" alle fünf Methoden als abstrakte Methoden aufgeführt sind.
108
6 OOP II : Polymorphie, Abstrakte Klassen und Interfaces
Tabelle 6.1 MouseListener und zugehörige Methoden
Listener-Interface
MouseListener
Methoden
mouseClicked
mouseEntered
mouseExited
mousePressed
mouseReleased
Auslösende Aktion
Eine Maustaste wurde gedrückt und wieder
losgelassen
Der Mauszeiger "betritt" die Komponente
Der Mauszeiger verlässt die Komponente
Eine Maustaste wurde gedrückt
Eine Maustaste wurde losgelassen
Lösung 1: Implementieren des Interface
Im Beispiel "Event1" wird das Interface "MosueListener" implementiert. Das Programm gibt einen Zählerstand aus (Bild 6.2), der
inkrementiert wird, wenn der Mauszeiger das Fenster "betritt".
Beispiel, Event1
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
Bild 6.2 Event1
public class Event1 extends JPanel implements MouseListener{
private int count = 0;
Event1() { addMouseListener(this); }
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString("Counter: " + count, 20, 20);
}
public static void main(String[] args) {
JFrame window = new JFrame("Event1");
window.setSize(160, 100);
window.setLocation(100, 100);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.getContentPane().add(new Event1());
window.setVisible(true);
}
public void mouseEntered(MouseEvent me) { count++; repaint(); }
public
public
public
public
void
void
void
void
mouseClicked(MouseEvent me) {}
mouseExited(MouseEvent me) {}
mousePressed(MouseEvent me) {}
mouseReleased(MouseEvent me) {}
}
addMouseListener(this);
Die Klasse "Event1" meldet sich selbst als Ereignisempfänger an.
public void mouseClicked(MouseEvent me) {}
...
public void mouseReleased(MouseEvent me) {}
6 OOP II : Polymorphie, Abstrakte Klassen und Interfaces
109
Die leeren Methoden werden eigentlich nicht benötigt, es müssen aber alle fünf Methoden des
Interface überschrieben werden.
Lösung 2: Adapterklasse und Innere Klasse
Mit Adapterklassen (Tabelle 6.2) lässt sich verhindern, dass Ereignisempfänger leere
Methoden enthalten, die für die Anwendung eigentlich unnötig sind. In Java gibt es Adapterklassen für alle Listener-Interfaces mit mehr als einer Methode.
Tabelle 6.2 Einige Event-Listener und die zugehörigen Adapterklassen
Listener-Interface
MouseListener
Adapterklasse
MouseAdapter
MouseMotionListener
KeyListener
MouseMotionAdapter
KeyAdapter
Methoden
mouseClicked, mouseEntered, mouseExit,
mousePressed, mouseReleased
mouseDragged, mouseMoved
keyPressed, keyReleased, keyTyped
Eine Adapterklasse ist eine abstrakte Klasse, die das zugehörige Listener-Interface mit leeren
Methoden implementiert. Die Methoden sind zwar leer, aber nicht abstrakt.
Klassen, die von einer Adapterklasse abgeleitet sind, brauchen deshalb nur noch die in der
Anwendung wirklich benötigten Methoden zu implementieren, sie überschreiben für diese
Fälle also die leeren Methoden der Adapterklasse.
Da in Java Mehrfachvererbung nicht zulässig ist, wird im folgenden Beispiel eine innere
Klasse eingesetzt. Die Vorgehensweise ist aus vielen früheren Beispielen schon bekannt.
Beispiel, Event2
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Event2 extends JPanel {
private int count = 0;
Event2() { addMouseListener(new MyMouseListener()); }
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString("Counter: " + count, 20, 20);
}
public static void main(String[] args) {
JFrame window = new JFrame("Event2");
window.setSize(160, 100);
window.setLocation(100, 100);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.getContentPane().add(new Event2());
window.setVisible(true);
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - class MyMouseListener extends MouseAdapter {
public void mouseEntered(MouseEvent me) { count++; repaint(); }
}
}
6 OOP II : Polymorphie, Abstrakte Klassen und Interfaces
110
addMouseListener(new MyMouseListener());
Eine Instanz von "MyMouseListener" wird angelegt und als Ereignisempfänger angemeldet.
class MyMouseListener extends MouseAdapter {
public void mouseEntered(MouseEvent me) { count++; repaint(); }
}
Die innere Klasse überschreibt die Methode "mouseEntered" der Adapterklasse.
Lösung 3: Adapterklasse und anonyme lokale Klasse
Man kann eine lokale Klasse deklarieren, ohne ihr einen Namen zu geben. Im Programm
"Event3" wird eine derartige anonyme lokale Klasse eingesetzt. Da anonyme Klassen keinen
Namen haben, müssen sie gleich bei der Definition instanziiert werden.
Beispiel, Event3
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Event3 extends JPanel {
private int count = 0;
Event3() {
addMouseListener(
new MouseAdapter() {
public void mouseEntered(MouseEvent me) {
count++; repaint();
}
}
);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString("Counter: " + count, 20, 20);
}
public static void main(String[] args) {
JFrame window = new JFrame("Event3");
window.setSize(160, 100);
window.setLocation(100, 100);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.getContentPane().add(new Event3());
window.setVisible(true);
}
}
Im Konstruktor wird eine Kurzschreibweise eingesetzt, die Bild 6.3 erläutert. Der Compiler
erzeugt hier also ein Objekt einer anonymen Klasse, die von "MouseAdapter" abgeleitet
worden ist. Zu beachten ist, dass bei anonymen Klassen die Definition der Klasse und ihre
Instanziierung in einer Anweisung erfolgen.
6 OOP II : Polymorphie, Abstrakte Klassen und Interfaces
addMouseListener(new MyMouseListener());
zusammen mit der Definition
class MyMouseListener extends MouseAdapter {
public void mouseEntered(MouseEvent me) {
count++; repaint(); }
}
wird ersetzt durch:
addMouseListener(
new MouseAdapter() {
public void mouseEntered(MouseEvent me) {
count++; repaint();
}
}
);
Bild 6.3 Verwendung einer anonymen lokalen Klasse
111
7 Timer und Threads
Für Animationen muss eine Folge von einzelnen Bildern ausgegeben werden. Dazu kann man
Timer (Zeitgeber) oder Threads (Programmfäden) einsetzen.
7.1
Timer
Im folgenden Programm wird der aktuelle Stand eines Zählers fortlaufend ausgegeben. Mit
den Maustasten lässt sich der Zähler anhalten und wieder starten. Ein Timer spricht hier alle
500 ms seinen Ereignisempfänger an. Es wird dann die Methode "actionPerformed" des Interface "ActionListener" ausgeführt (Tabelle 7.1).
Tabelle 7.1 Action Listener und zugehörige Methode
Listener-Interface
ActionListener
Methode
actionPerfomed
Auslösende Aktion
Ein Ereignis hat stattgefunden, eine Timerverzögerung
ist z.B. abgelaufen
Beispiel, Counter
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Counter extends JPanel implements ActionListener {
private Timer timer;
private int number = 0;
Counter() {
addMouseListener(new MyMouseListener());
// Timer(int delay, ActionListener a), delay in ms
timer = new Timer(500, this);
timer.setInitialDelay(0);
timer.start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString("Counter: " + number, 20, 40);
}
public static void main(String[] args) {
JFrame window = new JFrame("Counter");
window.setSize(260, 160);
window.setLocation(100, 100);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.getContentPane().add(new Counter());
window.setVisible(true);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public void actionPerformed(ActionEvent e){ number++; repaint(); }
113
7 Timer und Threads
class MyMouseListener extends MouseAdapter {
public void mousePressed(MouseEvent me) {
if(timer.isRunning())
timer.stop();
else
timer.start();
}
}
}
Das Programm gibt das Fenster in Bild 7.1 aus.
Bild 7.1 Counter
timer = new Timer(500, this);
Die Anweisung instanziiert einen Timer mit einer Verzögerung von 500 ms und legt
das aktuelle Objekt (this) als ActionListener fest.
timer.setInitialDelay(0);
Mit der Methode lässt sich die Verzögerung für die erste Ausführung von "actionPerformed" angeben. Ohne den Aufruf würde "actionPerformed" nach der an den Konstruktor übergebenen Verzögerung zum ersten Mal ausgeführt, hier also nach 500 ms.
if(timer.isRunning())
timer.stop();
else
timer.start();
Die Anweisungen ändern den gegenwärtigen Zustand des Timers. Ein laufender Timer (running) wird gestoppt und umgekehrt.
Aufgabe 7.1
Schreiben Sie ein Programm "Clock", das die laufende Uhrzeit auf der Konsole und
zusätzlich das aktuelle Datum und die laufende Uhrzeit in einem Fenster ausgibt (Bild 7.2).
Auf der Konsole
...
5:41:19
5:41:20
5:41:21
...
Bild 7.2 Ausgabe von "Clock"
Der folgende Programmausschnitt zeigt, wie man Instanzen der Klassen "GregorianCalendar"
und "Date" zur Ausgabe von Zeit und Datum einsetzen kann.
...
import java.util.*;
...
// For GregorianCalendar and Date
// Get a calendar using the default time zone
GregorianCalendar cal = new GregorianCalendar();
// Print the current time, GregorianCalendar is a
//
subclass of Calendar
114
7 Timer und Threads
System.out.println(cal.get(Calendar.HOUR) + ":" +
cal.get(Calendar.MINUTE) + ":" + cal.get(Calendar.SECOND));
// Copy date and time to a Date object and display it
Date date = cal.getTime();
g.drawString("Current date and time: " + date, 10, 25);
...
Im folgenden Programm werden zwei Timer verwendet.
− Timer "t1" führt zum Aufruf der Methode "addString",
die die Zeichenkette "Two timers" mit zufälliger
Position und Farbe ausgibt (Bild 7.3).
− Timer "t2" führt zum Aufruf von "repaint" und hat den
Aufruf von "paintComponent" zur Folge.
Beispiel, TwoTimers
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
Bild 7.3 Zwei Timer
public class TwoTimers extends JPanel {
private Font font = new Font("Serif", Font.BOLD, 20);
TwoTimers() {
setBackground(Color.WHITE);
Timer t1 = new Timer( 100,new Timer1());
Timer t2 = new Timer(2000,new Timer2());
t1.start();
t2.start();
}
public void paintComponent(Graphics g)
{ super.paintComponent(g); }
void addString(Graphics g) {
int x = -50 + (int) (Math.random() * (getSize().width + 40));
int y = (int) (Math.random() * (getSize().height + 20));
float hue = (float) Math.random(); // hue = Farbton
g.setFont(font);
g.setColor(Color.getHSBColor(hue, 1.0F, 1.0F));
g.drawString("Two timers", x, y);
}
public static void main(String[] args) {
JFrame window = new JFrame("Two timers");
window.setSize(240, 200);
window.setLocation(100, 100);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.getContentPane().add(new TwoTimers());
window.setVisible(true);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - class Timer1 implements ActionListener {
115
7 Timer und Threads
public void actionPerformed(ActionEvent e)
{ addString(getGraphics()); }
}
class Timer2 implements ActionListener {
public void actionPerformed(ActionEvent e){ repaint(); }
}
}
7.2
Threads und das Interface Runnable
Ein Thread ist ein "Programmfaden". Multithreading ermöglicht es, mehrere Programmfäden
gleichzeitig oder quasi gleichzeitig auszuführen.
− In Einprozessorsystemen ist nur quasi gleichzeitige Abarbeitung möglich. Das Betriebssystem teilt dazu den Threads nacheinander den Prozessor in Zeitscheiben zu.
− In Mehrprozessorsystemen können dagegen mehrere Threads tatsächlich gleichzeitig
ablaufen.
Threads werden durch Erweitern der Klasse "Thread" oder durch Implementieren des
Interface "Runnable" realisiert.
Beispiel, Thread1
public class Thread1
public static void
MyThread alpha =
MyThread beta =
{
main(String[] args) {
new MyThread(" Thread Alpha");
new MyThread(" Thread Beta");
alpha.start();
beta.start();
}
}
class MyThread extends Thread {
private String s;
MyThread(String s) { this.s = s; }
public void run() {
for(int i = 0; i < 5; i++) {
System.out.println(i + s);
try {
// Sleep for 1 ms
sleep(1);
} catch (InterruptedException e) {}
}
}
Auf der Konsole
0
0
1
1
2
2
3
3
4
4
Thread
Thread
Thread
Thread
Thread
Thread
Thread
Thread
Thread
Thread
Alpha
Beta
Alpha
Beta
Beta
Alpha
Alpha
Beta
Alpha
Beta
}
alpha.start();
beta.start();
Der Programmfäden "alpha" und "beta" werden gestartet und laufen nun quasi gleichzeitig
ab.
7 Timer und Threads
116
class MyThread extends Thread {
...
public void run() {
...
Die Methode "run" wird beim Start eines Thread aufgerufen. Sie enthält den Programmcode
des Thread. Ein Thread beendet sich selbst, wenn "run" abgearbeitet worden ist.
try {
sleep(1);
} catch (InterruptedException e) {}
Mit der Methode "sleep" kann man einen Programmfaden für eine vorgebbare Anzahl von
Millisekunden pausieren lassen. Die Konsolenausgabe zeigt, dass die beiden Programmfäden
tatsächlich quasi gleichzeitig ablaufen. Man erkennt außerdem, dass das Ende von "main"
nicht zum Stopp der beiden Threads führt. Eine Java-Applikation ist erst dann beendet, wenn
der letzte Thread abgeschlossen worden ist4.
Aufgabe 7.2
Schreiben Sie ein Programm "Thread2". Das Programm soll sich von "Thread1" dadurch
unterscheiden, dass nur ein zusätzlicher Thread gestartet wird und die Konsolenausgaben in
"main" und in der Methode "run" des Threads dann quasi gleichzeitig erfolgen.
Da Java Mehrfachvererbung nicht unterstützt, kann in dem nachfolgenden Applet die von
JPanel abgeleitete Klasse ACPanel nicht zusätzlich noch von "Thread" abgeleitet werden.
Stattdessen muss das Interface "Runnable" implementiert werden, das als einzige Deklaration
"public abstract void run()" enthält. Die abstrakte Methode "run" ist dann zu überschreiben.
Beispiel, AppletCounter2
import java.awt.*;
import javax.swing.*;
public class AppletCounter2 extends JApplet
{
private Thread countThread = null;
private ACPanel acPanel;
public void init() {
acPanel = new ACPanel();
getContentPane().add(acPanel);
}
public void start() {
countThread = new Thread(acPanel);
countThread.start();
}
4
Sogenannte Hintergrund-Threads oder Dämonen werden beim Beenden von "main" gestoppt, vgl. z.B. [1].
7 Timer und Threads
117
public void stop() {
acPanel.beenden();
}
public void destroy() {
acPanel.beenden();
}
}
class ACPanel extends JPanel implements Runnable
{
private int num = 0;
private boolean running = false;
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString("Value: " + num++, 60, 40);
}
public void run() {
num = 0;
running = true;
while(running == true)
{
repaint();
try
{
Bild 7.4 AppletCounter1
Thread.sleep(500);
}
catch(InterruptedException e) { }
}
System.out.println("Thread Terminated!");
}
public void beenden() {
running = false;
}
}
Zum Abschluss noch ein typisches Beispiel für die Verwendung von Threads. In dem
nachfolgenden Applet läuft im Hintergrund eine Schrift durchs Bild, während man parallel
dazu im Vordergrund mit der Maus einen Kreis immer wieder neu positionieren kann. Wenn
das Applet unsichtbar wird oder geschlossen wird, wird der Thread mit der Methode
interrupt() gestoppt.
Beispiel, MouseAndThread02
import java.awt.*;
import java.awt.event.*; // For MouseEvent
import javax.swing.*;
public class MouseAndThread02 extends JApplet {
Thread laufThread;
MouseAndThreadPanel mtPanel;
7 Timer und Threads
public void init() {
mtPanel = new MouseAndThreadPanel();
getContentPane().add(mtPanel);
}
public void start() {
laufThread = new Thread(mtPanel);
laufThread.start();
}
public void stop() {
laufThread.interrupt();
}
public void destroy() {
laufThread.interrupt();
}
}
//---------------------------------------------------class MouseAndThreadPanel extends JPanel implements Runnable
{
private static final int R = 40;
private static final Color BG_COLOR = Color.WHITE;
private static final Color FG_COLOR = Color.GREEN;
private static final Color TXT_COLOR = Color.RED;
private int x = 40, y = 40; // Center of circle
// Variablen für Laufschrift
String text;
String hello;
int lengthText,
xpos,
ypos,
xmax;
Font fontText;
Font fontHello;
int fs;
//
//
//
//
Laenge des
x-Position
y-Position
Breite des
Textes
des Textes
des Textes
Fensters
MouseAndThreadPanel()
{
// Add MyMouseListener and MouseMotionListener
// to receive mouse motion events
addMouseListener(new MyMouseListener());
addMouseMotionListener(new MyMouseMotionListener());
setBackground(BG_COLOR);
text = " ... Java ... ";
fs = 28;
fontText = new Font("SansSerif", Font.BOLD, fs);
FontMetrics fm = getFontMetrics(fontText);
lengthText = fm.stringWidth(text);
ypos = (int) (fs*1.2) ;
}
118
7 Timer und Threads
public void paintComponent(Graphics g)
{
super.paintComponent(g);
// Update if window size has been changed
if (xmax != getSize().width) {
xmax = getSize().width;
}
g.setColor(TXT_COLOR);
g.setFont(fontText);
g.drawString(text, xpos , ypos);
draw(g);
}
void display(Graphics g, Color c)
{ g.setColor(c); g.drawOval(x-R, y-R, R+R, R+R); }
void draw(Graphics g)
{ display(g, FG_COLOR); }
public void run() {
Graphics g = getGraphics();
g.setFont(fontText);
xmax = getSize().width;
xpos = xmax;
while(true) {
xpos--;
if(xpos < -lengthText) {
xpos = xmax ;
}
repaint();
try {
Thread.sleep(20);
}
catch(InterruptedException e) {
System.out.println("Thread Interrupted!");
return;
}
}
}
//---------------------------------------------------class MyMouseListener extends MouseAdapter
{
public void mousePressed(MouseEvent me)
{ x = me.getX(); y = me.getY(); repaint(); }
}
//---------------------------------------------------class MyMouseMotionListener extends MouseMotionAdapter
{
public void mouseDragged(MouseEvent me)
{ x = me.getX(); y = me.getY(); repaint(); }
}
}
119
7 Timer und Threads
120
Die Ausgabe dieses Applets ist in der nachfolgenden Abbildung 7.5 zu sehen. Weitergehende
Erläuterungen zu Threads, insbesondere zu Threadgruppen, Threadprioritäten und Synchronisationsproblemen, sind z.B. in [1] zu finden.
Bild 7.5 Applet MouseAndThread02
Aufgabe 7.3
Schreiben Sie ein Applet "Lines", das wie ein
Bildschirmschoner ein farbiges Linienbündel ausgibt (Bild 7.5). Das Linienbündel läuft im Uhrzeigersinn auf einer Spiralbahn um ein Zentrum in
der Fenstermitte. Der Umlaufradius schrumpft bis
zu einer Untergrenze und wächst dann wieder bis
zu einer Obergrenze.
Bild 7.6 Applet Lines
7.3
Übungsaufgaben
Übungsaufgabe 7.1
Schreiben Sie ein Applet "Laufschrift", das eine Textnachricht als Laufschrift auf dem Bildschirm darstellt. Lesen Sie den auszugebenden Text aus der HTML Datei ein.
a) Verwenden Sie in Ihrer Lösung einen Timer.
b) Verwenden Sie in Ihrer Lösung das Interface Runnable.
8 Swing-Komponenten
In diesem Abschnitt wird eine Auswahl von Swing-Komponenten vorgestellt. Mit diesen
Komponenten lassen sich übersichtliche und intuitiv einsetzbare Schnittstellen zum Anwender
programmieren.
8.1
Übersicht über die Swing-Komponenten
Die folgende Übersicht stammt aus dem Java Tutorial [4]. Dort sind auch weitergehende
Erläuterungen zu allen Swing-Komponenten zu finden.
Top-Level Containers
Applet
Frame
Dialog
General-Purpose Containers
Panel
Scroll pane
Tabbed pane
Tool bar
Special-Purpose Containers
Internal frame
Layered pane
Split pane
122
8 Swing-Komponenten
Root pane
Basic Controls
Buttons
Combo box
Text fields
Menu
Slider
List
Uneditable Information Displays
Label
Progress bar
Editable Displays of Formatted Information
Color chooser
File chooser
Tool tip
123
8 Swing-Komponenten
Table
Text
Tree
In der Regel wird man die genaue Platzierung der Komponenten im jeweiligen Fenster einem
"Layout-Manger" überlassen. Ein Layout-Manager erhält Vorgaben und passt dann Größe
und genaue Platzierung der Dialogelemente der jeweiligen Fenstergröße an. Man kann für die
Dialogelemente aber auch feste Positionen und Größen angeben.
8.2
JButton und JLabel
Ein Button (JButton) ist eine beschriftete Schaltfläche. Wenn man einen Button anklickt, wird
die aus dem vorausgehenden Abschnitt schon bekannte Methode "ActionPerformed" des
Interface "ActionListener" ausgeführt.
Ein Label (JLabel) dient zur Ausgabe eines Textes und reagiert nicht auf Benutzereingaben.
Der ausgegebene Text lässt sich während der Programmausführung ändern.
Im folgenden Programm wird kein Layout-Manager eingesetzt, die Komponenten werden
also fest platziert.
Beispiel, Layout1
import
import
import
import
java.awt.*;
java.awt.event.*;
javax.swing.*;
javax.swing.border.*;
public class Layout1 extends JFrame implements ActionListener {
final private String[] BTXT = {"Pass", "Back", "Back to OS"};
final private JLabel lbl = new JLabel("Start", JLabel.CENTER);
Layout1() {
Container con = getContentPane();
// public void setLayout(LayoutManager mgr)
// mgr -> layout manager or null
con.setLayout(null);
// public void setBounds(int topLeftX, int topLeftY,
//
int width, int height)
lbl.setBounds(12, 8, 248, 40);
// public void setBorder(Border border)
// border -> border to be drawn for this component
lbl.setBorder(new LineBorder(Color.BLACK));
con.add(lbl);
for(int i = 0; i < BTXT.length; i++) {
JButton btn = new JButton(BTXT[i]);
btn.setBounds(10+85*i, 55, 80, 25);
8 Swing-Komponenten
124
con.add(btn);
btn.addActionListener(this);
}
}
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if(cmd == BTXT[2])
System.exit(0);
lbl.setText(cmd);
}
public static void main(String[] args) {
JFrame window = new Layout1();
window.setTitle("Layout 1");
window.setSize(280, 110);
window.setDefaultCloseOperation(EXIT_ON_CLOSE);
window.setVisible(true);
}
}
Bild 8.1 zeigt das vom Programm ausgegebene
Fenster. Der Button "Back to OS" beendet das
Programm. Die beiden anderen Schaltflächen führen hier nur dazu, dass die jeweilige Beschriftung
im Label ausgegeben wird. Da die Komponenten
vorgegebene Koordinaten haben, passen sich ihre
Positionen Änderungen der Fenstergröße nicht
an.
Bild 8.1 Feste Koordinaten
Die Vorgabe fester Koordinaten ist in der Regel keine gute Idee. Man sollte die Platzierung
vielmehr einem Layout-Manager überlassen.
8.3
Layout-Manager
Es stehen sechs Layout-Manager mit unterschiedlichen Platzierungsstrategien zur Verfügung.
Im Folgenden werden vier der Layout-Manager erläutert, eine vollständigere Beschreibung ist
z. B. in [1] zu finden.
− FlowLayout ordnet Komponenten nebeneinander in einer Zeile an.
− BorderLayout verteilt die Komponenten auf die vier Randbereiche und den Mittelbereich
eines Fensters.
− GridLayout positioniert die Komponenten in einem rechteckigen Gitter, dessen Zeilenund Spaltenzahl frei wählbar ist.
− BoxLayout ordnet die Komponenten in einer horizontalen oder in einer vertikalen Leiste
an.
FlowLayout
FlowLayout platziert die Dialogelemente ähnlich zu Textzeilen. Wenn keine weiteren Komponenten in die Zeile passen, wird mit der nächsten Zeile fortgefahren. In der Voreinstellung
werden die Komponenten in den Zeilen zentriert ausgegeben.
125
8 Swing-Komponenten
Beispiel, Layout2
import
import
import
import
java.awt.*;
java.awt.event.*;
javax.swing.*;
javax.swing.border.*;
public class Layout2 extends JFrame implements ActionListener {
final private String[] BTXT = {"Pass", "Back", "Back to OS"};
final private JLabel lbl = new JLabel("Start", JLabel.CENTER);
Layout2() {
Container con = getContentPane();
// FlowLayout() -> centered alignment and a default
//
5-unit horizontal and vertical gap
// FlowLayout(int align) -> align = FlowLayout.LEFT, RIGHT
//
or CENTER, 5-unit horizontal and vertical gap
// FlowLayout(int align, int hgap, int vgap)
con.setLayout(new FlowLayout());
lbl.setBorder(new LineBorder(Color.BLACK));
con.add(lbl);
for(int i = 0; i < BTXT.length; i++) {
JButton btn = new JButton(BTXT[i]);
con.add(btn);
btn.addActionListener(this);
}
}
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if(cmd == BTXT[2])
System.exit(0);
lbl.setText(cmd);
}
public static void main(String[] args) {
JFrame window = new Layout2();
window.setTitle("Layout 2");
window.pack();
window.setDefaultCloseOperation(EXIT_ON_CLOSE);
window.setVisible(true);
}
}
Bild 8.2 zeigt das vom Programm ausgegebene Fenster.
Man erkennt, dass sich die Größe der Komponenten
nach dem Platzbedarf der Beschriftung richtet.
Bild 8.2 FlowLayout
Man kann die bevorzugte Komponentengröße vorgeben. Der folgende Programmausschnitt
zeigt die Vorgehensweise.
...
Layout2() {
Container con = getContentPane();
8 Swing-Komponenten
126
con.setLayout(new FlowLayout());
lbl.setPreferredSize(new Dimension(248, 40));
lbl.setBorder(new LineBorder(Color.BLACK));
con.add(lbl);
for(int i = 0; i < BTXT.length; i++) {
JButton btn = new JButton(BTXT[i]);
btn.setPreferredSize(new Dimension(80, 25));
con.add(btn);
btn.addActionListener(this);
}
}
...
public static void main(String[] args) {
JFrame window = new Layout2();
window.setTitle("Layout 2");
window.setSize(280, 110);
window.setResizable(false);
window.setDefaultCloseOperation(EXIT_ON_CLOSE);
window.setVisible(true);
}
}
Bild 8.3 zeigt das Fenster, das nach diesen
Änderungen ausgegeben wird. Der Aufruf von
"setResizable(false)" führt dazu, dass sich die
Fenstergröße nicht mehr ändern lässt.
Bild 8.3 Vorgegebene Größe
BorderLayout
Mit BorderLayout lassen sich Dialogelemente an den vier Rändern ("BorderLayout. EAST",
"BorderLayout.NORTH", "BorderLayout.WEST", "BorderLayout.SOUTH") und in der Mitte
("BorderLayout.CENTER") platzieren.
Beispiel, Layout3
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Layout3 extends JFrame implements ActionListener {
final private String[] BTXT = {"Pass", "Back", "Back to OS"};
final private JLabel lbl = new JLabel(" Start ",
JLabel.CENTER);
final private String [] BLOC = {BorderLayout.WEST,
BorderLayout.EAST, BorderLayout.SOUTH};
Layout3() {
Container con = getContentPane();
// BorderLayout() -> no gaps between components
127
8 Swing-Komponenten
// BorderLayout(int hgap, int vgap) -> layout with
//
the specified gaps between components
con.setLayout(new BorderLayout());
con.add(lbl, BorderLayout.CENTER);
for(int i = 0; i < BTXT.length; i++) {
JButton btn = new JButton(BTXT[i]);
con.add(btn, BLOC[i]);
btn.addActionListener(this);
}
}
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if(cmd == BTXT[2])
System.exit(0);
lbl.setText(cmd);
}
public static void main(String[] args) {
JFrame window = new Layout3();
window.setTitle("Layout 3");
window.pack();
window.setDefaultCloseOperation(EXIT_ON_CLOSE);
window.setVisible(true);
}
}
Bild 8.4 zeigt das vom Programm ausgegebene Fenster. Wenn das
Fenster vergrößert oder verkleinert wird, passen sich die Komponenten dieser Änderung an. Komponenten an den vertikalen
Rändern ändern dazu ihre Höhe, Komponenten an den
horizontalen Rändern ihre Breite. Ist eine Komponente in der Mitte
platziert, ändert sie sowohl Höhe als auch Breite.
Bild 8.4 BorderLayout
GridLayout
Mit GridLayout lassen sich Komponenten in einem Raster mit wählbarer Spalten- und
Zeilenzahl anordnen. Alle Rasterplätze haben dabei ein- und dieselbe Größe. Die
Komponenten werden in der obersten Zeile von links beginnend der Reihe nach platziert,
dann in der zweiten Zeile, usw.
Beispiel, Layout4
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Layout4 extends JFrame implements ActionListener {
final private String[] BTXT = {"Pass", "Back", "Back to OS"};
final private JLabel lbl = new JLabel("Start", JLabel.CENTER);
Layout4() {
Container con = getContentPane();
128
8 Swing-Komponenten
// GridLayout() -> grid layout with a default of one
//
column per component, in a single row
// GridLayout(int rows, int cols) -> grid layout with the
//
specified number of rows and columns
// GridLayout(int rows, int cols, int hgap, int vgap) ->
//
Grid layout with the specified
//
number of rows and columns and gaps
con.setLayout(new GridLayout(2,2));
con.add(lbl);
for(int i = 0; i < BTXT.length; i++) {
JButton btn = new JButton(BTXT[i]);
con.add(btn);
btn.addActionListener(this);
}
}
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if(cmd == BTXT[2])
System.exit(0);
lbl.setText(cmd);
}
public static void main(String[] args) {
JFrame window = new Layout4();
window.setTitle("Layout 4");
window.pack();
window.setDefaultCloseOperation(EXIT_ON_CLOSE);
window.setVisible(true);
}
}
Bild 8.5 zeigt das vom Programm ausgegebene Fenster. Wenn
das Fenster vergrößert oder verkleinert wird, passen sich die
Komponenten diesen Änderungen an.
Bild 8.5 GridLayout
Schachteln von Layout-Managern
Zum Schachteln von Layout-Managern setzt man Objekte der Klasse "JPanel" ein. In diesen
"Tafeln" platziert man Sublayouts, die dann zum Gesamtlayout zusammengefügt werden. Das
folgende Beispiel zeigt die Vorgehensweise.
Beispiel, Layout5
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Layout5 extends JFrame implements ActionListener {
final private JLabel lbl = new JLabel(" Start ",
JLabel.CENTER);
129
8 Swing-Komponenten
final private int BNUM = 3;
Layout5() {
JPanel pnl1 = new JPanel(new GridLayout(BNUM, 1));
for(int i = 0; i < BNUM; i++) {
JButton btn = new JButton("BV" + i);
pnl1.add(btn);
btn.addActionListener(this);
}
JPanel pnl2 = new JPanel(new GridLayout(1, BNUM));
for(int i = 0; i < BNUM; i++) {
JButton btn = new JButton("BH" + (i+BNUM));
pnl2.add(btn);
btn.addActionListener(this);
}
Container con = getContentPane();
// con.setLayout(new BorderLayout());
con.add(BorderLayout.CENTER, lbl);
con.add(BorderLayout.WEST, pnl1);
con.add(BorderLayout.EAST, pnl2);
// Default layout
}
public void actionPerformed(ActionEvent e) {
lbl.setText(e.getActionCommand()); }
public static void main(String[] args) {
JFrame window = new Layout5();
window.setTitle("Layout 5");
window.pack();
window.setDefaultCloseOperation(EXIT_ON_CLOSE);
window.setVisible(true);
}
}
Bild 8.6 zeigt das vom Programm ausgegebene
Fenster. Auch hier passen sich die Komponenten
Änderungen der Fenstergröße an.
Bild 8.6 Geschachtelte Layout-Manager
Aufgabe 8.1
Schreiben Sie ein Programm "Layout6", das das in
Bild 8.7 gegebene Fenster ausgibt. Die Komponenten sollen sich Änderungen der Fenstergröße
anpassen.
Bild 8.7 Fenster zur Aufgabe
BoxLayout
BoxLayout ordnet die Komponenten in einer horizontalen oder in einer vertikalen Leiste an.
Dir Breite von Schaltflächen richtet sich dabei nach ihrer Beschriftung.
8 Swing-Komponenten
Beispiel, Layout7
import java.awt.*;
import javax.swing.*;
public class Layout7 extends JFrame {
Layout7() {
JPanel pnlv = new JPanel();
// BoxLayout(Container target, int axis)
//
target - the container that needs to be laid out
//
axis - the axis to lay out components along. Can
//
be BoxLayout.X_AXIS or BoxLayout.Y_AXIS
pnlv.setLayout(new BoxLayout(pnlv, BoxLayout.Y_AXIS));
for(int i = 0; i < 4; i++)
pnlv.add(new JButton("BV " + i));
JPanel pnlh = new JPanel();
pnlh.setLayout(new BoxLayout(pnlh, BoxLayout.X_AXIS));
pnlh.add(new JButton("--- BH 0 ---"));
for(int i = 1; i < 4; i++)
pnlh.add(new JButton("BH " + i));
Container cp = getContentPane();
cp.add(BorderLayout.EAST, pnlv);
cp.add(BorderLayout.SOUTH, pnlh);
}
public static void main(String[] args) {
JFrame window = new Layout7();
window.setTitle("Layout 7");
window.pack();
window.setDefaultCloseOperation(EXIT_ON_CLOSE);
window.setVisible(true);
}
}
Bild 8.8 zeigt das vom Programm ausgegebene
Fenster.
Swing enthält eine Container-Klasse "Box", die
"BoxLayout" verwendet. Mit statischen Methoden
dieser Klasse lässt sich die Anordnung der
Komponenten beeinflussen.
...
Layout7() {
Box bv = Box.createVerticalBox();
for(int i = 0; i < 4; i++) {
bv.add(new JButton("BV " + i));
bv.add(Box.createVerticalStrut(4));
}
Box bh = Box.createHorizontalBox();
bh.add(new JButton("--- BH 0 ---"));
for(int i = 1; i < 4; i++) {
Bild 8.8 BoxLayout
130
8 Swing-Komponenten
131
bh.add(Box.createHorizontalStrut(6));
bh.add(new JButton("BH " + i));
}
Container cp = getContentPane();
cp.add(BorderLayout.EAST, bv);
cp.add(BorderLayout.SOUTH, bh);
...
Bild 8.9 zeigt das nach dieser Änderung ausgegebene Fenster.
Die statischen Methoden "create...Strut" (Strebe,
Stütze) erzeugen unsichtbare Komponenten mit
vorgebbaren Abmessungen, mit denen sich der
Abstand zwischen den Komponenten beeinflussen
lässt. Eine vollständigere Beschreibung der
Methoden aus "Box" ist z.B. in [2] zu finden.
8.4
Bild 8.9 BoxLayout mit Abständen
JDialog und JOptionPane
Mit "JDialog" und "JOptionPane" lassen sich Fenster ausgeben, die von einem anderen
Fenster abhängig sind.
JDialog
Im folgenden Programm wird ein modales Dialogfenster eingesetzt. Man nennt ein Dialogfenster "modal", wenn es alle anderen Eingaben einer Anwendung blockiert, solange es nicht
geschlossen worden ist.
Beispiel, Dialog1
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class MyDialog extends JDialog implements ActionListener {
MyDialog(JFrame parent) {
//JDialog(Dialog owner, String title, boolean modal)
super(parent, "My dialog", true);
Container cnt = getContentPane();
cnt.setLayout(new FlowLayout());
cnt.add(new JLabel("Please press OK"));
JButton btn = new JButton("OK");
btn.addActionListener(this);
cnt.add(btn);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
Rectangle rct = new Rectangle(parent.getBounds());
setBounds(rct.x+40, rct.y+30, 160, 110);
}
132
8 Swing-Komponenten
public void actionPerformed(ActionEvent e) {
// Disposes the Dialog and then causes setVisible(true) in
// the calling program to return
dispose();
}
}
public class Dialog1 extends JFrame implements ActionListener {
Dialog1() {
getContentPane().setLayout(new BorderLayout());
JButton btn = new JButton("Show Dialog");
getContentPane().add(BorderLayout.SOUTH, btn);
btn.addActionListener(this);
}
public void actionPerformed(ActionEvent e) {
MyDialog dlg = new MyDialog(this);
// Makes the Dialog visible
dlg.setVisible(true);
}
public static void main(String[] args) {
JFrame window = new Dialog1();
window.setTitle("Dialog 1");
window.setSize(300, 200);
window.setDefaultCloseOperation(EXIT_ON_CLOSE);
window.setVisible(true);
}
}
Bild 8.10 zeigt die vom Programm ausgegebenen
Fenster.
MyDialog dlg = new MyDialog(this);
dlg.setVisible(true);
Der Aufruf von "setVisible(true)" aktiviert das
Dialogfenster und führt dazu, dass es auf dem
Bildschirm sichtbar wird.
dispose();
Das Dialogfenster wird "beseitigt". Bei modalen
Dialogfenstern kehrt "setVisible(true)" erst dann in das
aufrufende Programm zurück.
Bild 8.10 Modales Dialogfenster
Aufgabe 8.2
Schreiben Sie ein Programm "TicTacToe", das die in
Bild 8.11 gegebenen Fenster ausgibt. Das Programm
soll es zwei Personen ermöglichen, "TicTacToe" zu
spielen.
Bild 8.11 TicTacToe
8 Swing-Komponenten
133
JOptionPane
Mit "JOptionPane" kann man mit geringem Aufwand kurze standardisierte Meldungen oder
Eingaben programmieren. Dabei lassen sich folgende Aufgabenbereiche abdecken:
− Mitteilung mit der Schaltfläche "Ok".
− Eingabe mit den Schaltflächen "Ok" und "Cancel".
− Bestätigung (Mitteilung oder Frage) mit unterschiedlichen Schaltflächen.
− Auswahl für beliebige Daten mit Schaltflächen.
Hier wird nur auf die ersten beiden Aufgabenbereiche eingegangen. Vollständigere Erläuterungen sind z.B. in [1] zu finden.
Mitteilung (message)
Mit der statischen Methode "showMessageDialog" kann man eine Mitteilung ausgeben. Das
Mitteilungsfenster ist modal. Solange es nicht beendet wird, blockiert es also alle anderen
Eingaben einer Anwendung. Das Fenster enthält eine Schaltfläche "Ok".
public static void showMessageDialog(Component parent,
Object message, String title, int messageType)
parent:
message:
title:
messageType:
"Elternfenster", in dem das Mitteilungsfenster zentriert ausgegeben wird.
Häufig ein String, der im Fenster als Mitteilung ausgegeben wird. Andere
Objekte sind ebenfalls möglich, wie z.B. ein Icon.
Der String wird in der Kopfleiste ausgegeben.
Typ der Mitteilung, zulässig sind PLAIN_MESSAGE,
ERROR_MESSAGE, INFORMATION_MESSAGE,
WARNING_MESSAGE oder QUESTION_MESSAGE.
Beispiel, ShowMessageBox
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ShowMessageBox extends JFrame
implements ActionListener {
ShowMessageBox() {
JButton btn = new JButton("Show error window");
btn.addActionListener(this);
getContentPane().add(BorderLayout.SOUTH, btn);
}
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(this,
"')' expected in line 12, column 34",
"Fatal error",
JOptionPane.ERROR_MESSAGE);
}
public static void main(String[] args) {
ShowMessageBox window = new ShowMessageBox();
8 Swing-Komponenten
134
window.setTitle("MessageBox Demo");
window.setSize(360, 200);
window.setDefaultCloseOperation(EXIT_ON_CLOSE);
window.setVisible(true);
}
}
Bild 8.12 zeigt das vom Programm ausgegebene Mitteilungsfenster. Mit der Ausnahme der "PLAIN_MESSAGE" enthalten alle
Fenster Icons, die auf die Art der jeweiligen
Meldung hinweisen.
Eingabe (input)
Bild 8.12 Mitteilungsfenster
Die statische Methode "showInputDialog" gibt ein Fenster mit Eingabefeld und den Schaltflächen "Ok" und "Cancel" aus. Das Fenster ist modal.
public static String showInputDialog(Component parent,
Object message, String title, int messageType)
Die Bedeutung der vier Parameter unterscheidet sich nicht von "showMessageDialog".
Rückgabewert:
Eingegebener Text oder "null", wenn die Eingabe abgebrochen worden ist.
Beispiel, ShowInputBox
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ShowInputBox extends JFrame
implements ActionListener {
ShowInputBox() {
JButton btn = new JButton("Show input box");
btn.addActionListener(this);
getContentPane().add(BorderLayout.SOUTH, btn);
}
public void actionPerformed(ActionEvent e) {
String inp = JOptionPane.showInputDialog(this,
"Please enter your name", "Input box",
JOptionPane.QUESTION_MESSAGE);
String mss = (inp != null)? "Your name is " + inp:
"Name not entered";
JOptionPane.showMessageDialog(this, mss, "Message box",
JOptionPane.INFORMATION_MESSAGE);
}
public static void main(String[] args) {
ShowInputBox window = new ShowInputBox();
window.setTitle("InputBox Demo");
window.setSize(360, 200);
8 Swing-Komponenten
135
window.setDefaultCloseOperation(EXIT_ON_CLOSE);
window.setVisible(true);
}
}
Bild 8.13 zeigt das vom Programm ausgegebene Eingabefenster.
8.5
JCheckBox und JRadioButton
Bild 8.13 Eingabefenster
Die Klassen "JRadioButton" und "JCheckBox" stellen "Schalter" zur Verfügung, die vom
Anwender ein- und ausgeschaltet werden können. Instanzen von "JRadioButton" werden in
aller Regel zu Gruppen zusammengefasst, in denen immer genau einer der Schalter aktiviert
sein kann.
Beispiel, RButtonCBox
import
import
import
import
java.awt.*;
java.awt.event.*;
javax.swing.*;
javax.swing.border.*;
public class RButtonCBox extends JFrame implements ActionListener{
final private String TXT[] = {"zero", "one", "two"};
private JRadioButton rb[] = new JRadioButton[TXT.length];
private JCheckBox cb[] = new JCheckBox[TXT.length];
RButtonCBox() {
Box brb = Box.createVerticalBox();
brb.setBorder(new LineBorder(Color.GRAY));
ButtonGroup group = new ButtonGroup();
for(int i = 0; i < TXT.length; i++) {
rb[i] = new JRadioButton("Button " + TXT[i], false);
rb[i].addActionListener(this);
brb.add(rb[i]);
group.add(rb[i]);
}
rb[0].setSelected(true);
Box bcb = Box.createVerticalBox();
bcb.setBorder(new LineBorder(Color.GRAY));
for(int i = 0; i < TXT.length; i++) {
cb[i] = new JCheckBox("Box " + TXT[i], false);
cb[i].addActionListener(this);
bcb.add(cb[i]);
}
Container con = getContentPane();
con.setLayout(new GridLayout(1, 2, 2, 2));
con.add(brb);
136
8 Swing-Komponenten
con.add(bcb);
}
public void actionPerformed(ActionEvent e) {
String srb = "", scb = "";
for(int i = 0; i < TXT.length; i++) {
if(rb[i].isSelected())
srb += i;
if(cb[i].isSelected())
scb += i + " ";
}
System.out.println("RadioButton: " + srb + "
}
CheckBox: " + scb);
public static void main(String[] args) {
JFrame window = new RButtonCBox();
window.setTitle("RButton CBox");
window.pack();
window.setDefaultCloseOperation(EXIT_ON_CLOSE);
window.setVisible(true);
}
}
Bild 8.14 zeigt das Programmfenster. Auf der Konsole
wird der jeweils aktuelle Zustand der sechs Schalter
ausgegeben.
Bild 8.14 JRadioButton und JCheckBox
Aufgabe 8.3
Schreiben Sie ein Programm, das das in Bild 8.15
gegebene Fenster ausgibt. Wenn der Benutzer die
Schaltfläche "Ok" betätigt, soll der aktuelle
Zustand der elf Schalter auf der Konsole ausgegeben werden.
Bild 8.15 JRadioButton und JCheckBox
8.6
JTextField, JTextArea und JScrollPane
"JTextField" stellt ein einzeiliges und "JTextArea" ein mehrzeiliges Feld zur Anzeige und
Eingabe von Texten zur Verfügung. "JScrollPane" erzeugt bei Bedarf horizontale und
vertikale "Schieber".
JTextField
Das folgende Programm zeigt die Vorgehensweise bei der Eingabe mit "JTextField". Wenn
der Benutzer den Textcursor (Caret) im Eingabefeld bewegt, wird die Methode "CaretUpdate"
137
8 Swing-Komponenten
aus "CaretListener" aufgerufen. Die Return-Taste führt zur Ausführung von "ActionPerformed" aus "ActionListener".
Beispiel, TextFieldDemo
import
import
import
import
java.awt.*;
java.awt.event.*;
javax.swing.*;
javax.swing.event.*;
// For "CaretEvent"
public class TextFieldDemo extends JFrame implements
ActionListener, CaretListener {
// JTextField(int columns) or
// JTextField(String text) or
// JTextField(String text, int columns)
private JTextField tf = new JTextField(16);
TextFieldDemo() {
Box bh = Box.createHorizontalBox();
bh.add(new JLabel(" Name: ", JLabel.LEFT));
tf.addActionListener(this);
tf.addCaretListener(this);
bh.add(tf);
Container con = getContentPane();
con.setLayout(new GridLayout(2, 1, 4, 4));
con.add(new JLabel(" Type and press Return "));
con.add(bh);
}
public void actionPerformed(ActionEvent e) {
System.out.println("Command: " + e.getActionCommand());
System.out.println("Text: " + tf.getText());
}
public void caretUpdate(CaretEvent e) {
System.out.println("Pos: " + e.getDot() + "
}
" + tf.getText());
public static void main(String[] args) {
JFrame window = new TextFieldDemo();
window.setTitle("Text Field Demo");
window.pack();
window.setDefaultCloseOperation(EXIT_ON_CLOSE);
window.setVisible(true);
}
}
Auf der Konsole
Bild 8.16 zeigt das einzeilige
Eingabefeld und die zugehörige Konsolenausgabe.
Bild 8.16 JTextField und Konsolenausgabe
Pos: 1 T
Pos: 2 Te
Pos: 3 Tes
Pos: 4 Test
Command: Test
Text: Test
138
8 Swing-Komponenten
Aufgabe 8.4
Schreiben Sie ein Programm, das das in Bild 8.17
gegebene Fenster ausgibt. Wenn der Benutzer die
Schaltfläche "Ok" betätigt, sollen die eingetragenen Daten zur Kontrolle auf der Konsole
ausgegeben werden.
Bild 8.17 Name und Adresse
Aufgabe 8.5
Schreiben Sie ein Programm, das den einfachen Rechner aus
Bild 8.18 ausgibt. Wenn man eine Taste betätigt, soll das
Programm hier nur die zugehörige Beschriftung im Rechnerdisplay ausgeben.
(Der Rechner wird in einer Übungsaufgabe zu diesem
Abschnitt vervollständigt.)
JTextArea und JScrollPane
Bild 8.18 Einfacher Rechner
"JTextArea" stellt ein mehrzeiliges Feld zur Anzeige und Eingabe von Texten zur Verfügung.
Die Klasse ist aber nicht in der Lage, den Text zu scrollen, wenn er nicht vollständig in das
Ausgabefeld passt. Für diesen Zweck ist "JScrollPane" vorgesehen.
Im folgenden Beispiel wird ein einfacher Editor programmiert. Das Programm zeigt die Vorgehensweise beim Einsatz von "JTextArea" und "JScrollPane".
Beispiel, BasicEditor
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class BasicEditor extends JFrame implements ActionListener {
final static String S[] = {"Duplicate Text", "Clear Text"};
private JButton b[] = {new JButton(S[0]), new JButton(S[1])};
// JTextArea(String text, int rows, int columns)
private JTextArea ta = new JTextArea(
"Das ist ein Test des einfachen Editors. ", 8, 26);
BasicEditor () {
ta.setFont(new Font("Arial", Font.ITALIC, 12));
// Long lines will be wrapped, default: false I
ta.setLineWrap(true) ;
// Lines will be wrapped at word boundaries, default: false
8 Swing-Komponenten
139
ta.setWrapStyleWord(true);
JPanel panel = new JPanel(new GridLayout(1, 2));
for(int i = 0; i < b.length; i++) {
panel.add(b[i]);
b[i].addActionListener(this);
}
getContentPane().add(BorderLayout.CENTER, new JScrollPane(ta));
getContentPane().add(BorderLayout.SOUTH, panel);
}
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if(cmd.equals(S[1]))
ta.setText("");
else {
String txt = ta.getText();
if(txt.length() < 16384)
ta.setText(txt + txt);
else
JOptionPane.showMessageDialog(this,
"Text too long, not duplicated",
"Simple Editor", JOptionPane.ERROR_MESSAGE);
}
}
public static void main(String[] args) {
JFrame window = new BasicEditor();
window.setTitle("Basic Editor");
window.pack();
window.setDefaultCloseOperation(EXIT_ON_CLOSE);
window.setVisible(true);
}
}
In Bild 8.19 ist das Programmfenster zu finden.
Da der Text am Zeilenende automatisch umgebrochen wird, ist hier nur ein vertikaler
Schieber erforderlich.
8.7
Bild 8.19 Einfacher Editor
JSlider und JProgressbar
Mit der Klasse "JSlider" lässt sich ein Schieberegler ausgeben, der mit Markierungen und
einer Bemaßung versehen werden kann. Wenn der Benutzer den Regler betätigt, wird die
Methode "stateChanged" des Interface "ChangeListener" ausgeführt (Tabelle 8.1).
Tabelle 8.1 ChangeListener und zugehörige Methode
Listener-Interface
ChangeListener
Methode
stateChanged
Auslösende Aktion
Der Zustand hat sich geändert, der Anwender hat den
Schieberegler betätigt
8 Swing-Komponenten
140
Die Klasse "JProgressBar" kann man zur Darstellung anloger Werte einsetzen. Eine typische
Anwendung ist zum Beispiel eine Fortschrittsanzeige bei länger dauernden Vorgängen.
Beispiel, CelsiusConverter
import
import
import
import
java.awt.*;
java.awt.event.*;
javax.swing.*;
javax.swing.event.*;
// For ChangeListener
public class CelsiusConverter extends JFrame
implements ChangeListener {
// JSlider(int orientation, int min, int max, int initialValue)
JSlider sl = new JSlider(JSlider.HORIZONTAL, -20, 100, 0);
// JProgressBar(int orient, int min, int max)
JProgressBar pb = new JProgressBar(
JProgressBar.HORIZONTAL, -4, 212);
JLabel cfLabel = new JLabel("0 °C = 32 °F", JLabel.CENTER);
CelsiusConverter () {
JLabel slLabel = new JLabel("Temperatur in °C", JLabel.CENTER);
slLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
JLabel pbLabel = new JLabel("Temperatur in °F", JLabel.CENTER);
pbLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
cfLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
sl.setMajorTickSpacing(40);
sl.setMinorTickSpacing(10);
sl.setPaintTicks(true);
sl.setPaintLabels(true);
sl.addChangeListener(this);
// public static Border createEmptyBorder(int spaceTop,
//
int spaceLeft, int spaceBottom, int spaceRight)
sl.setBorder(BorderFactory.createEmptyBorder(2, 2, 10, 6));
pb.setValue(32);
pb.setBorder(BorderFactory.createEmptyBorder(2, 2, 10, 6));
pb.setStringPainted(true);
Container con = getContentPane();
con.setLayout(new BoxLayout(con, BoxLayout.Y_AXIS));
con.add(slLabel);
con.add(sl);
con.add(pbLabel);
con.add(pb);
con.add(cfLabel);
}
public void stateChanged(ChangeEvent e) {
int tempF = (int) (1.8*sl.getValue() + 32);
pb.setValue(tempF);
cfLabel.setText(sl.getValue() + " °C = " + tempF +" °F");
}
public static void main(String[] args) {
141
8 Swing-Komponenten
JFrame window = new CelsiusConverter();
window.setTitle("Celsius Converter");
window.pack();
window.setDefaultCloseOperation(EXIT_ON_CLOSE);
window.setVisible(true);
}
}
Bild 8.20 zeigt das Programmfenster. Die Temperatur in
Grad Celsius wird am Schieberegler mit der Maus oder mit
der Tastatur eingestellt.
8.8
JMenu und JPopupmenu
Bild 8.20 Celsius Converter
Ein Menü ("JMenu") ist Bestandteil einer Menüleiste ("JMenuBar"). Wenn man einen Menüeintrag ("JMenuItem") anklickt, wird die Methode "ActionPerformed" des Interface "ActionListener" ausgeführt. Das folgende Programm zeigt, wie man eine einfache Menüstruktur
implementiert.
Beispiel, Menu1
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Menu1 extends JFrame implements ActionListener {
Menu1() {
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
JMenu menu1 = new JMenu("File");
menuBar.add(menu1);
addMenuItem(menu1, new JMenuItem("Load"));
addMenuItem(menu1, new JMenuItem("Save"));
menu1.addSeparator();
addMenuItem(menu1, new JMenuItem("Exit"));
JMenu menu2 = new JMenu("Options");
menuBar.add(menu2);
addMenuItem(menu2, new JMenuItem("Settings"));
}
private void addMenuItem(JMenu m, JMenuItem mi) {
m.add(mi); mi.addActionListener(this); }
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if(cmd.equals("Exit"))
System.exit(0);
JOptionPane.showMessageDialog(this, cmd,
"Menu 1", JOptionPane.INFORMATION_MESSAGE);
}
142
8 Swing-Komponenten
public static void main(String[] args) {
JFrame window = new Menu1();
window.setTitle("Menu 1");
window.setSize(300, 180);
window.setDefaultCloseOperation(EXIT_ON_CLOSE);
window.setVisible(true);
}
}
Bild 8.21 zeigt das vom Programm ausgegebene
Fenster. Der Menüeintrag "Exit" hat die zu erwartende
Wirkung: das Programm wird beendet. Die anderen
Menüeintragungen führen hier nur dazu, dass der
jeweilige Menütext in einem Meldungsfenster zur
Bestätigung ausgegeben wird.
Bild 8.21 Menü
Mit der Methode "setActionCommand" kann man einem Menüeintrag einen Text zuweisen,
der von der Beschriftung dieses Menüeintrags abweicht. Auf diese Weise lassen sich Menüeintragungen mit demselben Text unterscheiden. Im folgenden Programmausschnitt ist die
Vorgehensweise zu finden.
...
// wie im vorausgehenden Beispiel
Menu1() {
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
JMenu menu1 = new JMenu("File");
menuBar.add(menu1);
addMenuItem(menu1, new JMenuItem("Exit"), "StopProg");
JMenu menu2 = new JMenu("Rectangle");
menuBar.add(menu2);
addMenuItem(menu2, new JMenuItem("Red"), "RectRed");
JMenu menu3 = new JMenu("Circle");
menuBar.add(menu3);
addMenuItem(menu3, new JMenuItem("Red"), "CircleRed");
}
private void addMenuItem(JMenu m, JMenuItem mi, String s) {
m.add(mi);
mi.addActionListener(this);
mi.setActionCommand(s);
}
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if(cmd.equals("StopProg"))
System.exit(0);
JOptionPane.showMessageDialog(this, cmd,
"Menu 1", JOptionPane.INFORMATION_MESSAGE);
8 Swing-Komponenten
143
}
...
Bild 8.22 zeigt das vom Programm ausgegebene
Fenster.
Eine vollständigere Beschreibung der Möglichkeiten ist z.B. in [1] zu finden.
Bild 8.22 "Red" in "Rectangle" und in "Circle"
Kontextmenü
Kontextmenüs (Popup-Menüs) werden mit einem rechten Mausklick auf die jeweilige
Komponente angefordert und enthalten auf diese Komponente bezogene Menüepunkte.
Beispiel, Menu2
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class MyPanel extends JPanel {
private Color cl;
MyPanel(Color c) {cl = c; }
void setColor(Color c) { cl = c; }
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(cl);
g.drawRect(getSize().width/2-50,
getSize().height/2-50, 100, 100);
}
}
public class Menu2 extends JFrame implements ActionListener {
private final String[] TXT={"Black","White","Red" };
private final Color[] COL={Color.BLACK,Color.WHITE, Color.RED};
final private MyPanel mypnl = new MyPanel(COL[0]);
final private JPopupMenu popup = new JPopupMenu();
Menu2() {
addMenuItem(popup, new JMenuItem(TXT[0]));
addMenuItem(popup, new JMenuItem(TXT[1]));
popup.addSeparator();
addMenuItem(popup, new JMenuItem(TXT[2]));
addMouseListener(new MyMouseListener());
getContentPane().add(mypnl);
}
private void addMenuItem(JPopupMenu pm, JMenuItem mi) {
pm.add(mi); mi.addActionListener(this); }
144
8 Swing-Komponenten
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
for(int i = 0; i < TXT.length; i++)
if(cmd.equals(TXT[i]))
mypnl.setColor(COL[i]);
repaint();
}
class MyMouseListener extends MouseAdapter {
public void mouseReleased(MouseEvent e) {
if(e.isPopupTrigger())
popup.show(e.getComponent(), e.getX(), e.getY());
}
}
public static void main(String[] args) {
Menu2 window = new Menu2();
window.setTitle("Popup");
window.setSize(260, 180);
window.setDefaultCloseOperation(EXIT_ON_CLOSE);
window.setVisible(true);
}
}
Bild 8,.23 zeigt das vom Programm ausgegebene
Fenster. Mit Hilfe des Kontextmenüs lässt sich die Farbe
der Rechteckumrandung ändern.
8.9
JFileChooser
Bild 8.23 Kontextmenü
Die Klasse "JFileChooser" stellt die üblichen Dialogfenster zur Verfügung, die man zum
Laden oder Speichern einer Datei benötigt.
Mit dem folgenden Programm lassen sich Dateien zum Laden oder Speichern auswählen. Die
Dateien werden dann aber nicht wirklich geladen oder gespeichert, die jeweilige Auswahl
wird vielmehr nur in einem Textfeld protokolliert.
Beispiel, FileChooserDemo
import
import
import
import
java.awt.*;
java.awt.event.*;
javax.swing.*;
java.io.*;
public class FileChooserDemo extends JFrame
implements ActionListener {
final String[] TXT = {"Open", "Save"};
final JTextArea log = new JTextArea(10, 40);
final JFileChooser fc = new JFileChooser();
public FileChooserDemo() {
log.setEditable(false);
log.setMargin(new Insets(5, 5, 5, 5));
8 Swing-Komponenten
145
JPanel pnl = new JPanel();
for(int i = 0; i < TXT.length; i++) {
JButton btn = new JButton(TXT[i]);
btn.addActionListener(this);
pnl.add(btn);
}
Container contentPane = getContentPane();
contentPane.add(new JScrollPane(log), BorderLayout.CENTER);
contentPane.add(pnl, BorderLayout.SOUTH);
}
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
String mss = cmd + ": Cancelled";
int ret = (cmd.equals(TXT[0]))? fc.showOpenDialog(this):
fc.showSaveDialog(this);
if(ret == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
mss = cmd + ": " + file.getPath();
}
log.append(mss + '\n');
}
public static void main(String[] args) {
JFrame window = new FileChooserDemo();
window.setTitle("File Chooser Demo");
window.pack();
window.setResizable(false);
window.setDefaultCloseOperation(EXIT_ON_CLOSE);
window.setVisible(true);
}
}
Vom Programm ausgegebene Fenster sind in
Bild 8.24 zu finden. Es ist
gerade der Dialog zum
Öffnen einer Datei zu
sehen.
Bild 8.24 File Chooser
8 Swing-Komponenten
146
8.10 Übungsaufgaben
Übungsaufgabe 8.1
In einer der Aufgaben zu diesem Abschnitts ist das Programm
eines einfachen Rechners entwickelt worden (Bild 8.25).
Wenn man eine Taste betätigt, gibt das Programm bisher aber
nur die zugehörige Beschriftung im Rechnerdisplay aus.
Erweitern Sie das Programm nun zu einem funktionsfähigen
einfachen Rechner.
Bild 8.25 Einfacher Rechner
Übungsaufgabe 8.2
Bild 8.26 MyEditor
Schreiben Sie einen einfachen Editor (Bild 8.26) für Textdateien. Es soll möglich sein, eine
Datei über "File | Open" zu öffnen, den Text zu editieren und den geänderten Text dann mit
"File | Save As" in der ursprünglichen oder in einer anderen Datei zu speichern. Verwenden
Sie zum Öffnen und zum Speichern der Textdatei jeweils eine Instanz von "JFileChooser".
9 Anhang
9.1
Schlüsselwörter (keywords) in Java
Liste der Schlüsselwörter (reserviert)
abstract
boolean
break
byte
case
catch
char
class
const *
continue
default
do
double
else
extends
final
finally
float
for
goto *
if
implements
import
instanceof
int
interface
long
native
new
package
private
protected
public
return
short
static
strictfp **
super
switch
synchronized
this
throw
throws
transient
try
void
volatile
while
* Gegenwärtig nicht benutzt.
** In Java 2 dazugekommen.
true, false und null sind keine Schlüsselwörter, sie sind aber dennoch reserviert.
9.2
Einige Java-Begriffe
J2SE: Java 2 Standard Edition
J2EE: Java 2 Enterprise Edition für die Programmierung auf Server-Seite
J2ME: Java 2 Micro Edition für eingebettete Systeme (embedded systems)
Abstract Windows Toolkit (AWT, ab JDK 1.0): Stellt elementare Grafik- und Fensterfunktionen zur Verfügung.
Swing Toolset (in der aktuellen Form ab JDK 1.2): Konzept und Schnittstellen für plattformübergreifende GUI Applikationen (Graphical User Interface). Swing Applikationen
können das typische Aussehen (Look & Feel) einer Plattform annehmen (Windows,
Macintosh, Solaris). Die Swing Komponenten enthalten Duplikate aller AWT-Komponenten
und darüber hinaus zahlreiche zusätzliche Komponenten.
Beans, Javabeans (ab JDK 1.1): Wiederverwendbare Softwarebausteine. Beans ermöglichen
das "Zusammenstellen" von Programmen aus vorgefertigten Komponenten in einer grafischen
Entwicklungsumgebung (IDE, Integrated Development Environment).
Java Database Connectivity (JDBC): Schnittstelle zum Zugriff auf Datenbanken über SQLBefehle (System Querry Language). Die Schnittstelle muss dazu von der Datenbank unterstützt werden.
Remote Method Invocation (RMI): Programme können mit RMI in verteilten Systemen auf
Objekte von anderen Anwendungen zugreifen, die im Netzwerk von anderen Java-VM
ausgeführt werden.
9 Anhang
9.3
148
Package java.lang
9.3.1 Class Summary
Boolean
Byte
Character
The Boolean class wraps a value of the primitive type boolean in an object.
The Byte class is the standard wrapper for byte values.
The Character class wraps a value of the primitive type char in an object.
Instances of this class represent particular subsets of the Unicode character set.
Character.Subset
A family of character subsets representing the character blocks defined by the
Character.UnicodeBlock
Unicode 2.0 specification.
Class
Instances of the class Class represent classes and interfaces in a running Java
application.
ClassLoader
The class ClassLoader is an abstract class.
Compiler
The Compiler class is provided to support Java-to-native-code compilers and
related services.
Double
The Double class wraps a value of the primitive type double in an object.
Float
The Float class wraps a value of primitive type float in an object.
This class extends ThreadLocal to provide inheritance of values from parent Thread
InheritableThreadLocal to child Thread: when a child thread is created, the child receives initial values for all
InheritableThreadLocals for which the parent has values.
Integer
The Integer class wraps a value of the primitive type int in an object.
Long
The Long class wraps a value of the primitive type long in an object.
Math
Number
Object
Package
Process
Runtime
RuntimePermission
SecurityManager
Short
StrictMath
String
StringBuffer
System
Thread
ThreadGroup
ThreadLocal
The class Math contains methods for performing basic numeric operations such as
the elementary exponential, logarithm, square root, and trigonometric functions.
The abstract class Number is the superclass of classes Byte, Double, Float,
Integer, Long, and Short.
Class Object is the root of the class hierarchy.
Package objects contain version information about the implementation and
specification of a Java package.
The Runtime.exec methods create a native process and return an instance of a
subclass of Process that can be used to control the process and obtain information
about it.
Every Java application has a single instance of class Runtime that allows the
application to interface with the environment in which the application is running.
This class is for runtime permissions.
The security manager is a class that allows applications to implement a security
policy.
The Short class is the standard wrapper for short values.
The class StrictMath contains methods for performing basic numeric operations
such as the elementary exponential, logarithm, square root, and trigonometric
functions.
The String class represents character strings.
A string buffer implements a mutable sequence of characters.
The System class contains several useful class fields and methods.
A thread is a thread of execution in a program.
A thread group represents a set of threads.
This class provides ThreadLocal variables.
9 Anhang
Throwable
Void
9.3.2
149
The Throwable class is the superclass of all errors and exceptions in the Java
language.
The Void class is an uninstantiable placeholder class to hold a reference to the Class
object representing the primitive Java type void.
Class java.lang.Math
Field Summary
static double E
The double value that is closer than any other to e, the base of the natural
logarithms.
static double PI
The double value that is closer than any other to pi, the ratio of the circumference
of a circle to its diameter.
Method Summary
static double abs(double a)
Returns the absolute value of a double value.
static float abs(float a)
Returns the absolute value of a float value.
static int abs(int a)
Returns the absolute value of an int value.
static long abs(long a)
Returns the absolute value of a long value.
static double acos(double a)
Returns the arc cosine of an angle, in the range of 0.0 through pi.
static double asin(double a)
Returns the arc sine of an angle, in the range of -pi/2 through pi/2.
static double atan(double a)
Returns the arc tangent of an angle, in the range of -pi/2 through pi/2.
static double atan2(double a, double b)
Converts rectangular coordinates (b, a) to polar (r, theta).
static double ceil(double a)
Returns the smallest (closest to negative infinity) double value that is not less than
the argument and is equal to a mathematical integer.
static double cos(double a)
Returns the trigonometric cosine of an angle.
static double exp(double a)
Returns the exponential number e (i.e., 2.718...) raised to the power of a double
value.
static double floor(double a)
Returns the largest (closest to positive infinity) double value that is not greater than
the argument and is equal to a mathematical integer.
static double IEEEremainder(double f1, double f2)
Computes the remainder operation on two arguments as prescribed by the IEEE 754
standard.
static double log(double a)
Returns the natural logarithm (base e) of a double value.
static double max(double a, double b)
Returns the greater of two double values.
static float max(float a, float b)
Returns the greater of two float values.
9 Anhang
150
static int max(int a, int b)
Returns the greater of two int values.
static long max(long a, long b)
Returns the greater of two long values.
static double min(double a, double b)
Returns the smaller of two double values.
static float min(float a, float b)
Returns the smaller of two float values.
static int min(int a, int b)
Returns the smaller of two int values.
static long min(long a, long b)
Returns the smaller of two long values.
static double pow(double a, double b)
Returns of value of the first argument raised to the power of the second argument.
static double random()
Returns a double value with a positive sign, greater than or equal to 0.0 and less
than 1.0.
static double rint(double a)
Returns the double value that is closest in value to a and is equal to a mathematical
integer.
static long round(double a)
Returns the closest long to the argument.
static int round(float a)
Returns the closest int to the argument.
sin(double
a)
static double
Returns the trigonometric sine of an angle.
static double sqrt(double a)
Returns the correctly rounded positive square root of a double value.
static double tan(double a)
Returns the trigonometric tangent of an angle.
static double toDegrees(double angrad)
Converts an angle measured in radians to the equivalent angle measured in degrees.
static double toRadians(double angdeg)
Converts an angle measured in degrees to the equivalent angle measured in radians.
9.3.3
Class java.lang.String
Method Summary
char charAt(int index)
Returns the character at the specified index.
int compareTo(Object o)
Compares this String to another Object.
int compareTo(String anotherString)
Compares two strings lexicographically.
int compareToIgnoreCase(String str)
Compares two strings lexicographically, ignoring case considerations.
String concat(String str)
Concatenates the specified string to the end of this string.
static String copyValueOf(char[] data)
Returns a String that is equivalent to the specified character array.
9 Anhang
151
static String copyValueOf(char[] data, int offset, int count)
Returns a String that is equivalent to the specified character array.
boolean endsWith(String suffix)
Tests if this string ends with the specified suffix.
boolean equals(Object anObject)
Compares this string to the specified object.
boolean equalsIgnoreCase(String anotherString)
Compares this String to another String, ignoring case considerations.
getBytes()
byte[]
Convert this String into bytes according to the platform's default character
encoding, storing the result into a new byte array.
void getBytes(int srcBegin, int srcEnd, byte[] dst, int dstBegin)
Deprecated. This method does not properly convert characters into bytes. As of
JDK 1.1, the preferred way to do this is via the getBytes(String enc) method, which
takes a character-encoding name, or the getBytes() method, which uses the platform's
default encoding.
byte[] getBytes(String enc)
Convert this String into bytes according to the specified character encoding, storing
the result into a new byte array.
void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
Copies characters from this string into the destination character array.
hashCode()
int
Returns a hashcode for this string.
int indexOf(int ch)
Returns the index within this string of the first occurrence of the specified character.
int indexOf(int ch, int fromIndex)
Returns the index within this string of the first occurrence of the specified character,
starting the search at the specified index.
int indexOf(String str)
Returns the index within this string of the first occurrence of the specified substring.
int indexOf(String str, int fromIndex)
Returns the index within this string of the first occurrence of the specified substring,
starting at the specified index.
String intern()
Returns a canonical representation for the string object.
int lastIndexOf(int ch)
Returns the index within this string of the last occurrence of the specified character.
lastIndexOf(int
ch, int fromIndex)
int
Returns the index within this string of the last occurrence of the specified character,
searching backward starting at the specified index.
int lastIndexOf(String str)
Returns the index within this string of the rightmost occurrence of the specified
substring.
int lastIndexOf(String str, int fromIndex)
Returns the index within this string of the last occurrence of the specified substring.
int length()
Returns the length of this string.
boolean regionMatches(boolean ignoreCase, int toffset, String other,
int ooffset, int len)
Tests if two string regions are equal.
boolean regionMatches(int toffset, String other, int ooffset,
int len)
Tests if two string regions are equal.
9 Anhang
152
String replace(char oldChar, char newChar)
Returns a new string resulting from replacing all occurrences of oldChar in this
string with newChar.
boolean startsWith(String prefix)
Tests if this string starts with the specified prefix.
startsWith(String
prefix, int toffset)
boolean
Tests if this string starts with the specified prefix beginning a specified index.
String substring(int beginIndex)
Returns a new string that is a substring of this string.
String substring(int beginIndex, int endIndex)
Returns a new string that is a substring of this string.
char[] toCharArray()
Converts this string to a new character array.
String toLowerCase()
Converts all of the characters in this String to lower case using the rules of the
default locale, which is returned by Locale.getDefault.
String toLowerCase(Locale locale)
Converts all of the characters in this String to lower case using the rules of the
given Locale.
String toString()
This object (which is already a string!) is itself returned.
String toUpperCase()
Converts all of the characters in this String to upper case using the rules of the
default locale, which is returned by Locale.getDefault.
String toUpperCase(Locale locale)
Converts all of the characters in this String to upper case using the rules of the
given locale.
String trim()
Removes white space from both ends of this string.
static String valueOf(boolean b)
Returns the string representation of the boolean argument.
static String valueOf(char c)
Returns the string representation of the char argument.
static String valueOf(char[] data)
Returns the string representation of the char array argument.
static String valueOf(char[] data, int offset, int count)
Returns the string representation of a specific subarray of the char array argument.
static String valueOf(double d)
Returns the string representation of the double argument.
static String valueOf(float f)
Returns the string representation of the float argument.
static String valueOf(int i)
Returns the string representation of the int argument.
static String valueOf(long l)
Returns the string representation of the long argument.
static String valueOf(Object obj)
Returns the string representation of the Object argument.
9 Anhang
9.4
153
Class java.awt.Graphics
Method Summary
abstract clearRect(int x, int y, int width, int height)
void
Clears the specified rectangle by filling it with the background color of the current
drawing surface.
abstract clipRect(int x, int y, int width, int height)
void
Intersects the current clip with the specified rectangle.
abstract copyArea(int x, int y, int width, int height, int dx, int dy)
void
Copies an area of the component by a distance specified by dx and dy.
abstract create()
Graphics
Creates a new Graphics object that is a copy of this Graphics object.
Graphics create(int x, int y, int width, int height)
Creates a new Graphics object based on this Graphics object, but with a new
translation and clip area.
abstract dispose()
void
Disposes of this graphics context and releases any system resources that it is using.
void draw3DRect(int x, int y, int width, int height,
boolean raised)
Draws a 3-D highlighted outline of the specified rectangle.
abstract drawArc(int x, int y, int width, int height, int startAngle,
void int arcAngle)
Draws the outline of a circular or elliptical arc covering the specified rectangle.
void drawBytes(byte[] data, int offset, int length, int x, int y)
Draws the text given by the specified byte array, using this graphics context's current
font and color.
void drawChars(char[] data, int offset, int length, int x, int y)
Draws the text given by the specified character array, using this graphics context's
current font and color.
abstract drawImage(Image img, int x, int y, Color bgcolor,
boolean ImageObserver observer)
Draws as much of the specified image as is currently available.
abstract drawImage(Image img, int x, int y, ImageObserver observer)
boolean
Draws as much of the specified image as is currently available.
abstract drawImage(Image img, int x, int y, int width, int height,
boolean Color bgcolor, ImageObserver observer)
Draws as much of the specified image as has already been scaled to fit inside the
specified rectangle.
abstract drawImage(Image img, int x, int y, int width, int height,
boolean ImageObserver observer)
Draws as much of the specified image as has already been scaled to fit inside the
specified rectangle.
abstract drawImage(Image img, int dx1, int dy1, int dx2, int dy2,
boolean int sx1, int sy1, int sx2, int sy2, Color bgcolor,
ImageObserver observer)
Draws as much of the specified area of the specified image as is currently available,
scaling it on the fly to fit inside the specified area of the destination drawable surface.
abstract drawImage(Image img, int dx1, int dy1, int dx2, int dy2,
boolean int sx1, int sy1, int sx2, int sy2, ImageObserver observer)
Draws as much of the specified area of the specified image as is currently available,
scaling it on the fly to fit inside the specified area of the destination drawable surface.
abstract drawLine(int x1, int y1, int x2, int y2)
void
Draws a line, using the current color, between the points (x1, y1) and (x2, y2)
in this graphics context's coordinate system.
9 Anhang
154
abstract drawOval(int x, int y, int width, int height)
void
Draws the outline of an oval.
abstract drawPolygon(int[] xPoints, int[] yPoints, int nPoints)
void
Draws a closed polygon defined by arrays of x and y coordinates.
void drawPolygon(Polygon p)
Draws the outline of a polygon defined by the specified Polygon object.
abstract drawPolyline(int[] xPoints, int[] yPoints, int nPoints)
void
Draws a sequence of connected lines defined by arrays of x and y coordinates.
drawRect(int
x, int y, int width, int height)
void
Draws the outline of the specified rectangle.
abstract drawRoundRect(int x, int y, int width, int height,
void int arcWidth, int arcHeight)
Draws an outlined round-cornered rectangle using this graphics context's current color.
abstract drawString(AttributedCharacterIterator iterator, int x, int y)
void
Draws the text given by the specified iterator, using this graphics context's current
color.
abstract drawString(String str, int x, int y)
void
Draws the text given by the specified string, using this graphics context's current font
and color.
void fill3DRect(int x, int y, int width, int height,
boolean raised)
Paints a 3-D highlighted rectangle filled with the current color.
abstract fillArc(int x, int y, int width, int height, int startAngle,
void int arcAngle)
Fills a circular or elliptical arc covering the specified rectangle.
fillOval(int
x, int y, int width, int height)
abstract
void
Fills an oval bounded by the specified rectangle with the current color.
abstract fillPolygon(int[] xPoints, int[] yPoints, int nPoints)
void
Fills a closed polygon defined by arrays of x and y coordinates.
void fillPolygon(Polygon p)
Fills the polygon defined by the specified Polygon object with the graphics context's
current color.
abstract fillRect(int x, int y, int width, int height)
void
Fills the specified rectangle.
abstract fillRoundRect(int x, int y, int width, int height,
void int arcWidth, int arcHeight)
Fills the specified rounded corner rectangle with the current color.
finalize()
void
Disposes of this graphics context once it is no longer referenced.
abstract getClip()
Shape
Gets the current clipping area.
abstract getClipBounds()
Rectangle
Returns the bounding rectangle of the current clipping area.
Rectangle getClipBounds(Rectangle r)
Returns the bounding rectangle of the current clipping area.
Rectangle getClipRect()
Deprecated. As of JDK version 1.1, replaced by getClipBounds().
abstract getColor()
Color
Gets this graphics context's current color.
abstract getFont()
Font
Gets the current font.
FontMetrics getFontMetrics()
Gets the font metrics of the current font.
9 Anhang
155
abstract getFontMetrics(Font f)
FontMetrics
Gets the font metrics for the specified font.
boolean hitClip(int x, int y, int width, int height)
Returns true if the specified rectangular area intersects the bounding rectangle of the
current clipping area.
abstract setClip(int x, int y, int width, int height)
void
Sets the current clip to the rectangle specified by the given coordinates.
abstract setClip(Shape clip)
void
Sets the current clipping area to an arbitrary clip shape.
abstract setColor(Color c)
void
Sets this graphics context's current color to the specified color.
setFont(Font
font)
abstract
void
Sets this graphics context's font to the specified font.
abstract setPaintMode()
void
Sets the paint mode of this graphics context to overwrite the destination with this
graphics context's current color.
abstract setXORMode(Color c1)
void
Sets the paint mode of this graphics context to alternate between this graphics context's
current color and the new specified color.
String toString()
Returns a String object representing this Graphics object's value.
abstract translate(int x, int y)
void
Translates the origin of the graphics context to the point (x, y) in the current coordinate
system.
Herunterladen