Einführung in Java (Teil 2)
Crashkurs Objektorientierte
Programmierung
In der objektorientierten Programmierung wird das zu lösende
Problem durch Objekte modelliert, die miteinander in Beziehung stehen.
Die drei fundamentalen Elemente einer objektorientierten
Sprache sind Klassen, Vererbung und Polymorphie. Sie
werden im folgenden kurz vorgestellt.
Klassen: Eine Klasse ist eine abstrakte Beschreibung
einer Gruppe von Objekten. Objekte enthalten sowohl Variablen, die den
Zustand bzw. Eigenschaften des Objekts beschreiben, als auch Methoden,
die das Verhalten des Objekts bestimmen.
Beispiel: Objekt Hund
Zustand: Name, Rasse, Fellfarbe, ...
Methoden: bellen, Schwanz wedeln, apportieren, ...
Vererbung: Unter Vererbung versteht man die Möglichkeit,
ein neues Objekt von einem bereits vorhandenen Objekt abzuleiten, wobei
das neue Objekt alle Merkmale und Fähigkeiten des alten besitzt. Dem
neu erzeugten Objekt können noch weitere charakteristische Merkmale
hinzugefügt werden. Die Klasse, von der abgeleitet wird, nennt man
Oberklasse; die abgeleitete Klasse bezeichnet man als Unterklasse.
Beispiel: Oberklasse Fahrrad
Unterklassen: Mountainbike, Tandem ...
Polymorphie(Vielgestaltigkeit): Polymorphie bedeutet, daß
verschiedene Unterklassen dieselbe Botschaft verstehen, aber völlig
unterschiedlich umsetzen können.
Beispiel:
Oberklasse: Säugetiere
Unterklassen: Kühe, Löwen
Botschaft: Essen
Verschiedene Reaktionen auf die Botschaft Essen:
Kühe: wiederkäuen von Gras
Löwen: verschlingen der erlegten Beute
Das objektorientierte Programmieren basiert auf dem Konzept der Datenkapselung.
Dadurch ist der Zugriff auf bestimmte Objekte eingeschränkt und nur
über wohldefinierte Schnittstellen erlaubt.
Auf private Datenfelder kann nur über Methoden zugegriffen werden.
Beispiel 1:
//Programm Ausgabe von Vektoren.
public class vektor
{
private double x, y;
//Konstruktor
public vektor (double x, double y)
{
this.x = x;
this.y = y;
}
//Ausgabe Vektor
public String toString()
{
return new String("(" + x + ", " + y + ")");
}
public static void main(String[ ] args)
{
vektor u = new vektor(2,4);
System.out.println("Vektor u = " + u);
// System.out.println("x-Komponente: x = " + u.x);
nicht moeglich da x private
}
}
Output:
Vektor u = (2.0, 4.0)
Auf die privaten Datenfelder x und y kann nur über die Methode toString() zugegriffen werde. Direkter Zugriff, wie beispielsweise mit System.out.println("x-Komponente: x = " + u.x) führt zu Fehlermeldungen.
Im folgenden werden zwei spezielle Methoden beschrieben, wie man den Wert eines privaten Datenfeldes lesen (Accessor Methoden) bzw. verändern kann (Mutator Methoden).
Accessor Methoden sind read-only Methoden. Sie liefern
nur die Werte privater Datenfelder,
verändern aber nicht den Zustand des Objekts.
Syntax:
private Datentyp x;
public void getx(double x)
{
return x;
}
Es ist üblich als Methodenname getVariablenname zu verwenden.
Beispiel 2:
//Programm Ausgabe von Vektoren.
public class vektor
{
private double x, y;
//Konstruktor
public vektor(double x, double y)
{
this.x = x;
this.y = y;
}
//Accessor Methoden
public double getx()
{
return x;
}
public double gety()
{
return y;
}
//Ausgabe Vektor
public String toString()
{
return new String("(" + x + ", " + y + ")");
}
public static void main(String[ ] args)
{
vektor u = new vektor(2,4);
System.out.println("Vektor u = " + u);
System.out.println("x-Komponente: x = " + u.getx());
System.out.println("y-Komponente: y = " + u.gety());
}
}
Output:
Vektor u = (2.0, 4.0)
x-Komponente: x = 2.0
y-Komponente: y = 4.0
Mutator Methoden sind read-write Methoden. Sie können den Zustand des Objekts verändern.
private Datentyp x;
public void setx(double x)
{
this.x = x;
}
Es ist allgemein üblich als Methodenname setVariablenname zu verwenden.
Beispiel 3:
//Programm Ausgabe von Vektoren.
public class vektor
{
private double x, y;
//Konstruktor
public vektor(double x, double y)
{
this.x = x;
this.y = y;
}
//Mutator Methoden
public void setx(double x)
{
this.x = x;
}
public void sety(double y)
{
this.y = y;
}
//Ausgabe Vektor
public String toString()
{
return new String("(" + x + ", " + y + ")");
}
public static void main(String[ ] args)
{
vektor u = new vektor(2,4);
System.out.println("Vektor u = " + u);
u.setx(5);
u.sety(1);
System.out.println("Vektor u = " + u);
}
}
Output:
Vektor u = (2.0, 4.0)
Vektor u = (5.0, 1.0)
Komposition und Vererbung sind zwei verschiedene Möglichkeiten Programmcode wiederzuverwenden. Dabei werden bei der Komposition bereits programmierte Klassen in das aktuelle Programm eingebaut. Komposition ist daher eine Zusammenfassung von verschiedenen Klassen. Bei der Vererbung können Datenelemente und Methoden einer Klasse von einer anderen Klasse geerbt werden, die noch weiterere spezifische Datenelemente und Methoden hinzufügen darf. Vererbung bedeutet daher immer eine Spezialisierung.
Komposition ist die Erzeugung einer Klasse, wobei schon bereits programmierte Klassen genutzt werden können. Beispiel 4 und Beispiel 5 zeigen die Erzeugung eines Kreisobjekts mit und ohne Verwendung einer anderen Klasse. In Beispiel 4 wird eine Kreisklasse definiert mit den Datenfeldern Radius und x, y (für die Koordinaten des Kreismittelpunktes). Als Methoden werden die Berechnung des Kreisumfangs und der Kreisfläche implementiert.
Beispiel 4: Kreisobjekt
//Programm: Kreis.java
class Kreis
{
private double x,y;
private double Radius;
Kreis(double x, double y, double Radius)
{
this.x = x;
this.y = y;
this.Radius = Radius;
}
public double x()
{
return x;
}
public double y()
{
return y;
}
public double Durchmesser()
{
return 2*Radius;
}
public double Flaeche()
{
return Math.PI * Radius * Radius;
}
public String toString()
{
return new String("Mittelpunkt = (" + x + ", " + y + ")" + ",
Radius = " + Radius);
}
public static void main(String[ ] args)
{
Kreis kreis = new Kreis(3,1,2.0);
System.out.println("Kreis: " + kreis);
System.out.println("Kreismittelpunkt = (" + kreis.x() + ", "
+ kreis.y() + ")");
System.out.println("Durchmesser = " + kreis.Durchmesser());
System.out.println("Flaeche = " + kreis.Flaeche());
}
}
Output:
Kreis: Mittelpunkt = (3.0, 1.0), Radius = 2.0
Kreismittelpunkt = (3.0, 1.0)
Durchmesser = 4.0
Flaeche = 12.566370614359172
In Beispiel 5 wird eine Kreisklasse definiert, die als Datenelemente den Radius und den Mittelpunkt (Koordinaten x und y sind jetzt als Punkt zusammengefasst) besitzt. Die Implementierung der Methoden ist völlig analog zu Beispiel 4. Die Punktklasse war schon vorher vorhanden, so daß sie in das Kreisprogramm eingebaut werden konnte (Die Punktklasse ist dieselbe Klasse wie die vektorklasse, nur das der Name der Klasse auf Punkt geändert wurde).
Beispiel 5:
Strukturierter:
//Datei 1: Punkt.java
public class Punkt
{
private double x,y;
public Punkt(double x, double y)
{
this.x = x;
this.y = y;
}
public double x()
{
return x;
}
public double y()
{
return y;
}
public String toString()
{
return new String("(" + x + ", " + y + ")");
}
}
class Kreis
{
private Punkt Mittelpunkt;
private double Radius;
public Kreis(Punkt Mittelpunkt, double Radius)
{
this.Mittelpunkt = Mittelpunkt;
this.Radius = Radius;
}
public Punkt Mittelpunkt()
{
return Mittelpunkt;
}
public double Durchmesser()
{
return 2*Radius;
}
public double Flaeche()
{
return Math.PI * Radius * Radius;
}
public String toString()
{
return new String("Mittelpunkt = " + Mittelpunkt + ", Radius
= " + Radius);
}
}
class TestKreis
{
public static void main(String[] args)
{
Kreis kreis = new Kreis(new Punkt(3,1),2.0);
System.out.println("Kreis: " + kreis);
System.out.println("Kreismittelpunkt = " + kreis.Mittelpunkt());
System.out.println("Durchmesser = " + kreis.Durchmesser());
System.out.println("Flaeche = " + kreis.Flaeche());
}
}
Die drei Dateien werden im gleichen Verzeichnis abgelegt.
> ls Pfadname/Verzeichnisname
Kreis.java Punkt.java
TestKreis.java
> cd Pfadname/Verzeichnisname
> javac TestKreis.java
> ls
Kreis.class Punkt.class
TestKreis.class
Kreis.java Punkt.java
TestKreis.java
> java TestKreis
Kreis: Mittelpunkt = (3.0, 1.0), Radius = 2.0
Kreismittelpunkt = (3.0, 1.0)
Durchmesser = 4.0
Flaeche = 12.566370614359172
Eine der wichtigsten Eigenschaften von objektorientierten Systemen stellt die sogenannte "Vererbung" von Oberklassen auf Unterklassen dar. Die Unterklasse erbt alle (nicht privaten) Datenelemente und Methoden der Oberklasse und kann noch neue Methoden und Datenelemente hinzufügen. Jede Klasse besitzt eine Oberklasse. Ist diese jedoch nicht näher spezifiziert, ist die Klasse automatisch eine Unterklasse der Klasse Objekt. Da jede Klasse eine Oberklasse besitzt, bilden die Klassen in Java eine Klassenhierarchie, die durch eine Baumstruktur dargestellt werden kann.
Beispiel 6:
//Datei 1: Oberklasse.java
class Oberklasse
{
protected int o;
// 1
public int fo()
{
return -o;
}
public void printo()
{
System.out.println("(" + o + ")");
}
}
class Unterklasse extends Oberklasse
// 2
{
private int u = 4;
public int fu()
{
return -u;
}
public void printu()
{
System.out.println("(" + u + ", " + o + ")");
// 3
}
}
class TestVererbung
{
public static void main(String[ ] args)
{
System.out.println("In Oberklasse: ");
Oberklasse O = new Oberklasse();
System.out.print("o = ");O.printo();
System.out.println("O.fo()= " + O.fo());
System.out.println("In Unterklasse: ");
Unterklasse U = new Unterklasse();
System.out.print("u = ");U.printu();
System.out.println("U.fu()= " + U.fu());
System.out.println("U.fo()= " + U.fo());
// 3
}
}
Output:
In Oberklasse:
o = (0)
O.fo()= 0
In Unterklasse:
u = (4, 0)
U.fu()= -4
U.fo()= 0
Aus diesem Beispiel kann man einiges über Unterklassen lernen:
//Zusaetzliche Datenelemente
Color Fuellung, Kontur;
//Methode zum Zeichnen eines Kreises
public void draw(DrawWindow dw)
{
dw.drawCircle(x, y, r, Kontur, Fuellung)
}
Konstruktoren werden nicht automatisch von der Oberklasse übernommen
sondern müssen neu definiert werden, wenn man mehr als den Default-Konstruktor
mit leerer Parameterliste braucht.
Allerdings hätte man auch den Konstruktor der Oberklasse aufrufen
können, so daß man nur diejenigen Datenelemente initialisieren
muß, die gegenüber der Oberklasse neu sind. Der Konstruktor
der Oberklasse kann als erstes Statement mit super() aufgerufen
werden.
Damit läßt sich der GraphischerKreis - Konstruktor auch
folgendermaßen schreiben:
GraphischerKreis(double x, double y, double r, Color Kontur, Color Fuellung)
{
super(x, y, r);
this.Fuellung = Fuellung;
this.Kontur = Kontur;
}
Java unterstützt keine mehrfache Vererbung. Unter mehrfacher Vererbung versteht man die Möglichkeit, daß eine Klasse Unterklasse mehrerer Oberklassen ist. Nach extends darf also nur eine Oberklasse angegeben werden. Eine mehrfache Vererbung läßt sich mit Interfaces realisieren (s. Interfaces).
Beispiel 7:
//Datei 1: Oberklasse.java
class Oberklasse
{
protected int o;
public int fo()
{
return -o;
}
public void printo()
{
System.out.println("(" + o + ")");
}
}
class Unterklasse extends Oberklasse
{
private int o = 4, u = super.o;
public int fu()
{
return -o;
}
public void printu()
{
System.out.println("(" + o + ", " + u +")");
}
}
class TestShadowing
{
public static void main(String[ ] args)
{
System.out.println("In Oberklasse: ");
Oberklasse O = new Oberklasse();
System.out.print("o = ");O.printo();
System.out.println("O.fo()= " + O.fo());
System.out.println("In Unterklasse: ");
Unterklasse U = new Unterklasse();
System.out.print("o = ");U.printu();
System.out.println("U.fu()= " + U.fu());
System.out.print("(o, u) = ");U.printu();
}
}
Output:
In Oberklasse:
o = (0)
O.fo()= 0
In Unterklasse:
o = (4, 0)
U.fu()= -4
(o, u) = (4, 0)
Die Variable o in der Unterklasse verdeckt die entsprechende Variable der Oberklasse. Auf diese kann aber mit dem Schlüsselwort super noch zugegriffen werden.
Überschreiben von Methoden(Overriding)
Geerbte Methoden können überschrieben werden. Das nennt man Overriding. Dardurch hat eine Unterklasse die Möglichkeit eine Methode anders zu implementieren als ihre Oberklasse.
Beispiel 8:
//Datei 1: Oberklasse.java
class Oberklasse
{
protected int o;
public int fo()
{
return -o;
}
public String toString()
{
return new String("(" + o + ")");
}
}
class Unterklasse extends Oberklasse
{
private int u = 4;
public int fu()
{
return -u;
}
public String toString()
{
return new String("(" + u + ", " + o + ")");
}
}
class TestOverriding
{
public static void main(String[ ] args)
{
System.out.println("Overriding:");
//1
Oberklasse O1 = new Oberklasse();
System.out.println("O1.fo()= " + O1.fo());
//return new String("(" + o +")");
System.out.println("O1 = " + O1);
Unterklasse U = new Unterklasse();
System.out.println("U.fu()= " + U.fu());
//return new String("(" + u + ", " + o + ")");
System.out.println("U = " + U);
System.out.println("Polymorphie:");
//2
Oberklasse O2 = U;
System.out.println("O2.fo()= " + O2.fo());
System.out.println("O2 = " + O2);
}
}
Output:
Overriding:
O1.fo()= 0
O1 = (0)
U.fu()= -4
U = (4, 0)
Polymorphie:
O2.fo()= 0
O2 = (4, 0)
Abstrakte Klassen enthalten Methoden, die nicht mit einem Funktionskörper
ausgestattet sind.
Für abstrakte Klassen wird das Schlüsselwort abstract
verwendet.
Syntax für die Definition einer abstrakten Klasse:
abstract class KlassenName
{
public abstract Rückgabedatentyp Methodenname1 (Parameterliste)
;
public abstract void Methodenname2 (Parameterliste) ;
...
}
Jede abgeleitete Klasse muß alle geerbten Methoden implementieren, sonst meldet der Compiler einen Fehler.
In Beispiel 9 sollen Flächen und Umfänge verschiedener geometrischer Figuren (hier nur Kreis und Rechteck) aufaddiert werden. Zur Realisierung verwendet man einen Array, dessen Elemente die berechneten Flächen enthalten und einen weiteren, der die einzelnen Umfänge speichert. Diese werden unter Verwendung von Schleifen aufaddiert. Eine elegante Methode dies zu tun, ist die Definition einer abstrakten Klasse, in der zwei allgemeine Methoden Fläche und Umfang aufgeführt sind. Von der Klasse Shape abgeleitet sind zwei Klassen Kreis und Rechteck, in denen die abstrakten Methoden zur Berechnung der Fläche und des Umfangs implementiert werden.
Beispiel 9:
//Datei1
abstract class Shape
{
public abstract double Flaeche();
public abstract double Umfang();
}
class Kreis extends Shape
{
//Radius r
private double r;
//Konstruktoren
Kreis()
{
r = 1.0;
}
Kreis(double r)
{
this.r = r;
}
//Methoden zur Bestimmung von Flaeche und Umfang
public double Flaeche()
{
return Math.PI * r * r;
}
public double Umfang()
{
return 2 * Math.PI * r;
}
}
class Rechteck extends Shape
{
//Hoehe und Breite
private double h, b;
//Konstruktoren
Rechteck()
{
b = 1.0; h = 1.0;
}
Rechteck(double b,double h)
{
this.b = b;
this.h = h;
}
//Methoden zur Bestimmung von Flaeche und Umfang
public double Flaeche()
{
return b * h;
}
public double Umfang()
{
return 2 * (b + h);
}
}
class TestShape
{
public static void main(String[ ] arg)
{
Shape[ ] Shapes = new Shape[3];
Shapes[0] = new Kreis(2.0);
Shapes[1] = new Rechteck(1.0,2.0);
Shapes[2] = new Kreis(5.0);
double Gesamtflaeche = 0;
for(int i = 0; i < Shapes.length; i++)
Gesamtflaeche += Shapes[i].Flaeche();
System.out.println("Gesamtflaeche = " + Gesamtflaeche);
// Bestimmung des Gesamtumfangs geht völlig analog
}
}
Output:
Gesamtflaeche = 93.106186954104
In Java gibt es keine Mehrfachvererbung. Eine Klasse kann aber von einer
Oberklasse und mehreren Interfaces erben. Wenn eine Klasse ein Interface
erbt, sagt man, es implementiert das Interface.
Interfaces sind etwas ähnliches wie Klassen, die aber nur das
Konzept für die Unterklassen skizzieren, das dann von diesen "implementiert"
werden muß. Interfaces sind also reine Schnittstellen und enthalten
nur abstrakte Methoden und Konstanten.
Syntax für die Definition eines Interfaces:
public interface InterfaceName
{
public abstract Rückgabedatentyp Methodenname1 (Parameterliste)
;
public abstract void Methodenname2 (Parameterliste) ;
...
}
Für die Implementierung eines Interfaces wird das Schlüsselwort implements verwendet. Nach dem Wort implements werden ein oder auch mehrere Interfaces angegeben (durch Komma getrennt). Allgemein sieht dies folgendermaßen aus:
public class Unterklasse extends OberKlasse implements Interface1, Interface2, ... { }
Die Unterklasse muß alle in diesen Interfaces definierten Methoden implementieren.
Graphische Benutzeroberflächen (GUI: Graphical User Interfaces)
Graphische Benutzeroberflächen bestehen aus Menüs, Fenstern,
Eingabefeldern, Buttons usw. Das Paket, das die dafür notwendigen
Klassen enthält, ist das Abstract Windowing Toolkit (AWT).
Das AWT stellt eine Reihe von GUI-Elementen zur Verfügung, wie
beispielsweise:
Beispiel 10:
//Programm Fenster: Dieses Programm erzeugt ein leeres Fenster.
import java.awt.Frame;
class TestFrame
{
public static void main(String[ ] args)
{
System.out.println("Fenster der Groesse 250x100 Pixel.");
Frame Fenster = new Frame("Mein erstes Fenster");
Fenster.setLocation(10,10);
Fenster.setSize(250,100);
Fenster.setVisible(true);
System.out.println("Exit mittels Crtl+C.");
}
}
>javac TestFrame.java
>java TestFrame
Fenster der Groesse 250x100 Pixel.
Exit mittels Crtl+C.
Das Programm erzeugt eine Instanz von Frame namens Fenster der Groesse
250x100 Pixel (Methode setSize(250,100)) bei den Koordinaten (10,10) (Methode
setLocation(10,10)).Die
Koordinaten (x,y) werden dabei so gezählt, daß (0,0)
links oben ist.
Das Fenster erscheint erst, wenn die Methode setVisible(true) aufgerufen
wird.
Der Benutzer kann das Fenster vergrößern, verkleinern und
verschieben, allerdings nicht schliessen. Daher erfolgt das "Schliessen"
mit der Tastenkombination Crtl + C (Abbruch des laufenden Programms).
Output:
Wir haben in diesem Beispiel ein spezielle Fenster der Größe 250x100 Pixel erzeugt. Spezialisierung bedeutet aber immer, daß man das Problem auch mit Vererbung eleganter schreiben kann. Das nächste Beispiel definiert eine eigene Klasse (Klasse MyFrame) von Fenstern der Größe 250x100, die von der Klasse Frame abgeleitet werden können.
Beispiel 11:
//Datei 1
import java.awt.Frame;
class MyFrame extends Frame
{
MyFrame(String s)
{
super(s);
setLocation(10,10);
setSize(250,100);
setVisible(true);
}
}
class TestFrame
{
public static void main(String[ ] args)
{
System.out.println("Fenster der Groesse 250x100 Pixel");
new MyFrame("Mein erstes Fenster");
System.out.println("Exit mittels Crtl+C");
}
}
Möchte man nun etwas Farbe ins Spiel bringen, so braucht man Datei1 nur leicht abzuändern: mit der Methode setBackground() läßt sich die Hintergrundfarbe des Fensters ändern. Man muß allerdings die Klasse java.awt.Color importieren, in der die verschiedene Farben definiert sind.
Beispiel 12:
//Datei 1
import java.awt.Frame;
import java.awt.Color;
class MyFrame extends Frame
{
MyFrame(String s)
{
super(s);
setBackground(Color.red);
setLocation(10,10);
setSize(250,100);
setVisible(true);
}
}
Output:
In Beispiel 12 werden alle Veränderungen am Fenster direkt in der
Klasse MyFenster vorgenommen.
Will man z.B. anstatt eines roten Fensters ein grünes Fenster
erzeugen, so muß man in der Klasse MyFrame den Befehl setBackground(Color.red)
in
setBackground(Color.green)
abändern.
Man kann Veränderungen allerdings auch in die Klasse TestFrame
auslagern, die sie dann der Klasse MyFrame über Parameterübergabe
mitteilt.
Beispiel 13:
//Datei 1
import java.awt.Frame;
import java.awt.Color;
class MyFrame extends Frame
{
MyFrame(String s, Color color, int x, int y)
{
super(s);
setBackground(color);
setLocation(x,y);
setSize(250,100);
setVisible(true);
}
}
import java.awt.Color;
class TestFrame
{
public static void main(String[ ] args)
{
System.out.println("Fenster der Groesse 250x100 Pixel");
new MyFrame("Mein erstes gruenes Fenster", Color.green, 10,
10);
System.out.println("Exit mittels Crtl+C");
}
}
Output:
Frames können weitere Komponenten (z.B. Buttons) enthalten. Damit gehören sie zu den Containern. Container enthalten zusätzlich zu den Komponentenmethoden noch die folgenden Methoden:
Beispiel 14:
//Datei 1
import java.awt.Frame;
import java.awt.Color;
import java.awt.Button;
class MyFrame extends Frame
{
MyFrame(String s, Color color, int x, int y)
{
super(s);
setBackground(color);
setLocation(x,y);
setSize(250,100);
setVisible(true);
add(new Button("Button"));
}
}
import java.awt.Color;
class TestFrame
{
public static void main(String[ ] args)
{
System.out.println("Fenster der Groesse 250x100 Pixel");
new MyFrame("Mein erster Button", Color.cyan, 10, 10);
System.out.println("Exit mittels Crtl+C");
}
}
Output:
Die Methode add(new Button("Button")) fügt einen Button mit der Aufschrift "Button" in das Fenster ein.
LayoutManager:
Enthält ein Fenster mehrere Komponenten, so können diese in einer bestimmten Anordnung innerhalb des Fensters dargestellt werden. Die Anordnung bestimmt der LayoutManager. Es gibt fünf verschiedene Layouts, von denen wir hier aber nur zwei betrachten wollen:
Beispiel 15:
//Datei 1
import java.awt.Frame;
import java.awt.Color;
import java.awt.Button;
import java.awt.FlowLayout;
class MyFrame extends Frame
{
MyFrame(String s, Color color, int x, int y)
{
super(s);
setBackground(color);
setLocation(x,y);
setSize(250,100);
setVisible(true);
setLayout(new FlowLayout());
for (int i = 1; i <= 6; i++)
add(new Button("Button" + i));
}
}
import java.awt.Color;
class TestFrame
{
public static void main(String[] args)
{
System.out.println("Erstelle Fenster der Groesse 250x100 Pixel");
new MyFrame("6 Buttons", Color.yellow, 10, 10);
System.out.println("Exit mittels Crtl+C");
}
}
Output:
Problem:
GridLayout:
Beispiel 16:
//Datei 1
import java.awt.*; //dadurch werden alle Klassen aus java.awt eingebunden
class MyFrame extends Frame
{
MyFrame(String s, Color color, int x, int y)
{
super(s);
setBackground(color);
setLocation(x,y);
setSize(250,100);
setVisible(true);
setLayout(new GridLayout(2,3));
for (int i = 1; i <= 6; i++)
add(new Button("Button" + i));
}
}
import java.awt.Color;
class testFrame
{
public static void main(String[] args)
{
System.out.println("Erstelle Fenster der Groesse 250x100 Pixel");
new MyFrame("6 Buttons", Color.orange, 10, 10);
System.out.println("Exit mittels Crtl+C");
}
}
Output:
Ein Button ist definiert als ein Knopf mit Text, der vom Benutzer mit der Maus angeklickt werden kann und dabei etwas bewirkt wird. Unsere Buttons bewirken bislang garnichts. Das soll im folgenden geändert werden.
Ereignisse
Ein Ereignis ist eine von Benutzer angestoßene Aktion, wie ein
Tastendruck oder ein Mausklick. Jede Komponente kann Events aussenden.
Die Ereignisbehandlung beruht auf Objekten, die die Ereignisse entgegennehmen
können. Diese Objekte heißen Event-Listener. Sie implementieren
Listener-Interfaces und damit Methoden zur Behandlung von Ereignissen.
Wir betrachten im folgenden zwei Arten von Ereigissen: Action Events
und Window Events.
1.) Action Event
| Event-Klasse | Interface | Methoden | Quellen |
|---|---|---|---|
| ActionEvent | ActionListener | actionPerformed(ActionEvent) | Button
MenuItem TextField |
Als Beispiel behandeln wir das Ereignis, daß ein Button gedrückt wird. Jedes Mal, wenn man den Button drückt, soll die Methode actionPerformed(ActionEvent) aufgerufen werden, die im Terminal ein zusätzliches DANKE! erscheinen läßt.
Beispiel 17:
//Datei 1
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
class MyFrame extends Frame implements ActionListener
{
Button button;
MyFrame(String s, Color color, int x, int y)
{
super(s);
setBackground(color);
setLocation(x,y);
setSize(250,100);
setVisible(true);
setLayout(new FlowLayout());
button = new Button("Drueck mich!");
add(button);
button.addActionListener(this);
}
//Methode von ActionListener
public void actionPerformed(ActionEvent event)
{
if (event.getActionCommand().equals("Drueck mich!"))
System.out.println("DANKE!");
}
}
import java.awt.Color;
class testFrame
{
public static void main(String[] args)
{
System.out.println("Erstelle Fenster der Groesse 250x100 Pixel");
new MyFrame("Klick-Ereignis", Color.magenta, 10, 10);
System.out.println("Exit mittels Crtl+C");
}
}
>javac testFrame.java
>java testFrame
Erstelle Fenster der Groesse 250x100 Pixel
Exit mittels Crtl+C
DANKE!
DANKE!
Output:
Veranschaulichung:
button = new Button("Drueck mich!");
add(button);

button.addActionListener(this);

Beim Drücken des Buttons wird ein ActionEvent-Objekt erzeut, wobei der String "Drueck mich!" des Buttons an das actionCommandfeld des Event-Objekts zugewiesen wird. Das ActionEvent Objekt wird an die Methode actionPerformed() übergeben.

Die Methode getActionCommand() liefert den String "Drueck mich" . Dieser wird mit dem String "Drueck mich" über den Befehl event.getActionCommand().equals("Drueck mich!") verglichen. Falls Übereinstimmung vorliegt wird der String "DANKE!" im Teminal ausgegeben.
2.) Window Event
Das WindowListener-Interface dient dazu, auf Ereignisse zu reagieren,
die den Status eines Fensters betreffen.
| Event-Klasse | Interface | Methoden | Quellen |
|---|---|---|---|
| WindowEvent | WindowListener | windowActivated()
windowClosed() windowClosing() windowDeactivated() windowDeiconified() windowIconified() windowOpened() |
Window |
Im folgenden Beispiel möchten wir unser Fenster schliessen können. Dafür gibt es die Methode windowClosing(). Da im WindowListener aber eine Reihe von Methoden implementiert sind, müssen wir auch alle diese Methoden im Programm aufführen.
Beispiel 18:
//Datei 1
import java.awt.*;
import java.awt.event.*;
class MyFrame extends Frame implements ActionListener, WindowListener
{
Button button;
MyFrame(String s, Color color, int x, int y)
{
super(s);
setBackground(color);
setLocation(x,y);
setSize(250,100);
setVisible(true);
setLayout(new FlowLayout());
button = new Button("Drueck mich!");
add(button);
button.addActionListener(this);
addWindowListener(this);
}
// Methoden von WindowListener:
public void windowClosed(WindowEvent event) {}
public void windowDeiconified(WindowEvent event) {}
public void windowIconified(WindowEvent event) {}
public void windowActivated(WindowEvent event) {}
public void windowDeactivated(WindowEvent event) {}
public void windowOpened(WindowEvent event) {}
public void windowClosing(WindowEvent event)
{
System.exit(0);
}
//Methode von ActionListener
public void actionPerformed(ActionEvent event)
{
if (event.getActionCommand().equals("Drueck mich!"))
System.out.println("DANKE!");
}
}
import java.awt.Color;
class TestFrame
{
public static void main(String[] args)
{
System.out.println("Erstelle Fenster der Groesse 250x100 Pixel");
new MyFrame("Klick-Ereignis", Color.lightGray, 10, 10);
}
}
>javac testFrame.java
>java testFrame
Erstelle Fenster der Groesse 250x100 Pixel
DANKE!
DANKE!
Output:
Veranschaulichung:
new MyFrame("Klick-Ereignis", Color.lightGray, 10, 10);

addWindowListener(this);

Sobald der Benutzer den Schließknopf im Fensterrahmen betätigt, wird ein WindowEvent durch das Fenster erzeugt, daß die Methode windowClosing() aufgeruft und das Fenster schließt.
Umrechnung von Fahrenheit nach Celsius
In diesem letzten Beispiel darf der Benutzer eine Zahl (Temperatur in Fahrenheit) in ein Textfeld eingeben. Dieser Zahlenwert wird nach dem Betätigen der Returntaste in Celsius umgerechnet und das Ergebnis im Fenster bekannt gegeben.
Beispiel 19:
//Datei 1
import java.awt.*;
import java.awt.event.*;
class MyFrame extends Frame implements ActionListener, WindowListener
{
Label Eingabeaufforderung;
TextField Eingabe;
Label Ergebnis;
MyFrame(String s, Color color, int x, int y)
{
super(s);
setBackground(color);
setLocation(x,y);
setSize(250,100);
setLocation(100,100);
setLayout(new FlowLayout());
addWindowListener(this);
Eingabeaufforderung = new Label("Temperatur in Fahrenheit:
");
add(Eingabeaufforderung);
//Einfuegung der Eingabeaufforderung in das Fenster
Eingabe = new TextField(2);
add(Eingabe);
//Einfuegung des Textfeldes in das Fenster
Eingabe.addActionListener(this);
Ergebnis=new Label("
");
Ergebnis.setFont(new Font("TimesRoman 12 point bold.",20,20));
add(Ergebnis);
//Einfuegung des Ergebnisses in das Fenster
setVisible(true);
}
// Methoden von WindowListener:
public void windowClosed(WindowEvent event) {}
public void windowDeiconified(WindowEvent event) {}
public void windowIconified(WindowEvent event) {}
public void windowActivated(WindowEvent event) {}
public void windowDeactivated(WindowEvent event) {}
public void windowOpened(WindowEvent event) {}
public void windowClosing(WindowEvent event)
{
System.exit(0);
}
//Methode von ActionListener
public void actionPerformed(ActionEvent event)
{
int Fahrenheit = Integer.parseInt(Eingabe.getText());
//Umwandlung von String in int
long Celsius = Math.round(5.0*(Fahrenheit-32)/9.0);
Eingabe.setText("");
//Reset des Eingabefeldes
//Ausgabe des Ergebnisses
Ergebnis.setText(Fahrenheit + "\u00B0F = " + Celsius + "\u00B0C");
}
}
import java.awt.Color;
class testFrame
{
public static void main(String[ ] args)
{
System.out.println("Erstelle Fenster der Groesse 250x100 Pixel");
new MyFrame("Fahrenheit-Celsius", Color.orange, 10, 10);
}
}
Output:
Veranschaulichung:
Eingabeaufforderung = new Label("Temperatur in Fahrenheit: ");
add(Eingabeaufforderung);

Ein Label ist ein einfaches Feld für die Darstellung eines einzeiligen Textes.
Eingabe = new TextField(2);
add(Eingabe);

Ein TextField ist ein einzeiliges Feld für Tastatur-Eingaben.
Eingabe.addActionListener(this);

Ergebnis=new Label("
");
add(Ergebnis);

Beim Drücken der Returntaste nach Eingabe eines Zahlenwertes in
das Textfeld wird ein ActionEvent-Objekt erzeugt, das an die Methode actionPerformed()
übergeben wird. Die in das Textfeld eingegebene Zahl kann mit der
Methode getText() ausgelesen werden. Da mathematische Operatoren nicht
auf Objekte angewandt werden dürfen, muß der String zunächst
mit der Methode Integer.parseInt(Eingabe.getText()) in eine Variable vom
Typ int umgewandelt werden. Danach erfolgt die Umrechnung in Celsius. Das
Eingabefeld wird mit der Methode setText(); neu gesetzt, in unserem Fall
gelöscht und das Ergebnis mit dem Befehl Ergebnis.setText(Fahrenheit
+ "\u00B0F = " + Celsius + "\u00B0C"); im Fenster ausgegeben.
