promod-2 - Wirtschaftsinformatik

Werbung
PROMOD-2 / Ausgabe 2016
Ausgabe 2016 (BuggyVersion)
MitMach-Skript/Materialien zur Vorlesung
Prof. J. Anton Illik
PROMOD-2
„Java: Programmieren und Modellieren 2“
JAVA
: SE advanced-1 / verteilte Applikationen
(1. Das Skript ist durch die im Literaturverzeichnis
genannte Pflichtlektüre zu ergänzen.
2. Achten Sie auf Ergänzungen und
weitergehende Hinweise in der Vorlesung!
Fertigen Sie eine Mitschrift an.)
Prof. Illik
_PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx
1 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
„The capacity to learn is a gift.
The ability to learn is a skill.
The willingness to learn is a choice”
Brian Herbert, Kevin J. Anderson:
„Prelude to Dune – House Harkonnen”
"Ich höre und ich vergesse.
Ich sehe und ich erinnere mich.
Ich tue und ich verstehe."
Konfuzius
Ergänzend werden in der Vorlesung weitere Inhalte vermittelt (bevorzugt aus dem Bereich
„Projekt-Methodik“/SCRUM/QS- & Abnahme-Methodik, Projekt-Doku).
Wird in der Vorlesung behandelt und ist prüfungsrelevant.
Vorteil für Sie: Sie üben schon in der Vorlesung und prägen sich so die Inhalte ein.
Tipp: Fertigen Sie ein Protokoll als Mitschrift an!
Prof. Illik
_PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx
2 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Support im Internet:
Unter https://www.youtube.com/user/illikhans finden Sie zu allen Kapiteln VideoVorschauen („ScriptPreviews“), mit der ein oder anderen inhaltlichen Motivation zu dem,
was wir im Kolloquium besprechen und für die Projekte nützlich ist.
Die „ScriptPreviews“ dienen der Vor- und Nachbereitung des Kolloquiums.
Die ScriptPreview-Videos folgen dem Namens-Schema
„PROMOD2_Episode#_ILLIK_Kap_#_ScriptPreview“
Kapitel-Nummer
Episoden-Nummer
„ECLIPSE_HandsOn_#_PROMOD1“ liefert Erläuterungen zur IDE Eclipse.
Die Episoden erscheinen semesterbegleitend.
Prof. Illik
_PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx
3 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Inhaltsverzeichnis
1. Vererbung (Wiederholung & Ergänzung) ................................................................................. 11
1.1 Grundlagen ......................................................................................................................... 11
1.2 Die Klasse Object ............................................................................................................... 11
1.3 Konstruktoraufrufe: ............................................................................................................. 14
1.4 Vererbungsketten und Zuweisungskompatibilität: .............................................................. 15
1.5 Case-Study Objekt-Arrays, Zuweisungskompatibiliät, Operator instanceof ....................... 16
1.6 Case-Study Lösung: ........................................................................................................... 16
1.7 Polymorphie ....................................................................................................................... 18
1.7.1 Beispiel 1 – Methoden überschreiben: ........................................................................ 18
1.7.2 Beispiel 2 – "late binding – Polymorphismus": ............................................................. 19
1.8 Abstrakte Klassen und Methoden: ..................................................................................... 20
1.9 Finale Klassen: ................................................................................................................... 21
2. Packages (Selbststudium!) ...................................................................................................... 22
2.1 Grundlagen ......................................................................................................................... 22
2.2 Packages importieren: ........................................................................................................ 22
2.3 Packages definieren: .......................................................................................................... 23
2.4 Zugriffsrechte in Packages: ................................................................................................ 23
3. Interfaces (und Ergänzungen zu Klassen) ............................................................................... 26
3.1 Grundlagen ......................................................................................................................... 26
3.2 Adapterklassen ................................................................................................................... 29
3.3 Innere Klassen (auch: geschachtelte / eingebettete / nested class) .................................. 30
3.4 Lokale Klassen ................................................................................................................... 31
3.5 Anonyme Klassen .............................................................................................................. 31
3.6 Statische innere Klassen (static class) / statische innere Schnittstellen ............................ 32
4. Ein-/Ausgabe auf Konsole,Dateien u.a.(ADRELI_CON) .......................................................... 34
4.1 Grundlagen ......................................................................................................................... 34
4.2 Standard-Ein- und Ausgabe ............................................................................................... 35
4.3 Die Klasse File: .................................................................................................................. 37
4.4 Die Klasse RandomAccessFile: ......................................................................................... 40
5. Streams: (u.U. ADRELI_CON) ................................................................................................. 44
5.1 Grundlagen ......................................................................................................................... 44
5.2 Überblick (UNICODE-) Character-Stream-Klassen (Zeichendaten (Text) schreiben und
Lesen) ......................................................................................................................................... 45
5.3 Character–Stream-Methoden: ............................................................................................ 46
5.3.1 Ausgabe-Streams ........................................................................................................ 46
5.3.2 Eingabe-Streams ......................................................................................................... 46
5.4 Beispiele/Anwendungen zu Character–Streams: ............................................................... 48
5.5 Überblick Byte-Stream-Klassen (auch: binäres Schreiben u. Lesen) ................................ 51
5.6 Byte–Stream-Methoden: .................................................................................................... 51
6. Multi-Threading (ADRELI_THREADS++) ................................................................................ 56
6.1 Grundlagen ......................................................................................................................... 56
6.2 Klasse Thread und Interface Runnable .............................................................................. 58
6.3 Zustände von Threads: ...................................................................................................... 62
6.3.1 Threads unterbrechen ................................................................................................. 62
6.3.2 Die Thread-Methoden yield(), sleep() und interrupt() .................................................. 62
6.3.3 Die Thread-Methode join() ........................................................................................... 63
6.3.4 Weitere Thread-Methoden (ggf. geerbt von class Object) ........................................... 64
6.3.5 Thread-Konstruktoren .................................................................................................. 64
6.4 Daemon-Threads ............................................................................................................... 66
6.5 Prioritäten von Threads und zugehörige Thread-Methoden: .............................................. 67
Prof. Illik
_PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx
4 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
6.6 Gruppen von Threads ........................................................................................................ 67
6.7 Synchronisation und zugehörige Methoden (von class Object geerbt): ............................. 67
6.8 Datenaustausch zwischen Threads mit Pipes: ................................................................... 71
6.9 Weitere Synchronisationsmittel .......................................................................................... 74
6.10 Thread-Synchronisation mit CyclicBarrier ........................................................................ 75
6.11 Zusammenfassung Threads: ............................................................................................ 77
6.12 Zusammenfassung Pipes: ................................................................................................ 77
7. Exkurs: how to model an application? ..................................................................................... 78
7.1 Als Kompass/Leitplanke dienen Vorgehensmodelle .......................................................... 82
7.2 Ihre Einschätzung? Welches Vorgehensmodell ist wofür geeignet? .................................. 85
7.3 Und wie geht`s im Detail? .................................................................................................. 86
8. Netzwerkzugriff ........................................................................................................................ 87
8.1 Grundlagen ......................................................................................................................... 87
8.1.1 Protokolle ..................................................................................................................... 87
8.1.2 Adressierung: IP-Adresse und Port-Nummer .............................................................. 88
8.1.3 Die Datei hosts und DNS (Domain Name System) ..................................................... 90
8.2 Klassen für die Adressierung ............................................................................................. 91
8.2.1 Die Klasse InetAddress: (siehe API-Doku)This class represents an Internet Protocol
(IP) address. ............................................................................................................................ 91
8.2.2 Die Klasse URL ........................................................................................................... 93
8.3 Socket-Programmierung .................................................................................................... 96
8.3.1 Client-Sockets: die Klasse socket („Kommunikationssocket “) .................................... 98
8.3.2 ServerSockets: die Klasse ServerSocket .................................................................... 99
8.3.3 Beispiel – Server-Sockets (server.java): .................................................................... 100
8.3.4 Beispiel – Client-Sockets (client.java): ....................................................................... 101
8.4 Was kommt den da im Strom? Sockets und Streams ...................................................... 102
9. RMI Remote Method Invocation (skip it) ................................................................................ 104
9.1 Grundlagen ....................................................................................................................... 104
9.2 Die Bestandteile einer RMI-Anwendung .......................................................................... 105
9.3 Das Ablaufschema eines entfernten Methodenaufrufs .................................................... 106
9.4 Die Hilfsmittel RMI-Compiler und RMI-Registry ............................................................... 107
9.5 Der RMISicherheitsmanager (RMISecurityManager) ....................................................... 108
9.6 Die Komponenten und Bestandteile auf der Server-Seite ................................................ 109
9.7 Die Komponenten und Bestandteile auf der Client-Seite ................................................. 110
9.8 Vollständiges RMI-Beispiel ............................................................................................... 110
9.9 Zusammenfassung ........................................................................................................... 114
9.10 Kontrollfragen (Technologien / RMI) .............................................................................. 115
10. AWT-Basics: Graphical User Interface ................................................................................ 116
10.1 Grundlagen ..................................................................................................................... 116
10.2 AWT – Klassenhierarchie ............................................................................................... 118
10.3 „Fenster“: von der Component über den Container zum Frame .................................... 119
10.4 Klasse Window ............................................................................................................... 120
10.5 Klasse Panel: (abgeleitet von Container) ....................................................................... 121
10.6 Klasse Frame: (abgeleitet von Window) ......................................................................... 121
10.7 "Fensterbausteine" und der Umgang damit (Erzeugung / Konfiguration / Anzeige) ..... 123
10.8 Anzeigen und schließen: ................................................................................................ 124
10.9 Bestandteile eines Fensters (class Frame) .................................................................... 125
10.9.1 Das Fenstersymbol/Icon: ......................................................................................... 126
10.9.2 Fensterposition und -Größe: .................................................................................... 127
10.9.3 Fensterfarben: ......................................................................................................... 128
10.9.4 Der Mouse-Cursor: .................................................................................................. 129
11. AWT-Events: Ereignisse und Event-Handling ..................................................................... 131
Prof. Illik
_PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx
5 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
11.1 Grundlagen ..................................................................................................................... 131
11.2 Ereignisklassen .............................................................................................................. 133
11.3 Listener-Interfaces .......................................................................................................... 134
11.4 Listener bei Ereignisquellen registrieren ........................................................................ 134
11.5 Implementierungsmöglichkeiten: .................................................................................... 136
11.5.1 Verwendung von Adapterklassen: ........................................................................... 136
11.5.2 Listener Interface wird implementiert: ...................................................................... 136
11.6 Getrennter Code für GUI und Applikation ...................................................................... 136
11.7 Low-Level-Ereignisse (systemnahe Ereignisse / Einige Beispiele): ............................... 138
11.7.1 Component-Ereignisse ( Interface ComponentListener): ................................... 138
11.7.2 Window-Ereignisse ( Interface WindowListener): ............................................... 140
11.7.3 Focus-Ereignisse ( Interface FocusListener):..................................................... 142
11.7.4 Die Eventklasse Tastaturereignisse (Klasse KeyEvent) und das ListenerInterface KeyListener): ............................................................................................... 143
11.7.5 Die Eventklasse Klasse MouseEvent und die Listener-Interfaces MouseListener,
MouseMotionListener : .......................................................................................................... 146
11.8 Zusammenfassung Ereignisse und ihre Behandlung ..................................................... 149
12. AWT-Graphics: Grafikprogrammierung mit AWT ................................................................. 153
12.1 Grundlagen ..................................................................................................................... 153
12.2 Der Grafikkontext / Die Klasse Graphics: .................................................................... 153
12.3 Die Methoden paint() und repaint(): ............................................................................... 154
12.4 Anzeige von Text: ........................................................................................................... 156
12.4.1 Die Klasse Font / einige Grundbegriffe .................................................................... 157
12.5 Farben / die Klasse Color ............................................................................................... 161
12.6 Weitere Methoden zum Zeichnen aus der Klasse Graphics .......................................... 163
12.7 Bitmaps anzeigen: (nur gif und jpeg-Format)/ Die Klasse Image ................................... 165
13. AWT-Komponenten: ............................................................................................................ 169
13.1 Grundlagen ..................................................................................................................... 169
13.2 Überblick über AWT-Komponenten: ............................................................................... 170
13.3 Anwendung / Umgang mit den Komponenten: ............................................................... 171
13.3.1 Die Klasse Label ...................................................................................................... 172
13.3.2 Die Klasse Button .................................................................................................... 172
13.3.3 Die Klasse Textfeld (abgeleitet von TextComponent) ............................................. 174
13.3.4 Die Klasse TextArea (abgeleitet von der Klasse TextComponent) .......................... 176
13.3.5 Die Klasse Choice und die Klasse List: ................................................................... 176
13.3.6 Die Klasse Checkbox: .............................................................................................. 178
13.3.7 Die Klasse CheckboxGroup: .................................................................................... 178
13.3.8 Die Klasse Scrollbar: ............................................................................................... 179
13.3.9 Die Klasse Scrollpane .............................................................................................. 181
13.4 Dialoge ........................................................................................................................... 181
13.4.1 Die Klasse Dialog: ................................................................................................... 181
13.4.2 Der Dateidialog (Klasse FileDialog) ................................................................... 182
13.5 Menüs: ............................................................................................................................ 184
13.5.1 Die Menüleisten (Klasse MenuBar) ......................................................................... 185
13.5.2 Die Menüs (Klasse Menu) ........................................................................................ 185
13.5.3 Die Menü-Einträge (Klasse MenuItem) .................................................................. 185
13.5.4 Kontextmenüs (Klasse PopupMenu) ....................................................................... 189
14. LayoutManager (ein Überblick) ............................................................................................ 192
14.1 Grundlagen ..................................................................................................................... 192
15. Elementare Dienste für JEE: JDBC ..................................................................................... 200
15.1 Hinführung ...................................................................................................................... 200
15.2 Grundlagen ..................................................................................................................... 200
Prof. Illik
_PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx
6 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
15.3 JDBC als Basis für andere APIs: SQLJ und JDO .......................................................... 201
15.3.1 SQLJ ........................................................................................................................ 201
15.3.2 SQLJ im Vergleich zu JDBC .................................................................................... 202
15.3.3 SQLJ und der Übersetzungprozess ......................................................................... 203
15.3.4 Persistente Java Objekte: JDO Java Data Objects ................................................. 203
15.4 Architektur von JDBC ..................................................................................................... 203
15.5 Treibertypen ................................................................................................................... 204
15.6 Die Datenbank MySQL / Die DBMS Cloudscape / PointBase ....................................... 206
15.7 Aufbau einer Datenbankverbindung ............................................................................... 206
15.8 SQL-Anweisungen ausführen ........................................................................................ 210
15.9 Die Klasse Statement ..................................................................................................... 210
15.10 Klasse PreparedStatement .......................................................................................... 212
15.11 Klasse CallableStatement / StoredProcedure .............................................................. 213
15.12 Auswertung der Ergebnismengen ................................................................................ 213
15.12.1 Boolean und Integer als Rückgabewerte ............................................................... 214
15.12.2 ResultSet ............................................................................................................... 214
15.12.3 Konfigurieren der Ergebnismenge ......................................................................... 217
15.12.4 Nachladen von Datensätzen konfigurieren ............................................................ 217
15.12.5 Navigieren in der Ergebnismenge ......................................................................... 217
15.12.6 Werte der Ergebnismenge ermitteln ...................................................................... 217
15.12.7 Update der Ergebnismenge ................................................................................... 217
15.12.8 Nullwerte ................................................................................................................ 217
15.13 Auslesen von Metadaten .............................................................................................. 217
15.13.1 Datenbankinformationen ermitteln ......................................................................... 217
15.13.2 Informationen über eine Ergebnismenge ermittlen ................................................ 217
15.14 Transaktionen ............................................................................................................... 217
15.14.1 Manuelles und automatischs Commit .................................................................... 217
15.14.2 Isolationsebenen .................................................................................................... 217
15.14.3 Dirty Read .............................................................................................................. 217
15.14.4 Repeatabel Read ................................................................................................... 217
15.14.5 Phantom Read ....................................................................................................... 217
15.15 JDBC Option Package ................................................................................................. 217
15.15.1 DataSource ............................................................................................................ 217
15.15.2 Connection-Pooling ............................................................................................... 218
15.15.3 Verteilte Transaktionen .......................................................................................... 218
15.16 Datenbankzugriff über Applets ..................................................................................... 218
15.17 Debuggen von JDBC-Anwendungen ........................................................................... 218
15.18 CaseStudy Aufgabe (Aufgabe 1 von 030_JDBC) ........................................................ 218
15.19 CaseStudy Lösung ....................................................................................................... 219
15.20 Die Datenbank Derby ................................................................................................... 220
16. Promod Tipps ....................................................................................................................... 221
16.1 Methodik: Scrum ............................................................................................................ 221
17. Eclipse Tipps ........................................................................................................................ 221
17.1 Javadoc .......................................................................................................................... 221
18. JavaDoc - Tutorial ................................................................................................................ 221
18.1.1 Was ist JavaDoc? .................................................................................................... 221
18.1.2 Wie erstelle ich JavaDoc? ....................................................................................... 222
18.1.3 JavaDoc in den JBuilder einbinden ......................................................................... 222
18.1.4 Richtig dokumentieren & Tags ................................................................................. 223
18.1.5 Wichtige Optionen ................................................................................................... 223
18.1.6 Related Links ........................................................................................................... 224
19. Literatur (nehmen Sie jeweils die jüngste Ausgabe) ............................................................ 225
Prof. Illik
_PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx
7 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Prof. Illik
_PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx
8 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Didaktik, Methodik:
Während der Vorlesung wird nicht der gesamte Stoff präsentiert. Bestimmte Abschnitte sind von den
Studierenden eigenständig zu erarbeiten, insbesondere die Details!
Die Projekte, bzw. die Projektergebnisse dienen auch der Lernstantskontrolle! Deshalb unbedingt an den QS-Gesprächen
teilnehmen!
Der Modul Promod-2 ist als seminaristische Lehrveranstaltung organisiert.
Im theoretischen Teil erfolgt in einem Vorlesungsblock eine Einführung in die Theorie und die Problematik.
Grundlage des Vorlesungsblocks ist dieses Skript.
Die Einsicht in die Theorie wird in einem unmittelbar daran anschliessenden Vertiefungsblock („HandsOn“-Teil) in die Praxis umgesetzt und validiert. Für diesen Praktikumsblock werden Case-Studies
vorgegeben. Die Case-Studies sind teilweise in diesem Skript enthalten und teilweise auf dem
Materialsserver oder werden Ihnen auf einem anderen Medium übergeben.
Projekte (die „Übungen“) werdem im Praktikumsblock durchgeführt. Die hierfür vorhandenen
Aufgabenstellungen lösen die Praktikumsteilnehmer selbständig.
Der Praktikumsblock gestaltet sich wie die Arbeit in einem realen Projekt – stellen Sie sich vor, Sie und Ihre
Kommiliton/innen im Team sind Ihre eigene, neu gegründete Firma, und Sie bearbeiten die Projekte im
Kundenauftrag.
Im fortgeschrittenen Stadium der Lehrveranstaltung entwickeln die Studierenden eine zunehmende
Selbständigkeit und erarbeiten selbständig vorgegebne Themen im Rahmen eines angeleiteten
Selbststudiums.
Ab etwa Mitte der Vorlesungszeit wird der Vorlesungsblock zum Praktikumsblock dazugeschlagen um die
Projekte zu realisieren.
Lernziel: Sie können datenbankbasierte, im LAN oder Internet verteilte SystemArchitekturen entwerfen und mit JAVA implementieren. Die Architekturen können
multi-threaded sein und über eine GUI verfügen.
Unsere Vorgehensweise orientiert sich an SCRUM.
Lehr- und Lernmaterialien:
Das Pfeilsymbol (siehe Bild rechts) verweist auf „Case Studies“ für den „Hands-On“-Teil der
Vorlesung. Zu einem Teil der Case Studies bekommen Sie Experimentalquellen. Der Hands-On-Teil
findet jeweils nach dem. Vorlesungsblock statt und hat mittel- oder unmittelbar etwas mit den
Problemlösungen der Projektaufgaben zu tun.
Der die Vorlesung begleitende Projektteil startet mit den Case Studies im 2. Block und setzt sich im 3. Block fort. Für
den Projektteil gibt es eigene Projektaufgaben.
Die Vorlesung orientiert sich an den Inhalten der 5 Adreli-Projekte.
Das Skript lesen Sie am besten am Rechner mit geöffneter Entwicklungsumgebung (Eclipse) und den griffbereiten
CaseStudies. Eclipse selbst ist nicht zentraler Gegenstand der Veranstaltung, wird aber doch hin und wieder
thematisiert. Für die Auseinandersetzung mit Eclipse werden wir auf eine Reihe von Videos zurückgreifen.
Sonstiges:
Das Trichtersymbol (Bild rechts) macht auf Hinweise zur Entwicklungsumgebung aufmerksam.
Sun Tutorials:
Prof. Illik
_PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx
9 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
For the most accurate and up-to-date tutorials, please access the latest version from Oracle's
official website for the Java SE Tutorials (Last Updated 03/02/20112), which can be found at:
http://download.oracle.com/javase/tutorial.
Tools & Server:
•
Ggf mehr dazu in Kolloquium
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 10 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
1. Vererbung (Wiederholung & Ergänzung)
Was Sie lernen:
was es mit der Klasse Object auf sich hat
die Rolle von Konstruktoraufrufen in Ableitungen
Zusammenhang von Vererbungsketten und Zuweisungskompatibiltät
was early und late binding ist
was abstrakte und finale Klassen sind
Voraussetzungen:
Arbeiten mit Klassen und Methoden
1.1 Grundlagen
Bekannt:
class subclass extends superclass
{
//zusätzliche Attribute
//zusätzliche Methoden
}
nur eine Superklasse;
keine Mehrfachvererbung!
à nur additiv hinzufügen
1.2 Die Klasse Object
Jede Klasse besitzt eine "unsichtbare" Superklasse "Object". Die Klasse Object ist die ultimative Basisklasse. Es
bedarf jedoch keiner expliziten Vererbungsanweisung (... extends Object).
class New [extends Object]
{
//Klasseninhalt
}
Die Klasse Object definiert mehrere Methoden, die allen Ableitungen, also allen Klassen zur Verfügung steht.
boolean equals(Object o)
String.toString()
Methode prüft, ob Objekt identischen Inhalt wie anderes
Objekt beinhaltet.
Die Methode muss in abgeleiteten Klassen überschrieben
(override) werden, wenn Sie auf inhaltliche Gleichheit
eines Objekts testen möchten. Ansonsten wird überprüft, ob
es sich um dieselbe Referenz auf das betreffende Objekt
handelt.
Erzeugt neues Objekt als exakte Kopie, jedoch vom Typ
Object! (override)
Liefert Stringrepräsentation des Objekts. (override)
Class getClass()
Returns the runtime class of an object.
void notify()
Siehe Multi-Threading.
void notifyAll()
Siehe Multi-Threading.
void wait ()
Siehe Multi-Threading.
Object clone ()
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 11 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
protected void finalize()
Called by the garbage collector (… System.gc() …) on an
object when garbage collection determines that there are no
more references to the object.(override)
Umgedingt empfehlenswert: die API-Doku zu den Methoden lesen! (Insbesondere wg. „override“)
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 12 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Beispiel:
public class ObjektMethoden
{
private String data = "Objektdaten";
public String getData()
{
return data;
}
public void setData(String wert)
{
data = wert;
}
public String toString()
{
return data;
}
public Object clone()
{
ObjektMethoden om = new ObjektMethoden();
om.setData(data);
return om;
}
public boolean equals(Object o)
{
if(this == o)
return true;
if((o == null) || (this.getClass() != o.getClass()))
return false;
return (data.equals(((ObjektMethoden)o).getData()));
}
public static void main(String[] args)
{
ObjektMethoden o1 = new ObjektMethoden();
ObjektMethoden o2 = new ObjektMethoden();
o1.setData("Neu");
if(!o1.equals(o2))
System.out.println("Nicht gleich!");
System.out.println(o1.toString());
System.out.println(o2.toString());
ObjektMethoden o3 = (ObjektMethoden)o1.clone();
System.out.println(o3.toString());
}
}
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 13 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
1.3 Konstruktoraufrufe:
Es können lediglich Methoden und Variablen vererbt werden. Konstruktoren können nicht vererbt werden.
Allerdings kann der Konstruktor der Basisklasse aufgerufen werden.
Beispiel:
class Unterklasse extends Basisklasse
{
public Unterklasse() // Standardkonstruktor
{
super(); // Konstruktor der Superklasse wird aufgerufen
//muss die erste Anweisung sein!!
//Die Signatur ist wichtig!!
.
.
.
}
}
Sie können jeden beliebigen Konstruktor der Superklasse aufrufen: die Auswahl ergibt sich aus der Signatur.
Aufruf von super() muss als erste Anweisung im Konstruktor stehen.
Sie können nur den Konstruktor er unmittelbaren Superklasse aufrufen: super.super() ist nicht möchglich.
Verwenden Sie keinen Aufruf von super(), so wird automatisch der Standardkonstruktor (auch DefaultKonstruktor genannt) der Superklasse als erste „unsichtbare“ Anweisung aufgerufen. Zur Erinnerung: Der
Default-Konstruktor, der vom Compiler aufgerufen wird, allokiert (Speicherplatz für) ein Objekt, führt aber keine
Initialisierungen durch: Der Standardkonstruktor ist ein Konstruktor ohne Parameter. (Hat eine Superklasse
Konstruktoren, jedoch keinen Standardkonstruktor und der Konstruktor der Subklasse ruft keinen Konstruktor der
Superklasse explizit auf, so meldet der Java-Compiler einen Fehler. Grund:
Sobald nur ein einziger selbst geschriebener Konstruktor existiert – ob mit oder ohne Parameter – so ist
der vom Compiler zur Verfügung gestellte Default-Konstruktor nicht mehr sichtbar.
Beispiel:
class Basis
{
protected int i; // = in Ableitung zugreifbar
public Basis(int i)//Konstruktor
{
this i = i;
System.out.println("In Basisklasse");
}
}
class Unterklasse extends Basis
{
public Unterklasse(int i) // Konstruktor
{
super(i);
// Basisklassen-Konstruktor-Aufruf
System.out.println("In Unterklasse");
}
}
Um den Destruktor der Superklasse aufzurufen, kann die Anweisung
super.finalize() verwendet werden. (Es erfolgt kein automatischer Aufruf von
finalize() der Basisklasse.) Zur Erinnerung: die Java-Spezifikation garantiert
nicht, dass die Methode finalize() zu einem bestimmten Zeitpunkt aufgerufen wird!
Experimentieren Sie mit super.finalize() und Alternativen (z.B. dispose();
siehe: Promod1).
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 14 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
1.4 Vererbungsketten und Zuweisungskompatibilität:
Wie bekannt: von einer Klasse können weitere Klassen abgeleitet werden, von denen wiederum Klassen
abgeleitet werden: so entsteht eine Klassenhierarchie.
In Java hat man von einer abgeleiteten Klasse immer nur Zugriff auf die nicht privaten Daten und Methoden
der unmittelbaren Superklasse. Auf Elemente der Superklasse einer Superklasse hat man keinen direkten Zugriff:
super.super.methode() geht nicht. Allerdings stehen alle Daten und Methoden zur Verfügung, die jeweils
vererbt wurden.
Innerhalb der Klassenhierarchie sind Objekte abgeleiteter Klassen immer zu Objektvariablen der Superklassen
zuweisungskompatibel!
Der Grund liegt darin, dass diese Objekte immer mindestens die Methoden und Daten der Superklasse besitzen
und somit vollständig als ein solches Objekt funktionstüchtig sind.
Wenn man jedoch einer Objektvariablen der Superklasse ein Objekt der Subklasse zuweist, fehlen die Methoden,
die erst in der abgeleiteten (Sub-)Klasse definiert werden. (Muss auf diese Methoden der abgeleiteten Klassen
zugegriffen werden, so ist ein Cast der Superklassen-Objektvariable notwendig.) Siehe Beispiel class
computerverwaltung unten „instanceof“
Evaluieren Sie die oben genannten Punkte!
GraphikObjekt
Beispiel.:
Kreis
gefüllterKreis
Beispiel:
Rechteck
gefülltesRechteck
Kreis K = new gefuellterKreis();
.
.
.
GrafikObject go [] = new GrafikObjekt[10];
go[0] = new Kreis();
go[1] = new Rechteck();
go[2] = new gefuellterKreis();
go[3] = new gefuelltesRechteck();
.
.
.
Durch die Zuweisungskompatibilität können problemlos Typumwandlungen von der
Subklasse in die Superklasse vorgenommen werden. Z. Beispiel:
Kreis = (Kreis) gefuellterKreis();
Übung:Prüfen Sie, ob ein Cast in die umgekehrte Richtung möglich und sinnvoll
ist. Demonstrieren Sie das Ergebnis an einem Programmbeispiel.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 15 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
1.5 Case-Study Objekt-Arrays, Zuweisungskompatibiliät, Operator instanceof
(Klausur-Aufgabe PROMOD1 in WS0506)
1) Schreiben Sie eine übersetzungsfähige Java-Applikation, die folgende Aufgabenstellung löst:
Im Rahmen eines Computerverwaltungssystems gilt es Informationen über Computer
zu verarbeiten. Bei allen Computerarten sind folgende Daten von Bedeutung:
Hauptspeicherkapazität und Plattenspeicherkapazität (in MB) und Prozessortyp (Intel oder AMD).
Bei der Computerart „Laptop“ wird noch eine Angabe zum Gewicht (in Gramm) gebraucht. Bei der
Computerart „Server“ ist die Anzahl von Prozessoren (in Stück) bedeutsam.
A) Modellieren Sie ein geeignetes Java-Klassensystem (mit drei Klassen). Achten Sie darauf,
dass die Attribute nicht ohne die dafür vorgesehenen Methoden manipulierbar sind: Stellen Sie für
jedes Attribut eine separate read_in-Methode zur Verfügung, um das Attribut durch eine
Keyboard-Eingabe zu füllen. Ebenso stellen Sie für jedes Attribut eine separate print_outMethode zur Verfügung, um den Attributinhalt auf der Konsole auszugeben. Für jede der drei
Klassen bieten Sie Konstruktoren an, um bei der Instantiierung die Attribute nach
Programmiererwunsch entsprechend zu initialisieren. (30 Punkte)
B) Schreiben Sie eine main-Methode in der ein Array mit 3 Objekten angelegt wird. In die ArrayElemente müssen die Objekte der oben erwähnten drei Klassen passen: Legen Sie in das Array
je ein Objekt der oben erwähnten Kategorien. Danach müssen alle Objekte im Array über das
Keyboard gefüllt werden. Nachdem die Objekte im Array über das Keyboard gefüllt wurden,
werden die Inhalte aller Objekte wieder auf den Bildschirm ausgegeben. Für das Füllen und
Ausgeben des Array müssen Sie die dafür am besten geeignete Kontrollstruktur verwenden.(20
Punkte)
1.6 Case-Study Lösung:
public class computerverwaltung {
public static void main(String args[]){
System.out.println("Jetzt werden 3 Objekte in einem Array angelet.");
Computer computer_array[] = new Computer[3];
computer_array[0] = new Computer(1000,2000,"intel");
computer_array[1] = new Laptop(2000,3000,"amd",3);
computer_array[2] = new Server(3000,4000,"sparc",4);
// Zuweisungskompat.
for(int counter = 0; counter < 3; counter++) {
computer_array[counter].read_in_Hauptspeicher();
computer_array[counter].read_in_Plattenspeicher();
computer_array[counter].read_in_Proztyp();
// statische Lösung ; funktioniert, ist aber nicht wirklich
// brauchbar...:
if (counter == 1){
((Laptop)(computer_array[counter])).read_in_Gewicht();
}
if (counter == 2){
((Server)(computer_array[counter])).read_in_anzahlProz();
}
} // end for()
for(int counter = 0; counter <3; counter++) {
computer_array[counter].print_out_Hauptspeicher();
computer_array[counter].print_out_Plattenspeicher();
computer_array[counter].print_out_Proztyp();
// dynamische Lösung mit instanceof-Operator:
if (computer_array[counter] instanceof Laptop){
((Laptop)(computer_array[counter])).print_out_Gewicht();
}
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 16 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
if (computer_array[counter] instanceof Server){
((Server)(computer_array[counter])).print_out_anzahlProz();
}
} // end for()
}// end main()
} // end class computerverwaltung
class Computer {
private int hauptspeicher;
private int plattenspeicher;
private String proztyp;
Computer(int hauptspeicher, int plattenspeicher, String proztyp){
this.hauptspeicher = hauptspeicher;
this.plattenspeicher = plattenspeicher;
this.proztyp = proztyp;
}
public void read_in_Hauptspeicher(){
hauptspeicher = StdInput.readInt("Hauptspeicherkapazitaet: ");
}
public void read_in_Plattenspeicher(){
plattenspeicher = StdInput.readInt("Plattenspeicherkapazitaet: ");
}
public void read_in_Proztyp(){
proztyp = StdInput.readString("Prozessortyp: ");
}
public void print_out_Hauptspeicher(){
System.out.println("Hauptspeicherkapazitaet: "+hauptspeicher);
}
public void print_out_Plattenspeicher(){
System.out.println("Plattenspeicherkapazitaet: "+plattenspeicher);
}
public void print_out_Proztyp(){
System.out.println("Prozessortyp: "+proztyp);
}
} // end class Computer
class Laptop extends Computer{
private int gewicht;
Laptop(int hauptspeicher, int plattenspeicher, String proztyp, int gewicht){
super(hauptspeicher,plattenspeicher,proztyp);
this.gewicht = gewicht;
}
public void read_in_Gewicht(){
gewicht = StdInput.readInt("Gewicht: ");
}
public void print_out_Gewicht(){
System.out.println("Gewicht: "+gewicht);
}
} // end class Laptop
class Server extends Computer{
int anzahlProz;
Server(int hauptspeicher, int plattenspeicher, String proztyp, int anzahlProz){
super(hauptspeicher, plattenspeicher, proztyp);
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 17 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
this.anzahlProz = anzahlProz;
}
public void read_in_anzahlProz(){
anzahlProz = StdInput.readInt("Anzahl Proz.: ");
}
public void print_out_anzahlProz(){
System.out.println("Anzahl Proz.: "+anzahlProz);
}
} // end class Server
1.7 Polymorphie
Mit Polymorphy (aus dem Griechischen: Vielgestaltigkeit) ist die Fähigkeit eines Objekts gemeint, die richtige
Methodenauswahl zu treffen.
Wird eine Methode eines Objekts aufgerufen, so kann sich die Methodendefinition an folgenden Stellen stehen:
o Die Klasse des Objekts hat die Methode definiert
o Die Methode wurde von der Superklasse der Objektklasse übernommen (geerbt)
o Die Methode existiert in der Superklasse, wurde aber in der davon abgeleiteten Klasse redefiniert /
überschrieben
Je nachdem, wann die Methodenauswahl erfolgt, spricht man von early binding oder late binding.
o Beim early binding erfolgt die Methodenauswahl statisch zur Übersetzungszeit.
o Beim late binding erfolgt die Methodenauswahl dynamisch zur Laufzeit.
Hinweis: bei statischen (static), finalen (final) und privaten (private) Methoden kommt stets early binding zur
Anwendung, da diese Methoden nicht überschrieben werden können (-> Performance).
Beispiel:
.
.
.
GrafikObject go [] = new GrafikObjekt[10];
go[0] = new Kreis();
go[1] = new Rechteck();
go[2] = new gefuellterKreis();
go[3] = new gefuelltesRechteck();
.
.
.
go[0].zeichne(); // early binding polymorphismus
go[1].zeichne(); // early binding polymorphismus
.
.
.
è hier: "early binding"-Polymorphismus: Bereits während der Compilezeit wird festgelegt, welche Ausprägung einer
bestimmten Methode aufgerufen werden soll.
Im Gegensatz hierzu gibt es auch noch "late binding-Polymorphismus". Hier wird erst bei der Laufzeit festgelegt,
welche Ausprägung einer bestimmten Methode aufgerufen werden soll.
1.7.1
Beispiel 1 – Methoden überschreiben:
class Basis
{
public String getName()
{
return "in Basisklasse";
}
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 18 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
}
class Unter extends Basis;
{
public String getName(); // Methode mit gleicher Signatur wie in Klasse Basis
{
super.getName();
// Aufruf der Methode der Superklasse
return "in Unterklasse";
}
}
Zur Erinnerung: Es können keine Methoden einer Superklasse, die private, final oder static sind, überschrieben
werden.
1.7.2
Beispiel 2 – "late binding – Polymorphismus":
Das folgende Beispiel demonstriert das Wesen des late binding Polymorphismus:
public class Polymorph
{
public static void main(String[] args)
{
Zahl werte[] = new Zahl[2];
// … erfolgt häuftig erst zur Laufzeit
werte[0] = new DeutschZahl();
werte[1] = new EnglischZahl();
for(int i = 0; i < werte.length; i++)
werte[i].druckeZahl();
}
// late binding Polymorphismus
// Vergl. Bsp. “Computerverwaltung”
}
class Zahl
{
protected double wert = 123456.78;
public void druckeZahl()
{
System.out.println(wert);
}
}
class DeutschZahl extends Zahl
{
public void druckeZahl()
{
System.out.println("123.456,78");
}
}
class EnglischZahl extends Zahl
{
public void druckeZahl()
{
System.out.println("123,456.78");
}
}
Übung: Erläutern Sie, warum late binding Polymorphismus für eine Programmiersprache eine weitere Steigerung der
Flexibilität bedeutet.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 19 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
1.8 Abstrakte Klassen und Methoden:
Abstrakte Klassen dienen als Vorlage für andere Klassen.
Von abstrakten Klassen können keine Instanzen gebildet werden; nur Ableitungen sind möglich.
Nicht alle Methoden einer abstrakten Klasse müssen leer sein, es können auch schon bereits voll ausgeprägte
Methoden implementiert werden, dies ist jedoch nicht zwingend erforderlich. In d. R. besitzen abstrakte Klassen
mindestens eine abstrakte Methode, die nur aus Methodendefinition (Signatur) ohne Rumpf besteht.
Abstrakte Methoden werden mit dem Identifier "abstract" gekennzeichnet.
Sinn und Zweck: Die Grundfunktionalität/Schnittstelle soll vorgegeben werden, jedoch erfolgt die
Implementierung später. (Anwendung u.a. in der Design-Phase)
Es ist möglich, Objektvariablen von der abstrakten Klasse zu erzeugen. Diesen Variablen können Objekten von
Ableitungen zugewiesen werden.
Beispiel:
public class VorlageTest
{
public static void main(String[] args)
{
Vorlage v = new VorlageImpl();
}
}
abstract class Vorlage
{
public abstract String getName(); // abstrakte Methode
}
class VorlageImpl extends Vorlage
{
public String getName()
{
return "VorlageImpl";
}
}
Wird eine abstrakte Klasse definiert, die ausschliesslich abstrakte Methoden besitzt, so kann man stattdessen
besser ein Interface einsetzen. Von Interfaces ist eine „Mehrfachvererbung“ möglich. (Siehe weiter unten Kapitel
Interfaces.)
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 20 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
1.9 Finale Klassen:
Von finalen Klassen kann keine Ableitung gemacht werden.
Sie werden mit dem Attribut final erzeugt.
Der Aufruf von finalen Klassen ist sehr schnell (wg. early binding; kein Binding-Verwaltung zur Laufzeit).
Typische Nutzung: für Hilfsklassen mit static-Methoden, die ohne Instanz einer Klasse aufgerufen werden
können.
Im Wesentlichen wird durch den Identifier "final" die Vererbungsfähigkeit verhindert. Dies widerspricht
eigentlich zwar der Objektorientierung, wird aber z.B. aufgrund von Sicherheitsaspekten in Kauf genommen.
Beispielsweise soll eine Methode Passwortüberprüfung nicht einfach überschrieben werden können.
Beispiel:
public final class Formeln
{
public static int getKreisUmfang(int radius)
{
return (2 * java.lang.Math.PI * radius);
}
}
Finale Klassen des JDK sind z.B. java.lang.String und java.lang.Math
(Überprüfen Sie das.)
Bearbeiten Sie die Case Studies und lösen Sie die Übungsaufgeben zu
diesem Kapitel! (Siehe Unterlagen hierzu auf dem Materialserver.)
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 21 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
2. Packages (Selbststudium!)
Was Sie lernen:
warum in Java Packages verwendet werden
wie Sie Packages definieren
wie Sie Packages importieren/einbinden
was es mit den Zugriffsrechten im Zusammenhang mit Packages auf sich hat
Voraussetzungen:
Erzeugen, Übersetzen und Ausführen von Java-Anwendungen
2.1 Grundlagen
Eine moderene Programmiersprache muss das Design (den Entwurf) eines Programmes mit unterstützen. Hierfür
sind Sprachmittel erforderlich, die es erlauben, ein Programm in Programmeinheiten zu unterteilen, um das
Gesamtprogramm übersichtlich (u.a. wg. Wartbarkeit/Änderbarkeit/…) zu strukturieren.
Solche Programmeinheiten sind in Java:
• Klassen,
• Interfaces,
• Threads und
• Packages.
Betrachten wir zunächst Packages:
Pakete gruppieren Klassen in einer „Sammlung“. Pakete eignen sich hervorragend, um die Programme zu
organisieren. Packages lassen sich auch in Archiven (jar-Files im zip-Format) halten.
Mit Packages werden Namensräume organisiert
Für das Verwenden und Einbinden in ein Filesystem ist die Variable CLASSPATH relevant. Sie gibt den Pfad
zum Paket-Startverzeichnis an.
Andere Möglichkeit: jar-Files „lokal“ lassen (z.B. im src-Package) und in der Compiler-Option –classpath
(durch „:“ separiert) auflisten. So können „External Libraries/JARs“ genau definiert eingebunden werden.
In ECLIPSE: Project -> Properties -> Java Build Path -> Library -> Add JARs / Add Library / Add external
JARs
Der Zugriff auf eine Klasse erfolgt durch die Kombination des Package-Namens und des Klassennamens.
Package-Namen setzen sich aus kleingeschriebenen Worten zusammen, die einen Pfad repräsentieren, deren
Abschnitte durch Punkte voneinander getrennt werden.
2.2 Packages importieren:
Man kann eine bestimmte Klasse oder ein gesamtes Paket importieren.
Es ergeben sich keine negativen Auswirkungen auf die Kompilierzeit bzw. Codegröße, wenn alle Klassen
eines Paketes importiert werden. Jedoch aus Gründen der Übersichtlichkeit bzw. der Eindeutigkeit bietet sich
die explizite Importierung einzelner Klassen an.
Die Importanweisung steht am Beginn der Quelldatei zwischen einer optionalen package-Anweisung und der
ersten Klassendefinition.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 22 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Über eine import-Anweisung kann immer nur ein Package eingebunden werden. Besitzt ein Package weiterere
Unterpackages, so müssen diese separart importiert werden.
Bsp:
import java.util.*;
import java.sql.*;
import java.util.Date;
.
.
.
è da die Klasse "Date" sowohl in java.util bzw. java.sql vorkommt, muss hier der Namenskonflikt mit einer
expliziten Importanweisung aufgehoben werden. Ansonsten ergeben sich Fehler zur Kompilierzeit.
2.3 Packages definieren:
Um Klassen in ein Paket aufzunehmen, muss der Name des Pakets am Beginn der Klasse angegeben werden.
Dies steht noch vor dem eigentlichen Code: package <PackageName>
Vor der Package-Anweisung dürfen sich nur Kommentare befinden.
Wird keine Package-Anweisung in die Quelldatei aufgenommen, dann gehören die Klassen in dieser Quelldatei
zum Standardpaket / Default-Package. Das Standardpaket hat keinen Paketnamen. Alle Klassen im aktuellen
Verzeichnis gehören somit zusammen.
Bsp:
package my.package.proccon;
.
.
//weiterer Quellcode
2.4 Zugriffsrechte in Packages:
Für Pakete gelten nach wie vor die Zugriffsmodifizierer public, private und protected. Wird
kein Zugriffsattribut angegeben so wird ein defaultmässiges Attribut ("package" oder
"friendly") gesetzt, was den Zugriff aller Methoden im selben Paket möglich macht.
Beispiel Package-Import und -Definition:
import bsp_u_ueb_erstes_semester.woche_10_1gp_kap10_packages.util.*;
public class PackageDemo
{
public static void main(String[] args)
{
greg_calendar.prefix = "Heute: ";
System.out.println("Aufgepasst...");
System.out.println(greg_calendar.getDate());
// greg_calendar.prefix = "Heute ist der ";
System.out.println(bsp_u_ueb_erstes_semester.woche_10_1gp_kap10_packages.util.
greg_calendar.getDate());
}
}
--------------------------------util.greg_calenadr.java----------------------------------------------------------package bsp_u_ueb_erstes_semester.woche_10_1gp_kap10_packages.util;
public class greg_calendar
{
static String prefix;
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 23 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
public static String getDate()
{
java.util.GregorianCalendar gc = new java.util.GregorianCalendar();
System.out.println("greeting from the long named package...");
return (gc.get(gc.DATE) + "." + (gc.get(gc.MONTH)+1) + "." + gc.get(gc.YEAR));
}
}
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 24 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Bearbeiten Sie die Case Studies und lösen Sie die Übungsaufgeben zu diesem Kapitel!
(Siehe Unterlagen hierzu auf dem Materialserver.)
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 25 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
3. Interfaces (und Ergänzungen zu Klassen)
Was Sie lernen:
wie Sie Interfaces definieren und verwenden
was innere und anonyme Klassen sind
was Adapterklassen sind und wozu sie verwendet werden
Voraussetzungen:
Klassen und Vererbung
3.1 Grundlagen
Beim Entwurf von objektorientierten Systemen geht man über verschiedene Stufen der Abstraktion:
Systemanalyse: „Welche Klassen werden benötigt und welche Beziehungen bestehen zwischen den Klassen?“
(Konzeptionelle Sicht: welche Klassen spielen eine Rolle („Klassenkandidatenkatalog“ ); in welcher
Beziehung stehen die Klassen untereinander?).
Entwurf/Design: „Welche Schnittstellen haben die Klassen?“ (Spezifizierende Sicht: hier kümmert man sich um
die Schnittstellen (= Methodensignaturen) der Klassen!)
Implementierung: übernimmt die Ausprogrammierung der Schnittstellen (Interfaces) à Verfeinerung der
Spezifikation. (Implementierende Sicht: hier werden die Schnittstellen implementiert!)
Java unterstützt das „Programmieren im Großen“ (spezifizierende Sicht) mit dem Sprachmittel interface.
Ein Interface definiert einen abstrakten Datentyp, der nur Konstanten und Methodensignaturen (=
Schnittstelllen der Klasse / Protokoll der Klasse) besitzt.
Methoden werden nur deklariert, nicht implementiert.
Klassen können mehrere Interfaces implementieren (=> „Mehrfachvererbung“). D.h. sie implementieren die
Methoden meherer Interfaces.
Interfaces können von anderen Interfaces erben/abgeleitet werden.
Interfaces werden mit dem Schlüsselwort "interface" eingerichtet
Definition:
[public] interface <Name> [extends <Interface>[,<Interface>]]
{
[public, static] final <Typ> Konstante;
[public] <Typ> Methode(<Parameter>);
}
Im plem entierung:
class <Name> implements <InterfaceName>, <InterfaceName>
Alle Methoden in einem Interface sind automatisch abstract und public.
Elemente von Interfaces können nicht protected oder private sein.
Interfaces haben keine Konstruktoren/Destruktoren
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 26 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Es können aber Variablen vom Typ "Interface" definiert werden Interfacename Variable; è hier können
Objekte zugewiesen werden, deren Klasse das Interface implementiert.
Über den Operator instanceof kann geprüft werden, ob ein Objekt ein Interface implementiert.
Interfaces sind keine Klassen. Insbesondere kann man eine Schnittstelle nicht mit dem Operator new
instantiieren. (Man spricht auch nicht von Ableitung, sondern von Implementierung).
Beispiel 1 – Interface: (Phänomen: Mehrfachtypisierung)
public class Interface {
public static void main(String[] args) {
TestClass1 tco1 = new TestClass1();
TestClass2 tco2 = new TestClass2();
tco1.sayHello(); /*1*/
tco2.sayHello(); /*1*/
if (tco1 instanceof TestClass1) /*2*/ // tco1 ein Klassen-Tpy
System.out.println("tco1 is instance of TestClass1");
if (tco1 instanceof PoliteObject) /*2*/ // tco1 ein Interface-Typ
System.out.println("tco1 is instance of PoliteObject");
PoliteObject po; /*3*/ //Interface-Varable nimmt auch “Ableitungen” auf.
po = tco1;
/*4*/ // po referenziert TestClass1-Objekt
po.sayHello();
po = tco2;
/*5*/ // po referenziert TestClass2-Objekt
po.sayHello();
}
}
interface PoliteObject {
public void sayHello();
}
// nur Methoden-Signatur
class TestClass1 implements PoliteObject { //
public void sayHello() {
//
System.out.println("Hello World");
//
}
// unterschiedliche Implementierungen
}
// eines Interfaces
//
class TestClass2 implements PoliteObject { //
public void sayHello() {
//
System.out.println("Guten Tag");
//
}
}
Erläuterungen zum Beispiel: Das Beispiel definiert die Schnittstelle "PoliteObject" welche die Methode "sayHello()"
anbietet. Die beiden Klassen "TestClass1" und "TestClass2" implementieren jeweils Methoden für die durch die
Schnittstelle definierten Operationen. Der erste Anweisungsblock zeigt die (simple) Ausführung /*1*/ der genannten
Methode. Die darauffolgende Codesequenz /*2*/ hebt den Aspekt der Mehrfachtypisierung hervor (Objekt von
schnittstellen-implementierender Klasse ist sowohl Ausprägung der Klasse selbst (im Beispiel: TestClass1), als auch
der Schnittstelle (PoliteObject). Abschließend wird eine Variable vom Typ der Schnittstelle deklariert /*3*/ und
zunächst mit der Referenz auf ein Objekt der Klasse "TestClass1" belegt /*4*/. Der (statisch typsicher mögliche)
Aufruf "sayHello()" ist auch hier möglich. Gleiche Bedingungen herrschen nach der Zuweisung eines Objektes vom
Typ "TestClass2" an dieselbe Variable.
Beispiel 2 – Interface: (Tpyische Verwendung eines Interfaces)
// Ausgabetypen.java im default-Package-------------------------------------public interface Ausgabetypen
{
void ausgabe();
void ausgabeLang();
void ausgabeKurz();
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 27 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
}
// Datum1.java im default Package-------------------------------------------public class Datum1 implements Ausgabetypen // alle Ausgabetypen-Methoden
{
// müssen implementiert werden!!!
private int tag, monat, jahr;
public Datum1()
{
this(1, 1, 2002);
}
alle Interface-Methoden
werden implementiert
public Datum1(int tag, int monat, int jahr)
{
this.tag = tag;
this.monat = monat;
this.jahr = jahr;
}
public void ausgabe()
{
System.out.println("Datum: " + tag + "." + monat + "." + jahr);
}
public void ausgabeLang()
{
}
System.out.println("Das gespeicherte Datum ist der " + tag + "." + monat + "." + jahr);
public void ausgabeKurz()
{
System.out.println(tag + "." + monat + "." + jahr);
}
}
// Zeit1.java im default-Package--------------------------------------------------public class Zeit1 implements Ausgabetypen
{
private int sekunde, minute, stunde;
public Zeit1()
{
this(0, 0, 0);
}
public Zeit1(int sekunde, int minute, int stunde)
{
this.sekunde = sekunde;
this.minute = minute;
this.stunde = stunde;
}
alle Interface-Methoden
werden implementiert
public void ausgabe()
{
System.out.println("Zeit: " + sekunde + ":" + minute + ":" + stunde);
}
public void ausgabeLang()
{
System.out.println("Die gespeicherte Zeit ist " + sekunde + ":" + minute + ":" +
stunde);
}
public void ausgabeKurz()
{
System.out.println(sekunde + ":" + minute + ":" + stunde);
}
}
// Test1.java im default-Package----------------------------------------------------public class Test10
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 28 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
{
private Ausgabetypen[] at = new Ausgabetypen[4];
public Test10()
{
at[0] = new Datum1(4, 3, 2002);
at[1] = new Zeit1(0, 12, 12);
at[2] = new Datum1(8, 9, 2001);
at[3] = new Zeit1(59, 59, 23);
for(int i = 0; i < at.length; i++)
if (at[0] instanceof Ausgabetypen)
at[i].ausgabe();
}
public static void main(String[] args)
{
new Test10(); // Start über Objektinstanziierung
}
}
Übung: Fassen Sie die Unterschiede zwischen abstrakten Klassen und Interfaces zusammen.
3.2 Adapterklassen
Eine Adapterklasse in Java ist eine Klasse, die ein vorgegebenes Interface mit leeren Methodenrümpfen
implementiert, bzw. Rümpfen, die einen Standardwert zurückliefern.
Adapterklassen können verwendet werden, wenn aus einem Interface lediglich ein Teil der Methoden benötigt
wird, der Rest aber „uninteressant“ ist, (momentan) nicht gebraucht wird.
In diesem Fall leitet man einfach eine neue Klasse von der Adapterklasse ab, anstatt das zugehörige Interface zu
implementieren, und überlagert die benötigten Methoden.
Entwicklungsidee: stets zu Interfaces gleich die Adapterklassen mit anbieten….!!!!
Hinweis:
- wird vor allem bei der GUI-Programmierung verwendet.
- Adapterklassen können auch weitere/zusätzliche Methoden anbieten.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 29 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Beispiel – Adapterklassen (vgl. Beispiel 2 oben):
// AusgabetypenAdapter.java im default-Package------------------------------------------------------public class AusgabetypenAdapter implements Ausgabetypen // Adapter impl.
Interface
{
public void ausgabe() {}
public void ausgabeLang() {}
// Leere Methoden, die im extend überlagert werden
public void ausgabeKurz() {}
}
// Datum2.java im default Package---------------------------------------------------------------public class Datum2 extends AusgabetypenAdapter // jetzt nur die gewünschten
{
// Methoden implementieren
private int tag, monat, jahr;
public Datum2()
{
this(1, 1, 2002);
}
public Datum2(int tag, int monat, int jahr)
{
this.tag = tag;
this.monat = monat;
this.jahr = jahr;
}
public void ausgabe() // die anderen Methoden bleiben wie sie sind
{
System.out.println("Datum: " + tag + "." + monat + "." + jahr);
}
}
Übung: Machen Sie das Beispiel lauffähig durch die Implementierung der fehlenden Klassen.
3.3 Innere Klassen (auch: geschachtelte / eingebettete / nested class)
Eine innere Klasse ist eine Klasse, die innerhalb einer anderen Klasse definiert ist.
Aus inneren Klassen können auf alle Methoden und Daten der äußeren Klasse zugegriffen werden, auch
wenn diese private sind.
Innere Klassen sind den anderen Klassen des Pakets verborgen. (Weitere Stufe der Kapselung)
Es können nur in der äußeren Klasse Objekte der inneren Klasse erzeugt werden
àAusnahme: statische innere Klassen, von denen auch in anderen Klassen Objekte erzeugt werden können.
Statische innere Klassen besitzen keine Referenz auf ein Objekt der äußeren Klasse.
Für jede innere Klasse wird beim Compilieren eine eigene Klassendatei erzeugt. Diese wird so gekennzeichnet:
outer$inner.class
Der Klassenname nach außen lautet outer.inner
Der Konstruktoraufruf sieht folgendermaßen aus:
in outer:
new inner(…)
sonst:
outer.new inner(…)
Die bekannten Modifier public, private, protected, final und static können in inneren Klassen
verwendet werden
Beispiel – innere Klassen:
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 30 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
class aussen // Implementierungsdetails verborgen in inneren Klassen
{
class mitte // nur zur Verwendung in aussen
{
class innen // nur zur Verwendung in mitte
{
.
.
.
}
}
}
Hinweis/Motivation für innere Klassen:
Innere Klassen sind sehr komfortabel, wenn man ereignisgesteuerte Programme schreibt (siehe weiter unten
„Ereignisse und Event-Handling“).
Anonyme innere Klassen bieten sich an, wenn man Callbacks en passant definieren möchte (siehe weiter
unten „Ereignisse und Event-Handling“ z.B. bei der AWT-Programmierung).
Ausserhalb dieser Anwendungsgebiete sieht man innere Klassen relativ selten, weil sie nicht im Brennpunkt des
objektorientierten Modellierens liegen. a) Die typische Vorgehensweise ist der Entwurf „kleiner“ Klassen, die dann
per Ableitung um weitere Daten und Methoden angereichert werden. Atypisch: komplexes im Inneren immer feiner zu
modellieren. b) Innere Klassen kann man in anderen Klassen nicht nutzen, das möchte man aber im Sinne einer
Wiederverwendung!
3.4 Lokale Klassen
Lokale Klassen sind Klassen, die in Methoden definiert werden.
Quiz: Wann können lokale Klassen sinnvoll sein? => Wie eine innere Klasse kann eine lokale Klasse auf alle
Methoden (hier nicht relevant) und Daten einer äusseren Klasse (hier eine Methode) zugreifen. Damit sind die Daten
aus der Sicht der Methoden der inneren Klasse „global“. Das kann für die Implementierung von Hilfsmethoden
sinnvoll und hilfreich sein.
3.5 Anonyme Klassen
Anonyme Klassen sind spezielle Varianten von inneren Klassen. Ort der Verwendung: typischerweise auf
Parameterposition von Methodenaufrufen.
Anonyme Klassen besitzen keinen Namen. Es werden die Klassendeklaration und Objekterzeugung in einem
Schritt zusammengefasst.
Anonyme Klassen müssen von einer anderen Klasse abgeleitet werden oder ein Interface/Adapterklasse
implementieren.
Anonyme Klassen werden verwendet, wenn eine einzige Objektinstanz gebraucht wird. Meist auf der
Parameterposition von Methoden. (z. B. bei Ausdrücken oder aber der GUI-Programmierung).
o Statt eines Objektes wird über new und die nachfolgende Angabe eines Klassen- oder
Interfacenamens die Erstellung sowie Implementierung einer Klasse mit Objekterzeugung
verbunden.
o Im runden Klammernpaar können Parameter für den Konstruktor übergeben werden (falls eine
Superklasse vorhanden ist).
o Bei Interfaces können keine Parameter übergeben werden, da sie keine Konstruktoren besitzen.
o Das erzeugte Objekt wird der Methode als Parameter übergeben.
Für anonyme Klassen wird beim Compilieren eine eigene Klassendatei erzeugt. Anonyme Klassen werden
durchgezählt und mit dem $-Zeichen mit äußeren Klassennamen verbunden: outer$1.class
Anonyme Klassen haben keine Konstruktoren.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 31 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Beispiel – anonyme Klassen:
// Ausgabetypen.java im default-Package------------------------------------------public interface Ausgabetypen
{
void ausgabe();
void ausgabeLang();
void ausgabeKurz();
}
// AusgabetypenAdapter.java im default-Package-----------------------------------public class AusgabetypenAdapter implements Ausgabetypen
{
public void ausgabe() {}
public void ausgabeLang() {}
public void ausgabeKurz() {}
}
// Test14.java im default-Package-------------------------------------------------public class Test14
{
private Ausgabe a = new Ausgabe();
private String s = "Anonym bleiben";
public Test14() // Konstruktor
{
a.drucke(new AusgabetypenAdapter() /// anonyme Klasse impl. Adapter ///////
{
/// Objekt wird erzeugt und der
///////
public void ausgabe()
/// Methode drucke() mitgegeben
///////
{
System.out.println(s); 2
}
Überschreibt
});
leere Methode
}
public static void main(String[] args)
{
new Test14(); // Start via Konstruktor
}
aus der
Adapterklasse
1
class Ausgabe ////////////////////////////////////////////////////////////////
{
public void drucke(Ausgabetypen at)
{
at.ausgabe();
}
}
}
Das oben dargestellte Beispiel wird uns als „Muster“ bei der AWT-Programmierung wieder begegnen!
3.6 Statische innere Klassen (static class) / statische innere Schnittstellen
Bei inneren Klassen/geschachtelten Klassen kennt das eingeschlossene Objekt sein umgebendes Objekt und
umgekehrt. Bei statisch geschachtelten Klassen gibt es diesen Bezug nicht: bei der statischen inneren Klasse
braucht man keine Objekt von der äusseren Klasse, um ein Objekt der inneren Klasse zu erzeugen.
Im äusseren Objekt können mehrere statische innere Objekte erzeugt werden.
Übung: Überlegen Sie sich eine Beispiel, anhand dessen Sie den oben beschreibenen Sachverhalt
experimentell studieren können.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 32 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Bearbeiten Sie die Case Studies und lösen Sie die Übungsaufgeben zu diesem Kapitel!
(Siehe Unterlagen hierzu auf dem Materialserver.)
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 33 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
4. Ein-/Ausgabe auf Konsole,Dateien u.a.(ADRELI_CON)
Was Sie lernen:
wie Sie Ein- und Ausgabe über die Konsole und die Tastatur realisieren
wie Sie mit Dateien (class File) arbeiten: die Schnittstelle zu Pfadnamen
was Dateien mit wahlfreiem Zugriff (random access) sind und wie sie benutzt werden
Voraussetzungen:
Arbeiten mit Klassen und Methoden
Arbeiten mit dem Exceptionhandling
Im Grunde könnte das Kapitel auch „Persistierung – Erster Teil“ heissen: es gibt die zwei großen
Persistenz-Konzepte, um Daten dauerhaft zu machen.
Zum einen können dazu Dateien genutzt werden und
zum zweiten Datenbanken. Beide Ansätze haben ihere Berechtigung und jeweilse ihre
spezifischen Vor- und Nachteile.
In diesem Kapitel kümmern wir uns um die ersten Schritte der Persistierung mit Hilfe der Dateien; hierfür gibt
es mehrere Konzept: wir können Dateien als Random-Access-Files sehen oder auch als Streams.
Die Streams wiederum sind selbst eine abstrahierende Schicht, hinter der sich neben
Dateien auch Sockets, Pipes und Strings verbergen können.
Auf für die Persistierung mit Datenbanken gibt es mehrere Java-Konzepte; diese behandeln wir in
separaten Kapiteln.
4.1 Grundlagen
Es ist zwischen (Java-)Applikationen und (Java-)Applets zu Unterscheiden. Applets haben i. d. R. keinen Zugriff
auf Files des lokalen Rechners.
I/O - Operationen laufen in Java über:
1.) Streams (Class InputStream/OutputStream) für Binär-I/O und Zeichen-I/O
2.) Dateien (Random-Access-Files, Dateien für wahlfreien Zugriff; Class RandomAcessFile und zus. File)
Merkmale von Streams:
Besitzen Datenquelle (Source) und Datensenke (Destination). Ein Datenstrom kann von einer beliebigen Quelle
kommen: Dateien, Netzwerkschnittstelle (Sockets: siehe Kapitel „Socket-Programmierung. Für Sockets gibt’s
z.B. getInputStream() und getOutputStream()-Methoden, wenn es um Byte-Stream-Kommunikation (= binäre
i/o) geht. (Prüfen Sie ein entsprechendes Methoden-Angebot für die Charakter-Stream-Kommunikation (= Zeicheni/o))!
Pipes (für Pipes gibt es eigene Stream-Klassen, jeweils für die Byte- oder Character-Kommunikation), oder auch
Strings (für Stings/Arrays gibt es jeweils eigene Stream-Klassen für die Byte- oder Charcter-Kommunikation)
können Quellen sein. Das gleiche gilt für das Ziel des Datenstroms. Und last but not least: Mittels class
PipedInputStream und class PipedOutputStream lassen sich (serializable) Objekte streamen.
Jeder Datenstrom kann nach dem gleichen Prinzip, mit den gleichen Methoden verarbeitet werden, egal woher
der Strom kommt und für welches Ziel er bestimmt ist.
Streams sind unidirektional (Monolog). Für einen Dialog zwischen zwei Kommunikationspartnern (Applikationen,
Threads) werden zwei Streams benötigt.
Streams unterliegen dem FIFO - Prinzip ("first in first out”)
Während der Datenübertragung können Daten gefiltert (bearbeitet) werden
Merkmale von wahlfreien Dateien/ Random-Access-Dateien:
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 34 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
für den wahlfreien Zugriff ist die Quelle und das Ziel immer eine Datei.
Dateien können sind bidirektional: sie können geschrieben und gelesen werden.
Während der Übertragung der Daten ist keine Filterung möglich.
Basis-Philosophie für den Umgang mit Streams und Dateien:
Bevor Dateien und Streams verwendet werden können (Anwendung von E/A-Operationen) müssen sie eröffnet
werden.
Nicht mehr benötigte Dateien und Streams sollten wieder geschlossen werden (Methode close()).
Das Öffnen von Dateien und Streams wird in Java üblicherweise über den Konstruktor der jeweiligen Klasse
realisiert.
Standard-Streams (siehe unten) werden automatisch geöffnet und geschlossen.
4.2 Standard-Ein- und Ausgabe
Die Ein-/Ausgabe über die Standard-E/A-Geräte (in der Regel Tastatur und Bildschirm) ist in der Klasse
System implementiert. (Diese Klasse ist im Paket java.lang definiert; bei der Verwendung der Standard E/A ist
also kein zusätzliches Paket einzubinden.)
Die Standard-E/A arbeitet mit Objekten der Stream-Klassen InputStream und PrintStream.
Stream-Typen
Standard-Eingabestrom
Standard-Ausgabestrom
Standard-Fehlerausgabestrom
Datenfelder-Felder
der Klasse System
static InputStream in
static PrintStream out
static PrintStream err
Genaueres zu den Methoden siehe Class
E/A-Beispiele, Methoden,
wie bereits bekannt
System.in.read()
System.out.print()
System.err.print()
InputStream und Class Printstream
Beispiel – I/O
import java.io.*;
public class IO
{
public static void main(String[] args)
{
byte [] b = new byte[1];
try
{
}
}
System.out.println("Ein Zeichen eingeben: ");
System.in.read(b);
System.out.println( (char) b[0] + " hat ASCII-Wert " + b[0]);
catch (IOException ioe)
{
System.out.println(ioe.getMessage());
}
}
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 35 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
"out"-Methoden aus class PrintStream
print(char c)
/ println(char c)
print(double d) / println(double
d)
print(int i)
/ println(int i)
print(Object o) / println()
print(String s) / println()
read()
"read"-Methoden aus class InputStream
Liefert nächstes Zeichen aus
dem Eingabestrom als return
read(byte[] b)
füllt ein Byte-Array mit maximal
b.length ASCII-Zeichen
read(byte[] b, int off,
int len)
- ,, - , jedoch mit Startpos. und
Länge
Methode zum den Eingabepuffer leeren: System.in.skip(System.in.available());
Beispiel:
//
//
//
//
//
//
//
PROMOD; Prof. Illik
Gepufferte Ein-/Ausgabe
Experimentieren Sie mit
dem Programm und prüfen Sie,
was passiert, wenn Sie die jeweiligen
Zeilen mit der skip()-Methode
auskommentieren. Erklärung!
import java.io.*;
public class StdIO
{
public static void main(String[] args)
{
int a;
byte[] b = new byte[1];
byte[] line = new byte[81];
try
{
System.out.print("1. Bitte geben Sie ein Zeichen ein: ");
a = System.in.read();
System.out.println("Zeichen "+ (char)a + " hat den ASCII-Code " + a + "!");
// Vergleiche ASCII-Code mit dem UNICODE (Unicode-Tabelle in Kap15)
// www.unicode.org
System.out.println("2. Bitte geben Sie ein Zeichen ein: ");
//System.in.skip(System.in.available()); //Leeren des Tastaturpuffers
//(“skip over and discard”)
System.in.read(b);
System.out.println((char)b[0] + " hat den ASCII-Code " + b[0]);
System.out.print("3. Bitte geben Sie noch ein Zeichen ein: ");
System.in.skip(System.in.available());
System.in.read(b);
System.out.println((char)b[0] + " hat den ASCII-Code " + b[0]);
System.out.print("4. Geben Sie jetzt mehrere Zeichen ein: ");
System.in.skip(System.in.available());
System.in.read(line);
//for(int i=0;line[i] != '\u0000'; i++)
for(int i = 0;line[i] != 0; i++)
//for (int i = 0; i < line.length; i++)
{
System.out.print((char)line[i]);
}
}
catch(IOException ioex)
{
System.out.println(ioex.getMessage());
}
}
}
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 36 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
4.3 Die Klasse File:
Was die Klasse File ist: „An abstract representation of file and directory pathnames.“ (Zitat aus der File-API).
Die Methoden der Klasse File sind auf Dateien (Files) und Verzeichnisse ausgerichtet. Z. B. Namen festlegen;
Dateien anlegen, umbenennen, Attribute auflisten,...
Die Klasse "File" besitzt drei verschiedene Konstruktoren:
File(String path)
File(String parentpath, String filename)
File(File directory, String filename)
è Die Angabe des Pfads ist plattformabhängig, dass heißt, auf Windows-Rechnern trennt ein Backslash die
Pfade (»temp\dasda«) und auf UNIX-Maschinen ein normaler Slash (»temp/dasda«). Glücklicherweise können wir
die Klasse File nach dem separatorChar fragen.
è Ebenso wie bei den Pfadtrennern gibt es einen Unterschied in der Darstellung des Wurzelverzeichnisses (Root).
Unter UNIX ist dies ein einzelner Slash (»/«), und unter Windows ist die Angabe des Laufwerks vor dem Doppelpunkt
und dem Backslash-Zeichen gestellt (»D:\«)
è Eine absolute Pfadangabe wird inklusive der Wurzel angegeben. Bsp: C:\Programme\MyJavaProg\IO-Prog.jar
è Eine relative Pfadangabe enthält keine Wurzel uns ist relativ zum aktuellen Verzeichnis. Bsp: \MyJavaProg\IO.jar
è Siehe auch: UNC (Universal Naming Convention) für Microsoft Windows.
Bsp – Pfadangaben unter Linux
File f1 = new File("/tmp/file1.txt"); //absolute Pfadangabe inkl. Wurzel (/)
File f2 = new File("tmp/file2.txt"); //relative Pfadangabe ohne Wurzel
File f3 = new File("tmp" + File.separatorChar + "file3.txt");
wichtige Methoden der
Klasse File (Auszug):
Pfad und Dateiangaben
String getName()
String getPath()
String getParent()
String getAbsolutPath()
File [] listRoots()
Gibt einen String zurück, der den Dateinamen des File-Objekts enthält
Gibt einen String zurück, der den Pfadnamen der Datei enthält
Gibt einen String zurück, der den übergeordneten Pfad des File-Objekts
enthält
Gibt einen String zurück, der den absoluten Pfadnamen enthält
Gibt ein Array von File-Objekten zurück, die allen verfügbaren
Stammverzeich-nissen entsprechen
Manipulstionsoperationen
boolean createNewFile()
Legt eine neue, leere Datei mit dem entsprechenden Namen an
(RECHTE!!!)
boolean delete()
Versucht die Datei zu löschen, liefert im Erfolgsfall "true", andernfalls
"false"
boolean mkdir()
Versucht ein Unterverzeichnis anzulegen, im Erfolgsfall "true", andernfalls
"false"
boolean renameTo(File f)
Liefert "true", falls der Name entsprechend geändert wurde, andernfalls
"false"
boolean setReadOnly()
Setzt das Dateiatribut »schreibgeschützt«. Im Erfolgsfall "true", andernfalls
"false"
Datei-und
Verzeichnisinfos
boolean canRead()
boolean canWrite()
boolean exists()
Gibt an, ob die Datei durch die aktuelle Anwendung gelesen werden kann
Gibt an, ob die Datei beschreibbar oder nur lesbar ist
Liefert "true", wenn die Datei oder das Verzeichnis existiert, andernfalls
"false"
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 37 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
boolean isDirectory()
Gibt "true" zurück, wenn das File-Obj. ein Verzeichnis repräsentiert, sonst
"false"
boolean isHidden()
Prüft, ob das File-Objekt eine versteckte Datei oder Verzeichnis
repräsentiert
long lastModified()
Gibt die Zeit der letzten Änderung in mSec als Unix-Timestamp zurück
(1.1.1970)
long length()
Gibt die Länge der Datei in Byte zurück, oder 0, wenn die Datei nicht
existiert
string [] list() und FILE [] listfile()-Methoden listen die Verzeichnisinhalte auf. Siehe APIDoku class File.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 38 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Beispiel:
import java.io.*;
public class FileInfo
{
public static void main(String[] args)
{
File verzX = new File("/temp/a/b/c/d");
// File verzX = new File("." + File.separatorChar + "d");
if(!verzX.exists())
{
verzX.mkdir(); // +++++ wirft keine IOException -> siehe Doku!
System.out.println("Verzeichnis " + verzX.getPath() + " angelegt: " +
verzX.exists());
}
File file = new File("/temp/test.txt");
if(! file.exists())
{
try
{
file.createNewFile();
}
catch(IOException e)
{
System.out.println("Die Datei konnte nicht erzeugt werden.");
return;
}
System.out.println("Datei " + file.getName() + " angelegt: " +
file.exists());
}
if(file.isFile())
System.out.println(file.getName() + " ist eine Datei ");
else
System.out.println(file.getName() + " ist keine Datei ");
if(file.isDirectory())
System.out.println(file.getName() + " ist ein Verzeichnis ");
else
System.out.println(file.getName() + " ist kein Verzeichnis ");
System.out.println("Parent: " + file.getParent());
System.out.println("Parentdirectory: " + file.getParentFile());
System.out.println("Pfad (ohne Wurzel): " + file.getPath());
System.out.println("absoluter Pfad (mit Wurzel): " + file.getAbsolutePath());
System.out.println("Dateiattribute:");
System.out.println("lesen erlaubt: " + file.canRead());
System.out.println("schreiben erlaubt: " + file.canWrite());
System.out.println("versteckt: " + file.isHidden());
System.out.println(System.getProperty("user.dir"));
// HINWEIS:
// "user.dir" ist ein Key der eine System-Property beschreibt
// Alle Keys sind in der Klasse System bei der Methode getProperties gelistet.
}
}
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 39 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
4.4 Die Klasse RandomAccessFile:
Dateien mit wahlfreiem Zugriff – d.h. über geeignete Methoden kann auf jede gewünschte Dateiposition
zugegriffen werden.
Es gibt eine aktuelle Schreib- und Leseposition, welche durch den Schreib-Lese-Zeiger (Seek-Pointer)
kennzeichnet (Siehe Bild an der Tafel.)
Eine RandomAccess-Datei ist vorstellbar als ein großes Byte-„Array“. Allerdings ist kein indizierter Zugriff
möglich– wie bei Arrays üblich. Eine Random-Access-Datei ist eben doch kein Array.
Der Seek-Pointer kann innerhalb der Datei beliebig auf jede Byte-Position verschoben werden
Wenn eine RA-Datei eröffnet wird, muss sie bereits physisch existieren, ansonsten ergibt sich eine
FileNotFound-Exception
Wird eine RA-Datei zum Schreiben eröffnet, so wird sie als File angelegt, falls sie noch nicht existiert
Eine RA-Datei kann nicht geleert werden. Hierzu muss sie gelöscht und anschließend wieder angelegt werden
Methoden um den Seek-Pointer zu verstellen:
Jedes Lesen und Schreiben versetzt den Seek-Pointer um die Anzahl der gelesenen/geschriebenen Bytes (impliziter
Versatz in Richtung Dateiende)
void seek(long pos)
int skipBytes(int i)
long getFilePointer()
Setzt den Dateizeiger auf pos Bytes gerechnet vom Beginn der Datei
Positioniert den Dateizeiger relativ zur aktuellen Position in Richtung Dateiende.
Rückgabewert ist die Anzahl der übersprungenen Zeichen.
Liefert die aktuelle Position des seek-pointers
Die Klasse RandomAccessFile bietet für sämtliche primitiven Datentypen Methoden an, um entweder eine RA-Datei
zu beschreiben oder auszulesen. Es gibt aber noch weitere Write- und Readmethoden. Hierzu gibt die Java-Doku
nähere Informationen.
Beispiel:
import java.io.*;
public class RandomAccessFileTest
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 40 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
{
public static void main(String[] args)
{
try
{
RandomAccessFile file = new RandomAccessFile("zzz_randfile.txt", "rw");
file.writeBytes("AAAAA\r\nBBBBB\r\nCCCCC\r\n"); // writeBytes() schreibt
ASCII
//file.writeChars("AAAAA\nBBBBB\nCCCCC\n"); // writeChars() schreibt UNICODE
file.setLength(12); // File wird "abgeschnitten" nach 12. Zeichen
file.seek(0);
System.out.println("das erste Mal lesen");
for(int i = 1; i <= 3; i++)
System.out.println(file.readLine());
file.writeBytes("DDDDD\nEEEEE\nFFFFF\n");
file.seek(0);
System.out.println("Das zweite Mal lesen");
for(int i = 1; i <= 10; i++)
System.out.println(file.readLine());
file.close();
}
catch(FileNotFoundException fnfex)
{
System.out.println(fnfex.getMessage());
}
catch(IOException ioex)
{
System.out.println(ioex.getMessage());
}
}
}
Hinweis: System Independent Newline Characters
Für Files: public static String newline = System.getProperty("line.separator");
Für die Konsole: ’\n’ ist für Konsolausgaben auf Windows, Linux, Mac gleichermaßen OK.
Aus der API-Beschreibung von
class RandomAcessFile :
Method Summary
void close()
Closes this random access file stream and releases any system resources associated with
the stream.
FileChannel getChannel()
Returns the unique FileChannel object associated with this file.
FileDescriptor getFD()
Returns the opaque file descriptor object associated with this stream.
long getFilePointer()
Returns the current offset in this file.
long length()
Returns the length of this file.
int read()
Reads a byte of data from this file.
int read(byte[] b)
Reads up to b.length bytes of data from this file into an array of bytes.
int read(byte[] b, int off, int len)
Reads up to len bytes of data from this file into an array of bytes.
boolean readBoolean()
Reads a boolean from this file.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 41 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
byte readByte()
Reads a signed eight-bit value from this file.
char readChar()
Reads a Unicode character from this file.
double readDouble()
Reads a double from this file.
float readFloat()
Reads a float from this file.
void readFully(byte[] b)
Reads b.length bytes from this file into the byte array, starting at the current file pointer.
void readFully(byte[] b, int off, int len)
Reads exactly len bytes from this file into the byte array, starting at the current file pointer.
int readInt()
Reads a signed 32-bit integer from this file.
String readLine()
Reads the next line of text from this file.
long readLong()
Reads a signed 64-bit integer from this file.
short readShort()
Reads a signed 16-bit number from this file.
int readUnsignedByte()
Reads an unsigned eight-bit number from this file.
int readUnsignedShort()
Reads an unsigned 16-bit number from this file.
String readUTF()
Reads in a string from this file.
void seek(long pos)
Sets the file-pointer offset, measured from the beginning of this file, at which the next read
or write occurs.
void setLength(long newLength)
Sets the length of this file.
int skipBytes(int n)
Attempts to skip over n bytes of input discarding the skipped bytes.
void write(byte[] b)
Writes b.length bytes from the specified byte array to this file, starting at the current file
pointer.
void write(byte[] b, int off, int len)
Writes len bytes from the specified byte array starting at offset off to this file.
void write(int b)
Writes the specified byte to this file.
void writeBoolean(boolean v)
Writes a boolean to the file as a one-byte value.
void writeByte(int v)
Writes a byte to the file as a one-byte value.
void writeBytes(String s)
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 42 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Writes the string to the file as a sequence of bytes.
void writeChar(int v)
Writes a char to the file as a two-byte value, high byte first.
void writeChars(String s)
Writes a string to the file as a sequence of characters.
void writeDouble(double v)
Converts the double argument to a long using the doubleToLongBits method in class
Double, and then writes that long value to the file as an eight-byte quantity, high byte first.
void writeFloat(float v)
Converts the float argument to an int using the floatToIntBits method in class Float, and
then writes that int value to the file as a four-byte quantity, high byte first.
void writeInt(int v)
Writes an int to the file as four bytes, high byte first.
void writeLong(long v)
Writes a long to the file as eight bytes, high byte first.
void writeShort(int v)
Writes a short to the file as two bytes, high byte first.
void writeUTF(String str)
Writes a string to the file using UTF-8 encoding in a machine-independent manner.
Bearbeiten Sie die Case Studies und lösen Sie die Übungsaufgeben zu diesem Kapitel!
(Siehe Unterlagen hierzu auf dem Materialserver.)
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 43 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
5. Streams: (u.U. ADRELI_CON)
Was Sie lernen:
für welche Zwecke die verschiedenen Stream-Klassen eingesetzt werden
was Byte-Streams (Binär-Streams) und Charakter-Streams (Zeichen-Streams)
unterscheidet
was gepufferte E/A bedeutet
Voraussetzungen:
Kenntnisse der Klasse File
Arbeiten mit dem Exceptionhandling
5.1 Grundlagen
Ein Datenstrom kann aus beliebiger Quelle stammen (engl. Source, Spring); siehe oben: Datei, Netz, …
Ein Datenstrom kann in beliebige Senke (engl. Sink) gehen: dies kann wiederum seine ein Datei, ein Socket,
eine Pipe, ein String.
Ein Datenstrom ist aber immer unidirektional, geht also immer in eine Richtung
Fehlerbehandlung
Bei fast allen Operationen (z.B. beim Anlegen einer Datei, beim Öffnen, Lesen, Schreiben, …) können Fehler
auftreten. Die Fehlerursachen hierfür können vielfältig sein: die Rechte für eine Operation fehlen, betroffene Datei
existiert nicht, usw.
Solche Fehler lösen eine Ausnahme vom Typ IOException aus. Aus diesem Grund müssen alle Operationen
in einem try-catch-Block eingeschlossen sein.
Tritt zum Beispiel beim Schreiben ein Fehler auf, so kann im catch-Block eine entsprechende Meldung
ausgegeben und die Datei geschlossen werden.
Gepufferte Ein- und Ausgabe
Aus Gründen er Effizienz sind Ein- und Ausgaben meißt gepuffert. Das bedeuted, dass die Daten z.B. beim
Lesen zunächst in einen Zwischenspeicher (Puffer) eingelesen und gesammelt werden. Von dort werden die
Daten dann an ihr Ziel weitergeleitet, entweder
o wenn der Puffer voll ist oder
o wenn eine Methode zum Leeren des Puffers (flush()) aufgerufen wird oder
o wenn das File geschlossen wird.
So werden größere Mengen an Zeichen transportiert, was wesentlich effizienter ist als der direkte
Einzelzeichenzugriff auf die vergleichsweise langsamen peripheren Ein-/Ausgabegeräte.
Auch die Ein-/Ausgabe über die Standard-Geräte ist gepuffert. Dies ist daran erkennbar, dass erst mit dem
Betätigen der RETURN-Taste die Daten gelesen werden. (Siehe oben, Kapitel „Standard-Ein-/Ausgabe.)
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 44 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Die Hauptklassen von java.io:
Object
OutputStream
InputStream
Writer
Reader
File
RA-File
...
Byte-Stream
Binäerdaten Lesen/Schreiben
FlterOutputStream
Character-Streams
Zeichendaten Lesen/Schreiben
ObjectStreamClass
StreamTokenizer
Siehe oben println()
PrintStream
FileDescriptor
Siehe oben read()
In Java werden generelle zwei Streamtypen unterschieden: Character Streams (zeichenartige Daten (Unicode Text))
und Byte Streams (binäre Daten, ASCII Daten). Wie der Name schon andeutet, sind Byte Streams auf die
Verarbeitung von beliebigen Byte-artigen Informationseinheiten -- und damit acht Bit große Einheiten -- beschränkt.
Damit lassen sich ASCII-Daten verarbeiten, die historisch begründet, in sehr großer Anzahl vorliegen. Auch JavaQuellcode ist ASCII-Code.
Mit dem JDK v1.1 zusätzlich das Konzept der Character Streams eingeführt, die generell 16 Bit lange UNICODEZeichen (Character) bereitstellen.
5.2 Überblick (UNICODE-) Character-Stream-Klassen (Zeichendaten
(Text) schreiben und Lesen)
CharacterStreams verwenden den 16-Bit-Unicode und sind daher in Java für die Arbeit mit Strings
und Zeichentypen besser geeignet als Byte-Streams.
Abstrakte Klassen
Writer
Bridge-/Brücken-Klassen
BufferedWriter
CharArrayWriter
OutputStreamWriter
FilterWriter
PipedWriter
StringWriter
PrintWriter
FileWriter
Reader
BufferedReader
CharArrayReader
LineNumberR.
InputStreamReader
FilterReader
FileReader
PushBackR.
PipedReader
StringReader
Wichtige Klassen - Writer-Ableitungen:
(Character-)
Klassen
BufferedWriter
CharArrayWriter
OutputStreamWriter
Was die Klasse leistet
Gepuffertes Schreiben von Chars/Char-Arrays/Char-Strings. Zeilenvorschub wird
transparent plattformabhängig umgesetzt
Zeichen werden in ein Char-Array geschrieben, das Array wächst automatisch
Brücke zwischen Character- und Byte-Strömen; Unicode-Zeichen werden
automatisch während des Schreibens in die entsprechende ASCII/Byte-Repräsentation
umgesetzt. Abstrakte Klasse (für FileWriter).
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 45 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
FilterWriter
PipedWriter
StringWriter
PrintWriter
Abstrakte Basisklasse der verschiedenen Implementierungen zum gefilterten Schreiben
Ausgabestrom, geschrieben wird in eine Character-Pipe
Ausgabestrom, der in einen String schreibt
Ausgabestrom, der formatierte Objektrepräsentationen schreibt. Methoden dieser Klasse
verursachen keine E/A-Ausnahmeereignisse
HINWEIS: auf PrintWriter wird typ. mit den Methoden .print(), .println() geschrieben,
während auf die anderen Steam-Objekt typ. mit .write() geschreiben wird.
Wichtige Klassen - Reader-Ableitungen:
(Character-)
Klasse
BufferedReader
CharArrayReader
InputStreamReader
FilterReader
PipedReader
StringReader
Was die Klasse leistet
Liest Textinhalte gepuffert aus einem Character-Eingabestrom
Einlesen von Unicode-Zeichen aus Character-Puffer
Brücke zwischen Character- und Byte-Strömen; ASCII/Byte-Zeichen werden
automatisch während des Lesens in die entsprechende Character-Repräsentation
umgesetzt.
Abstrakte Klasse (für FileReader).
Abstrakte Basisklasse verschiedener filternder Eingabeströme. Konkrete
Implementierungen werden durch die verschiedenen Subklassen zur Verfügung gestellt
Unicode-Character Eingabestrom als Lese-Ende einer Character-Pipe
Unicode-Character Eingabestrom, der aus einem String liest
5.3 Character–Stream-Methoden:
5.3.1
Ausgabe-Streams
Generelle Methoden von java.io.Writer:
void close()
void flush()
write(int)
write(char[])
write(char[]
cbuf, int off, int
len)
write(String)
write(String str,
int off, int len)
5.3.2
Schließt Ausgabestrom und gibt durch ihn belegte Systemressourcen frei
Leert Ausgabestrom und schreibt alle Pufferbereiche
Schreibt ein UNICODE-Zeichen
Schreibt Charracter-Array
Schreibt Teil der Länge len eines Character-Arrays beginnend ab Position off
Schreibt String
Schreibt Teil der Länge len eines Strings beginnend ab Position off
Eingabe-Streams
Generelle Methoden von java.io.Reader:
void close()
void mark(int r)
Schließt Ausgabestrom und gibt durch ihn belegte Systemressourcen frei
Markiert gegenwärtige Position des Eingabezeigers. Ein nachfolgender
Aufruf von reset versucht den Positionszeiger auf die Markierte Stelle
zurückzusetzen, falls die aktuelle Position nicht weiter als readAheadLimit Zeichen
entfernt liegt
boolean
markSupported()
Gibt Auskunft darüber, ob der Eingabestrom die Positonsmarkierung, und
das Rücksetzen darauf, unterstützt
int read()
zur Extraktion(=
EOF
int read(char[] cbuf)
zur Extraktion
Lesen)
genau eines Zeichens (16 Bit Character), -1 bei
(= Lesen) einer Sequenz, beginnend ab der aktuellen
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 46 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Stromzeigerpositon
int read(char[]
cbuf, int off, int
len)
boolean ready()
reset()
long skip(long n)
zur Lesen einer Sequenz der Länge len oder weniger von Zeichen, und Ablage in
Array beginnend ab der Position off
Lesetest. Liefert dieser Aufruf true zurück, so liegen weitere Eingaben vor, die
durch ein folgendes read() gelesen werden können
Versucht den Positionszeiger auf die durch die letzte gesetzte Markierung
bezeichnete Stelle zurückzurücken
Versucht n Zeichenpositionen zu überspringen. Die Anzahl der
tatsächlich übersprungenen Positionen wird zurückgeliefert
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 47 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
5.4 Beispiele/Anwendungen zu Character–Streams:
Beispiel 1: Anwendung Klassen FileWriter und FileReader
import java.io.*;
public class FileWriterTest
{
public static void main(String[] args)
{
String s = "Das ist eine Testausgabe\r\neines dreizeiligen Textes\r\n";
// Ausgabe in Datei
try
{
FileWriter fw = new FileWriter("zzz_testFileWriter.txt");
// System.out.println();
fw.write(s);
fw.close();
}
catch(IOException io)
{
System.out.println(io.getMessage());
}
// Einlesen und anzeigen
// String lese = "";
StringBuffer lese = new StringBuffer();
int x = 0;
try
{
FileReader fr = new FileReader("zzz_testFileWriter.txt");
while((x = fr.read()) != -1) // -1 meldet EOF, andernfalls das Zeichen
// lese += (char)x;
lese.append((char)x);
fr.close();
}
catch(IOException io)
{
System.out.println(io.getMessage());
}
System.out.println(lese);
}
}
Beispiel 2: Anwendung Klassen BufferedWriter und BufferedReader
import uebungen.util.*;
import java.io.*;
public class BufferedWriterTest
{
public static void main(String[] args)
{
char[] cf = {'0','0','0','0','0','0','0','0','0','0'};
try
{
BufferedWriter bw =new BufferedWriter(new FileWriter("zzz_test.txt"));
for(int i = 0; i < 750; i++)
{
cf[i%10]++;
bw.write(cf);
bw.newLine();
}
bw.close();
}
catch(IOException io)
{
System.out.println(io.getMessage());
}
String s;
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 48 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
BufferedReader br = null;
try
{
br = new BufferedReader(new FileReader("zzz_test.txt"));
try
{
while((s = br.readLine()) != null)
{
System.out.println(s);
}
}
finally
{
if(br != null)
br.close();
}
}
catch(IOException io)
{
System.out.println(io.getMessage());
}
}
}
Beispiel 3: Anwendung Klasse PrintWriter
import java.io.*;
public class PrintWriterTest
{
public static void main(String[] args)
{
try
{
// Ausgabe ins File
PrintWriter pw= new PrintWriter(new FileWriter("zzz_test.txt"), true);
// true = autoFlush
// Ausgabe auf Konsole
PrintWriter pw2 = new PrintWriter(System.out, true);
// true = automatic flushing
pw.println("Berechnung des Flächeninhalts für Kreise");
pw2.println("Berechnung des Flächeninhalts für Kreise");
for(int r = 1; r <= 100; r++)
{
pw.print("Radius "+r+": "); // auf PrintWriter-Objekte kann mit .print()
pw2.print("Radius "+r+": "); // und .println() geschrieben werden
pw.println(3.14 * r * r);
// während auf andere “Schreib-“Streams mit
pw2.println(3.14 * r * r);
// write()-Methoden geschrieben wird
}
pw.close();
pw2.close();
}
catch(IOException io)
{
System.out.println(io.getMessage());
}
}
}
Beispiel 4: Anwendung Klasse StringWriter und StringReader
import java.io.*;
public class StringReaderWriterTest
{
public static void main(String[] args)
{
String s = "Java macht Spass ";
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 49 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
// arbeiten mit StringWriter
System.out.println("Verwendung von StringWriter:");
String s1 = s;
StringWriter sw = new StringWriter();
int l = s1.length();
for(int i = 0; i < l; i++)
{
s1 = s1.substring(0, s1.length()-1);
sw.write(s1 + "\n");
}
System.out.println(sw.toString());
// arbeiten mit StringReader
System.out.println("Verwendung von StringReader:");
StringReader sr = new StringReader(s);
int z;
try
{
while((z = sr.read()) != -1)
System.out.println((char)z);
}
catch(IOException _uh)
{
}
}
}
Beispiel 5: Ausgabe auf Standard Out mit PrintLn
import java.io.FileDescriptor;
import java.io.FileWriter;
import java.io.IOException;
public class PrintLn
{
public static void main(String[] args) {
FileWriter fw = null;
try {
fw = new FileWriter(FileDescriptor.out);
for (int i=0; i < args.length; i++)
fw.write (args[i]+" ");
} catch (IOException ioe) {
System.out.println("cannot open stdout!");
} finally {
try {
fw.close();
} catch (Exception e) {
//ignore it
} //catch
} //finally
} //main()
} //class PrintLn
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 50 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
5.5 Überblick Byte-Stream-Klassen (auch: binäres Schreiben u. Lesen)
ByteStreams verwenden für die Ein- und Ausgabe von Datenströmen 1-Byte Größen.
Da externe Datensenken und Datenquellen (Dateien) byteweise arbeiten, sind grundsätzlich Bytestreams nötig,
um in eine externe Datensenke zu schreiben und aus einer externen Datenquelle zu lesen.
Deshalb gibt es in den CharacterStream-Klasssen auch keine Sink- und Spring-Stream-Klassen, die auf einer
Datei als externe Datensenke/quelle arbeiten.
Wichtige Klassen - OutputStream-Ableitungen:
Klasse (Byte-)
FileOutputStream
PipedOutputStream
FilterOutputStream
ByteArrayOutputStream
ObjectOutputStream
…
Was die Klasse leistet
Schreibt Byte-artige Daten in Dateien oder durch Dateideskriptoren bezeichnete Ziele
Hochsprachliches Äquivallent der Betriebssystem-Pipes (schreibend)
Verleiht Ausgabestömen die Möglichkeit Ausgabedaten zu verändern. Einige konkrete
Implementierungen sind durch die Subklassen gegeben
Ausgabestrom, der in einen Byte-Array schreibt
Schreibt Primitivtypen und Objekte. Ausschließlich Objekte, welche die java.ioSerializable-Schnittstelle implementieren können über diesen Mechanismus
persistent (z.B. in eine File) geschrieben werden (Siehe API-Doku-Bsp.)
…
Wichtige Klassen - InputStream-Ableitungen:
Klasse (Byte-)
FileInputStream
PipedInputStream
FilterInputStream
ByteArrayInputStream
StringBufferInputStream
SequenceInputStream
ObjectInputStream
…
Was die Klasse leistet
Byteweises lesen einer Datei
Hochsprachliches Äquivallent den Betriebssystem Pipes
Gemeinsam mit anderen Eingabeströmen eingesetzt. Einsatzgebiet:
Nachbearbeitung roher Eingaben. Die entsprechenden Subklassen stellen konkrete
Implementierungen häufiger Einsatzfälle dar
Lesen aus einem Byte Array
deprecated!! Lesen aus String. Achtung: Diese Klasse geht von der (i. A. falschen)
Entsprechung zwischen Bytes und Charactern aus. StringReader liefert für
denselben Anwendungsfalle eine korrekte Implementierung
Konkatenation von Eingebströmen; nach vollständigem Lesen des i-ten
Eingebstroms wird der i+1-te angesprochen
Lesen vollständig serialisierter Objekte, die die Schnittstellen java.io.Serializable
oder java.io.Externalizable unterstützten
…
5.6 Byte–Stream-Methoden:
Generelle Methoden von java.io.OutputStream
void close()
void flush()
void write(byte[]
b)
void write(byte[]
b,int off,int len)
void write(int)
Schließt Ausgabestrom und gibt durch ihn belegte Systemressourcen frei
Leert Ausgabestrom und schreibt alle Pufferbereiche
Schreibt Byte-Array in Ausgabestrom
Schreibt len Bytes eines Byte-Arrays ab Position off
Schreibt Bytewert in Ausgabestrom
Generelle Methoden von java.io.InputStream
int available()
Liefert die Anzahl Bytes die an der Eingabeschnittstelle zur Verfügung stehen. Diese
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 51 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
void close()
void mark(int)
boolean
markSupported()
int read()
int read(byte[])
int read(byte []
b,int off,int len)
void reset()
long skip(long n)
Anzahl kann ohne Blockierung des Aufrufers gelesen werden
Schließt Eingabestrom unter Freigabe der belegten Systemressourcen
Markiert die gegenwärtige Position des Eingabezeigers im Eingabestrom; ein folgender
Aufruf von reset setzt den Eingabezeiger wieder an die markierte Position zurück
Gibt Auskunft darüber, ob der Eingabestrom die Positonsmarkierung, und das
Rücksetzen darauf, unterstützt
Ließt den nächsten Bytewert (>0 und <256) aus dem Eingabestrom. Ist das Ende des
Eingabestromes erreicht, wird -1 retourniert (kein Ausnahmeereignis!
Ließt eine Bytesequenz aus dem Eingabestrom, und legt sie im übergebenen Array ab.
Die Anzahl der gelesenen Zeichen, bzw. -1 beim Erreichen den Eingabeendes (EOF),
wird zurückgegeben
Ließt Bytesequenz der Länge len und speichert sie ab Position off im übergebenen Array
Setzt den Stromzeiger auf die Position der letzten vorhergehenden Markierung zurück,
falls eine solche existiert
Versucht den Stromzeiger um n Bytepositionen vorzurücken. Die Anzahl der tatsächlich
übersprungenen Bytes wird zurückgegeben
Nähere/weitere Informationen zu den vorgestellten Klassen, wie etwa Konstruktoraufbau und
Methodenimplementationen lassen sich der aktuellen JAVA API-Dokumentation entnehmen.
Die hauptsächliche Verwendung von Streams liegt vor allem zur Erstellen von Protokolldaten, also Logfiles, der
Portierung von bspw. Datenbank-Export-Inhalten. Streams werden auch typischerweise überall dort verwendet,
wo die Datenarchivierung in Datenbanken keinen Sinn macht, weil bspw. die typischen DB-Operationen keine
Anwendung finden. Dies ist häufig im technischen Bereich der Fall: bspw. liegen Anweisungen für CNCMaschinen in Files vor.
Beispiel 6: Ein-/Ausgabe eines Objekts (Binäre
Ein-/Ausgabe)
// Das Objekt aList wird in eine Datei geschrieben,
// dann wird das Objekt aus der Datei geholt und
// dem Objekt bList zugewiesen
import
import
import
import
import
import
import
java.io.FileInputStream;
java.io.FileOutputStream;
java.io.IOException;
java.io.ObjectInputStream;
java.io.ObjectOutputStream;
java.io.Serializable;
java.util.ArrayList;
public class ObjectStreams_2
// <<--
{
public static ArrayList<Person>aList= new ArrayList<Person>();
public static ArrayList<Person>bList = new ArrayList<Person>();
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
public static void main (String [] args){
Person pers_1= new Person("Mueller","Susi");
Person pers_2=new Person("Beta","Rudolf");
Person pers_3=new Person("Gamma","Natascha");
// Objekt aList wird gefüllt
aList.add(pers_3);
aList.add(pers_2);
aList.add(pers_1);
//Ausgeben auf Stream
//sichern();
sichern(aList);
// Das Objekt aList ausgeben in Datei
//Einlesen vom Stream == das File auslesen
auslesen();
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 52 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
//...und ausgeben auf die Konsole
// entweder
for (Person buffer:bList)
{
// Objektinhalt auf Console ausgeben
System.out.println(buffer.vorname);
System.out.println(buffer.name);
}
// ...oder nicht ganz so kurz...
/*
Ob_St buffer;
for (int i=0; i<3;i++)
// nicht so gut wie oben
{
buffer = bList.get(i);
System.out.println(buffer.vorname);
System.out.println(buffer.name);
}
*/
System.out.println("Ende Test...");
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//public static void sichern()
public static void sichern(ArrayList<Person> aList)
{
try{
FileOutputStream fos = new FileOutputStream("Datei.dat");
ObjectOutputStream oos = new ObjectOutputStream(fos);
// wir haben nur ein Objekt aList
oos.writeObject(aList); // und schreiben dieses Objekt aur
// den Stream
oos.close();
}
catch(IOException e){
}
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
@SuppressWarnings("unchecked")//jai, nicht unbedingt notwendig;
// nur Warning wird unterdrückt
public static void auslesen()
{
try{
FileInputStream fis=new FileInputStream("Datei.dat");
//Binärfile
ObjectInputStream ois= new ObjectInputStream(fis);
try{
bList = (ArrayList<Person>)ois.readObject();
// cast von Object auf ArrayList<Ob_St> passt;
// links und rechts gleicher Typ
// Objekt wird geholt und bList zugewiesen
} catch(ClassNotFoundException e){
System.exit(1); // jai
}
ois.close();
} catch(IOException e){
}
} // end of auslesen()
} // end of class ObjectStreams
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
class Person implements Serializable {
String name;
String vorname;
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 53 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
static final long serialVersionUID = 0L;
public Person(String name,String vorname)
{
this.name=name;
this.vorname=vorname;
}
} // end of class Person
// Nicht optimal ist die Verteilung der Methoden!(„Quick&Dirty“-Testbett)
// für Objekt-Ein-/Ausgabe.
Beispiel 7: Nochmals Eingabe von der Konsole („quick and easy“ per
API-Docu
Siehe CaseStudy
Scanner Klasse – Siehe dazu
PROMOD1/kap10b_io_datei_ScannerPrintWriter
import java.util.*;
import java.io.*;
public class FileIO {
public static void main(String[] args) throws
Exception{
Scanner sc = new Scanner(System.in);
PrintWriter pw = new
PrintWriter("personen.txt");
String myLine;
for(int i = 0; i < 4; i++)
{
System.out.print("Eingabe Zeile "+i+": ");
myLine = sc.nextLine();
pw.println(myLine);
// pw.flush();
}
pw.flush();
System.out.println("Ende der Eingabe.");
}
}
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 54 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Bearbeiten Sie die Case Studies und lösen Sie die Übungsaufgeben zu
diesem Kapitel! (Siehe Unterlagen hierzu auf dem Materialserver.)
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 55 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
6.
Multi-Threading (ADRELI_THREADS++) hier weiter 21.10.2015
Was Sie lernen:
was Threads sind und wie sie aufgebaut sind
die Eigenschaften und Methoden der Klasse Thread
was unter Multi-Threading und Parallelität zu verstehen ist
wie Threads miteinander kommunizieren können
Voraussetzungen:
Arbeiten mit Streams (ggf. für die Thread-Kommunikation über Pipes)
6.1 Grundlagen
Ein Prozess verfügt über einen eigenen Adressraum und beinhaltet ein in „Ausführung“ befindliches
Programm. Ein gutes OS garantiert (mit Unterstützung durch die Hardware) die strenge Trennung der
Prozessadressräume.
Ein Prozess kann aus mehreren „Teilprozessen“ bestehen, die als Threads bezeichnet werden.
Ein Thread ist also eine Untereinheit der OS-Verwaltungseinheit Prozeß (Task). Moderne OS sind in der Lage,
mehrere Programme gleichzeitig auszuführen (Multi-Tasking / Multi-Programming / Multi-Processing /
Concurrency/Concurrent Programming).
Sind mehrere Threads gleichzeitig aktiv, so spricht man von Multi-Threading.
Alle Threads arbeiten im selben Prozessadreßraum.
Jeder Thread besitzt einen eigenen Stack.<
Jeder Thread bestitzt einen eigenen Programmzähler (PC, Program Counter).
Jeder Thread kann weitere Threads erzeugen (keine privillegierte Operation).
Threads werden häufig auch als leichtgewichtige Prozesse
< (lightweighted processes) bezeichnet.
Threads können leicht auf gemeinsame Objekte und Variablen zugreifen. Hierbei besteht aber die Gefahr von
inkonsistenten Objektzuständen. Als Lösung steht in Java die Synchronisation zur Verfügung. Allerdings können
auch vollkommen voneinander getrennte Threads existieren, die sich keine gemeinsamen Objekte teilen.
Anwendungsmöglichkeiten:
Threads werden hauptsächlich angewandet um Parallelität bzw. Verteilung von Abläufen zu realisieren:
o z.B. rechenintensive Algorithmen laufen in einem Hintergrund-Thread während die GUI in einem
anderen Thread (im Vordergrund) läuft.
o Z.B. mit einem Thread interagiert die Anwendung mit dem User („View“) mit einem anderen Thread
wird die Kommunikation mit der Datenbasis („Modell“) abgewickelt. Hauptthread = „Controller“
macht die eigentliche Verarbeitung/Haupaufgabe („MVC-Ansatz“).
Konstruktive Vorteile bietet die Zerlegung einer Gesamtapplikation in einen/mehrere Server-Thread/s und in
einen/mehrere Client-Threads.
Anmerkungen:
Das Hauptprogramm läuft in einem Thread (Hauptthread, Steuerungsthread). Das Hauptprogramm wird erst
beendet, wenn alle darin enthaltenen Threads beendet sind. Ausnahmen bilden sog. "Daemons". Dies sind
Hintergrundthreads, auf die der Hauptthread nicht wartet. Wird der Hauptthread und alle User-Threads
beendet, so werden dann auch die Daemons beendet, weil nichts mehr existiert, was auf das Ergebnis eines
Hintergrundthreads wartet.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 56 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Wird vom eingesetzten Betriebssystem die Thread-Verwaltung nicht unterstützt (kein natives Threading), so
übernimmt die Virtual Machine die Threadverwaltung (Simulation): user-level-Threads mit Hilfe der POSIXThreadbibliothek. Dies ist i.a.R. bei Unix/Linux-Systemen der Fall.
Threads sind prioritätenbehaftet und einem Scheduling ausgesetzt. Das Schedulingverfahren ist abhängig
vom Betriebssystem und von der Thread-Implementierung (siehe unten „Thread-Implementierungen“)
Exkurs: Thread-Implementierungen im Java-Umfeld:
User-Level Threads:
o Werden innerhalb eines Prozesses durch Verwendung einer Thread-Bibliothek erzeugt.
o Vorteil: Erzeugung erfolgt ohne Intervention des Kernels -> höhere Performance.
o Nachteil: u.U. unfaires Scheduling (weil der Prozess eine Zeitscheibe zugeordnet bekommt,
unabhängig von der Anzahl der in ihm laufenden Threads). Wenn der Prozess über mehrere KernelLevel Treads verfügt, dann kann einem Prozess proportional zur Anzahl seiner Kernel-Level Threads
eine größere Zeitscheibe zugeordnet werden.
o Bei einem System-Call, der einen Prozeß blockiert, werden mit dem Prozeß alle User-Level-Threads
blockiert.
o Für User-Level Treads kann auf der Anwendungsebene ein eigener Scheduling-Algorithmus
implementiert werden (was bei Kernel-Level Threads nicht geht, da sie ausschliesslich vom Kernel
geschedult werden).
Kernel-Level Threads:
o Werden vom Kernel erzeugt (Kontetxt Switch) (siehe oben).
o Vorteil: der Kernel kennt den Thread. Thread wird beim Scheduling berücksichtigt (siehe oben).
o Vorteil: bei Mehrprozessorsystemen (Mehrkernsystemen) werden Kernel-Threads i.d.R. auf einzelne
Prozessoren (Prozessorkerne) verteilt.
Threads auf Hardwareebene:
o Vorteil: Kontext-Switch sehr schnell.
o Realisiert z.B. im Transputer mit Helios als Betriebssystem.
o Intel Hyperthreading-Technologie: mehrere Threads können parallel von einem Prozessor ausgeführt
werden.
èJava favorisiert "kooperatives Multithreading".
- d.h. der Programmierer gibt die CPU (im Java-Programm) freiwillig ab.
- nicht geeignet bei Real-Time-Multithreading für kritische Anwendungsprozesse, die deterministisches
Scheduling verlangen
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 57 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
6.2 Klasse Thread und Interface Runnable
Threads werden in Java durch die Klasse Thread und/oder das Interface Runnable implementiert.
In beiden Fällen wird der Thread-Body, also der
parallel auszuführende Code, in Form der
zu überlagernden Methode run() zur Verfügung gestellt. Diese Methode muss in der Klasse, die als
Thread laufen soll überschrieben werden.
Jeder Thread hat eine Priorität, welche die Reihenfolge der Abarbeitung der Threads innerhalb eines Prozesses
beeinflusst. Siehe unten Kapitel „Prioritäten und Gruppen von Threads“
Beispiel 1: Erster Thread – alle berechnen Fakultäten
Eine Aufgabe wird mehrfach parallel ausgeführt:
public class AufrufErsterThread
{
public static void main(String[] args)
{
ErsterThread t1 = new ErsterThread(1, 12);
ErsterThread t2 = new ErsterThread(2, 10);
ErsterThread t3 = new ErsterThread(3, 7);
t1.start (); // asynchroner Start von run() !! Parallelität
t2.run();
// synchroner Start; Anomalie !! ein Nacheinander
t3.start();
while (t1.isAlive() || t2.isAlive() || t3.isAlive() )
{
System.out.println("Hauptprogramm ist aktiv");
}
}
}
class ErsterThread extends Thread
{
int thread_nr, a;
long fak = 1;
// Daten für Threads sichtbar
ErsterThread(int nummer, int fakultaet)
{
thread_nr = nummer;
a = fakultaet;
}
public synchronized void run() // Das ist der Thread-Code
{
for (int x = 1; x <= a; x++)
{
fak *= x;
System.out.println("Thread " + thread_nr + ":
" + x + "! = " + fak );
Thread.currentThread().yield();
// “unterbricht” den aktuellen Thread
}
// Siehe unten “Zustände von Threads”
}
// -> kooperatives Multithreading
}
!! ein Nacheinander
!! ein Nacheinander
Hinweis: Wenn Objekte instanziiert werden, dann sind die Instanzvariablen jeweils objektspezifisch –
nicht aber der Code! Der Code für die Methode run() ist zunächst nur einmal existent im Hauptspeicher:
Wird die Methode run() traditionell gestartet, so reicht dieses einmalige Vorhandensein für die sequentielle
Abarbeitung der Methoden im Hauptthread.
Dagegen sorgt der Start mit start() für das Einrichten eines eigenen Threads mit jeweils eigenem Programm
Counter PC und eigenem Stack, so wird der Code wiedereintrittsfähig („reentrant“).
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 58 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Das Schlüsselwort „synchronized“ sorgt dafür, dass nur jeweils ein Thread durch seinen Code läuft
und auf die gemeinsamen Daten (lokale Variablen) zugreift.
Siehe auch Kapitel „Synchronisation“ weiter unten.
Nach dem Start der Threads mit der Methode start() laufen die Threads asynchron.
Beispiel 2 – ThreadDemo:
Mehrere Aufgaben werden parallel ausgeführt / Threads erledigen
unterschiedliche Aufgaben:
class MyThread1 extends Thread
{
public void run()
{
int i = 0;
while (i <= 50)
{
System.out.println(i++);
}
}
}
class MyThread2 extends Thread
{
public void run()
{
int i = 0;
while (i <= 50)
{
System.out.println("zweiter Thread");
i++;
}
}
}
Typischerweise ist
in den ThreadAbleitungen nur
Code
public class ThreadDemo
{
public static void main(String[] args)
{
MyThread1 t1 = new MyThread1();
MyThread2 t2 = new MyThread2();
t1.start();
t2.start();
}
}
Beispiel 3 – Thread mit sleep():
Hinweis: Experimentieren Sie mit unterschiedlichen sleep()-Zeiten.
class MyThread1_ extends Thread
{
public void run()
{
int i = 0;
try
{
}
while (i <= 50)
{
System.out.println(i++);
this.sleep(100);
// kooperatives Multithreading
}
// höher Prior wie MyThread2_
catch (InterruptedException ie)
{
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 59 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
System.out.println(ie.getMessage());
}
}
}
class MyThread2_ extends Thread
{
public void run()
{
int i = 0;
try
{
while (i <= 50)
{
System.out.println("zweiter Thread");
this.sleep(100);
// kooperatives Multithreading
// niedriger Prior wie MyThread2_
i++;
}
}
catch (InterruptedException ie)
{
System.out.println(ie.getMessage());
}
}
}
public class ThreadSleep
{
public static void main(String[] args) throws InterruptedException
{
MyThread1_ t1 = new MyThread1_();
MyThread2_ t2 = new MyThread2_();
t1.start();
t2.start();
}
}
Beispiel 4 – Runnable:
Es gibt noch eine weitere Möglichkeit Threads zu erschaffen, wie wir bereits wissen. Anstatt von der Klasse
"Thread" zu erben kann man auch das Interface Runnable implementieren.
Dies ist immer dann sinnvoll (und notwendig), wenn eine Klasse bereits von einer anderen erbt und somit nicht mehr von der Klasse Thread erben kann (Java unterstützt ja keine Mehrfachvererbung). Das
Interface Runnable enthält nur eine einzige Methoden-Deklaration, nämlich die der Methode run().
// class Bzzz extends Parent implements Runnable //Bsp. wäre realistischer,
// aber länger
class Bzzz implements Runnable
{
public void run()
{
int i = 0;
while (i<100)
{
System.out.println(i++);
}
}
}
public class ThreadRunnable
{
public static void main(String args[])
{
Bzzz b = new Bzzz();
// 1. Schritt: Objekt einer Klasse, die
// Runnable implementiert
Thread t = new Thread(b); // 2. Schritt: Objekt der o.g. Klasse
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 60 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
// wird dem
// Konstruktor der Klasse Thread als
//Parameter
// übergeben. (“Copy Initializer“)
t.start();
// 3. Schritt: Starten des Threads
while(t.isAlive()){}
// Hauptthread ist “busy waiting”=Leerlauf;
//besser t.join();
System.out.println("Thread ist beendet");
System.out.println("Bitte warten Sie noch 8 Sekunden...");
try { Thread.sleep(8000); }
catch (InterruptedException e){}
System.out.println("Danke...");
}
}
Anmerkung zum Threading/zur Parallelisierung: Weiter oben haben wir schon gesehen, wenn Threads durch
die Methode start() aktiviert werden, dann haben wir es zunächst mit einer asynchronen Parallelität zu
tun. Das heißt, die Threads laufen parallel zueinander („concurrent“) ab, ohne dass sie per se voneinander
abhängig sind – soweit so gut.
Dies bedeuted konkret aber auch, dass selbst inhaltlich gleichartige Threads sich u. U. in ihrem zeitlichen
Ablaufverhalten unterscheiden.
Warum?
Antwort:
Bedenken Sie, das in multithreadingfähigen Umgebungen, wie z.B. den Betriebssystem Windows (XP, Vista oder
Windows 7, 8) oder Linux, MAC OS X, der Gesamtmaschinenzustand sich während des Ablaufs eines Thread
beträchtlich unterscheiden kann vom Zustand während des Ablaufs eines anderen Threads!!!!!!!!!!!!!!
Auf einem Laptop laufen durchaus über 100 Prozesse mit über 1000 Threads (siehe bspw. „ProcessExplorer“ ->
„View“ -> „System Information“; oder WindowsTaskmanager -> Prozesse -> Ansicht -> Treads). Je nachdem, was
diese konkurrierenden Prozesse und Threads im Augenblick machen, beeinflusst dies auch das Zeitverhalten der
Threads aus den Beispielprogrammen.
Asynchron/Synchron: In vielen (Anwendungs-)Fällen ist die asynchrone Prallelität ausreichend: Threads
werden gestarted (mit der Methode start()) und tun dann ihre Arbeit, bis sie terminieren oder zum
terminieren aufgefordert werden (z.B. mit der Methode interrupt()). In anderen Anwendungsfällen reicht
diese asynchrone Parallelität nicht aus: die Threads müssen in einer aufeinander abgestimmten Art und Weise
zusammenarbeiten. Dazu gehört, dass sie sich zeitlich oder über Ereignisse oder auch über einen
Datenaustausch miteinander „synchronisieren“.
Die „zeitgesteuerte Synchronisation“ kann z.B. mit der static-void-Methode sleep() bewerkstelligt
werden.
Die „ereignisgesteuerte Synchronisation“ kann z.B. über die Methoden void wait(), void
notify(), join(), static void yield(), void interrupt() erfolgen
Die „kommunikationsgesteuerte Synchronisation“ kann z.B. über einen Datenaustausch via Pipes und
1
Sockets erfolgen. Pipes sind unter Umständen selbstsynchronisierend, was sich auch synchronisierend
auf ihre Nutzer auswirkt. Mehr dazu siehe Kapitel „Datenaustausch zwischen Threads mit Pipes“: (siehe PipeAPI-Doku) “Whether or not a thread writing bytes to a pipe will block until another thread reads those bytes, or
some previously-written bytes, from the pipe is system-dependent and therefore unspecified. (Auf Windows,
Linux und Max OS X: selbstsynchronisierend) Many pipe implementations will buffer up to a certain
number of bytes between the sink and source channels, but such buffering should not be assumed.”
Siehe hierzu auch Kapitel 6.9 Weitere Synchronisationsmittel
1
Betriebssystemabhängig
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 61 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
6.3 Zustände von Threads:
Wie Prozesse haben auch Threads verschiedene Zustände, die sie einnehmen.
Diese Zustände können durch Methoden der Klasse Thread hervorgerufen, bzw. geändert werden. Das
folgende Bild gibt einen Überblick:
Prozessorzuteilung durch
Scheduling/Dispatcher
start()
new
ready to
run
Ende von run()
Oder:
freiwilliges Beenden, wenn
Interrupted-Flag gesetzt.
active
running
end
yield()
ERZEUGEN
notify()
notifyAll()
blocked
BEENDEN
-
Ressourcen fehlen (i/o)
sleep ()
wait()
join()
LAUFEN (isAlive() = true)
6.3.1
Threads unterbrechen
Why are Thread.stop(), Thread.suspend() and Thread.resume() deprecated? (Siehe API-Doku für Klassse
Thread)
Because it is inherently unsafe. Stopping a thread causes it to unlock all the monitors that it has locked. (The
monitors are unlocked as the ThreadDeath exception propagates up the stack.) If any of the objects previously
protected by these monitors were in an inconsistent state, other threads may now view these objects in an
inconsistent state. Such objects are said to be damaged. When threads operate on damaged objects, arbitrary
behavior can result. This behavior may be subtle and difficult to detect, or it may be pronounced. Unlike other
unchecked exceptions, ThreadDeath kills threads silently; thus, the user has no warning that his program may be
corrupted. The corruption can manifest itself at any time after the actual damage occurs, even hours or days
in the future.
What should I use instead of Thread.stop()?
Most uses of stop should be replaced by code that simply modifies some variable to indicate that the target thread
should stop running. The target thread should check this variable regularly, and return from its run method in an
orderly fashion if the variable indicates that it is to stop running. (This is the approach that JavaSoft's Tutorial has
always recommended.) To ensure prompt communication of the stop-request, the variable must be volatile (or
access to the variable must be synchronized).
Dies bedeutet, dass ein Thread nur mit der unten vorgestellten Methode interrupt() zur Beendigung aufgefordert
werden kann. Die Methode interrupt() setzt eine Interrupted-Flag. In der Methode run() wird am besten zyklisch
(in einer Schleife) abgefragt (das muss Programmierer/in selbst tun!!!!!), ob das Interrupted Flag gesetzt ist
und damit der Thread terminieren soll. (Passt zur Philosopie „kooperatives Multithreading“.)
6.3.2 Die Thread-Methoden yield(), sleep() und interrupt()
HINWEIS: Werden Ihre Threads nicht quasi-parallel abgearbeitet, sondern nacheinander, so basiert Ihre JavaImplementierung (betriebssystemabhängig!) möglicherweise nicht auf einem Time-Slice-Scheduler
(Zeitscheibenverfahren). Dieses Problem kann durch kooperatives Multitaskting gelöst werden: Threads müssen
angewiesen werden, ihre Ausführung nach einer gewissen Zeit zu unterbrechen („sich kooperativ zu verhalten“).
Dazu muss in einer Schleife eine yield() oder sleep()-Anweisung eingebaut werden. (Siehe hierzu auch das
sleep()-Beispiel weiter oben.) sleep() und yield() sind static-Funktionen.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 62 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
static void yield()
Der Thread gibt CPU freiwillig ab (und wird in den Zustand „ready-to-run“ versetzt bis ihn der Scheduler
wieder aktiviert). Ein anderer Thread bekommt Prozessorzeit zugeteilt.(Siehe Beispiel 1 – Erster Thread)
public static void sleep(long millis) throws InterruptedException
public static void sleep(long millis, int nanos) throws
InterruptedException
Der Thread wird für die angegebene Zeit unterbrochen.
Während der Ausführung der sleep()-Methode kann eine Exception vom Typ InterruptedException auftreten,
die in einem try-catch-Block abgefangen werden muss.
try
{
sleep(millis);
}
catch(InterruptedException ie)
Auch die main()-Methode kann sleep() verwenden, um die Programmausführung zu unterbrechen. Dazu ist es
nötig, dass die Klasse der main()-Methode von Tread erbt (Implementierung vom Interface Runnable reicht
nicht).
public void interrupt()
Setzt das interrupted-Flag eines Threads und fordert damit den Thread auf sich zu beenden. (Versetzte sich
ein Thread durch die Methoden sleep(), join() und wait() in den Zustand „blocked“, so kann dieser
Thread mit der Methode interupt() vorzeitig wieder geweckt werden; er wird dann in den Zustand „readyto-run“ versetzt. Damit werden also die blockierenden Methoden wie join() und sleep() beendet und die
Methode run() macht mit dem catch-Konstrukt InterruptedException hinter der blockierenden Methode
weiter.)
public static boolean isInterrupted()
Liefert den Wert des interrupted-Flags (verändert dieses nicht, im Gegensatz zur Methode
interrupted())
public static boolean interrupted()
Tests whether the current thread has been interrupted. The interrupted status of the thread is cleared by this
method. In other words, if this method were to be called twice in succession, the second call would return false
(unless the current thread were interrupted again, after the first call had cleared its interrupted status and before
the second call had examined it)
6.3.3 Die Thread-Methode join()
public final void join() throws InterruptedException
public final void join(long millis) throws InterruptedException
public final void join(long millis, int nanos) throws InterruptedException
Die parameterlose join()-Methode wartet bis der angegebene Thread beendet ist, Siehe Torso:
Class ManagerThread {
....
Thread Producer = new ProducerThread();
Thread Consumer = new ConsumerThread();
Producer.start();
Consumer.start();
Producer.join(); // Manager-Thread wartet, bis der Producer-Thread fertig ist
Consumer.join(); // Manager-Thread warten, bis der Consumer-Thread fertig ist
System.out.println(„Producer & Consumer: beide fertig.“)
Dadurch wird der Thread, der die Methode aufruft (im Beispiel oben der ManagerThread), in den Zustand
„blocked“ versetzt, bis der Thread, dessen join()-Methode aufgerufen wird (oben Consumer und Producer),
beendet ist.
o Wird die Methode join() eines bereits beendeten Threads aufgerufen, so wird der aufrufende
Thread nicht in den Zustand „blocked“ versetzt.
Werden ein oder zwei Parameter angegeben, so wartet der aufrufende Thread höchstens die angegebene Zeit,
bis er die Abarbeitung fortsetzt.
Die Methode join() muss gegen eine Exception vom Typ InterruptedException abgesichert werden. (->
Aufruf von interrupt() für den in join() wartenden Thread.)
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 63 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
6.3.4 Weitere Thread-Methoden (ggf. geerbt von class Object)
void wait()
void wait(long millis, int nanos)
Bewirkt, dass ein Thread wartet, bis entweder eine Benachrichtigung (notify()) eintrifft oder die angegebene
Zeit abgelaufen ist, oder die Methode interrupt() für den Thread aufgerufen wird. HINWEIS: Diese Methode
lässt sich nur aus einer synchronisierten Methode heraus aufrufen.
Die Methode löst eine IllegalMonitorStateException aus, wenn der aktuelle Thread nicht der Besitzer der
Objektsperre ist.
wait() ist von der Klasse Object geerbt.
(See: Case-Study threads_sync/synchronisation.java)
Aktiviert einen Thread, welcher duch den Aufruf von wait() in den Wartezustand versetzt wurde.
void notify()
static Thread currentThread()
Gibt eine Referenz auf den aktuell ausgeführten Thread zurück.
void destroy()
Zerstört den Thread ohne Aufräumarbeiten durchzurühren („Any monitors it has locked remain locked.“).
string getName()
Gibt den Namen des Threads zurück.
void setName(String name)
Umbenennung eines Threads: verändert den Thread-Namen in den als Parameter übergebenen Namen.
boolean isAlive()
Gibt true zurück, wenn der Thread läuft.
6.3.5
Thread-Konstruktoren
zu Details siehe API-Beschreibung “Thread“.
Constructor Summary
Thread()
Allocates a new Thread object.
Thread(Runnable target)
hiervon wird die run()-Methode aufgerufen
Allocates a new Thread object.
Thread(Runnable target, String name)
Allocates a new Thread object.
Thread(String name)
Allocates a new Thread object.
Thread(ThreadGroup group, Runnable target)
Allocates a new Thread object.
Thread(ThreadGroup group, Runnable target, String name)
Allocates a new Thread object so that it has target
as its run object, has the specified name
as its name, and belongs to the thread group referred to by group.
Thread(ThreadGroup group, Runnable target, String name, long stackSize)
Allocates a new Thread object so that it has target as its run object, has the specified name
as its name, belongs to the thread group referred to by group, and has the specified stack size.
Thread(ThreadGroup group, String name)
Allocates a new Thread object.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 64 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Beispiel 5 – TestTimer:
Case-Study-Aufgabe: Finden Sie raus , was Beispiel 5 macht! Siehe hierzu
auch case_study_06.txt.
public class TestTimer implements TimerListener
{
private Timer t1;
private Timer t2;
int i = 1;
public static void main(String[] args) // ----------------------------------{
TestTimer tt = new TestTimer(100);
}
public TestTimer(int t) // -------------------------------------------------{
t1 = new Timer(1, this);
t2 = new Timer(2, this);
t1.setDelay(t);
t2.setDelay(t);
// t1.setDaemon(true); // Siehe unten Kapitel Daemon-Threads
// t2.setDaemon(true);
t1.start();
t2.start();
System.out.println("2 Timer-Threads gestartet");
}
public void timeBreak(Timer t) // -----------------------------------------{
if (i <= 20)
{
System.out.println("Timer-Thread "+t.getTimerNr()+" TimeBreak
"+i++);
}
else
{
System.out.println("Timer-Thread "+t.getTimerNr()+" beendet");
t.stopThread();
}
}
}
public class Timer extends Thread
{
TimerListener tlistener;
int millis;
int nr;
boolean laeuft = true;
Timer(int n, TimerListener tl) // --------------------------------------------{
tlistener = tl;
nr = n;
}
public void run() // ---------------------------------------------------------{
while(laeuft)
{
try
{
sleep(millis);
tlistener.timeBreak(this);
}
catch(InterruptedException ie)
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 65 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
{
}
}
}
public void setDelay(int m) // -----------------------------------------------{
millis = m;
}
public int getTimerNr() // ---------------------------------------------------{
return nr;
}
public boolean stopThread() // -----------------------------------------------{
return laeuft = false;
}
}
interface TimerListener
{
public void timeBreak(Timer t);
}
6.4 Daemon-Threads
Es gibt zwei Arten von Threads: nämlich User-Threads und Daemon-Threads (= typw. „Dienstleister“). Der
Unterschied zwischen den beiden Thread-Arten besteht darin, dass
Daemen-Threads im „Hintergrund“ laufen (d.h. u.a. keine Verbindung zur Standard-E/A haben),
sich der Programmierer nicht darum kümmern muss, ob ein Daemon-Thread ordnungsgemäß beendet wird, da
Daemon-Threads automatisch gestoppt werden, wenn alle anderen User-Threads eines Prozesses
beendet sind. (Weil dann keine Clients mehr existieren, welche die Dienstleistungen der Daemons in Anspruch
nehmen könnten. Java-VM wird runtergefahren.)
Standardmäßig laufen alle erzeugten Threads als User-Threads.
Aus einem wird ein Daemon-Thread durch den Aufruf der Methode setDaemon(true). Dieser Aufruf muss
VOR dem Aufruf der start()-Methode für den entsprechenden Thread stehen.
void setDaemon(boolean on)
Kennzeichnet einen Thread als einen User-Thread (on == false) oder als einen Daemon-Thread (on ==
true).
boolean isDaemon()
Gibt true zurück, wenn aufrufender Thread ein Daemon ist; andernfalls false.
Beispiel – siehe oben Beispiel 5 TestTimer
Lassen Sie im Beispiel 5 die beiden Timer-Threads als Deamons laufen. Was beobachten Sie?
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 66 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
6.5 Prioritäten von Threads und zugehörige Thread-Methoden:
Jeder Java-Thread erbt seine Priorität von seinem übergeordneten Thread.
Die Klasse Thread hat die Standardpriorität 5, die in der statischen Variable NORM_PRIORITY festgelegt ist.
Die höchstmögliche Priorität ist MAX_PRIORITY: 10.
Die niedrigste Priorität ist MIN_PRIORITY: 1.
Die Zuteilung von Rechenzeit orientiert sich an Prioritäten. Der Thread mit der momentan höchsten Priorität
läuft solange:
- bis die Methode run() beendet ist
- bis die Methode yield() (freiwillige Prozessoraufgabe) gerufen wird
- bis ein Thread höherer Priorität gestartet wird
- weiteres ist unbestimmt (insbesondere keine Aussage über eine Preemption…) (siehe auch „ThreadImplementierung im Java-Umfeld“ weiter oben“)
è Thread-Prioritäten sind in hohem Maße systemabhängig. Ein OS kann mehr oder weniger Prioritätsebenen
haben. Die VM regelt, wie die Java-Prioritäten auf OS-Prioritätsebenen abgebildet werden. (WIE??)
void setPriority(int newPriority)
Ändert die Priorität auf den übergebenen Wert.
int getPriority()
Gibt die Priorität des Threads zurück.
Wegen der oben erwähnten Systemabhängigkeit ist die Vergabe von Prioritäten mit Vorsicht anzuwenden. Soll die
Abarbeitung von Threads exakt beeinflusst werden, ist es i.d.R. notwendig, einen eigenen Thread-Handler zu
programmieren (für User-Level-Threads).
6.6 Gruppen von Threads
Die Klasse ThreadGroup gestattet es Threads mit ähnlichen, zusammengehörenden Aufgaben wahlfrei zu
gruppieren und zu strukturieren.
Damit lassen sich threadspezifische Operationen gesammelt auf einer Gruppe von Threads ausführen.
Bei der Erzeugung von Thread-Gruppen wird wie folgt vorgegangen:
Erzeugen Sie ein Objekt der Klasse ThreadGroup.
Verwenden Sie für die Erzeugung von Thread-Objekten einen Konstruktor, bei dem Sie als ersten Parameter die
Thread-Gruppe übergeben, z.B.:
o Thread(ThreadGroup group, String name) oder
o Thread(ThreadGroup group, Runnable target)
Über die Methoden der Klasse ThreadGroup können Sie nun alle Threads der Gruppe erreichen, z.B.
unterbricht (genau genommen Unterbrechungsaufforderung) die folgende Anweisung alle Threads der Gruppe
group: group.interrupt();
Beispiel – siehe unten Beispiel Thread-Kommunikation via Pipe
6.7 Synchronisation und zugehörige Methoden (von class Object geerbt):
Führen mehrere Threads Änderungen auf gemeinsamen Daten (Nutzung gemeinsamer Ressourcen) durch, so
müssen sie synchronisiert werden, denn andernfalls können undefinierte Ergebnisse entstehen.
Beispiel – mehrere Threads ohne Synchronisation:
public class NoSynchronisation extends Thread
//CaseStudy sync_test
{
static int cnt = 0;
// für die Threads sichtbar
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 67 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
public void run()
{
while(cnt<100)
{
int i = cnt; // Kritischer Abschnitt Beginn
try{ sleep(10);} // Simuliert zeitaufwendige Berechnungen
catch(InterruptedException e){}
i++;
cnt=i; // Kritischer Abschnitt Ende
System.out.println(i);
}
}
public static void main(String[] args)
{
Thread t1 = new NoSynchronisation();
Thread t2 = new NoSynchronisation();
t1.start();
t2.start();
}
}
Die Ausgabe dieses Beispiels sollte eigentlich die Zahlen 1 bis 100 ausgeben, doch tatsächlich werden die Zahlen
teils doppelt und teils auch in falscher Reihenfolge ausgegeben. Dies kommt dadurch zustande, weil beide
Threads auf die static-Variable cnt zugreifen.
Hinweis: Möglicherweise entdecken Sie auf Ihrem System keine der Anomalien. An welcher „Schraube“ in der
run()-Methode müssen Sie drehen, um die Anomalie sichtbar zu machen?
Lösung:
Zur Synchronisation von Threads hat Java das Konzept des Monitors implementiert. Ein Monitor ist die
Kapselung eines kritischen Bereichs (also eines Programmteils, der nur jeweils ausschliesslich von einem
Thread durchlaufen werden darf) mit Hilfe einer automatisch verwalteten Sperre. Diese Sperre wird beim
Betreten des Monitors gesetzt und beim Verlassen wieder zurückgenommen. Ist sie beim Eintritt in den Monitor
bereits von einem anderen Thread gesetzt, so muß der aktuelle Thread warten, bis der Konkurrent die Sperre
freigegeben und den Monitor verlassen hat.
Das Monitor-Konzept wird mit Hilfe des in die Sprache integrierten Schlüsselworts synchronized
realisiert. Durch synchronized kann entweder eine komplette Methode oder ein Block ({}) innerhalb
einer Methode geschützt werden.
Beispiel – Synchronisation des Zugriffs auf gemeinsame Ressourcen:
In der unten gezeigten Lager-Klasse sind die Attribute bestand, zugang und abgang kritisch. Um ein Lagerobjekt
konsistent zu halten, darf der Zugang zu diesen Daten (z.B. für eine weitere Write-/Read-Transaktion) erst dann
freigegeben werden, wenn der komplette Datensatz (bestand, zugang und abgang) insgesamt aktualisiert wurde.
public class
{
private int
private int
private int
private int
private int
Lager
artikelnr;
bestand = 0;
zugang = 0;
abgang = 0;
aendeungsnr = 0;
public Lager(int anr)
{
artikelnr = anr;
}
public int getBestand()
{
return bestand;
}
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 68 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
public int getZugang()
{
return zugang;
}
public int getAbgang()
{
return abgang;
}
public int getAendeungsNr()
{
return aendeungsnr;
}
public synchronized void aenderungBestand(int x)
{
bestand+=x;
aendeungsnr++;
if (x > 0)
zugang += x;
else
abgang -= x;
}
public int getNr()
{
return artikelnr;
}
}
Übung: Siehe Übung zu diesem Kapitel; dort gilt es dieses Beipiel lauffähig zu machen.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 69 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Beispiel – Synchronisation zusätzlich mit wait() und notify():
import java.util.Vector2;
class
{
Cnt
static Vector<Integer>
cnt = new Vector<Integer>();
}
class Thread1 extends Thread
{
private int i=0;
public void run()
{
while (true)
{
// bei Bloecken muß hier ein Reference-Type
// stehen -> sonst compile-error.
synchronized(Cnt.cnt)
{// kritischer Bereich / kritischer Block
Cnt.cnt.set (0,new Integer(i++));
try{Cnt.cnt.notify();}
catch(IllegalMonitorStateException e){}
}
try {sleep(100);} catch(InterruptedException e) {}
}
}
}
class Thread2 extends Thread
{
public void run()
{
while (true)
{
synchronized(Cnt.cnt) //kritischer Bereich
{
try{Cnt.cnt.wait();}
catch (InterruptedException e) {}
}
System.out.println(Cnt.cnt.get (0));
}
try{sleep(50);} catch (InterruptedException e) {}
}
}
public class Synchronisation
{
public static void main(String[] args)
{
Cnt.cnt.add(new Integer(0));
Thread1 t1 = new Thread1();// fungiert als set()er-Thread
Thread2 t2 = new Thread2();// fungiert als get()er-Thread
t2.start();
t1.start();
// Überlegen Sie: was passiert, wenn in den Threads wait() und notify()
// weggenommen werden? Warum sind diese Methoden zwingend notwendig?
2
Zur Erinnerung an „Algorithmen & Datenstrukturen“ / Sie sollten folgende Collection-Typen kennen:
Listen -> Vector, Stack, ArrayList, LinkedList
Sets -> Collections ohne Duplikate: HashSet, TreeSet,
Maps -> Schlüssel-Value-Paare: HashMap, TreeMap,
Die Klasse Vector und ArrayList sind praktisch identisch bis auf den Unterschied, dass die Methoden der Klasse
Vector synchronisiert sind. Siehe API-Doku!
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 70 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
// Experimentieren Sie: mal mehr set()-er-, mal mehr get()-er-Threads…
}
}
Hands On: Untersuchen und erläutern Sie das Beispiel „Synchronisation2“ im Hands-OnTeil.
6.8 Datenaustausch zwischen Threads mit Pipes: hier weiter 28.10.15
Voraussetzung: gemeinsamer (Prozess-)Adressraum (gemeinsame Objekte, Datenfelder)
Für die Realisierung von Pipes gibt es jeweils eigene Stream-Klassen für die byte- und characterorientierte Kommunikation
Pipes sind stets Einweg-Kanäle:
o unidirektionales Kommunikationsmittel
Peer to Peer
“read side”
“write side”
Client-Server
Selbstsynchronisierend je nach Basis-OS!
§ haben bestimmte Kapazität (häufig Vielfaches von 2KB, OS-abhängig)
§ Produzent wird automatisch (!) angehalten, wenn die Pipe voll ist
§ Konsument wird automatisch (!) angehalten, wenn die Pipe leer ist
o FIFO-Prinzip
o keine Seek-Operation
- Wird ein Thread beendet, welcher liest und/oder schreibt, so erhält der Partnerthread die Exception "broken pipe"
- Muss durch IOException abgesichert werden
o
- Siehe: Pipe-API-Doku: Whether or not a thread writing bytes to a pipe will block until another thread reads those bytes, or some
previously-written bytes, from the pipe is system-dependent and therefore unspecified. Many pipe implementations will buffer up to
a certain number of bytes between the sink and source channels, but such buffering should not be assumed.
Zwei Klassen im Package java.io:
Eingabestrom
PipedInputStream
Ausgabestrom
PipedOutputStream
PipedReader
PipedWriter
Stream-Typ
ByteStream (binäre Kommunikation
und ASCII)
CharacterStream (UNICODE; TextKommunikation)
In die Pipes, bzw von den Pipes werden jeweils Einzelbyte/Einzelcharacter oder positionierte und
dimensionierte Pufferinhalte geschrieben oder gelesen. Siehe z.B. API-Doku PipedWriter:
void write(char[] cbuf, int off, int len)
Writes len characters from the specified character array cbuf starting at offset off to
this piped output stream.
void write(int c)
Writes the specified char to the piped output stream.
See next page: Die Erbschaft: (Siehe auch oben Kapitel “Streams”)
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 71 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Die Erbschaft: (Siehe auch oben Kapitel “Streams”)
Object
OutputStream
InputStream
PipedOutputStream
PipedInputStream
Writer
PipedWriter
automatisch (!)
Reader
automatisch (!) automatisch (!)
PipedReader
automatisch (!)
Das Methoden-Angebot: Das Methoden-Angebot für die Pipes ist vergleichbar mit den Methoden in den
Basisklassen:
Für die lesenden Klassen: available(), close(), connect(), read(), receive(), ready()
Für die schreibenden Klassen: close(), connect(), flush(), write()
Details siehe API-Dokumentation.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 72 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Beispiel – Thread-Kommunikation via Pipe:
import java.io.*;
public class TestPipe
{
public static void main(String[] args)
{
try
{ // 1. Step: Prepare infrastructur to communicate
PipedReader pr1 = new PipedReader(); // Character-Streams, UNICODE
PipedWriter pw1 = new PipedWriter();
pw1.connect(pr1); // verbindet PipedWriter und PipedReader
// 2. Step Instantiate Communicators and start() them
ThreadGroup tg = new ThreadGroup("TG");
Produzent produzent = new Produzent (tg, pw1); // Schreibseite als Para
Konsument verbraucher = new Konsument (tg,pr1); // Leseseite als Para
produzent.start();
verbraucher.start();
Thread.sleep(20000);
tg.interrupt();
// Operation auf der Thread-Gruppe
}
catch(InterruptedException ie){}
catch(IOException io)
{
System.out.println("Fehler beim Verbinden der Pipe");
}
}
}
class Produzent
{
PipedWriter pw;
int x = 0;
extends Thread
public Produzent (ThreadGroup group, PipedWriter pw)
{
super(group, "Produzent");
this.pw = pw;
}
public void run()
{
while(isInterrupted() == false)
{
// int x = (int)(100 * Math.random());
x++;
try
{
hier wird synch
pw.write(x);// dran denken,
.
System.out.println(this.getName() + "\t" + "produziert: " + x);
}
}
catch(IOException io)
{
System.out.println("Produzent: Fehler beim Schreiben");
}
}
}
class Konsument
{
PipedReader pr;
extends Thread
public Konsument (ThreadGroup group, PipedReader pr)
{
super(group, "Konsument");
this.pr = pr;
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 73 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
}
public void run()
{
while(isInterrupted() == false)
{
try
{
int x = pr.read(); //liefert -1 (= end-of-stream),wenn Produzent Pipe closed.
im read() wird synch .
//
System.out.println(this.getName() + "\t" + "entnimmt: " + x);
}
catch(IOException io)
{
System.out.println("Konsument: Fehler beim Lesen");
}
}
}
}
Hands On:
•
Untersuchen Sie, was passiert, wenn der Produzent kontinuierlich auf die Pipe schreibt,
ohne dass ein Konsument aktiv ist (bzw. sich schon beendet hat).
•
Untersuchen Sie, was passiert, wenn der Produzent kontinuierlich „rasch“ auf die Pipe
schreibt, aber der Konsument nur gelegentlich/“langsam“ von der Pipe liest.
•
Untersuchen Sie, was passiert, wenn der Konsument von einer Pipe lesen möchte, ohne
dass ein Produzent aktiv ist (oder sich schon beendet hat).
•
Untersuchen Sie, was passiert, wenn der Konsument kontinuierlich/„schnell“ von der Pipe
liest, der Produzent aber nur „gelegentlich“ etwas auf die Pipe schreibt.
6.9 Weitere Synchronisationsmittel
(opional)
Weiterreichende Mittel für die Synchronisation stellt Java mittels der folgenden Klassen bereit:
Class Semaphore: zählende Semaphore zur Verwaltung des Zugriffs auf
Ressourcen
Class CountDownLatch: “A synchronization aid that allows one or more
threads to wait until a set of operations being performed in other threads completes”
Class CyclicBarrier: Wartezeiten/Synchronisation wird auf der Basis
bestimmter Arbeitsvolumen/Arbeitsaufträge implementiert
Class Exchanger: “A synchronization point at which threads can pair and swap
elements within pairs.” An den Synchronisationspunkten tauschen Threads Daten aus.
Hinweis. Siehe hierzu API-Doku.
Hinweis: Vergleiche dazu die Hinweise im Kapitel 6.2 Klasse Thread und Interface Runnable
zu den Synchronisationsarten
Zeitgesteuerte Synchronisation
Ereignisgesteuerte Synchronisation
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 74 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Kommunikationsgesteuerte Synchronisation
6.10 Thread-Synchronisation mit CyclicBarrier
Siehe: Class CyclicBarrier http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/CyclicBarrier.html
“A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point. CyclicBarriers are
useful in programs involving a fixed sized party of threads that must occasionally wait for each other. The barrier is
called cyclic because it can be re-used after the waiting threads are released”
Siehe auch Beispiel: http://programmingexamples.wikidot.com/cyclicbarrier
A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point. The
barrier is called cyclic because it can be re-used after the waiting threads are released.[1]
An example taken from [2] follows:
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierExample
{
private static int matrix[][] =
{
{ 1 },
{ 2, 2 },
{ 3, 3, 3 },
{ 4, 4, 4, 4 },
{ 5, 5, 5, 5, 5 } };
private static int results[];
private static class Summer extends Thread
{
int row;
CyclicBarrier barrier;
Summer(CyclicBarrier barrier, int row)
{
this.barrier = barrier;
this.row = row;
}
public void run()
{
int columns = matrix[row].length;
int sum = 0;
for (int i = 0; i < columns; i++)
{
sum += matrix[row][i];
}
results[row] = sum;
System.out.println("Results for row " + row + " are : " + sum);
// wait for others
try
{
barrier.await();
} catch (InterruptedException ex)
{
ex.printStackTrace();
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 75 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
} catch (BrokenBarrierException ex)
{
ex.printStackTrace();
}
}
}
public static void main(String args[])
{
final int rows = matrix.length;
results = new int[rows];
Runnable merger = new Runnable()
{
public void run()
{
int sum = 0;
for (int i = 0; i < rows; i++)
{
sum += results[i];
}
System.out.println("Results are: " + sum);
}
};
/*
* public CyclicBarrier(int parties,Runnable barrierAction)
* Creates a new CyclicBarrier that will trip when the given number
* of parties (threads) are waiting upon it, and which will execute
* the merger task when the barrier is tripped, performed
* by the last thread entering the barrier.
*/
CyclicBarrier barrier = new CyclicBarrier(rows, merger);
for (int i = 0; i < rows; i++)
{
new Summer(barrier, i).start();
}
System.out.println("Waiting...");
}
}
Siehe auch „Getting to know Synchronizers”:
http://java.sun.com/developer/JDCTechTips/2005/tt0216.html#1
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 76 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
6.11 Zusammenfassung Threads:
Aus der API-Beschreibung entnommen:
public class Thread
extends Object
implements Runnable
A thread is a thread of execution in a program. The Java Virtual Machine allows an application to have multiple
threads of execution running concurrently.
Every thread has a priority. Threads with higher priority are executed in preference to threads with lower priority.
Each thread may or may not also be marked as a daemon.
When code running in some thread creates a new Thread object, the new thread has its priority initially set equal
to the priority of the creating thread,
and is a daemon thread if and only if the creating thread is a daemon.
When a Java Virtual Machine starts up, there is usually a single non-daemon thread (which typically calls the method
named main() of some designated class). The Java Virtual Machine continues to execute threads until either of the
following occurs:
The exit method of class Runtime has been called and the security manager has permitted the exit operation to
take place.
All threads that are not daemon threads have died, either by returning from the call to the run method or by
throwing an exception that propagates beyond the run method.
There are two ways to create a new thread of execution. One is to declare a class to be a subclass of Thread. This
subclass should override the run method of class Thread. An instance of the subclass can then be allocated and
started.
The other way to create a thread is to declare a class that implements the Runnable interface. That class then
implements the run method. An instance of the class can then be allocated, passed as an argument when creating
Thread, and started.
6.12 Zusammenfassung Pipes:
Pipes sind ein streamorientiertes Kommunikationsmedium für die Inter-Thread-Kommunikation.
Pipes können nur zwischen lokalen Thread verwendet werden.
Pipes sind für eine methodenorientiert Zusammenarbeit zwischen Threads nicht so gut geeignet.
Vorausschau: für die netzwerkweite Inter-Thread-Kommunikation (Programm-Kommunikation) sind geeignet
•
Sockets
•
RMI
• CORBA
• Messages (JMS Java Message Queue Service)
• Web-Services,
• u.a.
Bearbeiten Sie die Case Studies und lösen Sie die Übungsaufgeben zu diesem Kapitel!
(Siehe Unterlagen hierzu auf dem Materialserver.)
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 77 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
7. Exkurs: how to model an application?
Oder anders gefragt: How to manage the project?
Und wie sieht die Theorie dazu aus?
Warum steht der Exkurs an dieser Stelle? Hätte man die „Theorie“ nicht vorher bringen können? Nun, das wäre
sicher möglich gewesen – wäre dann allerdings eben Theorie geblieben. Nun ist die Situation die, dass Sie aus
unserm Projektfahrplan (siehe unten) das ADRELI_CON Projekt und das Projekt ADRELI_THREADS bereits fertig
haben.
Durch die Präsentation/QS der Projekte haben Sie auch konkret miterlebt, dass es teilweise ganz unterschiedliche
Ansätze für die Umsetzung der Spezifikation gab. Manche Projektteams versuchten gleich von Anfang an ein MVCModell zu implementieren, andere haben eine monolithische Architektur bevorzugt – Hauptsache „es läuft“.
Unterschiede gab es auch im Bereich
der I/O,
des Threadings,
der Pipe-Kommunikation,
der Klassenorganisation,
usw..
Um das Verständnis für die Modellierungsproblematik zu sensibilisieren, sollen in diesem Kapitel einige
Überlegungen diskutiert werden.
ADRELI_CON
ADRELI_THREADS
ADRELI_NETCOM
ADRELI_GUI
ADRELI_JDBC
Die ADRELI-Projekte
Die Projektsequenz (= 1 Adreli-Projekt) ermöglicht es, dass bestimmte Aspkete des Vorgehensmodells von Barry W.
Boehm („Spiralmodell“, 1988) „erlebt“ werden können. Das Spiralmodell hat andere Vorgehensmodelle beinflußt,
siehe dazu beispielsweise auch „Evolutionäre Softwareentwicklung“, „Extreme Programming“ und „SCRUM“. Mit
ein zentraler Punkt ist das „divide and conquere“-/„divide et impera“-: „teile und herrsche“-Prinzip. (Zur Erinnerung:
dieses Prinzip haben wir auch in PROMOD-1 für die Entwicklung von Algorithmen – im Zusammenhang mit der
„Funktionalen Abstraktion“ - diskutiert.)
.
Abb.: Spiralmodell von Böhm
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 78 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Die (verborgene) Gesamtaufgabenstellung von PROMOD2 ist die Entwicklung einer
1. Datenbank-Applikation die
2. im Netz verteilt ist (Client-Sever, Web-Service, …)
3
3. mulituser-fähig .
4
4. skalierbar und
5
5. ausfallsicher ist.
Diese Gesamtaufgabenstellung kann durch einen Gesamt-Entwurf gelöst werden. Die Konsequenz:
hoher Designaufwand und („Design for the future“)
lange Wartezeit bis zu einem testbaren ersten (Zwischen-)ergebnis.
....
Auf der Managementseite:
o Schwer(er) administrierbar,
o damit auch hohes Risiko (möglicherweise Abbruch, hohe Korrektur-/Änderungskosten; 10x10x10Regel!).
o ....
Die Projektsequenz (eine „Drehung“ in der Boehm’schen Spirale / bzw. ein „Sprint“ im Sinne von SCRUM)
konzentierte sich auf jeweils dedizierte Probleme – ausgehend
1. von der (benutzergetriebenen) Applikationsfunktionalität (Datenmodell & Funktionalität) (Siehe oben Punkt 1)
2. hin zu fortgeschrittenen technischen Fähigkeiten der Applikation (Siehe oben die Punkte 2 bis 5)
In der Retrospektive sollten Sie feststellen, dass Sie sich
6
zuerst mit dem Applikationskern (dem Datenmodell und der Funktionalität) vertraut und sicher gemacht
haben. Vorteil: der Applikationskern ist testbar, für die Weiterentwicklung stabilisiert und er ist auch
vorführbar.
Danach wurden teilweise – für den Benutzer nicht sichtbar – weitere technisch Eigenschaften eingebaut.
(Threading, Pipes, Sockets, multi-threaded Server), die in Summe den Gesamt-Entwurf umgesetzt haben.
Vorteil: wie oben. Jede „Spiraldrehung“/“Sprint“ ist testbar, stabilisiert sich und ist auch demonstrierbar.
Die in den Teilprojekten
verfassten UML-Diagramme und
Source-Codes
sollten den inkrementell zunehmenden Komplexitätsgrad verdeutlichen und für Sie greifbar (= kalkulierbar)
machen, wo die Aufwände stecken. Solche Kenntnisse sind unerlässlich für die
Abschätzung von Design-Entscheidungen,
Abschätzung von Zeitbedarf für die Implentierung (einer User-Funktionalität)
Kostenschätzungen (i.d.R. -> (Zeit in Stunden) x Stundensatz. )
Adreli_1_Con „How-To“
Entwicklungsstruktur: Top-Down
Entwurfsverfahren: Divide-and-Conquer
1. Basis-UI (= User Interface) realisieren; Menue zur Auswahl der Funkionalität
2. Business-Logik = Schleife und Verzweigung, je nach gewählter Funktion
§ i.W.: Keyboard-Eingaben holen (Adressfelder) -> Puffern -> in File speichern (write)
§ oder: Inhalt aus File holen (read) -> puffern -> auf Konsole anzeigen
3. Am besten zunächst für nur zwei Felder implementieren
4. Regex einbauen
5. Programm nun für alle Felder mit Regex implementieren
6. Regex perfektionieren
Bei der Verwendung neuer API-Schnittstellen: „check – test – integrate“:
3
Nicht wirklich, da wir uns z.B. um keinerlei Rechteverwaltung kümmern.
Am Ende ist der Server multi-threaded: um jeden Client kümmert sich je ein dedizierter Thread
5
Zum Beispiel durch Einsatz eines clusterfähigen DB-Servers (z.B. MySQL [MySQL Cluster integrates the standard
4
MySQL server with an in-memory clustered storage engine called NDB. Siehe: http://dev.mysql.com/doc/refman/5.0/en/mysqlcluster-overview.html ])
6
Datenmodell und Funktionalität sind in unserem Fall zugegebenermaßen trivial. Dies ist aber so beabsichtigt, damit
die „Vorgehensstruktur“ und die benutzten Java-APIs im Vordergrund stehen und gut sichtbar sind!
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 79 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
1.
2.
3.
Im Einzelnen:
•
•
•
Check = neue API in separater Case-Study implentieren
Test = die Case-Study studieren/modifizieren/testen
Integrate = nach dem totalen Verständnis der Case-Study neue API in App einbauen
Bottom-Up vorgehen: d.h. in einem separaten Java-File die API studieren (ausprobieren und testen
analog zu den Case-Studies)
Dieses separate Java-File als Case-Study archivieren: so können Sie die Case-Study später in dem
Maße erweitern, wie Sie die API-Schnittstelle weiter erforschen!
Integrieren: die funkionierenden Code-Sequenzen aus Ihrer Case-Study in die zu entwickelnde App
übernehmen.
Mehr dazu: Siehe Adreli-1-Pattern
Adreli_2_Thread „How-To“
1) CaseStudies zu Thread und Runnable
2) Adreli_CON mit einem expliziten Thread implementieren/umschreiben
3) „Refactoring“ überlegen!
3a) Ziel: 2 – 3 Threads (Hauptthread als “Controler” und zwei separate Threads für “Model” und “View”):
Adreli_2_Thread
Adreli_3_Net
VIEW / UI-Thread
migriert bei Adreli_3_Net in das Client-Programm
CONTROLLER-Thread
migriert bei Adreli_3_Net
(Hauptthread)
• z.T in den Client- und
• z.T. in das Server-Prog.
(Client und Server haben jeweils ihren eigenen
CONTROLLER-Thread)
MODEL-Thread
migriert bei Adreli_3_Net (komplett) in das ServerProgramm
(ggf. der einfachheithalber UI- und CONTROLLERModel-Thread zusammenfassen, da die GUI sehr primitiv
ist)
Kommunikation der Threads, lt. Aufgabenstellung, mittels Kommunikationsmittel Pipe.
a) UI bleibt mit der switch-case-Konstruktion wie bisher bestehen
b) „Trick 1“: File-I/O-Methoden (sind 3 Methoden) redurzieren sich auf die Pipe-Kommunikation zwischen
UI-Thread (VIEW) und File-I/O-Thread (MODEL). Nun: MODEL-Thread schreibt auf und liest von CVSRAC-File
c) Entweder: der bisherige Buffer (z.B. ArrayList oder besser Vector) muss in den char[]-Buffer, den
z.B. PipedWriter.write(), bzw. PipedOutputStream.write() braucht, „umgeladen“ /
konvertiert werden.
d) Oder PIPE_FITTING: Mit ObjectOutputStream- und ObjektInputStream-Methoden
können Objekte über eine Pipe (nur über PipedOutputStream/PipedInputStream NICHT
7
PipedWriter/PipedReader) gestreamt werden . Vergleiche Beispiel aus der API-Doku dazu:
Schreibender THREAD:
FileOutputStream fos = new FileOutputStream("personen.dat"); //durch pipe/socket ersetzen
// später PipedOutputStream (und PipedInputStream im lesenden THREAD)
// später ObjectOutputStream und ObjectInputStream im lesenden THREAD)
// später OutputStream = <Socket>.getOutputStream()
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeInt(12345);
oos.writeObject("Today");
oos.writeObject(new Date());
oos.close();
7
Wäre für unsere Anwendung (Adreli_2) ideal! Warum?
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 80 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Lesender THREAD:
FileInputStream fis = new FileInputStream("personen.dat"); // durch pipe/socket ersetzen
// später PipedInputStream pis = new PipedInputStream(PipedOuputStream src);
// später InputStream sis = <Socket>.getInputStream()
ObjectInputStream ois = new ObjectInputStream(fis/pis/sis);
int i = ois.readInt();
String today = (String) ois.readObject();
Date date = (Date) ois.readObject();
ois.close();
Mehr dazu: Siehe Adreli-2-Pattern
Adreli_3_NetCom „How-To“
4. Client-Programm schreiben (VIEW)
§ Mit User-Interaktion wie bisher (aus bestehenden Code-Teilen)
§ Mit Pufferung (ggf. so wie bisher)
§ Noch OHNE SOCKET-Kommunikation
5. Server-Programm schreiben (MODEL)
§ Mit File-I/O (aus bestehenden Code-Teilen)
§ Mit Pufferung (ggf. so wie bisher)
§ Noch OHNE SOCKET-Kommunikation
Mehr dazu: Siehe Adreli-3-Pattern
Adreli_4_GUI „How-To“
Adreli_5_JDBC „How-To“
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 81 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
7.1 Als Kompass/Leitplanke dienen Vorgehensmodelle
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 82 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 83 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Rollen
SCRUM-Modell
Scrum Master
Burn-down-chart
Product
Owner
Sprint Backlog
Release Backlog
Product
Backlog
Development
Team
Pair-Programming
2
•
•
Sprint Planning
Meeting
Daily Scrum
Zeremonien
Review
Meeting
Prof. J. Anton Illik
„SCRUMin unter 10 minutes...“ (YouTube)
Video2brain, Udo Wiegärtner:
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 84 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
7.2 Ihre Einschätzung? Welches Vorgehensmodell ist wofür
geeignet?
Für sehr grosse Projekte gut geeignet:
???-Modell
Für sehr kleine Projekte gut geeignet:
???-Modell
Für kleine und mittlere Projekte gut geeignet:
???-Modell
Für mittlere und größere Projekte gut geeignet:
???-Modell
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 85 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
7.3 Und wie geht`s im Detail?
Siehe Artikel: Tom Gilb, „Evolutionäres Entwickeln“;
tom_gilb_1987_scan0273.pfd
Siehe Video:
Hamid Shojaee, „SCRUM Agile Development in under 10 Minutes“
SCRUM_video_under_10_minutes.flv
Siehe Artikel: Bernd Oestereich und Christian Weiss,
„Wie werden große Softwareprojekte agiler?“
wum-009-0049-y_agil_grosse_sw_projekte_agiler.pdf
aus Magazin „Wirtschaftsinformatik & Management“/Heft 02.2009
•
•
Den (historischen) Artikel von Tom Gilb und den Artikel von Bernd Oestereich und
Christian Weiss finden Sie im Wintra.
Das Video von Hamid Shojaee finden Sie bei YouTube
Siehe Literartur:
Siehe Kapitel “Algorithmen”
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 86 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
8. Netzwerkzugriff hier weiter 04.11.15
Was Sie lernen:
wie mit Java auf das Netzwerk zugegriffen wird
wie Client- und Server-Sockets programmiert werden
Voraussetzungen:
Arbeiten mit Threads
8.1 Grundlagen
Als Netzwerke werden miteinander verbundene Computer bezeichnet, die miteinander kommunizieren
können. Das bedeutendste Computernetz ist das Internet. Stehen die miteinander verbundenen Computer
innerhalb eines Unternehmens, so spricht man von einem Intranet, wenn zum Aufbau des firmeninternen
Computernetzes die Internet-Technologie verwendet wird (TCP/IP-Protokoll-Stack).
8.1.1
Protokolle
Um die komplexen Abläufe und Mechanismen der softwareseitigen Verbindung von Computern besser verstehen
zu können, wurde unter dem Dach der ISO (International Organization for Standardization) das ISO/OSI-7Schichten-Modell (1983) definiert (siehe unten ISO-Referenzmodell). (OSI = Open System Internconnection).
OSI-Paradigma: Zwei Netz-Computer können Informationen nur auf der gleichen Ebene (= Schicht)
austauschen. Beispielsweise wird eine E-Mail von der Anwenderschicht abgeschickt und kommt auf dem anderen
Rechner in der Anwendungsschicht an. Für den Datenaustausch muss es bestimmte Vorschriften geben, die den
Aufbau und Abbau der Verbindung und den Datenaustausch reglementieren. Diese (Umsetzung der
Vorschriften in Software) werden Protokolle genannt. Eine Protokollebene nutzt eine tieferliegende Schicht und
reichen Ergebnisse an eine höhere Schicht weiter.
8
Das Protokoll, welches sich zur Kommunikation vernetzter Computer durchgesetzt hat, ist TCP/IP
(Transmission Control Protocol / Internet Protocol). TCP/IP ist eine Sammlung von Protokollen, die
entwickelt wurden, um Computeranwendern das kooperative Arbeiten in einem Netz von örtlich verteilten
Rechnern (ohne Netzwerk-Zentrale) zu ermöglichen. Diese geographisch verteilten Computer sind üblicherweise
Mitglieder lokaler Netzwerke. Sowohl im lokalen Netz wie auch in der „Zusammenfassung“ dieser lokalen Netze,
dem sogenannten Internet, ist die TCP/IP-Protokoll-Suite die Software-Basis für die lokale und weltumspannende
Kommunikation.
Die TCP/IP-Protokoll-Suite wurde auf Anregung des US-amerikanischen Departement of Defence (DoD) ab dem
Jahr 1962 entwickelt.
8
Vor TCP/IP gab es zahlreiche proprietäre Protokolle. Von der IBM: SNA System Network Architecture; von Digital
Equipment (kurz DEC): DNS Digital Network Architectur. Praktisch hatte jeder große Computer-Hersteller (ca. bis
Beginn der 1990er Jahre) seine eigene Protokoll Suite. Ab dann setzte sich TCP/IP durch.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 87 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
ISO Referenzmodell
ursprüngliches
ARPAnet
Referenzmodell
Application
Presentation
Session¥
Transport
Prozesse
/
Applikationen
TCP/IP
Schichten
Beispiel für die SW
einzelner Schichten
Benutzerprogramme,
Bibliotheken
HTTP, FTP, SMTP,
rcp,
rlogin,
…
telnet,
ftp, rlogin,
rcp
Socket
SKT_stream
TCP
Host - Host
Protokolle
IP
Network
Data Link
Netzwerkschnittstelle
Hardware
Netz-Hardware
UDP
ICMP
Netzwerkschnittstelle
Ethernet Driver
Netz-HW
Netz-Hardware
Die Transport-Schicht (Transmission Level). TCP und UDP sind die am meisten verwendeten Protokolle in
dieser Schicht. Die Protokolle dieser Schicht bieten Netzwerkdienste für Programme der Applikations-Schicht.
Jedes dieser Protokolle hat eigene besondere Fähigkeiten. Beispiele für solche Fähigkeiten sind:
o
TCP: ist verbindungsorientiert und stellt eine sichere Punkt-zu-Punkt-Verbindung zwischen zwei
Computern her. TCP gewährleistet einen fehlerfreien Datentransport.
o
UDP: ist verbindungslos. Hier muss das Anwendungsprogramm gewährleisten, dass die
abgeschickten Pakete fehlerfrei beim Empfänger ankommen. Auch um die richtige Reihenfolge
der Pakete muss sich das Anwendungsprogramm selbst kümmern.
Verbindungsorientiert / verbindungslos: Bei der Verbindungsorientierung wird mit einem „Handshake“Verfahren gearbeitet. D.h. der empfangende Rechner bestätigt den Empfang eines/mehrerer Datenpakete. Bei
der verbindungslosen Übertragung werden Datenpakete übertragen, ohne eine Rückantwort abzuwarten.
Paket: Größere Datenmengen werden in mehrere kleine Pakete (= Segmente; max 1500 Bytes) zerlegt und
zwischen Computern übertragen.
8.1.2
Adressierung: IP-Adresse und Port-Nummer
Voraussetzung für ein funktionierendes Computer-Netzwerkt ist eine eindeutige Adressierung der beteiligten
Rechner.
Unter TCP/IP wird eine 32-Bit-IP-Adresse verwendet (also 4 Byte bei IP Version 4; zum Beispiel 80.130.234.185
in Dezimalnotation). Bei IPv6 sind es 128-Bit-Adressen, sechzehn Byte; zum Beispiel
0588:2353:0000:0000:0000:0000:1428:57ab in hexadezimaler Notation). Das Paket java.net unterstützt seit
JDK 1.4 auch IPv6 Internet-Adressen, vorausgesetzt dass das Host-Betriebssystem auch IPv6 untersützt.
Die Adresse besteht aus der Netzwerkadresse und der Rechneradresse. Der Adressraum ist eingeteilt in
verschieden Klassen. Siehe Bild unten. Beispiel für ein Klasse-C-Adresse: 217.89.65.160
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 88 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Adressierung erfolgt über IP-Adressen und Port-Nummern.
Es gibt eine spezielle IP-Adresse für den lokalen Arbeitsplatz: 127.0.0.1. Diese Adresse kann auch unterm dem
Alias localhost angesprochen werden und referenziert den eigenen PC.
Alle Adressen die mit 10. und 192.168. beginnen, sind für die Verwendung in privaten Netzen, wie z.B. dem
Intranet, reserviert. Weiter Informationen hierzu siehe RFC1918 unter www.rfc.net.
Oft werden Verbindungen zwischen zwei Computern als Client-Server-Verbindungen aufgebaut. Der Server
wird dabei i.d.R gestartet, läuft im Hintergrund („Daemon“) und stellt seine Services den anfragenden Clients zur
Verfügung. Der als Client fungierende Computer nimmt Kontakt mit dem Server auf um einen Service des
Servers in Anspruch zu nehmen. Ein Rechner kann gleichzeitig Client und Server sein (Peer-to-PeerVerbindung).
Die Portnummer adressiert einen Dienst/Service auf einem Server. Das IAB (Internet Activity Board /
Internet Architecture Board http://www.iab.org bzw. die IANA Internet Assignd Numbers Authority ) regelt
die Port-Zuordnung. Sog. "well-known port numbers" (21,23,25,80, .. usw. Siehe
http://www.iana.org/assignments/port-numbers. Siehe auch Bild unten).
.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 89 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Da auf einem Computer mehrere Serverprogramme laufen, die wiederum von mehreren Clients benutzt werden
können, ist es notwendig, zur Unterscheidung der Serveranwendungen neben der IP-Adresse für den
Verbindungsaufbau noch eine Port-Nummer anzugeben. Eine Client-Server-Verbindung kann nur aufgebaut
werden, wenn die Client-Software den Server (mittels IP-Nummer) über richtiger Port-Nummer anspricht.
Protokoll/Dienst
ICMP/Ping
Internet Control
Message Protocol
Port
Nicht festgelegt
Transportprotokoll
TCP, UDP
RFCs
RFC792
FTP FileTransfer
Protocol
TELNET
Terminal-Verbindung
21
TCP
RFC959, RFC765
23
TCP
RFC854
SMTP/MailServer Simple Mail
Transfer Protocol
25
TCP
RFC821
HTTP Hypertext
Transfer Protocol
80
TCP, UDP
RFC1945, RFC2068
POP3 Post
Office Protocol
110
TCP, UDP
RFC1939
Erläuterung
Protokoll zur
Übermittlung von
Kontroll-und
Fehlermeldungen
während der
Datenübertragung
Datenaustausch mit
einem FTP-Server
Protokoll zur
Terminalemulation
bei der
Kommunikation mit
einem anderen
Computer.
Einfaches PostÜbertragungsprotokoll
für die Sendung von
E-Mails im Internet.
Protokoll zur
Übertragung von
Informationen (HTMLSeiten und ihre
Inhalte) im Internet.
Mail-TransferProtokoll zum
Empfangen von EMails.
Die Namen der Protokolle stimmen meist mit den Namen der Server/Services überein.
Die Portnummer kann eine Zahl zwischen 0 und 65535 sein. Bei der Programmierung eigener Client-ServerVerbindungen dürfen bereits belegte Portnummern nicht verwendet werden. Dies sind i.a. alle Zahlen kleiner
(vor einiger Zeit: 300) 50000 … am Besten auf der iana-Site nachsehen! Zahlen ab 50000 aufwärts
verwenden.
Info: siehe auch: http://de.wikipedia.org/wiki/Liste_der_standardisierten_Ports
8.1.3
Die Datei hosts und DNS (Domain Name System)
Ist die Angabe von localhost beispielsweise nicht möglich, kann die Zuordnung eines Namens zu einer IPAdresse in der Datei hosts vorgenommen werden. In Windows XP Professional befindet sich die Datei z.B.
unter C:\windows\i386. Unter Windows 8 C:\windows\System32\drivers\etc Unter
Linux/UNIX/MAC OS X unter /etc.
Beispiel - C:\windows\i386\hosts:
#
#
#
#
#
#
Copyright (c) 1993-1999 Microsoft Corp.
Dies ist eine HOSTS-Beispieldatei, die von Microsoft TCP/IP
für Windows 2000 verwendet wird.
Diese Datei enthält die Zuordnungen der IP-Adressen zu Hostnamen.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 90 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
#
#
#
#
#
#
#
#
#
#
#
#
#
#
Jeder Eintrag muss in einer eigenen Zeile stehen. Die IPAdresse sollte in der ersten Spalte gefolgt vom zugehörigen
Hostnamen stehen.
Die IP-Adresse und der Hostname müssen durch mindestens ein
Leerzeichen getrennt sein.
Zusätzliche Kommentare (so wie in dieser Datei) können in
einzelnen Zeilen oder hinter dem Computernamen eingefügt werden,
aber müssen mit dem Zeichen '#' eingegeben werden.
Zum Beispiel:
102.54.94.97
38.25.63.10
rhino.acme.com
x.acme.com
# Quellserver
# x-Clienthost
127.0.0.1 localhost
In dieser Datei können weitere oft verwendete IP-Adressen symblischen Namen (Aliasnamen) zugeordnet werden.
Auch im Internet werden symbolische Namen für die IP-Adressen der Zielrechner verwendet; diese Aliasnamen
werden als Domain-Namen bezeichnet (www.illik.de). Der Domain-Name-Server (steht i.d.R. beim InternetProvider) wandelt den Domain-Namen in die IP-Adresse um und gibt sie z.B. dem Browser zurück. Damit kann
die Verbindung zum Zielserver aufgebaut werden.
8.2 Klassen für die Adressierung
8.2.1
Die Klasse InetAddress: (siehe API-DokuThis class represents an Internet Protocol (IP) address.
Ein Objekt der Klasse InetAddress enthält den logischen Namen und die IP-Adresse des Zielrechners.
Die Klasse InetAddress befindet sich im package java.net
Methoden zur Erzeugung eines InetAddress-Objekts:
static InetAddress getLocalHost()
static InetAddress getByName(String host)
static InetAddress[] getAllByName(String
host)
static InetAddress getByAddress(byte[]
addr)
static InetAddress getByAddress(String
host, byte[] addr)
Adresse (IP-Nummer) des eigenen Computers
(siehe Beispiel unten)
liefert die Adresse (IP-Nummer) für den als Parameter
genannten Host
(siehe Beispiel unten)
Alle Adressen (IP-Nummer) eines remote Hosts
(siehe Beispiel unten)
Liefert Adresse auf der Basis der übergebenen (raw) IPAdresse (siehe Methode unten getAddress())
Kreiert Adresse basierend auf der übergebenen IP-Nr.
und dem Hostnamen. (Der Hostname wird nicht geprüft;
kein Aufruf eines Namensdienstes.)
…u.v.a.m.
Weitere Methoden, um Informationen zum InetAddress-Objekt zu erhalten:
String getHostName()
String getHostAddress()
byte[] getAddress()
Liefert den logischen Namen der IP-Adresse zurück
(siehe Beispiel unten)
Liefert die IP-Adresse zurück
(siehe Beispiel unten)
Liefert IP-Adresse als Byte-Array zurück (Sprechweise
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 91 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
boolean
isMulticastAddress()
boolean
isSiteLocalAddress()
(API-Doku): „raw IP address“)
Liefert true zurück, wenn die IP-Adresse eine MulticastAdresse ist (kann gleichzeitig an mehrere Stationen
Nachrichten verschicken).
“Utility routine to check if the InetAddress is a site local
address”
…u.v.a.m.
Siehe API-Beschreibung zu den Themen:
Address types
IP address scope
Textual representation of IP addresses
Host Name Resolution
Inet Address Caching
Beispiel - Umgang mit InetAddress-Methoden:
import java.io.*;
import java.net.*;
public class InternetAdressen
{
public static void main (String [] args)
{
try
{ System.out.println("Meine Adresse (Rechnername/IP-Adresse): " +
InetAddress.getLocalHost());
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
System.out.println ("Geben Sie einen Internet-Host-Namen ein (z.B. www.fhfurtwangen.de)");
while (true)
{
System.out.print ("Host-Name: ");
String s = in.readLine();
if (s.equals("ende")) break;
try
{
//InetAddress iAddr = InetAddress.getByName(s);
InetAddress []iAddr = InetAddress.getAllByName(s);
//System.out.println("IP-Adresse:
" + iAddr.getHostAddress() + "\n");
for (int i = 0; i < iAddr.length; i++) {
System.out.println("IP-Adresse:
" + iAddr[i].getHostAddress() + "\n");
System.out.println("Rechnername: " + iAddr[i].getHostName() + "\n");
}
}
catch(UnknownHostException ex)
{
System.out.println ("Host konnte nicht gefunden werden" + "\n");
}
}
}
}
catch (Exception ex){ }
}
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 92 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
8.2.2
Die Klasse URL
Ein URL = „Uniform Ressource Locator“ ist die Beschreibung einer Adresse (Quelle) im Internet / dient dazu, um
eine Ressourcen im Netzwerk zu lokalisieren.
Siehe API-Doku: „Class URL represents a Uniform Resource Locator, a pointer to a "resource" on the
World Wide Web. A resource can be something as simple as a file or a directory, or it can be a reference to a
more complicated object, such as a query to a database or to a search engine…..”
Eine URL besteht aus mehreren Bestandteilen
<Protokoll>://[Login[:Passwort]@]Rechnername.Domain[:Port]/Verzeichnis/Re
ssource
o Die Java -Plattform unterstützt sowohl HTTP- als auch FTP-Ressourcen
Die Klasse URL ermöglicht nicht nur den komfortablen Zugriff auf die oben genannten Teile der URL, sondern
auch auf die hinter der URL stehende Ressource. So kann eine Datei durch Angabe der URL auch direkt
geladen werden.
Beispiel URL-String: http://www.fh-furtwangen.de:80/user/derda/index.html
Zugriff: Wenn man den Inhalt der Ressource abholen möchte, kann dies mit der Methode
openStream() der Klasse URL erledigt werden. Siehe Beispiel weiter unten.
Konstruktoren
Für die Klasse URL gibt es sechs Konstruktoren. Hier zwei davon:
URL(String spec) throws
MalformedURLException
URL(String protocol,
Sting host,
int port,
String file) throws
MalformedURLException
…
Der String spec enthält die URL
Es kann die URL, in ihre Bestandteile zerlegt,
übergeben werden.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 93 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
9
… und dann gab es noch URIs und URNs
Das Paket java.net ab JDK 1.4 unterscheidet zwischen URLs und URIs (Uniform Ressource Identifiers). Eine
URI ist ein syntaktisches Konstrukt, das die verschiedenen Teile der Zeichenfolge spezifiziert, die eine
Webressource kennzeichnen. Eine URL ist eine spezielle Form eines URIs: die URL enthält ausreichend
Informationen, um eine Ressource zu lokalisieren.
URIs, wie beispielsweise [email protected] sind keine Lokatoren (Bezeichner für den Standort von Ressourcen) –
der Identifier enthält keine Daten für die Lokalisierung. Ein solcher URI heisst URN (Uniform Ressource
Name)
In der Java-Bibliothek hat die Klasse URI keine Methoden für den Zugriff auf die Ressource, die der
Bezeichner spezifiziert – ihre einzige Aufgabe ist das Parsing (Wir verfolgen das Thema hier nicht weiter. Siehe
API-Doku und Literatur.)
Wichtige Methoden der Klasse URL
String getFile()
String getHost()
int getPort()
String getProtocol()
URLConnection openConnection() throws
IOException
InputStream openStream()throws
IOException
Boolean sameFile(URL ofher)
Gibt den Datei-Namen, der in dem URL enthalten ist,
zurück.
Liefert den Host-Namen, der in dem URL enthalten ist.
Liefert die Port-Nummer des URLs.
Liefert das Protokoll des URLs.
Öffnet eine neue Verbindung zu der URL-Adresse und
gibt ein URLConnection-Objekt zurück.
Öffnet eine neue Verbindung zu der URL-Adresse, von
der gelesen werden soll, und gibt ein InputStreamObjekt zurück (remember: it’s a byte stream)
(siehe Beispiel unten)
Vergleicht zwei URLs in Bezug auf die URL-Adresse und
gibt true zurück, wenn beide auf dieselbe Ressource
verweisen.
…
Beispiel – Fragment URL-Methode openStream()
Wenn man einfach den Inhalt der Ressource abholen (lesen) möchte, kann man dies mit Hilfe der Methode
openStream(): diese Methode liefert ein InputStream-Objekt zurück, mit dem man den Inhalt der Ressource lesen
kann:
…
try
{
URL url = new URL(urlString);
…
InputStream uin = url. openStream(); // stream of bytes
BufferedReader in = new BufferedReader(new InputStreamReader(uin)); // bridge class
String line;
while ((line = in.readLine()) != null) // Zeilenweise lesen
{
// Zeile verarbeiten
}
}
catch(MalformedURLRxception ex)
{
// ex handling
}
…
9
Anmerkung: Die früher übliche Unterscheidung zwischen URI, URL und URN ist nicht mehr relevant (vgl.
[W3C01]). Wenn Sie den Eindruck vermitteln möchten, ganz besonders auf der Höhe der Zeit zu stehen, können Sie
den Begriff „IRI” (Internationalized Ressource Identifier) verwenden. IRIs werden durch RFC 398 7(vgl.
http://www.w3.org/International/iri-edit/]) definiert und sind im Prinzip URIs mit der Möglichkeit, alle Zeichen aus dem
Unicode-Zeichensatz zu verwenden. Prüfen Sie den Sachverhalt hinsichtlich evtl. Auswirkungen auf Java!
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 94 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Hinweis: Benötigt man jedoch zusätzliche Angaben über die Ressource, muss auf die Klasse URLConnection
zurückgegriffen werden. Diese Klasse bietet eine weitergehende Kontrolle über den Zugriff auf Web-Ressourcen;
z.B. Setup-Parameter und Request-Properties können gesetzt werden. Siehe API-..\..\..\jdk-6u10docs\docs\api\index.htmlDoku und Literatur.
Beispiel – URL-Methoden: (urls.java)
import java.io.*;
import java.net.*;
public class URLs
{
public static void main (String [] args)
{
int l = 1000;
byte[] is_byte = new byte[l];
try
{
URL url = new URL(args[0]); // url-String mit Protokollangebe...
InputStream is = url.openStream();
System.out.println ("Inhalt von " + url.getProtocol()+ "://" +
url.getHost() +": " + url.getPort());
while ((l = is.read(is_byte)) != -1) // Reads some number of bytes from the
//input stream and stores them
//into the buffer array
{
System.out.print(tochar(is_byte));
}
}
catch (MalformedURLException ex)
{
System.out.println ("falsche URL"); // Protocol - Domain - Port:
http://www.ambit.de:80
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
private static char[] tochar(byte[] b)
{
char[] c = new char[b.length];
for (int i = 0; i < b.length; i++)
c[i] = (char) ((b[i] & 0x7F) + (b[i] < 0 ? 128 : 0));
return c;
}
}
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 95 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
8.3 Socket-Programmierung
Sockets wurden erstmalig in BSD-Unix („Berkeley System Distribution“) angeboten (Anfang der 1980er Jahre).
Die Socket-Schnittstelle ist danach in allen bedeutenden Betriebssystemen implementiert worden.
Die Socket-Schnittstelle dient der bidirektionalen Kommunikation zwischen zwei Computern im Netzwerk: mit
den Methoden getOutputStream() und getInputStream() werden entsprechende Byte-Streams
geöffnet.
Die Datenübertragung über Sockets wird grob in drei Schritten vollzogen:
1. Zurest wird eine
Punkt-zu-Punkt-Verbindung zwischen zwei Computern hergestellt. („unicast“)
Dann werden die Daten übertragen;
danach erfolgt der Verbindungsabbau.
2. Bei einer Client-Server-Verbindung wird dazu (vom Client) via IP-Nummer und Port-Nummer des
Servers eine Verbindung zu diesem hergestellt.
3. Bei Peer-to-Peer-Verbindungen braucht auch der Server die IP- und Port-Daten vom Client.
Es gibt Socket-Klassen für den Server (Klasse ServerSocket) und für den Client (Klasse Socket) à siehe
API-Doku. Worin sich die Sockets unterscheiden? Siehe unten „Hinweis“ bei Client- und Server-Sockets.
Schema für die Kontaktaufnahme und die Kommunikation zwischen Client und Server (vgl. unten public
class server und public class client) (11.11.2015)
Der Server
ServerSocket erzeugen
Der Client (rein/pur)
Socket erzeugen
bind() auf Port-Nr.
mit ServerSocket
verbinden
connect()
(u.U. blockierend)
auf Verbindung warten [u.
U. mit Backlog
dimensionieren] und
bestätigen accept()
(u.U. blockierend)
Anfrage schreiben
/
Antwort lesen
.
Thread generieren
Kann auch
schon beim
Erzeugen
erledigt werden.
Siehe API-Docu
Verbindung abbauen
close()
lesen/schreiben
(kooperieren über
Sockets)
ServerSocket schließen
close()
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 96 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Anmerkung: es gibt Stream-Sockets (= verbindungsorientierte TCP-Sockets) und Datagram-Sockets
(verbindungslose UDP-Sockets). Wir betrachten im Folgenden nur die Stream-Sockets. Zu den DatagramSockets siehe Socket-API-Doku.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 97 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Client und Server verwenden Sockets auf unterschiedliche Weise:
Das Server-Programm verwendet einen Socket, der mit einem lokalen Port des Server-Rechners verbunden
(Methode bind()) ist. Damit kann ein Client mit dem Server-Socket per connect() Verbindung
aufnehmen.
Das Client-Programm verwendet einen Socket, der eine Verbindung zu einem Port des Server-Rechners
aufbaut (mit der Methode connect()).
8.3.1
Client-Sockets: die Klasse
socket („Kommunikationssocket “)
Hinweis: In der Klasse socket spielt stets die Adresse (in diversen Formen) des Servers eine Rolle. In der Klasse
ServerSocket spielt nur der Port – über den der Service erreicht werden soll – eine Rolle. Siehe auch Hinweis zu
ServerSocket.
Konstruktoren der Klasse Socket:
Socket(String host,
int port) throws IOException
Socket(String host,
int port,
InetAddress localAddr,
int localPort) throws IOException
Socket(InetAddress address,
int port) throws IOException
Socket(InetAddress address,
int port,
InetAddress localAddr,
Int localPort) throws IOException
Als Parameter werden Host-Name und
Port-Nummer des Servers angegeben.
Creates a stream socket and connects it to the specified
port number on the named host.
Wie oben.
Wie oben
… vom eigenen Rechner / mit bind() darauf
Wie oben, jedoch Host-Bezeichnung nicht über String
sondern InetAddress-Objekt.
Analog oben, mit bind() des Sockets auf locale Adresse
/ Port.
Creates a socket and connects it to the specified remote
address on the specified remote port.
Wird der angegebene Zielrechner nicht gefunden, wird eine Exception vom Typ UnknownHostException (von
IOException abgeleitet) ausgelöst.
Wichtige Methoden der
class Socket
Die Klasse Socket besitzt einige get- und set-Methoden zum Abfragen und Setzen der Verbindungseigenschaften.
Die wichtigsten Methoden sind getOutputStream() und getInputStream(). Beide geben ein
entsprechendes Byte-Stream-Objekt zurück, über das der Datenaustausch erfolgt, wenn die Verbindung erfolgreich
eingerichtet werden konnte. (Siehe 7.4 „Was kommt den da im Strom?“: diese Streams werden dann ggf. noch
überführt, je nach dem, was im Stream kommt (Text, binäre Daten/serialisierte Objekte).
InetAddress getInetAddress()
InetAddress getLocalAddress()
int getPort()
public OutputStream
throws IOException
getOutputStream()
Liefert die Adresse des Host-Computers, zu dem die
Verbindung besteht, das ist der Netzknoten zu dem der
Client connect()ed ist. Wird die Methode im Server für
den Server-Socket aufgerufen, so liefert sie die eigene
Adresse (des Server Hosts).
Gibt die lokale Adresse des Client zurück, mir der der
Client-Socket verbunden ist. (Das Analogon im Server
heist getLocalSocketAdress().)
Gibt die Portnummer des Hosts zurück
Erzeugt ein OutputStream-Objekt (das ist ein
Byte-Stream) für das Übertragen von Daten zum
Server ; mit write(byte[]...);
10
public InputStream getInputStream()
throws IOException
10
auch: Stream-
„Fitting“ um Ojekte zu
streamen/writeObject() (serializable)
Gibt ein InputStream-Objekt (das ist ein Byte-
Siehe auch unten: Kapitel „Was kommten den da im Stream? Sockets und Streams“
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 98 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Stream) zurück für das Lesen von Daten vom
Server ; mit read(byte[]...);
11
auch: Stream-
„Fitting“ um Ojekte zu
streamen/readObject() (serializable)
public int getSoTimeout() throws
SocketException
public int setSoTimeout(int timeout)
throws SocketException
8.3.2
ServerSockets: die Klasse
Liefert die Zeit in ms, die eine Leseanforderung an den
Host dauern darf, bevor eine InterruptedIOException
ausgelöst wird (0 entspricht unbegrenzter Wartezeit).
Setzt die Timeout-Zeit in ms für eine Leseanforderung
an den Host. 0 entspricht unbegrenzter Wartezeit.
ServerSocket
Bevor ein Client die Kommunikation mit einem Server aufnehmen kann, muss natürlich der Server gestartet werden.
Der Server bleibt dann in der accept()-Methode geblockt, bis sich ein Client bei ihm anmeldet.
Hinweis 1:
Beim ServerSocket spielt die IP-Addresse des Partners keine Rolle (vgl. oben Hinweis zum Client Socket.)
Ursache: der ServerSocket wird nie als Erster initiativ! Die Initiative geht immer vom Client aus,
deswegen muss dieser die Adresse des Severs wissen.
Für die „Rückwärtskommunikation“ zum Client bekommt der Server einen geeigneten Client-Socket von
der Methode accept() als Returnwert zur Verfügung gestellt. Für diesen Client Socket kann sich der
Server mit getOutputStream() ein OutputStream Objekt besorgen, über dass er dem Client Infos
zukommen lassen kann.
Zusammenfassend kann man also sagen,
der ServerSocket dient nur für die Kontaktaufnahmen mit dem Server.
Die Kommunikation läuft über die Client Sockets (=„Kommunikationssocket“).
Hinweis 2:
Wenn über einen einzigen Socket gelesen und geschrieben werden soll, geht das zwar, wie wir eben
gesehen haben, ist jedoch „wenig komfortabel“/“flexibel“.
Man muss sein eigenes „Protokoll“ implementieren, d.h. der Client meldet durch ein „over“, dass jetzt der
Server ein Antwort (über den selben Socket) geben kann.
Für eine echte Peer-to-Peer-Architektur würde man beiden Kommunikationspartner jeweils einen eignen
ServerSocket und einen eigenen Client Socket spendieren.
Konstruktoren der Klasse ServerSocket
ServerSocket()
ServerSocket(int port)
ServerSocket(int port, int backlog)
Creates an unbound server socket. Siehe Beispiel
unten: ist dann noch mit bind() an einen Port zu binden.
Creates a server socket, bound to the specified port.
Mit dem zweiten Parameter kann die maximale Länge
der Warteschlange bestimmt werden. Wird eine
Verbindung an einem Socket mit voller Warteschlange
angemeldet, so wird die Verbindung abgelehnt.
Wichtige Methoden der Klasse ServerSocket
Socket accept()
void bind(SocketAddress endpoint)
11
Server wartet auf Verbindungsanfragen am betreffenden
Port. Kommt eine Verbindungsanforderung (connect())
von einem Client, so liefert die Methode das SocketObjekt für die Client-Verbindung. (Wird die Verbindung
vomClient beendet, so kann der Server einen weiteren
Client bedienen.)
Bindet ServerSocket an angegebene IP-Adresse und
Port
Siehe auch unten: Kapitel „Was kommten den da im Stream? Sockets und Streams“
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 99 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
void bind(SocketAddress endpoint, int backlog)
void close()
InetAddress getInetAddress()
int getLocalPort()
int getSoTimeout()
void setSoTimeout(int timeout)
8.3.3
Bindet ServerSocket an angegebene IP-Adresse und
Port. Dimensioniert backlog.
Schließt den Server-Socket.
Gibt die (eigene) Adresse zurück, mit der der Socket
verbunden ist
Gibt den Port zurück, mit dem der Server-Socket
verbunden ist.
Liefert die Timeout-Zeit in ms, für die ein Client-Socket
den Server-Socket blockieren darf.
Setzt die Timeout-Zeit in ms, die ein Client-Socket den
Server blockieren darf.
Beispiel – Server-Sockets (server.java):
import java.net.*;
import java.io.*;
import java.util.*;
public class Server
{
public static void main (String [] args) throws IOException
{
System.out.println (">>> Server wird gestartet");
/* ALTERNATIVE A) Für LOKALE Sockets:
****************************************
// The range for assigned ports managed by the IANA is 0-1023
// Creates ServerSocket bound to a specific (local) port;
// Phantasie-Port > 45678
ServerSocket myServerSocket = new ServerSocket ( 56789 );
// incl. bind() to port ServerSocket
//*/
//* ALTERNATIVE B) Für INTERNET-Sockets
***************************************
// class InetSocketAdress extends class SocketAdress
InetSocketAddress myInetSocketAddress = new
InetSocketAddress("localhost",56789);
ServerSocket myServerSocket = new ServerSocket(); // unbound ServerSocket
myServerSocket.bind(myInetSocketAddress);
// we bind()it
//*/
f or (; ; )
{ // start infinite loop
Socket myClientSocket = myServerSocket.accept ();
// accept() blocked und liefert Client-Socket
// zurück; von dem kann gelesen werden,
// wenn's ein schreibender Client ist
BufferedReader in = new BufferedReader(new InputStreamReader
//bridge from byte streams to character streams to text
(myClientSocket.getInputStream()));
System.out.println ("--> Local Address angemeldet: "
+ myClientSocket.getLocalAddress()); // mit wem?
System.out.println ("--> InetAddress angemeldet: "
+ myClientSocket.getInetAddress());
String s;
try
{
while ((s = in.readLine()) != null)
// Lesen vom Client-Socket
System.out.println ("> " + s);
// und Ausgabe des Gelesenen
}
catch (Exception e){}
System.out.println ("<-- LocalAddress (address to which the socket is bound): " +
myClientSocket.getLocalAddress());
System.out.println ("<-- InetAddress (addr to which the socket is connected): " +
myClientSocket.getInetAddress());
myClientSocket.close();
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 100 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
} // end infinite loop
}
}
8.3.4
Beispiel – Client-Sockets (client.java):
import java.io.*;
import java.net.*;
public class Client
{
public static void main (String [] args) throws IOException
{
String s; // wird via Keyboard gefüllt und dann auf Socket geschreiben
if (args.length != 1)
{
System.out.println ("falscher oder fehlender Parameter\n"+
"Geben Sie als Parameter die Adresse des Servers an. Z.B.
'localhost'");
System.exit(1);
}
try
{
// ENTWEDER: Client Socket kreieren (UND gleich den Server connect()en)
// Socket clientSocket = new Socket (args[0], 56789);
// ODER; unconnected Socket kreieren und DANACH connect()en:
InetSocketAddress serverInetSocketAddress =
new InetSocketAddress(" localhost ", 56789 );
Socket clientSocket = new Socket();
clientSocket.
connect (serverInetSocketAddress);
System.out.println ("Verbindung zu Server mit der Adresse "
+ clientSocket.getInetAddress()
+ " hergestellt.");
//client braucht in unserem Beispiel kein bind(), da er ja nicht über
//den/einen Port angesprochen
//werden soll; für peer-to-peer-Applikationen wirds gebraucht:
//InetSocketAddress myInetSocketAddress =
//
new InetSocketAddress("localhost",45678);
// clientSocket.bind(myInetSocketAddress);
BufferedWriter
out
= new BufferedWriter( // zum Schreiben auf Socket
new OutputStreamWriter(clientSocket.getOutputStream()));
// bridge from text to character streams to byte streams
BufferedReader
do
{
}
in
= new BufferedReader( // zum Lesen von Console
new InputStreamReader (System.in));
System.out.print ("Nachricht an Server: ");
s = in.readLine(); // Lesen von Keyboard
out.write (s);
// Schreiben auf den Client-Socket
out.newLine();
out.flush();
}
while ( !s.equals ("ende") && clientSocket != null);
in.close();
out.close();
}
catch ( Exception e )
{
e.printStackTrace();
System.exit(1);
}
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 101 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
}
Anmerkung: Dem Javaprogramm Kommandozeilenargumente mitgeben
Textpad: „Konfiguration“ -> „Einstellungen“ -> „Extras“ -> „Java Programm starten“ -> „Parameterabfrage“
Eclipse: „Run“ -> Open Run Dialog bzw. „Run Configuration“ -> Tab „(x) Arguments“
8.3.5
Was kommt den da im Strom? Sockets und Streams
Byte-Streams, geliefert von den SocketMethoden getOutputStream() und getInputStream().
Was die Kommunikationspartner über die Sockets austauschen sind
Was in den Streams „drinsteckt“, wissen die Kommunikationspartner (wie der Byte-Strom zu interpretieren ist):
Wird Text über die Sockets übertragen, so wird der Stream in BufferedReader und BufferedWriter unter
Zuhilfenahme der Bridge-Klassen OutputStreamWriter und InputStreamReader
überführt, um dann z.B. mit write(char[]...) und mit read(char[]...) CharacterArrays zu schreiben und zu
lesen.
Werden binäre
Daten über die Sockets übertragen, so wird der Stream in DataInputStream und
DataOutputStream überführt, um dann z.B. mit write(byte[]...) und mit read(byte[]...) Einzelbyte
oder Byte-Arrays zu schreiben und zu lesen.
Bei serialisierten
Objekten verwendet man dagegen ObjectInputStream und
ObjectOutputStream. um dann z.B. mit writeObject(Object)und mit Object =readObject() Objekte
zu schreiben und zu lesen.
In unseren Beispielen oben übertragen wir Text über Sockets und nutzen deshalb die Bridge-Klassen
Bild: ServerSockets und Client-Sockets.
Über ServerSockets wird der Kontakt aufgenommung. Kommuniziert wird über die Client Sockets:
Die Kommunikation in der Case Study
ServerSocket
Server
ClientSocket
c li en t Socket.c on ne c t(serverInetSocketAddress
);
ClientSocket
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 102 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
ClientSocket
ServerSocket
Server
(ggf. multi-threaded)
ClientSocket = accept()
f or (; ; )
ClientSocket
getInputStream() bzw.
getOutputStream()
gibt es nur für
ClientSocket
Client Sockets
Bearbeiten Sie die Case Studies und lösen Sie die Übungsaufgeben zu
diesem Kapitel! (Siehe Unterlagen hierzu auf dem Materialserver.)
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 103 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
9. RMI Remote Method Invocation
Was Sie lernen:
wie mit Java auf entfernte Objekte zugegriffen wird
wie Client- und Server-Programme mittels verteilter Objekte programmiert werden
Voraussetzungen:
Arbeiten mit Interfaces
Arbeiten mit den Tools
o RMI-Registry-Tool: rmregistry
o RMI-Compiler: rmic
o Policytool für den Sicherheitsmanager: policytool
(Hinweis: Das Kapitel basiert auf dem Buch J. Anton Illik: „Verteilte Systeme – Architekturen und SoftwareTechnologien“, Expertverlag, Stuttgart-Renningen, 2007.
Im Buch wird die RMI-Variante unter Java VM-Version < 5 und JDK 1.4 beschrieben. Hier im
Skript beschreiben wir „new RMI“ ab VM-Version 5 zusammen mit dem JDK 1.6.)
9.1 Grundlagen
RMI (remote method invocation) ist die Java-Implementierung des „Remote
Procedure Calls“ (RPC). Der Begriff „method invocation“ ist im Java-Umfeld
natürlich treffender, da im Zusammenhang mit Java die Methoden eines
entfernten Objekts aufgerufen werden. Abgesehen davon, sind viele Details,
die im Zusammenhang mit dem Methodenaufruf stehen, wie beispielsweise
Parameter- und Returnwert-Übergabe, mit den gleichen Problemen behaftet, wie beim RPCMechanismus (siehe Buch „Verteilte Systeme“, S. 94). Glücklicherweise passen damit auch die
für den RPC kennengelernten Lösungsverfahren (wie z.B. der Einsatz von Stubs für das
Marshalling/Unmarshalling).
RMI impliziert also für die Entwicklung verteilter Anwendungen einen Entwurf, der auf die
Verteilung von Objekten setzt, lokale Objekte kommunizieren mit entfernten Objekten.
Das entfernte Objekt kann dabei entweder auf einem entfernten Rechner residieren oder aber
auch auf dem gleichen Rechner gehostet werden wie das aufrufende Objekt – entscheidend dabei
ist, dass das lokale und das entfernte Objekt in zwei verschiedenen virtuellen Maschinen
(virtual machine12) laufen.
Der Aufruf der Methode eines entfernten Objekts sieht dabei genauso aus, wie der Aufruf einer
Methode von einem lokalen Objekt. Im Gegensatz zum lokalen Methodenaufruf gibt es für den
entfernten Methodenaufruf allerdings mehr Exceptions13, da ja beispielsweise der entfernte
Methodenaufruf durch einen Verbindungszusammenbruch scheitern kann.
12
Java-Programme werden vom Java-Compiler in den Byte-Code übersetzt, der vom Prozessor eines Computers
nicht direkt ausgeführt wird. Die Virtual Machine ist die Ablaufumgebung für die in den Byte-Code übersetzten JavaProgramme. Diese Ablaufumgebung sorgt für die Interpretation des Byte-Codes.
13
Durch die Anzeige einer Exception signalisiert die Virtual Machine der Applikation eine Ausnahmesituation. Auf
eine angezeigte Ausnahmesituation muss die Applikation reagieren, andernfalls wird die Applikation von der Virtual
Machine abgebrochen..
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 104 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Alle notwendigen RMI-Bestandteile (Klassen, Interfaces, RMI-Compiler, RMI-Registry, usw.) sind
bereits in der Java Standard Edition JSE enthalten, so dass der Java-Entwickler bereits ohne
den Einsatz von JEE (Java Enterprise Edition) einfache verteilte Applikationen entwickeln kann.
Eine weitere Ursache, dass RMI bereits in JSE enthalten ist, liegt darin, dass für die Entwicklung
verteilter Java-Applikationen auf der Basis von RMI keine Application Server notwendig sind,
wie es sonst bei anderen JEE-Mechanismen (Enterprise Java Beans, Servlets, JSP) der Fall ist.
9.2 Die Bestandteile einer RMI-Anwendung
Eine RMI Anwendung besteht im Wesentlichen aus folgenden Bestandteilen:
•
•
•
•
•
•
•
einem RMI-Client (= der Teil des Anwendungsprogramms, der die zentral vom RMIServer zur Verfügung gestellte Funktionaliät nutzt),
Stub-Klassen auf der Client-Seite14,
einem RMI-Server (= der Teil des Anwendungsprogramms, der den RMI-Clients
zentrale Applikationsfunktionen zur Verfügung stellt),
Skeleton-Klassen auf der Server-Seite (in den jüngeren Java-Releases ist das Skeleton
identisch mit dem Stub; Stub auf der Server-Seite wird Skeleton genannt) und
einer RMI-Registry (ist ein Service, bei dem der RMI-Server (zur Laufzeit) die Objekte
registriert, die von RMI-Clients genutzt werden können). Das Programm rmiregistry
liegt in java/bin des JDK.
Wenn der RMI-Server (bzw. eines seiner bei der RMI-Registry registrierten Remote
Objekte) nicht skalare Ergebnisse zurückliefern muss, kommt auch noch der
RMISecurityManager ins Spiel (für den Austausch serialisierter Objekte).
Der RMISecurityManager braucht dann ein Policy-File (dies heißt per default .java.policy,
kann vom Entwickler aber auch anderst benannt werden) in dem die Grants (= die
gewährten Zugriffsrechte) verzeichnet sind. Hergestellt, bzw. bearbeitet wird das Policy-File
mit dem Policytool. Das Programm policytool liegt in java/bin des JDK.
Auf der Client-Seite kümmert sich der Stub um die eigentliche Kommunikation mit der
Serverseite. Ein Stub-Objekt ist ein client-seitiger Stellvertreter für das entfernte Objekt im RMIServer. Im RMI-Server steht dem Stub als konkreter Kommunikationspartner ein entsprechendes
Skeleton-Objekt15 zur Verfügung.
Ruft ein RMI-Client eine Methode von einem entfernten Objekt auf, so wird der Aufruf an das
Stub-Objekt delegiert, das dann für die Weiterleitung des Methodenaufrufs an das entfernte
Objekt sorgt. Dort kümmert sich als serverseitiger Vertreter für das aufrufende Objekt das
Skeleton-Objekt um den Aufruf der gewünschten Methode. Stub und Skeleton übernehmen
jeweils auf ihrer Seite die Abwicklung der Kommunikation (z.B. auch das Marshalling, bzw.
Unmarshalling).
14
Es besteht auch die Möglichkeit, sich die vom Client benötigte Stub-Klassen dynamisch vom Server geben zu
lassen. Diesen Ansatz werden wir hier zunächst nicht weiter verfolgen.
15
Das Skeleton-Objekt ist notwendig, wenn mit der Java-Entwicklungsversion JDK1.1 gearbeitet wird. In den
Nachfolgeversionen wurde das Skeleton-Objekt auf der Serverseite durch das Stub-Objekt ersetzt, das auch auf der
Client-Seite Verwendung findet.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 105 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Weitergabe
Client
Datenstrom weiterleiten
Stub
Aufruf einer
Marshalling der
entfernten Methode
Parameter
Aufruf der Methode
Stub
Remote-Objekt
Unmarshalling der
Parameter
Bild 1: Die Rolle von Stub und Skeleton
Die Rolle der RMI-Registry
Ein RMI-Client, der einen bestimmten Dienst aufrufen möchte, fragt zunächst bei der RMIRegistry nach, welches entfernte Objekt dafür das richtige ist und bekommt als Antwort eine
entsprechende Objektreferenz auf das entfernte Objekt im Server zurück.
Die Adresse der zuständigen RMI-Registry muss der Client kennen (URL + Port (1099 per
default)). Die RMI-Registry muss auf demselben Rechner laufen, auf dem das Remote-Objekt
ausgeführt wird.
Der RMI-Server dient dazu, ein entferntes Objekt zu instantiieren und es unter einem bestimmten
Dienstenamen in der RMI-Registry zu registrieren. Das Programm rmiregistry liegt in
java/bin des JDK. Per default ist die RMI-Registry an den Port 1099 gebunden.
9.3 Das Ablaufschema eines entfernten Methodenaufrufs
Im Folgenden geben wir schematisch den Ablauf eines entfernten Methodenaufrufs wieder und
zeigen dabei, welche Rolle die einzelnen Bestandteile der RMI-Anwendung spielen. Danach
besprechen wir die für die Herstellung notwendigen Werkzeuge. In einem abschließenden
konkreten Beispiel werden die Details dann auf der Ebene des Codes beleuchtet.
Wir gehen bei der Beschreibung unten davon aus, dass die verteilte RMI-Applikation hergestellt
und bereits auf die entsprechenden Rechner verteilt ist. Beim Start der verteilten Applikation läuft
dann folgendes ab.
1. Schritt: Der RMI-Server registriert ein entferntes Objekt bei der RMI-Registry unter einem
eindeutigen Namen. (In unserem Beispiel unten ist die das Server-Programm
HelloServer.java)
RMI-Server-Host
RMI-Registry
RMI-Server
J
V
M
B
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 106 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Bild 2: Der RMI-Server registriert ein Objekt in der RMI-Registry
2. Schritt: Der Client (in unserem Beispiel heißt der Client HelloClient) schaut bei der RMIRegistry unter dem Namen (in unserem Beispiel lautet der Objekt-Name für das RemoteObjekt HelloImpl) nach und bekommt im positiven Fall eine Objektreferenz, die seinem
RMI-Client-Host
RMI-Server-Host
RMI-Registry
RMI-Client
Remote Interface entsprechen muss (siehe Bild 3).
Bild 3: Die RMI-Registry liefert die Objektreferenz zurück
3. Schritt: Der Client (HelloClient) ruft die gewünschte Methode aus der Objektreferenz auf.
4. Schritt: Der Server (HelloServer) gibt dem Client die Rückgabewerte16 dieses Aufrufs, oder
der Client bekommt eine Fehlermeldung (bei einem Verbindungsabbruch, beispielsweise.)
Das Bild 49 fasst die Abläufe nochmals insgesamt zusammen:
RMI-Client-Host
RMI-Server-Host
RMI-Client
RMI-Registry
Stub
RMI-Server
J
V
M
A
OS A
Skeleton
OS B
J
V
M
B
Bild 4: RMI Gesamtablauf
9.4 Die Hilfsmittel RMI-Compiler und RMI-Registry
Die Aufgabe des RMI-Compilers17 rmic ist die Erzeugung von Stub (und Skeleton) aus den
Klassendateien. Für eine Klasse, die das Interface Remote implementiert, erzeugt der RMI16
Ist der Rückgabewert skalar, so ist kein RMISecurityManager notwendig.
Note: If the server needs to support clients running on pre-5.0 VMs, then a stub class for the remote object
implementation class needs to be pregenerated using the rmic compiler, and that stub class needs to be made
available for clients to download. See the codebase tutorial for more details. Siehe:
http://java.sun.com/javase/6/docs/technotes/guides/rmi/index.html
17
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 107 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Compiler rmic die benötigten Stubs. Standardmäßig werden die erzeugten Stubs in demselben
Verzeichnis abgelegt, wie die zugrunde liegenden Klassendateien. Für den RMI-Compiler gibt es
eine Reihe von Optionen; u.a. ist damit auch möglich, Stubs in anderen Verzeichnissen ablegen
zu lassen.
9.5 Der RMISicherheitsmanager (RMISecurityManager)
Der RMISecurityManager legt fest, welche Rechte „fremde“ Klassen (remote Klassen) an lokale
Klassen haben. Um ein gewisses Maß an Sicherheit zu gewährleisten, sollte jedes RMI-ServerProgramm, einen RMISecurityManager haben, ansonsten können nur Klassen aus dem lokalen
classpath verwendet werden.
Um einem Programm die passenden Rechte zu geben, werden die zugesicherten Rechte in einer
Rechte-Datei gesammelt, der Policy-Datei.
Wenn die Laufzeitumgebung (virtual machine) gestartet wird, wird ein Verweis auf diese PolicyDatei mitgegeben (mit der Option –D, also für unser Beispiel: $java –D.java.policy HelloServer
1099), damit der Security-Manager Berechtigungen vergeben kann. Die Policy-Dateien bestehen
aus einer Reihe von grant-Anweisungen.
Das grafische Dienstprogramm policytool (siehe Bild 5: Das policytool) bietet die Möglichkeit,
Applikationen und signierten Applets spezielle Rechte einzuräumen oder zu verweigern. Das
Policy-Tool nimmt dem Entwickler die Arbeit ab, die Rechte-Dateien von Hand zu editieren.
Bild 5: Das policytool (aus dem JDK-Verzeichnis java/bin)
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 108 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Nach dem Aufruf des Programms policytool öffnet sich ein Fenster, das uns einige Menüpunkte
bereitstellt, über die man bestehende Rechte-Dateien editieren oder auch neue anlegen kann.
Neue Einträge für die Zugeständnisse der Laufzeitumgebung an das Programm werden über das
Menü Add Policy Entry („Richtlinieneintrag hinzufügen“) gemacht. Folgende Tabelle zeigt einige
Permissions und ihre Bedeutungen
Policy Permissions
Bedeutung
AllPermission
Anwendung oder Applet dürfen alles
FilePermission
Zugriff auf Dateien und Verzeichnisse
NetPermission
Zugriff auf NetzwerkRessourcen
PropertyPermission
Zugriff auf Systemeigenschaften
ReflectPermission
Zugriff über Reflection auf andere Objekte
RuntimePermission
Einschränkungen von Laufzeitsystemen
SecurityPermission
allgemeines Sicherheitskonzept, etwa für den
Zugriff auf Policies
SerializablePermission Beschränkung der Serialisierung
SocketPermission
Spezielle Einschränkungen an Sockets
Bild 6: Sicherheitseinstellungen (Permissions)
Aus welchen Bestandteilen besteht nun der RMI-Client, bzw. der RMI-Server in unserem
Beispiel?
9.6 Die Komponenten und Bestandteile auf der Server-Seite
Auf der Server-Seite liegen:
• Das Remote Interface des entfernten Objekts (remote Interface), welches das JavaInterface remote erweitert. (In unserem Beispiel unten das interface Hello; File:
Hello.java)
• Das entfernte Objekt steckt in unserem Beispiel in Server-Programm (File:
HelloServer.java).
• Stub und Skeleton für das entfernte Objekt ist das generierte File:
HelloImpl_Stub.class. (Wird durch den RMI-Compiler rmic erzeugt: $rmic HelloImpl)
• Der Server, der das entfernte Objekt instanziiert und bei der RMI-Registry anmeldet. In
unserem Beispiel unten HelloServer; (File: HelloServer.java).
• Die Policy-Datei. In unserem Beispiel unten die Datei .java.policy. (Per default heißt
die Policy-Datei üblicherweise so und liegt üblicherweise im $HOME-Verzeichnis und
enthält mehrere Policies). Bei uns liegt die Datei im Verzeichnis des Servers. Die
Sicherheitseinstellungen müssen der laufenden VM ($java-Aufruf) bekannt sein, damit sie
die Sicherheitsüberwachung machen kann. Die Policy-Datei kann im Verzeichnis des
Servers liegen und muss mit der Option –D beim java-Aufruf mitgegeben werden. In
unserem Beispiel: $java –D.java.policy HelloServer)
• Die RMI-Registry. Wird auf der Server-Seite gestartet (manuell oder vom Betriebssystem
als Service: $rmiregistry, bzw. unter Angabe des Ports, z.B. $rmiregistry 1099).
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 109 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
9.7 Die Komponenten und Bestandteile auf der Client-Seite
Auf der Client-Seite liegen:
• Das Interface des entfernten Objekts, welches das Java-Interface remote erweitert. (In
unserem Beispiel unten das interface Hello)
• Der Stub18 für das entfernte Objekt. In unserem Beispiel unten ist dies
HelloImpl_Stub.class. (Ist ab Java VM-Version 5 nicht mehr notwendig.)
• Der Client, der das entfernte Objekt nutzen möchte. In unserem Beispiel unten ist dies
HelloClient.
9.8 Vollständiges RMI-Beispiel
Im Folgenden Beispiel werden die oben genannten Bestandteile konkret vorgestellt und wichtige
Details erläutert.
Entfernte Methoden werden durch das Interface java.rmi.Remote definiert:
// Java-Beispiel: Das remote Interface und die entfernte Methode sayHallo()
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Hello extends Remote{ // Liegt auf der SERVERSEITE.
// Liegt ebenfalls auf der CLIENTSEITE.
public String sayHello (String language) throws RemoteException;
}
// das Interface gibt vor, wie die Signatur von sayHello() aussieht
// sayHello() gibt einen String zurückgibt
♣
Bild 7: Das remote Interface Hello.java
Das erweiterte remote-Interface wird dann implementiert um als entferntes Objekt genutzt zu
werden.
Server-Programme HelloServer.java implementieren also das Interface Remote und erweitern
java.rmi.server.UnicastRemoteObject, welches die wichtigsten Methoden für die Verwendung
von RMI bereitstellt.
Bis jetzt haben wir:
•
•
•
Ein Remote-Objekt samt Interface (Hello), dessen Methoden von einer entfernten
JVM aufgerufen werden können. Das Programm HelloServer.java meldet das
Remote Object bei der Registry an.
Ein Client-Programm, das einen solchen Aufruf vornimmt.
Die Stub-Generierung mit rmic ist ab Java VM-Version 5 nicht mehr notwendig
18
Wenn der Stub zur Laufzeit nicht auf der Client-Seite vorhanden ist, wird er beim Name-Binding vom Server
zurückgegeben.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 110 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Für eine Klasse, die das Interface Remote implementiert, erzeugt der RMI-Compiler rmic die
benötigten Stubs und Skeletons:
$javac Hello.java
$javac HelloClient.java
// bis Version 5, danach entfällt der rmic-Einsatz
$rmic HelloImpl # erzeugt HelloImpl_Stub.class // bis Version 5
Für Interessierte: Durch rmic -keepgenerated HelloImpl kann man die erzeugten Stubund Skel-Klassen als Java-Quelltext anschauen.
Wie bekommt ein Client Zugriff auf ein Hello-Objekt?
Wir wissen: Stub für Client und Server.
•
•
•
Der Server muss den Stub unter einem (eindeutigen) Namen bei einer rmiregistry
anmelden.
Diese rmiregistry muss auf demselben Rechner laufen, auf dem das Remote-Objekt
(Stub) ausgeführt wird!
Der Client muss
1. Über die Klasse LocateRegistry und die Methode getRegistry(host) sich auf
dem Remote Host eine Referenz auf das Remote Object besorgen
2. den Namen des Objekts kennen, unter dem der Stub bei der Registry bekannt
ist.
// Java-Beispiel: Ein RMI-Server. File: HelloServer.java (Variante 1)
import java.rmi.*;
import java.rmi.server.*;
import java.rmi.registry.*;
public class HelloServer implements Hello {
public HelloServer() {};
public String sayHello (String language) throws RemoteException
{
String lang = language.toLowerCase();
switch (lang.charAt (0))
{
case'd':return "Hello!";
case'f':return "Salut!";
case'i':return "Ciao!";
default: return"Hi!";
} // end switch
} // end sayHello()
public static void main(String[] args)
{
try {
HelloServer obj = new HelloServer();
Hello stub = (Hello)UnicastRemoteObject.exportObject(obj, 0);
A2:
// Bind the remote object's stub in the registry
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 111 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Registry registry = LocateRegistry.getRegistry();
registry.bind("Hello", stub);
System.out.println("HelloServer ready.");
} // end try
catch (Exception e)
{
System.err.println("Server exception: " + e.toString());
e.printStackTrace();
} // end catch
} //end main()
} // end class HelloServer
♣
Bild 8: Die Klasse HelloServer.java (Variante 1)
// Java-Beispiel: Ein RMI-Server. File: HelloServer.java (Variante 1)
//
import
import
import
import
import
java.rmi.*;
java.rmi.server.*;
java.rmi.registry.*;
java.io.*;
java.net.*;
public class HelloServer implements Hello {
public HelloServer() {};
public String sayHello (String language) throws RemoteException
{
String lang = language.toLowerCase();
InetAddress serverInetAddr;
// just to communicate the servers IP+name to the client
String hostname;
// just to communicate the servers IP+name to the client
try {
serverInetAddr = InetAddress.getLocalHost();
hostname = serverInetAddr.getHostAddress()
+ " "
+ serverInetAddr.getHostName();
System.out.println(hostname);
switch (lang.charAt (0))
{
case'd':return "Hello!" + " vom Server " + hostname;
case'f':return "Salut!" + " vom Server " + hostname;
case'i':return "Ciao!" + " vom Server " + hostname;
default: return"Hi!"+ " vom Server " + hostname;
} // end switch
}
catch (Exception ex){return "ERROR"; }
} // end sayHello()
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 112 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
public static void main(String[] args)
{
try {
HelloServer obj = new HelloServer();
Hello stub = (Hello)UnicastRemoteObject.exportObject(obj, 0);
// Bind the remote object's stub in the registry
Registry registry = LocateRegistry.getRegistry();
registry.bind("Hello", stub);
System.out.println("HelloServer ready.");
} // end try
catch (Exception e)
{
System.err.println("Server exception: " + e.toString());
e.printStackTrace();
} // end catch
} //end main()
} // end class HelloServer
♣
Bild 9: Die Klasse HelloServer.java (Variante 2: Server sendet seine IP und seinen Namen mit der Antwort zum Client)
Erläuterungen zu den Marken im RMI-Server-Beispiel:
A1: Ein RMISecurityManager wird eingerichtet (Siehe API-Doku: „RMI's class loader will not
download any classes from remote locations if no security manager has been set”) (fehlt hier
noch)
A2: Aus der API-Doku: Note that a getRegistry call does not actually make a connection to the
remote host. It simply creates a local reference to the remote registry and will succeed even
if no registry is running on the remote host.“ U.U. würde der Server die Registry selbst
starten. Siehe hier zu API-Doku.
// Java-Beispiel: Ein RMI-Client
import
import
import
import
java.rmi.*;
java.rmi.server.*;
java.rmi.registry.LocateRegistry;
java.rmi.registry.Registry;
public class HelloClient {
private HelloClient() {};
static public void main (String[]args)
{
String host = (args.length<1) ? null :args[0];
try{
Registry registry = LocateRegistry.getRegistry(host);
Hello stub = (Hello) registry.lookup("Hello");
System.out.println (stub.sayHello("Englisch"));
System.out.println (stub.sayHello("Deutsch"));
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 113 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
System.out.println (stub.sayHello("Italienisch"));
System.out.println (stub.sayHello("Franzoesisch"));
}
catch (Exception e)
{
System.err.println ("HelloClient failed, caught exception" +
e.getMessage());
System.err.println("Client exception: " + e.toString());
e.printStackTrace();
}
} // end main()
} // end class
♣
Bild 10: Die Klasse HelloClient.java
Das Beispiel in Betrieb nehmen
Insgesamt werden die Beispielprogramme übersetzt mit
$javac Hello.java HelloServer.java HelloClient.java
[Hinweis/Note: If the server needs to support clients running on pre-5.0 VMs, then a stub class for the remote object
implementation class needs to be pregenerated using the rmic compiler, and that stub class needs to be made available for clients
to download. See the codebase tutorial for more details.]
Um das Beispiel auszuführen:
•
die Registry starten $rmiregistry oder via $start rmiregistry
[To start the registry, run the rmiregistry command on the server's host. This command produces no output (when
successful) and is typically run in the background. For more information, see the tools documentation for rmiregistry]
•
den Server starten $java HelloServer
oder via $start java HelloServer
• den Client starten $java HelloClient
Siehe auch
„Getting Started Using Java RMI“
http://java.sun.com/javase/6/docs/technotes/guides/rmi/index.html -> „Getting Started“
Hinweis 1: auf Windows 7 machte die Ausführung bisher hartnäckig Probleme!
Hinweis 2: Auf Ubuntu 9.10 (Karmic Koala) läuft das Beispiel einwandfrei,
+) wenn Client und Server gemeinsam auf der Ubuntu-Maschine gehostet sind,
++) wenn Client auf Ubuntu mit Server auf Vista zusammenarbeitet
--) Es geht nicht: Server auf Ubuntu und Client auv Vista –> Client auf Vista crashed
mit „connection refused“
9.9 Zusammenfassung
RMI ist die objektorientierte Variante von RPC und exklusiv auf Java abgestimmt. Daraus
resultiert eine mangelnde Integrationsfähigkeit mit anderen Verteilungstechniken und
Programmiersprachen. Aus der Sicht der Programmierung ist einer der wesentlichen Vorteile,
dass bei nur geringem Initialisierungsaufwand lokale und entfernte Methodenaufrufe gleich
gestaltet sind. RMI versteckt alle Details der Netzwerkkommunikation vor dem Entwickler. Dieser
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 114 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
muss sich weder mit Fragen des Datenaustauschs über Sockets, noch mit Fragen zum Protokoll
auseinandersetzen.
Allerdings gilt es zu beachten, dass auch der parallele, gleichzeitige Zugriff auf entfernte Objekte
möglich ist und dass der Entwickler für solche Fälle die Zugriffssynchronisation selbst
übernehmen muss. Die Interaktion zwischen Client und Server selbst läuft synchron ab: ein
entferntes Objekt wartet, bis eine seiner Methoden aufgerufen wird. Während der Ausführung
einer Methode wartet wiederum der aufrufende Client bis die Methode fertig ausgeführt ist.
Um RMI in Betrieb zu nehmen und am Laufen zu erhalten sind einige wenige Werkzeuge und
Dienste notwendig. Ein einfach strukturierter Namensdienst, die RMI-Registry, besorgt die
(flache) Abbildung von Dienstenamen auf entfernte Objekte. (Der RMI-Compiler RMIC sorgt für
die automatische Stub- und Skeleton Generierung aus den entsprechenden Klassendateien.)
Java RMI kommt ohne Middleware aus, weil RMI, wie oben erwähnt, exklusiv auf Java
abgestimmt ist.
9.10 Kontrollfragen (Technologien / RMI)
Frage J1:
Frage J2:
Frage J3:
Frage J4:
Frage J5:
Worin besteht der konzeptionelle Unterschied zwischen RMI und RPC?
Welche Aufgaben haben Client-Stub und Server-Skeleton im Rahmen von RMI zu
lösen?
Welche Rolle spielt die RMI-Registry?
Welche Rolle spielt der RMI-Compiler?
Was hat der Sicherheitsmanager bei RMI zu leisten?
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 115 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
10. AWT-Basics: Graphical User Interface hier weiter
Was Sie lernen:
wer die wichtigen Mitglieder der AWT-Klassenhierarchie sind
wie Fenster erzeugt und angezeigt werden
wie Fenster geschlossen und gelöscht werden
was Ereignisse sind
wie und welche Ereignisse empfangen werden können
Voraussetzungen:
Arbeiten mit Klassen
10.1 Grundlagen
AWT steht für "Abstract Windowing Toolkit"; verfügbar seit Java Version 1.0
Notwendig: import java.awt.* (siehe API-Doku)
AWT (package java.awt) nutzt das native GUI des darunterliegenden OS („heavyweight GUI components“),
d.h. es nimmt das jeweilige native "look and feel" an - im Unterschied zu SWING („Bestandteil der JFC Java
Foundation Classes“, package javax.swing), welches das native GUI nicht nutzt („lightweight GUI
components“). Weitere Alternative: SWT („Standard Widget Toolkit“, IBM, 2001 / -> Eclipse), als „best of both
worlds“ propagiert
Das Peerkonzept des AWT: das Peer-Widget zeichnet sich selbst
Funktionalität von AWT
o
Anzeigen von Bitmaps, Ausgaben von Sound
o
Zeichnen primitiver geometrischer Objekte (Linie, Kreis, Rechteck,...) und füllen dieser Objekte
o
Ausgabe von Schriften
o
bietet Komponenten für den Dialog mit dem User: Fenster, Schaltfläche, Textfelder, Menüs, …
o
Handling von Ereignissen (Events, low level, high level), welche durch Umgang mit Tastatur und
Maus (z.B. Klicken auf Schaltfläche) oder Operationen auf Komponenten („Controls“, „Widgets“)
ausgelöst werden (z. B. Fenster schließen)
Für die Darstellung grafischer Elemente, wie z.B. Schaltflächen, Textfelder, usw., werden spezielle Klassen
verwendet à Java-Beans (Das sind Klassen, für die bestimmte syntaktische Regeln einzuhalten sind.) JavaBeans werden auch als Komponenten oder Widgets, Controls bezeichntet.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 116 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Unterschied zwischen AWT und Swing: AWT nutzt nur die nativen Elemente, die auf allen Plattformen
vorhanden sind, d.h. AWT-Komponenten sind auf diese überall verfügbaren nativen Elemente beschränkt. Die
SWING-Bibliothekt ist umfangreicher, da sie nicht die nativen Elemente des Betriebssystems nutzt, sondern alle
Komponenten selbst implementiert („Java zeichnet die Widgets selbst“).
o
Swing ist die GUI-Komponente in JFC Java Foundation Classes; im package javax.swing sind
noch darüber hinaus gehende Funktionalitäten enthalten:
§ Das Look & Feel ist zur Laufzeit veränderbar, ohne Neustart des Programmes
§ Barrierefreier Zugang (zur Programmbedienung) wird unterstützt (alternative
Interaktionstechniken für Menschen mit Handycap; Spracherkennung, Lesegeräte für Blinde,
Braille-Schrift, etc.)
§ Drag & Drop zum Datenaustausch zwischen Programmen (nicht notwendigerweise JavaProgramme)
§ Sehr gute 2D Bibliotheken (vergleichbar mit dem Postscript-Ansatz)
Gemeinsamkeit von AWT und Swing: Die zugrunde liegende Technik, wie die Ereignisbehandlung,
Arbeiten mit Formen, Farben, Schriften und Umgang mit den Layoutmanagern (für die Anordnung der
Elemente in einem Fenster) sind bei AWT und SWING gleich.
Hinweis zur Übersetzung und Ausführung von GUI-basierenden Java-Programmen
Bei Java-Installationen unter Windows (analog Mac OS X/Linux) gibt es im JDK zwei Java-Interpreter:
java.exe zeigt ein Konsolfenster an. Über dieses Konsolfenster haben wir bisher die I/O getätigt
javaw.exe zeigt kein Konsolfenster an. Ausgaben über System.out/err sind damit nicht mehr sichtbar.
Fertig entwickelte und geteste GUI-basierte Java-Applikationen werden am Besten mit javaw.exe gestartet. Während
der Entwicklung und des Tests ist aber java.exe nach wie vor nutzbar für Testausgaben.
Im Textpad muss die Nutzung von javaw.exe erst konfiguriert werden: „Konfiguration“ -> „Einstellungen“ ->
„Extras“ -> „Hinzufügen“: jetzt alle Einstellungen so vornehmen wie in der Einstellung „Java Programm
starten“ und statt java.exe nun javaw.exe einsetzen. Eclipse erkennt GUI-Programme und startet diese
unmittelbar mit javaw.exe.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 117 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
10.2 AWT – Klassenhierarchie
java.lang.Object
java.util.EventObject
java.awt.AWTEvent
java.awt.Component
java.awt.Container
Graphics
s
...Choice
...Label
...Button
...List
…Canvas
java.awt.Dialog
java.awt.Window
javax.awt.Panel
java.awt.Frame
javax.applet.Applet
…Scrollbar
…Checkbox
…TextComponet
nt
javax.swing.JDialog
javax.swing.JFrame
javax.swing.JComponent
javax.swing.Label
20.05.
Bild: Die AWT-Klassenhierarchie
19
java.applet.Applet
javax.swing.JApplet
(nonmenu-related Abstract Window Toolkit components)
Im Folgenden betrachten wir zunächst das Wesentliche aus den Klassen Window, Frame, Container und
Component, soweit es notwendig ist, um mit Fenstern umzugehen.
Danach machen wir einen Abstecher zur Grafikprogrammierung und der Klasse Graphics:
Kapitel „Grafikprogrammierung...“
Im Anschluß daran kümmern wir uns um Komponenten, also die Klassen Button, Choice bis TextCompontent:
Kapitel „AWT-Komponenten“
Und dann: die menu-related components: FileDialog, Dialog, beides Ableitungen von class
Window;
MenuBar, Menu, MenuItem, alle drei Klassen sind Ableitungen von Class MenuComponent;
PopupMenu, ist Ableitung von class Menu; usw.
19
In dieser Klassenhierarchie fehlt komplett die Klasse java.awt.Menu. Siehe API Doku!
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 118 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
10.3 „Fenster“: von der Component über den Container zum Frame
Klasse Component:
übernimmt/tätigt
- Festlegen von Größe und Position einer Komponente (Schaltfläche, Eingabefeld,...)
- Festlegen von Farbe und anderen grafischer Eigenschaften (Farbe, 2D, 3D,...)
- Befähigt Komponenten Ereignisse zu empfangen
Definition der Klasse Component aus der API Doku:
A component is an object having a graphical representation that can be displayed on the screen and that can
interact with the user. Examples of components are the buttons, checkboxes, and scrollbars of a
typical graphical user interface.
The Component class is the abstract superclass of the nonmenu-related Abstract Window Toolkit
components. Class Component can also be extended directly to create a lightweight component. A lightweight
component is a component that is not associated with a native opaque window.
Klasse Container: (abgeleitet von Component)
übernimmt/tätigt
- Aufnahme von Komponenten...
- Entfernen von Komponenten...
- Anordnen von Komponenten unter Nutzung eines LayoutManagers
Definition der Klasse Container aus der API Doku:
A generic Abstract Window Toolkit (AWT) container object is a component that can
contain other AWT
components.
Components added to a container are tracked in a list. The order of the list will define the components' front-toback stacking order within the container. If no index is specified when adding a component to a container, it will
be added to the end of the list (and hence to the bottom of the stacking order).
Klasse Window und Panel: (abgeleitet von Container)
-
Die Klasse Window erzeugt Top-Level-Fenster ohne Rahmen,
o Eine Anwendung müsste die fehlenden Teile selbst zeichnen
o
Menü, Titelleiste.
Die Objekte der Klasse haben keine (wie bei Fenstern) übliche Funktionalität. Diese muss
vollständig in der Anwenung implementiert werden.
Definition der Klasse Window aus der API Doku:
A Window object is a top-level window with no borders and no menubar. The default layout for a
window is BorderLayout.
A window must have either a frame, a dialog, or another window defined as its owner when it's constructed.
In a multi-screen environment, you can create a Window on a different screen device
-
Die Klasse Panel dient der Aufnahme anderer Komponenten, einschließlich anderer Panels.
o
Die Klasse dient also der Zusammenfassung
von mehreren Komponenten zu
o
einer Gruppe.
Für jedes Panel kann ein eigener LayoutManager zur Anordnung der Komponenten
verwendet werden.
Definition der Klasse Panel aus der API Doku:
Panel is the simplest container class. A panel provides space in which an application can attach any other
component, including other panels.
The default layout manager for a panel is the FlowLayout layout manager.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 119 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
10.4 Klasse Window
Siehe Java 2 Platform API Specification:
public class Window
extends Container
implements Accessible
A Window object is a top-level window with no borders and no menubar. The
default layout for a window is BorderLayout.
A window must have either a frame, dialog, or another window defined as its
owner when it's constructed.
In a virtual device multi-screen environment in which the desktop area could span
multiple physical screen devices, the bounds of all configurations are relative to the
virtual device coordinate system. The origin of the virtual-coordinate system is at the
upper left-hand corner of the primary physical screen. Depending on the location of
the primary screen in the virtual device, negative coordinates are possible, as shown
in the following figure.
The following code sets the location of a Window at (10, 10) relative to the origin of
the physical screen of the corresponding GraphicsConfiguration. If the bounds of the
GraphicsConfiguration is not taken into account, the Window location would be set at (10,
10) relative to the virtual-coordinate system and would appear on the primary
physical screen, which might be different from the physical screen of the specified
GraphicsConfiguration.
Window w = new Window(Window owner, GraphicsConfiguration gc);
Rectangle bounds = gc.getBounds();
w.setLocation(10 + bounds.x, 10 + bounds.y);
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 120 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Windows are capable of generating the following WindowEvents: WindowOpened,
WindowClosed, WindowGainedFocus, WindowLostFocus.
Windows können viele Listener zugeordnet werden (es gibt zahlreiche add..()-ListenerMethoden). Dazu kommen noch von class component geerbte Listener. Typisch für die ganze
AWT-Hierarchie!!!
10.5 Klasse Panel: (abgeleitet von Container)
Siehe Java 2 Platform API Specification:
public class Panel
extends Container
implements Accessible
Panel is the simplest container class. A panel provides space in which an
application can attach any other component, including other panels.
The default layout manager for a panel is the FlowLayout layout manager.
…der dann mit einem LayoutManager verwaltet werden kann.
Panels können so gut wie keine Listener zugeordnet werden (es gibt so gut wie keine add..()Listener-Methoden. Siehe API-Doc!). Panels sind wichtige Strukturierungsmittel /
Components zur Gliederung der Oberfläche.
Case-Study: vergleiche in der API-Spezifikation die „Method Summary“
für die Klassen Component, Window und Panel.
10.6 Klasse Frame: (abgeleitet von Window)
Repräsentiert das Fenster, wie man es bspw. von MS Windows oder
Linux/KDE/Gnome oder Mac OS X kennt.
o Besteht aus
§ einer Titelleiste,
§ einem Systemmenü
§ einem Rahmen,
§ Symbole zu minimieren, maximieren und schliessen.
Konstruktoren der Klasse Frame
public Frame()
public Frame(String title)
Ein Fenster mit leerer Titelleiste wird erzeugt.
Ein Fenster mit entsprechend gefülltem Titel wird
erzeugt.
In der Klasse Frame befinden sich viele Attribute (Größe, Position, Farbe des Fensters), die mit
entsprechenden get()- und set()-Methoden, die sich meist durch ihre Namen selbst erklären, gelesen bzw. gesetzt
werden können.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 121 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Die meisten Methoden erbt die Klasse Frame von der Klasse Component, einige aber auch von den
Klassen Container und Window. (Siehe API-Doku)
Wichtige Methoden der Klasse Frame (…und ihrer Basisklassen)
public
public
title)
public
public
String getTitle ()
void setTitle (String
isResizeable ()
Text in der
Titelleiste
Gibt den Fenstertitel zurück,
bzw. setzt ihn
Veränderbarkeit
der
Fenstergröße
Fenstersymbol
void
remove (MenuComponent
void
setLocation (int x,
s.u.
Hat der Parameter resizable den Wert true, so
kann das Fenster in seiner Größe geändert
werden, sonst nicht.
Das Symbol, das beim Minimieren des Fensters
angezeigt wird, kann abgefragt, bzw. gesetzt
werden. (Das Minimieren von Fenstern wird nicht
von allen OS unterstützt.)
Der Status eines Fensters kann normal
(Frame.NORMAL; Standard) oder minimiert
(Frame.ICONIFIED) sein.
Die mit dem Fenster verbundene Menüleiste wird
zurückgegeben, bzw. gesetzt. (Menüleisten sind
damit austauschbar.)
Die Methode remove() entfernt die verbundene
Menüleiste.
- siehe Beispiel unten -
void
setVisisble (Boolean
s.u.
- siehe Beispiel unten -
void
dispose ()
s.u.
- siehe Beispiel unten -
boolean
void
setResizable (boolean resizable)
public Image getIconImage ()
public void setIconImage (Image
image)
public int getState ()
public void setState (int state)
public
public
mb)
public
m)
public
int y)
public
b)
public
MenuBar getMenuBar ()
void setMenuBar (MenuBar
Fensterstatus
(normal oder
minimiert)
Menüleiste
è Weitere Methoden und Konstruktoren und Methoden siehe API – Documentation zum Package java.awt.
Beispiel – Fenster erzeugen, Attribute setzen und anzeigen
// Eclipse-Project: kap10_awt_basics_case_study_
import java.awt.*;
public class Fenster1
{
public static void main(String[] args)
{
Frame frame = new Frame();
// Erst Fenster “konfigurieren”
frame.setTitle("Mein erstes Java-Fenster"); // Text für die Titelleiste
frame.setSize(300,200);
// Fenstergrösse festlegen
frame.setLocation(350,300);
// Fensterposition festlegen
frame.setBackground(Color.blue);
// Fensterfarbe festlegen
// teilweise von Component (setBackground(), setLocation())
// und von Window (setSize()) geerbt.
// Dann Fenster anzeigen
frame.setVisible(true);
}
}
Objekte der Klasse Frame besitzten einiges an Funktionalität, die Objekte der Klasse Window
nicht haben:
Das Minimier- und Maximierfeld funktioniert.
Das Fenster kann verschoben werden.
Das Fenster kann durch ziehen am Rahmen vergrößert / verkleinert werden.
Die Titelleiste nimmt eine andere Farbe an, wenn das Fenster aktiviert oder deaktiviert wird (Werte im OS
eingestellt).
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 122 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Die Betätigung des Schließfeldes bleibt aber ohne Wirkung und muss erst programmiert werden. (Siehe
Beispiel unten.
10.7 "Fensterbausteine" und der Umgang damit (Erzeugung / Konfiguration /
Anzeige)
Vorgehensweise: Fenster erzeugen, dann konfigurieren und danach anzeigen
Im folgenden Beispiel wird
die Initialisierung eines Frames von
dessen Anzeige getrennt.
Wichtige Fenster und Dialoge können auf dies Weise zuerst im Hintergrund erzeugt werden und dann bei Bedarf
angezeigt werden.
Beispiel – Fenster mit funktionierendem Schließfeld
// Eclipse-Project: kap10_awt_basics_case_study_
import java.awt.*;
import java.awt.event.*;
public class FrameBeispiel extends Frame
{
Panel p = new Panel(); // Eine Panel wird im Frame
eingefügt
public FrameBeispiel()
//Im Konstruktor wird lediglich die
{
// Konfiguration des Frames vorgenommen
this.setSize(150,150);
p.setBackground(Color.green); // Hintergrundfarbe des Panels ändern
this.add(p);
// Panel wird dem Frame zugeordnet
this.addWindowListener(new WindowAdapter() // Listener beim Frame registrieren
{
public void windowClosing(WindowEvent we)
{System.exit(0);}
});
}
}
public static void main(String [] args)
{
FrameBeispiel fB = new FrameBeispiel(); // Frame erzeugen via Konstruktor
fB.setVisible(true);
// Frame anzeigen
}
Hinweise:
Wir haben mit dem Beispiel etwas vorgegriffen. Dem Frame (Ereignisquelle) wird ein WindowListener
zugeordnet (Ereignisempfänger), der für den Aufruf einer Call-Back-Methode verantwortlich ist, sobald
bestimmte Ereignisse eintreffen. Die Call-Back-Methode sorgt dann für die richtige Reaktion auf das Ereignis.
Diese Technik (Ereignisbehandlung; Event Delegation Model) werden wir in der Folge betrachten. (Zu class
Frame, class WindowListener, class WindowAdapter: siehe API-Doku)
Die Ereignisbehandlung ist hier mittels Verwendung einer Adapterklasse implementiert. (Methode „A“) ->
Adapterklasse enthält alle Call-Back-Methoden -> NUR diejenige Adaptermethode, die gebraucht wird, muss
redefiniert werden. (Siehe hierzu auch Fenster1_DoubleWindow_ClosingOnly.java)
Wir werden später sehen, dass die Ereignisbehandlung auch durch die Implementierung eines ListenerInterfaces realisiert werden kann.(Methode „B“). Dann müssen aber ALLE Interface-Methoden implementiert
wreden
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 123 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
10.8 Anzeigen und schließen:
Bevor das Fenster auf dem physikalischen Schirm zur Anzeige gebracht wird
(frame.setVisible(true)), sind entsprechende Ressourcen im Speicher allokiert. Nach
frame.setVisible(false) sind die Ressourcen noch belegt. Erst nach frame.dispose() werden
die belegten Ressourcen frei gegeben (um die sich dann der Garbage Collector kümmert).
frame.setVisible(true)
frame.setVisible(false)
frame.dispose()
//Fenster anzeigen
//Fenster unsichtbar machen - Ressourcen noch belegt
//Ressourcen freigeben
Anwendung ist aber noch nicht beendet!
o System.exit(status) è normal: status = 0!!
Beispiel – Fenster verschwinden lassen
// Eclipse-Project: kap10_awt_basics_case_study_
import java.awt.*;
public class Fenster2
{
public static void main(String[] args)
{
Frame frame = new Frame();
// erzeugen
frame.setTitle("Mein erstes Java-Fenster"); // Konfigurieren
frame.setSize(300,200);
frame.setLocation(350,300);
frame.setBackground(Color.blue);
frame.setVisible(true);
// anzeigen
try{
Thread.currentThread().sleep(5000);
}catch(Exception ex){}
frame.setVisible(false);
// …
frame.dispose();
System.exit(0);
}
}
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 124 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
10.9 Bestandteile eines Fensters (class
Frame)
Ein AWT-Fenster hat den im Folgenden gezeigten Grund-Aufbau. Auf den verschiedenen Plattformen
unterscheidet sich das Erscheinungsbild geringfügig. (Mac OS: Titelleiste modifiziert)
Fenstersymbol
(icon) os-spez.
Titel (title) in
Titelleiste
Minimier-, Maximier- und
Schließfeld (beeinflusst state)
Fensterposition (x,y)
bezieht sich auf die
linke obere Ecke
Hintergrund
(background)
Vordergrund
(foreground)
Fensterhöhe
(height)
Cursor
Fensterrahmen
(border)
Fensterbreite
(width)
In der folgenden Übersicht sind die Fensterbestandteile und die Methoden aufgelistet, über die ein Fenster
konfiguriert werden kann (siehe auch API-Doku):
Eigenschaft
Titel
title
Fenstersymbol
icon
Größe
height x width
(von Compontent
geerbt)
Position
X,Y
(von Compontent
geerbt)
Position und Größe
Setzen der Eigenschaft
setTitle(String title)
Lesen der Eigenschaft
String getTitle()
setIconImage(image i)
Image getIconImage()
setSize(int width, int height)
Dimension getSize()
int getHeight()
int getWidth()
setLocation(Point p)
Point getLocation()
setLocation(int x, int y)
int getX()
int getY()
Rectangle getBounds()
setBounds(Rectangle r)
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 125 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
(von Compontent
geerbt)
Vordergrundfarbe
foreground
(von Compontent
geerbt)
Hintergrundfarbe
background
(von Compontent
geerbt)
Cursor
(von Window
geerbt)
setBounds(int x, int y, int
width, int height)
setForeground(Color c)
Color getForeground()
setBackground(Color c)
Color getBackground()
setCursor(Cursor c)
Cursor getCursor()
10.9.1 Das Fenstersymbol/Icon:
Das Bild für das Fenstersymbol sollte plattformspezifisch erstellt werden, da die Darstellung nicht auf allen
Plattformen gleich ist – Der MAC zeigt keine Fenstersymbole/Icons.
Vorgeschriebene Formate: GIF, JPEG oder PNG (implementierungsspezifisch). Andere Formate werden nicht
verstanden (siehe API-Doku).
z. B. Windows 16 x 16 Pixel (small Icon); 32 x 32 Pixel (large Icon).
Hinweis: JDK passt Größe automatisch an, wenn GIF / JPEG / PNG eine andere Auflösung haben.
Bild am Besten ins Verzeichnis der class-Datei. (Siehe Case-Study.)
Beispiel – Ein Icon nutzen
entweder Methode a):
// URL des Bildes laden
URL url = this.getClass().getResource("./smile.gif");
// Icon laden (mit Methode aus der Klasse Toolkit))
Image icon = this.getToolkit().getImage(url);
// Icon dem Fenster zuweisen
frame.setIconImage(icon);
getClass() ist eine Methode von class
Object (returns the runtime class of this Object).
getResource() ist eine Methode von class Class. Die class Class ist: Instances of the
Hinweis 1:
class Class represent classes and interfaces in a running Java application.
oder Methode b):
Image icon = this.getToolkit().getImage(“./smile.gif”); //keine ImageIcons auf MAC
frame.setIconImage(icon);
Hinweis 2: die Methode getTookit() (aus der Klasse Window) liefert ein Toolkit-Objekt. getImage() ist
eine Toolkit-Methode. Class Toolkit : This class is the abstract superclass of all actual
implementations of the Abstract Window Toolkit. Subclasses of Toolkit are used to bind the various components to
particular native toolkit implementations. (Toolkit wird gebraucht für den Zugriff auf die (unter AWT liegenden PeerKomponenten des nativen Systems….)
Für Textpad: Die gif-Datei muss in diesem Beispiel in dem Verzeichnis liegen, in dem die class-Datei des
Beispiels liegt.
Wenn mit Eclipse gearbeitet wird gelten ggf. andere Ablageorte (z.B. Projektordner)
Beispiel – Siehe fenster3.java mit Methode b)
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 126 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
import java.awt.*;
public class Fenster3
{
public static void main(String[] args)
{
Frame frame = new Frame();
frame.setTitle("Java-Fenster mit Icon");
frame.setSize(300,200);
frame.setLocation(350,300);
frame.setBackground(Color.blue);
Image icon = frame.getToolkit().getImage("smile.gif");
frame.setIconImage(icon);
frame.setVisible(true);
try{
Thread.sleep(10000);
}catch(Exception ex){}
frame.setVisible(false);
frame.dispose();
System.exit(1);
}
}
Beispiel – Siehe fenster4.java mit Methode a)
import java.awt.*;
import java.net.*;
public class Fenster4 extends Frame
{
URL url;
public static void main(String[] args)
{
Fenster4 fenster = new Fenster4();
}
public Fenster4()
{
super("Java-Fenster mit Icon");
this.setSize(300,200);
// zur Erinnerung: viele für Frames verwendete Meth.
this.setLocation(350,300); // stammen von den Kl. Component,Container,Window
…
// im Programm unten komplettiert
…
//
try
{
url = this.getClass().getResource("smile.gif");
}
catch(Exception ex)
{
System.out.println(ex.getMessage());
}
Image icon = this.getToolkit().getImage(url);
this.setIconImage(icon);
this.setVisible(true);
try{
Thread.sleep(15000);
}catch(Exception ex){}
this.setVisible(false);
this.dispose();
System.exit(1);
}
}
10.9.2
Hilfsklassen für Fensterposition und -Größe: hier weiter
02.12.2015
Die Position des Fensters bezieht sich immer auf die linke obere Ecke des Bildschirms. Die oben aufgeführten
Methoden zur Festlegung von Position und Ausdehnung können Objekte der Klassen Point ,
Dimension und Rectangle als Parameter übergeben bekommen.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 127 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Die Objekte dieser Klassen werden erzeugt, indem Werte für deren Attribute dem jeweiligen Konstruktor
übergeben werden.
Point(int x, int y)
// Punkt x,y
Dimension(int width, int height) // Breite, Höhe der Komponente
Rectangle(int x, int y, int width, int height)
// Punkt x, y, Breite, Höhe
Z B.:
Point p = new Point(10,10);
setLocation(p);
10.9.3
Hilfsklassen Fensterfarben:
Zum Setzen von Vorder- und Hintergrundfarbe wird der entsprechenden Methode ein Color-Objekt übergeben.
Die Klasse Color kapselt die Farben des RGB (Rot-Grün-Blau)-Farbmodells (Zahlenwerte von 0 - 255 geben
Intensität des Farbanteils an; additives Farbmodell)
Einige Farben sind vordefiniert: z. B.: black, blue, cyan, gray, darkGray, lightGray, magenta, orange,
pink, green, red, white, yellow.
Color.blue // Farbe blau
Color(int red, int green, int blue)
// “Farbmischung” durch Angabe der
// Intensität der RGB-Farbanteile
// int-Werte von 0-255
Color(float red, float green, float blue)
// float-Werte von 0-1
this.setBackground(new
this.setBackground(new
this.setBackground(new
this.setBackground(new
Color(255, 255, 255));
Color(0, 0, 0));
Color(255, 0, 0));
Color(200, 70, 150));
//
//
//
//
weißer Hintergrund
schwarzer Hintergrund
roter Hintergrund
rosa Hintergrund
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 128 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
10.9.4
Hilfsklassen Mouse-Cursor: Klasse Cursor
Der Mauszeiger (Cursor) kann innerhalb eines Fensters verschiedene Formen annehmen.
Form wird durch eine symbolische Konstante repräsentiert
Mit der Methode setCursor() lässt sich die Form des Cursors
ändern.
Symbolische Konstante
DEFAULT_CURSOR
CROSSHAIR_CURSOR
HAND_CURSOR
TEXT_CURSOR
MOVE_CURSOR
E_RESIZE_CURSOR
W_RESIZE_CURSOR
NE_RESIZE_CURSOR
SW_RESIZE_CURSOR
NW_RESIZE_CURSOR
SE_RESIZE_CURSOR
S_RESIZE_CURSOR
N_RESIZE_CURSOR
WAIT_CURSER
CURSOR-Form
è Standardzeiger, meist als Pfeil
è Kreuz
è Handform
è Einfügemarke
è Positionsänderung
è Größenänderung
è Größenänderung
è Größenänderung
è Größenänderung
è Größenänderung
è Größenänderung
è Größenänderung
è Größenänderung
è Sanduhr / Wartezustand
Aufgabe: Suchen Sie den
passenden Cursor
zu den oben genannten
Cursorformen.
//Eclipse-Projekt: kap10_awt_basics_case_study_
import java.awt.*;
import java.net.*;
public class Fenster4 extends Frame
{
URL url;
public static void main(String[] args)
{
Fenster4 fenster = new Fenster4();
}
public Fenster4()
{
super("Java-Fenster mit Icon");
this.setSize(300,200);
this.setLocation(350,300);
this.setBackground(new Color(250,90,200));
this.setCursor(new Cursor(Cursor.HAND_CURSOR));
try
{
url = this.getClass().getResource("smile.gif");
}
catch(Exception ex)
{
System.out.println(ex.getMessage());
}
Image icon = this.getToolkit().getImage(url);
this.setIconImage(icon);
this.setVisible(true);
try{
Thread.sleep(15000);
}catch(Exception ex){}
this.setVisible(false);
this.dispose();
System.exit(1);
}
}
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 129 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Bearbeiten Sie die Case Studies und lösen Sie die Übungsaufgeben zu
diesem Kapitel! (Siehe Unterlagen hierzu auf dem Materialserver.)
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 130 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Nach dem Verständnis von Kapitel 11 ist unter Zuhilfename von Kapitel 13 „AWT
Komponenten“ die GUI für Adreli machbar.
11. AWT-Events: Ereignisse und Event-Handling
Was Sie lernen:
was Ereignisse sind und wer oder was Ereignisse auslöst
wie Ereignisse empfangen, weitergeleitet und ausgewertet werden
welche Arten von Ereignissen es gibt
Voraussetzungen:
Grundlagen der Fensterprogrammierung
Innere und anonyme Klassen
11.1 Grundlagen
In der Programmierung grafischer Benutzeroberflächen stellt die Ereignisbehandlung (Event-Handling) ein
zentrales Element dar.
Das aktuelle Modell der Ereignisbehandlung unterscheidet sich von dem in Java 1.0. (Auf dieses ursprünglich
implementierte Modell gehen wir nicht weiter ein.)
Die Arbeitsweise der Ereignisbehandlung ist für AWT- und SWING-Komponenten nahezu identisch.
Ereignismodell Java AWT
Maus
OS
wartet
Tastatur
leitet
Nachricht
weiter
via
VM
lfd.
Appl.
Weitere I/O HW
In einem Programm mit GUI können Eingaben jeder Art Ereignisse auslösen. Sollen die Ereignisse etwas
bewirken, so muss das entsprechend programmiert werden. In einem Java-Programm sind daran immer drei
Objekte beteiligt:
Ereignisquelle: das ist die Klasse, von der das Ereignis ausgeht (Class Component und Ableitungen). Dies
kann beispielsweise eine Schaltfläche (Class Button) sein, auf die mit der Maus geklickt wird. Oder etwa die
Class Frame, auf dessen Schließbutton geklickt wird, usw.
Ereignisziel / Ereignisempfänger: das sind Objekte, die das Ereignis empfangen à Listener empfangen die
Ereignisse. (Für Objekte der Class Button kann das z.B. der MouseLisenter sein. Oder für Objekte der Class
Frame z.B. der WindowListener u.v.a.m)
Die Ereignissquelle muss den entsprechenden Listener registrieren, damit das Ereignis verarbeitet werden
kann. (Für das Button-Objekte kommt z.B. die Methode addMouseListener() zum Einsatz, wenn Mouse-Events
(siehe Class MouseEvent) empfangen werden sollen.)
Ereignisobjekt: Wird dann zur Laufzeit ein Ereignis ausgelöst, so wird die zuständige („Callback“-)Methode des
Listener-Interfaces aufgerufen. Der zuständigen Callback-Methode des Listener-Interfaces wird dann ein
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 131 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Ereignisobjekt als Parameter mitgegeben. Die Callback-Methode kann dann ggf. Attribute und Methoden des
Ereignissobjekts für die Eventbehandlung nutzen.
Dieses Modell, bestehend aus Ereignisquelle, Ereigniszielen und Ereignisobjekt wird als " Event Delegation Model"
bezeichnet ("Ereignisdelegationsmodell"). D. h. die Ereignisquelle leitet („delegiert“) die Ereignisse an alle
registrierten Listener weiter. Der für das Ereignis zuständige Listener reagiert dann entsprechend darauf.
(Ereignisquellen haben Methoden, mit denen man Ereignisempfänger für die Ereignisbehandlung registrieren kann.
Diese Methoden-Namen beginnen stets mit add...())
Event-Delegation-Modell: mehrere Listener können bei einer Ereignisquelle registriert werden
Ereignis
z. B. Mausklick auf
Button
Programm
registrieren
Ereignisziel
z. B.
MouseListener
Ereignisquelle
z. B. Button
Delegation
registrieren
Delegation
Ereignisziel
z. B. ActionListener
Beispielsweise registriert eine Schaltfläche (Klasse Button) zwei Listener, einen für Klicks (auf Button, der für
20
eine bestimmte Operation/Command steht; siehe Interface ActionListener) und einen für Mausaktivitäten (siehe
Interface MouseListener). Wenn die Ereignisquelle ein Ereignis empfängt, wird es an die Listener delegiert.
Die Registrierung des Listeners bei der Ereignisquelle:
Enthält „Event-Handler“ (A) Adapter oder
(B) Interface (mit den „Callback”-Methoden
EreignisQuellObjekt.addEreignisListener(EreignisBehandlungsObjekt);
Das Ereignismodell erlaubt verschiedenen Möglichkeiten der Implementierung der Ereignisbehandlung, z.B:
Modell A: die Verwendung von Adapterklassen, die schon eine (leere) Implementierung des Interfaces
bereitstellen, so dass nur noch die Listener-Methoden implementiert werden müssen, die benötigt werden.
Modell B: die Implementation eines Listener-Interfaces. In diesem Fall müssen alle Methoden des Interfaces
implementiert werden.
Im Beispiel unten (EventFenster_LowLevel.java) werden die beiden Methoden gegenübergestellt.
Vorteile des Event-Delegation-Modells (ab Java-Version 1.1):
Nur für (Ereignis-Quell-)Objekte, bei denen ein entsprechender Listener angemeldet ist, werden Nachrichten
transportiert. Damit wird eine erhöhte Performance möglich.
Durch diesen Ansatz wird die Trennung des Programm-Codes für die GUI vom Programm-Code der
Anwendungslogik möglich.
20
So wird beispielsweise die Button-Beschriftung als Command-String gedeutet, der von der Class ActionEventMethode getActionCommand() geliefert wird...
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 132 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
11.2 Ereignisklassen
Die Klasse der AWT-Ereignisse sind im Package java.awt.event zusammengefasst.
Ereignisobjekte kapseln Informationen über das Ereignis, das die Ereignisquelle an Ihre Empfänger schickt.
Bei Bedarf kann dann die betroffene Listener-Methode das Ereignisobjekt analysieren und entsprechend
darauf reagieren.
"Semantische Events"
java.util.EventObject
java.awt.AWTEvent
(1) Action Event
(2) AdjustementEvent
(4) TextEvent
"low-level Event"
(systemnahe Ereignisse / technische Events)
(5) ComponentEvent
(7) ContainerEvent
(3) ItemEvent
(6) FocusEvent
InputEvent
(10) MouseEvent
(8) WindowEvent
(9) KeyEvent
Semantische Events: z. B. Klicken (mit oder ohne Maus) auf ein Button, Betätigung einer Taste. Für
Semantische Events ist die Eventsource eine AWT-Komponente.
Semantische Ereignisse werden für die Übertragung "höherwertiger" Ereignisse eingesetzt. z. B. Zustandsänderung einer Komponente, Ausführung einer Aktion.
Ein semantisches Ereignis wird durch
in ein Textfeld.
den Benutzer ausgelöst, z.B. ein Schaltflächenklick oder Eintrag
Low level Events sind an konkrete graphische Objekte wie Container (und Ableitungen; siehe Bild oben „Die
AWT-Klassenhierarchie“), Mouse und Key gebunden . Für Low level Ereignisse ist die Eventsource ein
konkretes grafisches Objekt aus der AWT-Fenster-Klassen-Hierarchie.
Ein low level Ereignis ermöglicht die semantischen Ereignisse.
Die Tabelle verdeutlicht nochmals den Unterschied zwischen semantischen Events und low level Events:
Semantische
Auslösung durch User-Interaktion mit einer AWT-Komponente
Ereignissklassen
ActionEvent
Schaltflächenklick, Menüauswahl, Doppelklick auf Listeneintrag, Drücken der
Return-Taste in einem Textfeld.
AdjustmentEvent
Benutzer ändert eine Bildlaufleiste.
ItemEvent
Benutzer trifft eine Auswahl aus einer Gruppe von Kontrollkästchen oder
Listenelementen.
TextEvent
Inhalt eines Textfeldes oder Textbereichs wurde geändert.
Systemnahe
Auslösung (konkrete grafische Objekte aus der AWT-Fenster-Klassen-Hierarchie)
Ereignissklassen
CompontentEvent
Größenänderung einer Komponente; Komponente wurde verschoben, angezeigt
oder ausgeblendet; (Basisklasse für alle systemnahen Ereignisse).
ContainerEvent
Eine Komponente wurde hinzugefügt oder entfernt.
FocusEvent
Eine Komponente hat den Focus erhalten oder verloren.
WindowEvent
Fensterzustand hat sich geändert.
MouseEvent
Maustaste wurde gedrückt oder gelöst, die Maus bewegt oder gezogen.
MouseWheelEvent
Mausrädchen wurde gedreht. (Im JDK1.4 hinzugekommen; Ableitung von
MouseEvent)
KeyEvent
Eine Taste wurde gedrückt oder gelöst.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 133 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
11.3 Listener-Interfaces
Für jede Ereignisklasse gibt es ein Listener-Interface. (Für Maus-Ereignisse gibt es zwei Listener-Interfaces.)
Listener werden für das Empfangen und Behandeln der Ereignisse benutzt.
In java.util gib es das Interface EventListener, von dem alle AWT-Listener abgeleitet sind.
java.util.EventListener
(1) Action Listener
(2) AdjustmentListener
(3) ItemListener
(4) TextListener
(5) ComponentListener
(6) FocusListener
(7) ContainerListener
(8) WindowsListener
(9) KeyListener
Listener werden mit entsprechenden add-Methoden bei der Ereignisquelle registriert.
Welche add-Methoden (also: welche Listener) verfügbar sind, ist mittels APIBeschreibung der Ereignissquelle erkennbar. Siehe z.B. Class Frame als EreignissQuelle. Dort taucht neben vielen anderen add-Methoden auch die Methode
addKeyListener() auf (geerbt von Class Component)
Parameter für die add()-Methode ist, wie oben gesagt, das zu implementierende
Listener-Interface
Das Listener-Interface legt die Methoden fest, über die die Reaktion auf das Ereignis
erfolgt.
Beispiel - Interface KeyListener:
public i nt er f ac e
{
public void
public void
public void
}
Ke y Li st en e r extends EventListener
K ey Ty p ed (KeyEvent ev);
K ey Pr e ss ed (KeyEvent ev);
K ey Re l ea se d(KeyEvent ev);
(10) MouseListener
(11) MouseMotionListener
11.4 Listener bei Ereignisquellen registrieren
Die Registrierung des Listeners bei der Ereignisquelle:
Enthält „Event-Handler“ (A) Adapter oder
(B) Interface (auch „Callback”-Methode)
…und u.U. weitere Daten zum Event
EreignisQuellObjekt.addEreignisListener(EreignisBehandlungsObjekt);
damit der Listener für die Behandlung des Ereignisses den zuständigen "Event-Handler“ / „Callback“Methode aufrufen kann.
In der Klasse Component und allen davon abgeleiteten Klassen ist für jeden Listenertyp eine Methode definiert,
mit der der Listener registriert wird („add....()“).
è setzt sich zusammen aus add und Name des Listeners (Beispiel: addKeyListener())
Beispiel – Eine erste Event-Behandlung
Im Beispiel unten wird in einem Java-Fenster eine Schaltfläche angeordnet. Wenn der Benutzer auf die
Schaltfläche klickt, soll das Fenster und die Anwendung geschlossen werden.
Auch wenn das Schließfeld des Fensters betätigt wird, soll alles geschlossen werden.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 134 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Im Beispielprogramm unten sind die oben erwähnten Methoden A (Verwendung von Adapterklassen für die
Ereignisbehandlung) und Methode B (Implementation eines Listener-Interfaces) implementiert.
(Im folgenden Bsp: nur Low Level Event MouseListener-Methoden im Einsatz)
//Eclipse-Projekt: kap11_EventFenster_LowLevel
import java.awt.*;
import java.awt.event.*;
public class EventFenster_LowLevel
{
public static void main(String[] args)
{
EFenster fenster = new EFenster();
fenster.setVisible(true);
}
}
Die Klasse EventFenster ist
Eventsource und Listener
class EFenster extends Frame implements MouseListener
{
Button btn;
public EFenster()
{
super("Bitte klicken");
this.setSize(180,100);
this.setLocation(350,300);
this.setBackground(new Color(50,200,255));
this.setLayout(null);
btn = new Button("schliessen");
btn.setCursor(new Cursor(Cursor.HAND_CURSOR));
btn.setBounds(40, 50, 100, 23);
MouseListener (this);
btn.add
MouseListener
this.add(btn);
this.add
}
// ß Button registriert den
WindowListener (new
WindowAdapter()
//Frame registriert den WindowListener
{ // anonyme Klasse
public void windowClosing(WindowEvent we)
{
ExitApp();
}
});
// Implementierung des Interfaces MouseListener ------------------------------public void mouseClicked(MouseEvent me)
{
ExitApp();
}
Modell A)
Nur eine Adaptermethode
(als „Callback“-Methode)
zu überschreiben.
public void mouseReleased(MouseEvent me)
{}
public void mousePressed(MouseEvent me)
{}
Modell B)
Alle Interfacemethoden
(als “Callback-Methode)
zu implementieren
public void mouseEntered(MouseEvent me)
{}
public void mouseExited(MouseEvent me)
{}
public void ExitApp()
{
// Der eigentliche Applikations-Code-------------------
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 135 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
// hier könnten sich weitere Anweisungen befinden,
// die beim Beenden der Anwendung ausgeführt werden
System.exit(0);
}
}
11.5 Implementierungsmöglichkeiten:
Im Beispiel oben sind zwei verschieden Implementierungstechniken für die Ereignisbehandlung zu Einsatz
gekommen. Hier nochmals eine Zusammenfassung.
11.5.1 Verwendung von Adapterklassen:
Adapterklassen implementieren ein Interface und stellen für alle Methoden leere Rümpfe zur Verfügung.
Besonders geeignet, wenn ein Interface viele Methoden enthält, aber nur wenige benötigt werden. Oder
umgekehrt ausgedrückt: nur in diesen Fällen sollten Adapterklassen zum Einsatz kommen, sonst wird der Code
leicht unübersichtlich.
11.5.2 Listener Interface wird implementiert:
Alle Methoden des Listeners müssen aufgeführt werden.
Relativ einfach zu implementieren, zu warten und zu erweitern.
Nachteil u. U. viele leere Methoden.
Vorteil: potentielle Funktionalität des Interfaces ist sichtbar
è Es ist eine Trennung von GUI und Applikationslogik möglich. Dadurch entstehen kleinere übersichtlichere Klassen.
Andererseits ist ein Austausch der Programmlogik möglich, ohne dabei das GUI zu verändern.
11.6 Getrennter Code für GUI und Applikation
Im obigen Beispiel EventFenster.java wurde schon versucht, den Code für die GUI vom Code für die Applikation
zu trennen. So richtig zufrieden stellend ist die obige Lösung jedoch nicht, da der Gesamt-Code in einer
einzigen Klasse (EFenster) untergebracht ist.
Unten sehen Sie die besser organisierte Lösung:
// Eclipse-Projekt: kap11_awt_events_case_study_
import java.awt.*;
import java.awt.event.*;
public class EventDelegationFenster
{
public static void main(String[] args)
{
EreignisBehandlung ereignisbehandlung = new EreignisBehandlung();
EDFenster fenster = new EDFenster(ereignisbehandlung);
}
}
//----------------- GUI ---------------------------------------------------class EDFenster extends Frame
{
Button btn;
public EDFenster(EreignisBehandlung ereignisbehandl) // Konstruktor mit EH
{
super("bitte klicken");
this.setSize(180,100);
this.setLocation(350,300);
this.setBackground(new Color(50,200,255));
this.setVisible(true);
this.setLayout(null);
btn = new Button("Schliessen");
btn.setCursor(new Cursor(Cursor.HAND_CURSOR));
btn.setBounds(40, 50, 100, 23);
// x, y, width, height
btn.addMouseListener(ereignisbehandl);
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 136 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
this.add(btn);
this.addWindowListener(ereignisbehandl);
}
}
//----------Application Code in den Callback-Methoden der Listener------------
class
EreignisBehandlung implements WindowListener, MouseListener
{
public void windowClosing(WindowEvent we) // WindowEvents----------------{
ende();
}
public void windowOpened(WindowEvent we)
{}
public void windowIconified(WindowEvent we)
{}
public void windowDeiconified(WindowEvent we)
{}
public void windowActivated(WindowEvent we)
{}
public void windowDeactivated(WindowEvent we)
{}
public void windowClosed(WindowEvent we)
{}
public void mouseClicked(MouseEvent me) // MouseEvents-------------------{
ende();
}
public void mouseReleased(MouseEvent me)
{}
public void mousePressed(MouseEvent me)
{}
public void mouseEntered(MouseEvent me)
{}
public void mouseExited(MouseEvent me)
{}
private void ende() // der eigentliche App Code ---------------------------{
System.exit(0);
}
}
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 137 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
11.7 Low-Level-Ereignisse (systemnahe Ereignisse / Einige Beispiele):
Zur Erinnerung:
Ein semantisches Ereignis wird durch AWT-Komponenten (Buttons, Choice, Label, usw.) ausgelöst, z.B. ein
Schaltflächenklick oder Eintrag in ein Textfeld.
Ein low level Ereignis ermöglicht die semantischen Ereignisse. Eventsource ist ein grafisches Objekt aus der
AWT-Fenster-Klassen-Hierarchie, also Component, Container und Ableitungen davon
11.7.1 Component-Ereignisse ( Interface
ComponentListener ):
Die Objekte der Klasse Component und alle davon abgeleiteten Klassen können Component-Ereignisse
empfangen. Sie müssen dazu den ComponentListener registrieren.
Component-Ereignisse werden ausgelöst, wenn eine
Komponente (Objekt der Klasse oder Ableitung
davon)…
…erzeugt wird oder von dem unsichtbaren in den
sichtbaren Zustand versetzt wird (Z. B. Aufruf der
Methode setVisible(true))
…unsichtbar gemacht wird (z.B. setVisible(false))
verschoben wird (per Maus oder setLocation())
…in ihrer Größe verändert wird (per Maus oder
setSize())
Listener-Methode („Call-Back“-Methode):
componentShown()
componentHidden()
componentMoved()
componentResized()
Den genannten Listener-Methoden wird ein ComponentEvent-Objekt als Argument übergeben.
Die Klasse ComponentEvent stellt die Methode getComponent() zur Verfügung, mit der ermittelt wird,
welche Komponente das Ereignis ausgelöst hat.
public Component getComponent()
(Siehe: Hilfsmethoden in low-level-Event-Klassen)
Beispiel – Eine low level Event-Behandlung; Reaktion auf ComponentEvent-Ereignisse
//Eclipse-Projekt: kap11_awt_events_case_study_
import java.awt.*;
import java.awt.event.*;
public class ComponentEventFenster
{
public static void main(String[] args)
{
CEFenster fenster = new CEFenster();
}
}
class CEFenster extends Frame implements ComponentListener
{
Button btn;
public CEFenster()
{
super("jetzt wird's bunt");
this.setSize(300,200);
this.setLocation(350,300);
this.setVisible(true);
this.addWindowListener ( new WindowAdapter()
// schon bekannt
{
// Modell A
public void windowClosing(WindowEvent e)
// mehrere Adapter{
// methode überschreiben
System.exit(0);
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 138 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
} // in der Folge u.U. weitere Adapterklassen:
// public void windowIconified(WindowEvent e)
// {…}
// public void windowDeiconified(WindowEvent e)
// {…}
// public void windowActivated(WindowEvent e)
// {…}
// public void windowDeactivated(WindowEvent e)
// {…}
// USW., USW., ...
}) ;
this.addComponentListener(this);
// neu
this.setLayout(null);
btn = new Button("ausblenden");
btn.setSize(90, 23);
btn.setLocation((this.getWidth()-90)/2,
//Button “einmitten”
(this.getHeight()-23)/2);
btn.addMouseListener(new AbgeleiteterMouseListener());
this.add(btn);
// Modell C, siehe unten
this.setVisible(true);
}
public void componentMoved (ComponentEvent ae)
// Modell B; Implementierung
{
// des Listener-Interfaces
int red = (int)(this.getBounds().getX() % 254);
// von ComponentListener
red = (red > 0) ? red : -red;
int green = (int)((this.getBounds().getX() + this.getBounds().getY()) % 254);
green = (green > 0) ? green : -green;
int blue = (int)(this.getBounds().getY() % 254);
blue = (blue > 0) ? blue : -blue;
this.setBackground(new Color(red, green, blue)); // Loca-spezif. Backgrnd
}
public void componentHidden (ComponentEvent ae)
{
System.out.println("Fenster wurde ausgeblendet");
}
public void componentResized (ComponentEvent ae)
{ // Button neu “einmitten””
btn.setLocation((ae.getComponent().getWidth()-btn.getWidth())/2,
(ae.getComponent().getHeight()-btn.getHeight())/2);
}
public void componentShown (ComponentEvent ae)
{
// alle Component
System.out.println("Fenster wird angezeigt");
// Listener-Methoden
}
AbgeleiteterMouseListener
class
extends MouseAdapter //Modell C:
Adapterntzg
{
// ohne anonyme Klasse
public void mouseClicked (MouseEvent me)
{
me.getComponent().getParent().setVisible(false);
try
{
Thread.sleep(1000);
}catch(Exception e){}
me.getComponent().getParent().setVisible(true);
}
}
}
Parent des Buttons ist der Frame
Die gelb umrahmten Hilfsmethoden sind von der class Component geerbt. Die
Hilfsmethode getComponent() von der class Container.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 139 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
11.7.2 Window-Ereignisse ( Interface
WindowListener ):
Window-Ereignisse treten ein, wenn sich der Zustand des Fensters ändert. Um auf diese Änderungen zu
reagieren, muss der WindowListener implementiert werden.
Windows-Ereignisse werden ausgelöst, wenn ein
Fenster…
…“geöffnet“ wurde (z.B. durch Methode
setVisible(true))
…geschlossen wird (durch Schließfeld, Menü in der
Titelleiste, ALT + F4)
…geschlossen wurde (per dispose()-Methode)
…minimiert wurde (z.B. durch das Minimierfeld oder
durch die Methode setState(Frame.ICONIFIED))
…wiederhergestellt wurde (via Icon anklicken, durch
Methode setState(Frame.NORMAL)
…in den Hintergrund gestellt wird (z.B.
setVisible(false) oder Methode hide())
…erstellt wurde oder wieder aktiviert wird (durch
Anklicken mit der Maus oder via Methode
setVisible(true))
Listener-Methode:
windowOpened()
windowClosing()
windowClosed()
windowIconified()
windowDeiconified()
windowDeactivated()
windowActivated()
Den genannten Listener-Methoden wird ein WindowEvent-Objekt als Argument übergeben.
Die Klasse WindowEvent stellt die Methode getWindow() zur Verfügung, mit der ermittelt wird, welches
Fenster das Ereignis ausgelöst hat.
public Window getWindow()
Beispiel – Eine low level Event-Behandlung; Reaktion auf WindowEvent-Ereignis
//Eclipse-Projekt: kap11_awt_events_case_study_
import java.awt.*;
import java.awt.event.*;
public class WindowEventFenster
{
public static void main(String[] args)
{
WEFenster fenster1 = new WEFenster(200,200);
WEFenster fenster2 = new WEFenster(300,300);
}
}
class WEFenster extends Frame implements
WindowListener
{
Button btn;
public WEFenster(int x, int y)
{
super("ein ganz normales Fenster");
this.setSize(300,200);
this.setLocation(x, y);
this.addWindowListener(this);
this.setVisible(true);
}
public void windowOpened (WindowEvent we)
{
we.getWindow().setBackground(Color.magenta);
}
public void windowClosing (WindowEvent we)
Methoden
{
we.getWindow().dispose();
// alle Window-Listner-
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 140 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
}
public void windowClosed (WindowEvent we)
{
System.out.println("Fenster wurden geschlossen");
System.exit(0);
}
public void windowIconified (WindowEvent we)
{
((Frame)we.getWindow()).setTitle("bin ganz klein");
}
public void windowDeiconified (WindowEvent we)
{
((Frame)we.getWindow()).setTitle("ein ganz normales Fenster");
}
public void windowActivated (WindowEvent we)
{
we.getWindow().setBackground(Color.cyan);
}
public void windowDeactivated (WindowEvent we)
{
we.getWindow().setBackground(Color.lightGray);
}
}
Hilfsmethoden aus
Class WindowEvent bzw. Frame
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 141 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
11.7.3 Focus-Ereignisse ( Interface
FocusListener ):
In jeder Anwendung kann immer nur ein Fenster aktiv sein.
In einem Fenster kann auch nur eine Komponente aktiv sein. Diese Komponente besitzt den Focus. Dies ist
durch verschiedene äußere Merkmale gekennzeichnet (Umrandung mit besonderm Aussehen (OS-abhängig),
Ein Textfeld hat den Focus, wenn der Cursor blinkt,…).
Focus-Ereignisse treten dann ein, wenn eine Komponente den Focus erhält oder verliert. Um auf diese
Änderung zu reagieren, muss der FocusListener implementiert werden.
Focus-Ereignisse werden ausgelöst, wenn eine
Komponente…
…den Focus erhält (durch Anklicken der Komponente
mit der Maus, Tab-Taste, Methode requestFocus())
…den Focus verliert (durch Anklicken anderer
Komponenten, Tab-Taste, requestFocus() einer
anderen Komponente)
Listener-Methode:
focusGained()
focusLost()
Den genannten Listener-Methoden wird ein FocusEvent-Objekt als Argument übergeben.
Eine Komponente kann mit der Methode requestFocus() den Focus für sich anfordern. Dazu muss die
Komponente sichtbar sein.
public void requestFocus()
Mit der Methode transferFocus() kann der Focus an die nächste Komponente weitergereicht werden.
Die Reihenfolge hängt davon ab, in welcher Reihenfolge die Komponenten zu dem Container-Objekt
hinzugefügt wurden.
public void transferFocus()
Soll überprüft werden, ob eine Komponente den Focus hat, steht die Methode hasFocus() zur Verfügung.
public void hasFocus()
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 142 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Input-Ereignisse:
Die InputEvent -Klasse ist die Oberklasse aller Eingabeereignisse (KeyEvent und MouseEvent,
MouseWheelEvent). Sie stellt Methoden zur Verfügung, mit denen überprüft werden kann, ob Funktions- oder
Umschalttasten betätigt wurden:
boolean
boolean
boolean
boolean
boolean
isAltDown()
isAltGraphDown()
isControlDown()
isShiftDown()
isMetaDown()
Alt wurde gedrückt
Alt Gr wurde gedrückt
Strg wurde gedrückt
Umschalttaste gedrückt
rechte Maus-Taste gedrückt
Die Klasse InputEvent stellt auch die Methode consume() zur Verfügung. Diese Methode „konsumiert“
einen Event und verhindert dadurch das Weiterleiten der Eingabe. Die Methode kann dazu verwendet werden,
um Zeichen „auszufiltern“, wenn nur bestimmte Zahlen und Buchstaben bei der Komponente ankommen
sollen.
public void consume()
Ob eine Eingabe „konsumiert“ (ausgefiltert) wurde, kann mit der Methode isConsumed() festgestellt werden.
public boolean isConsumed()
Wenn der Zeitabstand zwischen zwei Eingaben (z.B. zwischen zwei Mausklicks) ermittelt werden soll, kann die
Methode getWhen() eingesetzt werden. Die Methode liefert für ein Ereignis den zugehörigen Zeitstempel.
public long getWhen()
11.7.4 Die Eventklasse Tastaturereignisse (Klasse
KeyListener ):
KeyEvent )
und das Listener-Interface
Tastaturereignisse werden ausgelöst wenn eine Taste oder Tastenkombination betätigt wird. Um auf eine
bestimmte Taste zu reagieren muss das KeyListener-Interface implementiert werden.
Tastatur-Ereignisse werden ausgelöst, wenn eine
Taste…
…betätigt und wieder losgelassen wird
…betätigt wird
…losgelassen wurde
Listener-Methode:
keyTyped()
keyPressed()
keyReleased()
Den genannten Listener-Methoden wird ein KeyEvent-Objekt ke als Argument übergeben.
Klasse KeyEvent stellt Methoden zur Verfügung, über die ermittelt werden kann, welche Taste(n)
Die
betätigt wurde(n).
public char getKeyChar() liefert Zeichen, das der Taste entspricht
public int getKeyCode()
Tastencode (Virtual Key Code, eine symbolische Konstante)
public static String getKeyText()
liefert Namen der Taste als Zeichenkette z.B. "ALT"
System.out.println(ke.getKeyText(ke.getKeyCode()));
keyTyped(): nur Unicode-Zeichen liefern einen Wert und können über getKeyChar() abgefragt werden.
keyPressed(): alle Zeichen können abgefragt werden und über getKeyCode() lassen sich Tastaturcodes
abfragen.
Über den Virtual Key Code können auch die betätigten Steuerungstasten ermittelt werden. Einfacher ist es jedoch in
den meisten Fällen, die von der Klasse InputEvent geerbten Methoden isAltDown(), isShiftDown(),
usw. zu verwenden.
Virtual Key Code
Taste
Virtual Key Code
Taste
Virtual Key Code
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 143 / 229
Taste
15.03.2016
PROMOD-2 / Ausgabe 2016
VK_A
VK_B
…
VK_Z
VK_0
VK_1
…
VK_9
(„getKeyChar()“)
A
B
…
Z
0
1
9
VK_Enter
VK_F1
VK_F2
…
VK_F12
VK_SPACE
VK_END
VK_HOME
(„getKeyChar()“)
Enter
F1
F2
VK_ESCAPE
VK_INSERT
VK_DELETE
VK_TAB
ESC
EINFG
ENTF
TABULATOR
F12
Leertaste
Ende
POS1
Welche Virtual Key Codes es noch gibt: siehe API-Doku „Field Summery“ in class KeyEvent.
Beispiel – Eine low level Event-Behandlung; Reaktion auf Tastatur-Ereignis
//Eclipse-Projekt: kap11_awt_events_case_study_
import java.awt.*;
import java.awt.event.*;
public class KeyEventFenster
{
public static void main(String[] args)
{
KEFenster fenster = new KEFenster();
}
}
class KEFenster extends Frame implements
KeyListener
{
Label lbl1, lbl2, lbl3;
public KEFenster()
{
super("Farbe über Tastatur auswählen");
this.setSize(280,210);
this.setLocation(350,300);
this.setLayout(null);
lbl1 = new Label("o = orange, Alt+o = dunkelorange");//statischer Text
im Fenster
lbl1.setBounds(20, 50, 190, 23);
lbl1.setBackground(Color.orange);
// Eine Komponente
lbl1.setVisible(true);
this.add(lbl1);
lbl2 = new Label("g = grün, Alt+g = dunkelgrün");
lbl2.setBounds(40, 100, 190, 23);
lbl2.setBackground(Color.green);
this.add(lbl2);
lbl2.setVisible(true);
lbl3 = new Label("b = blau, Alt+b = dunkelblau");
lbl3.setBounds(60, 150, 190, 23);
lbl3.setBackground(Color.cyan);
this.add(lbl3);
lbl3.setVisible(true);
this.addKeyListener(this);
// neu
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent we)
{
System.exit(0);
}
});
this.setVisible(true);
this.requestFocus();
}
// Keylistener-Methoden
public void keyPressed (KeyEvent ke)
{
if (ke.getKeyCode() == KeyEvent.VK_ESCAPE)
System.exit(0);
if(ke.getKeyChar() == 'o')
{
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 144 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
if(ke.isAltDown())
ke.getComponent().setBackground(new Color(255,153,0));
else
ke.getComponent().setBackground(Color.orange);
}
if(ke.getKeyChar() == 'g')
{
if(ke.isAltDown())
ke.getComponent().setBackground(new Color(0,153,0));
else
// Keylistener-Methoden
ke.getComponent().setBackground(Color.green);
}
if(ke.getKeyChar() == 'b')
{
if(ke.isAltDown())
ke.getComponent().setBackground(new Color(0,51,255));
else
ke.getComponent().setBackground(Color.cyan);
}
}
public void keyReleased (KeyEvent ke)
{
if (ke.getKeyCode() == KeyEvent.VK_ESCAPE)
System.exit(0);
}
public void keyTyped (KeyEvent ke)
{
}
}
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 145 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
11.7.5 Die Eventklasse Klasse
MouseEvent und die Listener-Interfaces MouseListener,
MouseMotionListener :
Mausereignissen treten immer dann ein, wenn eine Maustaste betätigt wird oder die Maus bewegt wird (mit
oder ohne gedrückter Maustaste). Um auf diese Ereignise zu reagieren, muss das MouseMotionListenerInterface implementiert werden.
Die Trennung von Maus-Ereignissen und Maus-Bewegungs-Ereignissen ist sinnvoll, da in vielen
Programmen nur auf Mausklicks reagiert werden soll.
Mausbewegungen bringen eine Flut von Ereignissen mit sich (z.B. werden während einer Fenster-Verschiebung
laufend Ereignisse ausgelöst), was die Programmperformance negativ beeinflusst.
Mausereignisse:
Zum Erkennen von Mausereignissen muss das MouseListener-Interface implementiert werden.
Maus-Ereignisse werden ausgelöst, wenn…
…Maustaste betätigt wird
…Maustaste losgelassen wird
…Maustaste geklickt wird (betätigt und losgelassen)
…Maus in den Bereich einer Komponente bewegt
wird, die das Klickereignis empfängt
…Maus aus dem Bereich einer Komponente bewegt
wird, die das Klickereignis empfängt
Listener-Methode:
mousePressed()
mouseReleased()
mouseClicked()
mouseEntered()
mouseExited()
Mausbewegungsereignisse:
Zum Erkennen von Mausbewegungsereignissen muss das MouseMotionListener-Interface implementiert
werden.
Mausbewegungsereignisse werden ausgelöst,
wenn…
…eine Komponente mit der Maus angeklickt wurde
und bei gedrückter Maustaste verschoben wird
…die Maus über eine Komponente hinweg bewegt
wird, ohne dass eine Maustaste betätigt wird
Listener-Methode:
mouseDragged()
mouseMoved()
Den Methoden beider Listener wird ein MouseEvent-Objekt als Argument übergeben.
Klasse MouseEvent
Die
stellt noch folgende Hilfs-Methoden zur Verfügung, über die die Position
des Mauszeigers beim Klicken und die Anzahl der Klicks ermittelt werden kann.
Point getPoint()
int getX()
int getY()
int getClickCount
Zur Ermittlung der Mauszeigerposition
Zur Ermittlung der x-Mauszeigerposition
Zur Ermittlung der y-Mauszeigerposition
Anzahl er Mausklicks
Beispiel – Eine low level Event-Behandlung; Reaktion auf Mausevents
//Eclipse-Projekt: kap11_awt_events_case_study_
import java.awt.*;
import java.awt.event.*;
public class MouseEventFenster
{
public static void main(String[] args)
{
MEFenster fenster = new MEFenster();
}
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 146 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
}
class MEFenster extends Frame implements MouseListener, MouseMotionListener
{
Button btn1, btn2, btn3;
private int distX, distY;
public MEFenster()
{
super("Klick-Fenster");
this.setSize(230,200);
this.setLocation(400,300);
this.setLayout(null);
btn1 = new Button("Hintergrundfarbe pink");
btn1.setBounds(40, 50, 150, 23);
btn1.addMouseListener(this);
this.add(btn1);
btn2 = new Button("schiebe mich");
btn2.setBounds(40, 100, 150, 23);
btn2.addMouseMotionListener(this);
btn2.addMouseListener(this);
this.add(btn2);
btn3 = new Button("gib mir einen Doppelklick");
btn3.setBounds(40, 150, 150, 23);
btn3.addMouseListener(this);
this.add(btn3);
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent we)
{
System.exit(0);
}
});
this.setVisible(true);
}
public void mouseClicked (MouseEvent me)
{
if(me.getComponent() == btn1)
{
me.getComponent().getParent().setBackground(Color.magenta);
}
}
public void mouseReleased (MouseEvent me)
{
if((me.getComponent() == btn3) && (me.getClickCount() == 2))
{
btn3.setLabel("Klick Klick");
try
{
Thread.sleep(1000);
}
catch(Exception ex)
{}
btn3.setLabel("gib mir einen Doppelklick");
}
}
public void mouseEntered (MouseEvent me)
{
me.getComponent().setBackground(Color.cyan);
}
public void mouseExited(MouseEvent me)
{
me.getComponent().setBackground(Color.lightGray);
}
// alle MouseListener// Methoden
public void mousePressed (MouseEvent me)
{
distX = me.getX();
distY = me.getY();
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 147 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
}
// alle MouseMotion// Listener-Methoden
public void mouseDragged (MouseEvent me)
{
btn2.setLocation((int)btn2.getLocation().getX() + me.getX() - distX,
(int)btn2.getLocation().getY() + me.getY() - distY);
}
public void mouseMoved (MouseEvent me)
{}
}
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 148 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
11.8 Zusammenfassung Ereignisse und ihre Behandlung
Einige der in der Tabelle unten genannten Low-level-Ereignisse haben wir in den Beispielen
oben gesehen:
Ereignis
(low level
Ereignisse)
Zustands- und
Größenänderung
einer Komponente
Hinzufügen /
Enterfernen einer
Component / eines
Containers
Erhalten / Verlieren
des Focus
Fensterposititon und
–größe ändern
Tastendruck
Mausbewegung
Mausklicks
Mausradbewegung
ListenerInterfaces
Ereignisklasse
EventSource
(konkrete grafische Objekte
aus der AWT-FensterKlassen-Hierarchie)
Methoden des Listeners
ComponentLIstener
ComponentEvent
Component
componentHidden() – Komponente wurde verborgen
componentMoved() – Komponente wurde bewegt
componenResized() – Größe wurde geändert
componentShown() – Komponente wurde sichtbar
ContainerListener
ContainerEvent
Container
componentAdded() – Komponente wurde hinzugefügt
componentRemoved() – Komponente wurde entfernt
FocusListener
FocusEvent
Component
focusGained() – Komponente hat Focus erhalten
focusLost() – Komponente hat Focus verloren
WindowsListener
WindowEvent
Window
windowActivated() – Fenster wurde aktiviert
windowClosed() – Fenster wurde geschlossen
windowClosing() – Fenster wird gerade geschlossen
windowDeactivated() – Fenster wurde deaktiviert
windowDeiconified() – Fenster wurde wieder hergestellt
windowIconified() – Fenster wurde auf Symbolgröße minimiert
windowOpened() – Fenster wurde geöffnet
KeyListener
KeyEvent
Component
keyPressed() – Taste wurde betätigt
keyReleased() – Taste wurde losgelassen
keyTyped() – Taste wird betätigt und wieder losgelassen
MouseMotionListener MouseMotionEvent Component
mouseDraged() – Maus wird mit gedrückter Taste über
eine Komponente bewegt
mouseMoved() – Maus wird ohne eine gedrückte Taste über
eine Komponente bewegt
MouseEvent
Component
MouseListener,
mousePressed() – Maustaste wurde betätigt
mouseReleased() – Maustaste wurde losgelassen
mouseClicked() – Maustaste wurde betätigt und losgelassen
mouseEntered() – Maustaste wurde in den Bereich einer
Komponente bewegt
mouseExited() – Maustaste wurde aus dem Bereich einer
Komponente bewegt
MouseWheelListener
MouseWheelEvent
Component
mouseWheelMoved() – Mausrad wurde gedreht
Da für die Low-Level-Ereignisse die Event-Source i.d.R. die Klasse Component ist, können die
Low-Level-Listener auf (nahezu) alle Klassen der AWT-Hierarchie angewandt werden.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 149 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Semantische Ereignisse werden uns in den Folgekapiteln begegnen; hier ein Überblick:
Ereignis
(semantische
Ereignisse)
Aktion auf
Komponente
(Choice, Label,
Button, …)
Verschieben
Schieberegeler
oder Bildlaufleiste
Zustandsänderung
Textänderung
ListenerInterfaces
Ereignisklasse
EventSource
(AWT Komponenten)
Methoden des Listeners - Erläuterung
ActionEvent
ActionListener
actionPerformed() – Aktion wurde ausgeführt
AdjustmentListene AdjustmentEvent
z.B. Button, List,
MenuItem,
TextField
Scrollbar
r
adjustmentValueChanged() – Wert wurde geändert
ItemEvent
Checkbox, List,
ItemListener
CheckBoxMenuIte
m
itemStateChanged() – Zustand wurde geändert
TextEvent
TextComponent
TextListener
textValueChanged() – Text wurde geändert
Aufgabe:
Im Progjekt ADRELI_4_GUI geben Sie dem bisherigen ADRELI-Programm eine AWT-Oberfläche. Dabei sollen
verschiedene, in der Spalte „EventSource“ genannten AWT-Komponenten, verwendet werden.
Beschreiben Sie hier, wie Sie mit Hilfe der Java-API-Docu vorgehen, um herauszufinden, auf welche Events
mit welchen Methoden reagiert werden kann, ohne in der Tabelle oben nachzusehen.
Also z.B. konkret: „Welche Events sind mit einem Button (als EventSource) auslösbar und mit welchen CallBack-Methoden kann darau reagiert werden?“. Notieren Sie Ihre Vorgehensweise unten!
Nochmals: es geht also darum, dass Sie wissen, wie mit Hilfe der originalen Java-API-Docu – ohne die oben
gezeigte Tabelle – die notwendigen Informationen für die GUI-Interaktionen zusammen gesucht werden können!
Bearbeiten Sie die Case Studies (kap09_awt_event_case_study) und lösen
Sie die Übungsaufgeben zu diesem Kapitel! (Siehe Unterlagen hierzu auf dem
Materialserver.)
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 150 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 151 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Ab hier (Kapitel 12 bis Kapitel 14) wird das Script hinsichtlich AWT als
Beispielsammlung und CaseStudy-Pool genutzt: der Studierende nutzt es für das Selbststudium
– das ein oder andere Beispiel wird u.U. auch im Kolloquium thematisiert.
Beachten Sie: die Prüfungsrelevanz der Beispiele ist nach wie vor gegeben.
Case-StudyPool
(EINSCHUB: zunächst Kapitel 13. AWT-Komponeten: die wichtigen Window-Klassen Dialog /
FileDialog, und der MenuComponent-Klassen Menu/MenuBar/MenuItem,
PopUpMenu vorziehen. Diese AWT-Komponenten müssen ab Adreli_4 in Ihrer
Softwarelösung verwendet werden.)
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 152 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
12. AWT-Graphics: Grafikprogrammierung mit AWT
Was Sie lernen:
wie auf AWT-Komponenten gezeichnet wird
wie Text ausgegeben wird
wie Bilder aus Dateien angezeigt werden können
Voraussetzungen:
Kenntniss der Fensterprogrammierung und Ereignisbehandlung
12.1 Grundlagen
Die Klasse java.awt.Graphics kapselt einen graphischen Ausgabebereich und stellt u.a. Methoden zum
Zeichnen von Linien, Kreisen, Rechtecken, usw. zur Verfügung. Auch die Darstellung von Schriften und das
Anzeigen von Bildern gehören in den Bereich der Graphikprogrammierung. So speichert bspw. die Klasse
Graphics Zeicheneinstellungen wie die aktuelle Schriftfarbe und die Schriftart.
Anmerkung: Im Zusammenhang mit der Graphik und den Komponenten kommen jetzt auch semantische
Ereigniss zum Einsatz.
12.2 Der Grafikkontext / Die Klasse Graphics :
Unter dem Begriff Grafikkontext werden die aktuellen Einstellungen für die Zeichenfunktionen und die
Textausgabe zusammengefasst: z. B. Zeichenfarbe, Schriftart, Zeichenbereich,…
Der Grafikkontext (class Graphics) stellt Methoden zur Verfügung…
− zum Einstellen und Verändern der Grafikkontext-Eigenschaften
− zum Zeichnen von Linien, Bögen u. a. primitiven geometrischen Formen
− zum Füllen von Flächen
− zur Textausgabe
− zur Anzeige von Bildern
Ermittelt wird der Grafikkontext eines Fensters und anderer AWT-Komponenten mit der ComponentMethode getGraphics()
public Graphics getGraphics()
Die Methode getGraphics() stammt von der Klasse Component. Die Methode kann für Objekte aller
von Component abgeleiteten Klassen eingesetzt werden, wie z.B. Frame, Panel und den AWTKomponenten (z.B. Labels, Buttons, …).
Die Methode getGraphics() liefert ein Graphics-Objekt (als „Graphikkontext“) zurück. Ruft man für
dieses Objekt z.B. die Zeichenmethode drawRect() auf, so wird auf die entsprechende Komponente ein
Rechteck gezeichnet, mit den Eigenschaften die im Graphics-Objekt eingestellt sind.
Hinweis: Wird auf diese Weise eine Graphik gezeichnet (siehe Beisiel unten: hier wird eine Graphik auf einen Button
gezeichnet), so wird diese selbst gezeichnete Grafik gelöscht, wenn Teile davon verdeckt werden, z.B. durch ein
anderes Fenster. (Probieren Sie’s mit dem unten gelisteten Programm aus.)
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 153 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Gezeichnet mit Graphics.fillRect() und Graphics.fillOval().
Fenster wurde verkleinert und
dann wieder vergrößert:
Beispiel – Umgang mit dem Grafikkontext
//Eclipse-Projekt: kap12_awt_graphics_case_study_
import java.awt.*;
import java.awt.event.*;
public class GrafikKontext extends Frame implements ActionListener
{
Button btn;
public static void main(String[] args)
{
GrafikKontext fenster = new GrafikKontext();
}
public GrafikKontext()
{
super("Zeichnen auf Schaltflächen");
this.setSize(260,180);
this.setLocation(100,100);
this.setBackground(new Color(50,200,255));
this.setLayout(null);
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent we)
{
System.exit(0);
}
});
btn = new Button("Klicken Sie hier");
btn.setSize(200, 100);
btn.setLocation(30, 50);
this.add(btn);
Der Grafikkontext
btn.addActionListener(this);
vom Button
this.setVisible(true);
}
public void actionPerformed(ActionEvent ev) // wird aufgerufen nach Betätigung der
{
// Schaltfläche
Graphics g_btn = btn.getGraphics();
g_btn.setColor(Color.blue);
g_btn.fillRect(10, 10, 100, 50);
g_btn.setColor(Color.red);
g_btn.fillOval(70, 40, 120, 50);
}
}
12.3 Die Methoden paint() und repaint():
Die paint()-Methode wird in der Component-Klasse definiert:
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 154 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
public void paint(Graphics g)
Aufgerufen wird paint() immer dann, wenn die
Komponente das erste Mal gezeichnet wird oder wenn
ein erneutes Zeichnen erfoderlich ist.
Um mit der paint()-Methode auf einer Komponente zu zeichnen, brauchen Sie eine Klasse, die von
Class Compontent abgeleitet ist und in der die Methode paint() überschreiben wurde.
Zum Beispiel kann man eine neue Klasse von Frame ableiten, deren Methode paint() überschreiben und so in
das Fenster zeichnen.
Im Methodenrumpf von paint() wird von den oben behandelten Graphics-Methoden setColor(),
fillRect(), drawRect()usw. (ca.
50 nicht geerbte Methoden), Gebrauch gemacht.
Die Methode paint() wird automatisch aufgerufen, wenn ein Fenster verborgen war und (wieder) sichtbar
wird. (z.B. bei setVisible(true)) Siehe Beispiel ZeichenFenster1 unten.
èAuch Teile einer Komponente lassen sich "wieder zeichnen"!! Siehe AWT-Doku zu den Methoden
Compontent.repaint()
Beispiel – Die Methode paint()
//Eclipse-Projekt: kap12_awt_graphics_case_study_
import java.awt.*;
import java.awt.event.*;
public class ZeichenFenster1 extends Frame
{
public static void main(String[] args)
{
ZeichenFenster1 fenster = new ZeichenFenster1();
}
public ZeichenFenster1()
{
super("Zeichenfenster 1");
this.setSize(250,140);
this.setLocation(100,100);
this.setBackground(new Color(50,200,255));
this.setVisible(true);
this.setLayout(null);
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent we)
{
System.exit(0);
}
});
}
public void paint(Graphics g)
{
g.setColor(Color.yellow);
g.fillRect(45, 55, 150, 50);
g.setColor(Color.blue);
g.drawString("gefülltes Rechteck",60,80);
}
}
Die Positionierung der Graphik erfolgt ausgehend von der linken oberen Ecke (0,0) der Komponente, auf die
gezeichnet wird. Die Maßeinheit ist Pixel.
Jeder Zeichen- und Textausgabemethode muss die Position auf diese Weise übergeben werden.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 155 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Wenn im Fenster gezeichnet wird, ist zu beachten, dass ein Fenster einen Rahmen, eine Titelleiste und u. U.
eine Menüzeile besitzt.
Um die wahre Größe der (inneren) Fläche herauszufinden, auf die gezeichnet werden kann, steht die Methode
Container.getInsets() zur Verfügung. Reterunwerttyp: class Insets . Enthält die
int-Felder bottom, right, left, right und top. Siehe API-Doku.
Beispiel – Die Positionierung im Fenster
//Eclipse-Projekt: kap12_awt_graphics_case_study_
import java.awt.*;
import java.awt.event.*;
public class ZeichenFenster2 extends Frame
{
public static void main(String[] args)
{
ZeichenFenster2 fenster = new ZeichenFenster2();
}
public ZeichenFenster2()
{
super("Zeichenfenster2");
this.setSize(300,140);
this.setLocation(100,100);
this.setBackground(new Color(50,200,255));
this.setVisible(true);
this.setLayout(null);
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent we)
{
System.exit(0);
}
});
}
public void paint(Graphics g)
{
g.setColor(Color.white);
g.drawRect(this.getInsets().left + 20,
this.getInsets().top + 20,
(int)this.getSize().getWidth() - this.getInsets().left this.getInsets().right - 40,
(int)this.getSize().getHeight() - this.getInsets().top –
this.getInsets().bottom - 40
);
}
}
Die Methode getSize() stammt von Component und liefert einen Returnwert vom Typ
Dimension (enthält die zwei Felder Witdth und Height). Siehe API-Doku.
12.4 Anzeige von Text:
Für die Ausgabe von Text bietet die Klasse Graphics mehrere Methoden:
public void drawString(String str, int x, int y);
public void drawChars(char[] data, int offset, int length, int x, int
y);
public void drawBytes(byte[] data, int offset, int length, int x, int
y);
drawstring() gibt die übergebene Zeichenkette auf der Kompontente aus. Es wird nur soviel Text
geschreiben, wie in den Bereich passt. Der Rest wird abgeschnitten.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 156 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
drawChars() und drawBytes() schreiben Zeichen (UNICODE), bzw. Bytes (ASCII) eines Arrays auf die
Komponente. Der Parameter offset legt die Startposition fest. Der Prameter length gibt an, wie viele
Zeichen, bzw. Bytes aus dem Array zu schreiben sind.
Die Parameter x und y bezeichnen die Position der linken Seite der Grundlinie.
12.4.1 Die Klasse Font / einige Grundbegriffe
Die hier kurz skizzierten Grundbegriffe sind der API-Doku(Java™
Font entnommen.
Platform Standard Ed. 6) zur Klasse
The Font class represents fonts, which are used to render text in a visible way. A font provides the information
needed to map sequences of characters to sequences of glyphs and to render sequences of glyphs on Graphics
and Component objects. […]
12.4.1.1
Characters and Glyphs
A character is a symbol that represents an item such as a letter, a digit, or punctuation in an abstract way. For
example, 'g', LATIN SMALL LETTER G, is a character.
A glyph is a shape used to render a character or a sequence of characters. In simple writing systems, such as Latin,
typically one glyph represents one character. In general, however, characters and glyphs do not have one-to-one
correspondence. For example, the character 'á' LATIN SMALL LETTER A WITH ACUTE, can be represented by two
glyphs: one for 'a' and one for '´'. […]
Font („Abbildungsvorschrift“, „digitale Umsetzung“)
„Character“ (semantisches Konezpt)
„Glyphe“ (graphische Darstellung eines Schriftzeichens)
Bild: Zusammenhang zwischen Character, Font und Glyphe
12.4.1.2
Physical and Logical Fonts
The Java Platform distinguishes between two kinds of fonts: physical fonts and logical fonts.
1. Physical fonts are the actual font libraries containing glyph data and tables to map from character
sequences to glyph sequences, using a font technology such as TrueType or PostScript Type 1. All
implementations of the Java Platform must support TrueType fonts; support for other font technologies is
implementation dependent. […]
2. Logical fonts are the five font families defined by the Java platform which must be supported by any
Java runtime environment: Serif, SansSerif, Monospaced, Dialog, and DialogInput.
These logical fonts are not actual font libraries. Instead, the logical font names are mapped to physical
fonts by the Java runtime environment. The mapping is implementation and usually locale dependent, so
the look and the metrics provided by them vary. Typically, each logical font name maps to several physical
fonts in order to cover a large range of characters.
Peered AWT components, such as Label and TextField, can only use logical fonts. […]
12.4.1.3
Font Faces and Names
A Font can have many faces, such as heavy, medium, oblique („kursiv“), gothic („Fraktur“) and regular. All of
these faces have similar typographic design.
There are three different names that you can get from a Font object.
The logical font name is simply the name that was used to construct the font.
The font face name, or just font name for short, is the name of a particular font face, like Helvetica Bold.
The family name is the name of the font family that determines the typographic design across several faces,
like Helvetica. […]
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 157 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Quelle: Wikipedia
Die Klasse Font / Schriftarten
Wenn keine Schriftart festgelegt wird, so nutzt Java eine systemabhängige Standardschrift
(Dialog.plain)
Soll eine andere Schriftart verwendet werden, so ist ein Font-Objekt zu verwenden
Dieses Font-Objekt wird dem Grafikkontext/einem Graphics-Objekt mit
font)
setFont(Font
übergeben
public abstract void setFont(Font font)
Font setzten
Bezüglich der Font-Eigenschaftgen (Attribute & Methoden) siehe API-Beschreibung class
Font .
Methoden um Font-Einstellungen zu bekommen sind im Folgenden aufgelistet.
public
public
public
public
public
Font getFont()
String getFontName()
String getFamily()
int getStyle()
int getSize()
gibt Font aus der System-Properties-Liste zurück
gibt Font- -Namen zurück
gibt Font-Family-Namen zurück
gibt Schriftstil zurück (BOLD, PLAIN, ITALIC,…)
gibt Schriftgröße zurück
Konstruktor:
public Font(String Name, int Style, int
Size)
Konstruktor
Der erste Parameter ist der Name der Schriftart. Auf allen Systemen sollten die Schriften Dialog,
DialogInput, Monospaced(Courier), Serif(Times Roman), Sans Serif(Arial) und Symbol
verfügbar sein.
Der zweite Parameter kennzeichnet den Schriftstil. Der Schriftstil kann BOLD (fett), PLAIN (normal) und
ITALIC (kuriv) sein. Die Schriftstile sind symbolsiche Konstanten, die in der Klasse Font vereinbart sind. Es
können auch zwei Schriftstile miteinander verknüft werden: z.B. Font.BOLD + Font.ITALIC.
Der dritte Parameter spezifiziert die Schriftgröße. Die Schriftgröße kann durch eine Integer-Zahl angegeben
werden. Die Maßeinheit ist Pixel.
Beispiel – Der Umgang mit Schriftarten; Methoden der Klasse Font
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 158 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
//Eclipse-Projekt:
kap12_awt_graphics_case_study_
import java.awt.*;
import java.awt.event.*;
public class ZeichenFenster3 extends Frame
{
public static void main(String[] args)
{
ZeichenFenster3 fenster = new ZeichenFenster3();
}
public ZeichenFenster3()
{
super("Zeichenfenster3");
this.setSize(700,300);
this.setLocation(100,100);
this.setBackground(new Color(50,200,255));
this.setVisible(true);
this.setLayout(null);
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent we)
{
System.exit(0);
}
});
}
public void paint(Graphics g)
{
g.drawString("Das ist ein Beispieltext",40,50);
g.drawLine(40, 50, 170, 50);
g.drawString(g.getFont().getFontName(),40,80);
Font font = new Font("Monospaced", Font.BOLD, 12);
g.setFont(font);
g.drawString(""+g. getFont().toString() ,40,110);
g.drawString(""+g. getFont() ,40,140);
g.setFont(new Font("Serif", Font.BOLD, 12));
g.drawString(""+g.getFont(),40,170);
g.setFont(new Font("SansSerif", Font.PLAIN, 12));
g.drawString(""+g.getFont(),40,200);
g.setFont(new Font("Dialog", Font.ITALIC, 12));
g.drawString(""+g.getFont(),40,230);
g.setFont(new Font("DialogInput", Font.BOLD +
Font.ITALIC, 12));
g.drawString(""+g.getFont(),40,260);
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 159 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
}
}
Welche Schriften sind verfügber?
Welche Schriftarten aktuell verfügbar sind, kann über die Klasse GraphicsEnvironment
abgefragt werden. Mit der Methode getLocalGraphicsEnvironment() wird ein Objekt
dieser Klasse erzeugt. Die Methode getAvailableFontFamilyNames() liefert die Namen aller verfügbaren
Fonts in ein String-Array zurück.
public void paint(Graphics g)
{
GraphicsEnvironment ge =
GraphicsEnvironment. getLocalGraphicsEnvironment();
String[] fonts = ge.getAvailableFontFamilyNames();
int anz = fonts.length;
for (int i = 0; i < anz; i++)
{
g.setFont(new Font(fonts[i],Font.PLAIN, 10));
g.drawString(fonts[i], 20, 40 + (i * 13));
}
}
Grundaubau des Schriftsatzes (Selbstudium)
Wenn ein Text an eine bestimmte Stelle in einem Fenster positioniert werden soll, wird i.d.R. die Breite, die der
auszugebenden Text einnehmen wird, benötigt. Diese Information kann über die
Klasse
FontMetrics abgerufen werden.
Um die Informationen dieser Klasse richtig nutzen zu können, muss man den Grundaufbau eines Schriftsatzes
kennen. Das Bild unten nennt die wichtigsten Begriffe des Schriftsatzes. Die englischen Begriffe entsprechen den
Methodennamen der Klasse FontMetrics .
Mit den folgenden FontMetrics-Methoden lassen sich die Schrift-Attribute ermittlen:
public
public
public
public
int
int
int
int
getHeight()
getAscent()
getDescent()
getMaxDescent()
public int getLeading()
public int StringWidth(String s)
public int charWidth(int char)
liefert Zeilenhöhe (Gesamthöhe)
liefert Oberlänge (ascent)
liefert Unterlänge (descent)
liefert Größe der Unterlänge zurück,die maximal von einem
Zeichen erreicht werden kann
liefert den Durchschuss (leading, Zeilenabstand)
liefert die Breite, die der String einnimmt (mit eingestelltem Font)
liefert die Zeichenbreite
Beispiel – Der Umgang mit Schriftarten; Methoden der Klasse FontMetrics
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 160 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
//Eclipse-Projekt: kap12_awt_graphics_case_study_
import java.awt.*;
import java.awt.event.*;
public class ZeichenFenster5
extends Frame
{
public static void main(String[]
args)
{
ZeichenFenster5 fenster = new ZeichenFenster5();
}
public ZeichenFenster5()
{
super("Zeichenfenster5");
this.setSize(350,130);
this.setLocation(100,100);
this.setBackground(new Color(50,200,255));
this.setVisible(true);
this.setLayout(null);
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent we)
{
System.exit(0);
}
});
}
public void paint(Graphics g)
{
String s = "Dieser String steht in der Mitte des Fensters";
int breite = getSize().width - getInsets().left - getInsets().right;
int hoehe = getSize().height - getInsets().bottom - getInsets().top;
Font font = new Font("Serif", Font.BOLD + Font.ITALIC, 15);
FontMetrics fmetrics = getFontMetrics(font);
int zeilenMitte = fmetrics.getAscent() (fmetrics.getAscent() + fmetrics.getDescent()) / 2;
g.setFont(font);
g.setColor(SystemColor.windowText);
g.drawString(s, getInsets().left + (breite - fmetrics.stringWidth(s)) /
2,
getInsets().top + hoehe / 2 + zeilenMitte);
}
}
12.5 Farben / die Klasse Color
Java verwendet das RGB-Modell
Die Klasse Color kapselt die Farben des RGB (Rot-Grün-Blau)-Farbmodells (Zahlenwerte von 0 – 255 (int)
oder von 0.0 bis 1.0 (float)) geben Intensität des Farbanteils an)
Einige Farben sind vordefiniert: z. B.: black, blue, cyan, gray, darkGray, lightGrey, magenta, orange,
pink, green, red, yellow,
Bei der Erzeugung eines Color-Objects wird die Farbe festgelegt: entweder über drei Integer-Werte, die in einem
Wertebereich von 0 bis 255 liegen, oder durch float-Zahlen im Wertebereich zwischen 0.0 und 1.0.
Color(int r, int g, int b) è Werte 0 -255 oder aber
Color(float r, float g, float b) è Werte von 0.0 - 1.0
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 161 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
z.B.:
Color
Color
Color
Color
Color
c
c
c
c
c
=
=
=
=
=
new
new
new
new
new
Color(255,255,255); // weiß
Color(0,0,0); // schwarz
Color(255,0,0); // rot
Color(0,255,0); // grün
Color(0,0,255); // blau
Die Farbe für das nächste Grafikobjekt wird mit setColor(Color c) gesetzt
public abstract void setColor(Color c)
Farbe, in der das nächste Graphikobjekt gezeichnet
werden soll
Die Abfrage der aktuellen Zeichenfarbe erfolgt mit public abstract Color getColor()
Von der Klasse Color ist die Klasse SystemColor abgeleitet und kann ebenfalls für Farbeinstellungen
verwendet werden. Vorteil: Werden Systemfarben geändert, so werden auch diese Farben dynamisch angepasst:
Bereich
Funktion
desktop
Hintergrundfarbe des Desktops
activeCaption
Hintergrundfarbe der Titelleiste des aktuellen Fensters
inactiveCaption
Hintergrundfarbe der Titelleiste eines inaktiven Fensters
activeCaptionText
Schriftfarbe des Fenstertitels des aktiven Fensters
inactiveCaptionText Schriftfarbe des Fenstertitels von inaktiven Fenstern
window
Hintergrundfarbe eines Fensters
windowBorder
Farbe des Fensterrahmens
windowText
Farbe der Schrift im Fenster
menue
Hintergrundfarbe des Menüs
menueText
Farbe der Schrift im Menü
…
Auszug der 26 definierten Systemfarben.
Beispiel – Zeichnen mit Farben
//Eclipse-Projekt: kap12_awt_graphics_case_study_
import java.awt.*;
import java.awt.event.*;
public class
{
int n = 1;
Farbverlauf extends Frame
// ganze Zahl die den Abstand
// im Spektrum angibt
public static void main(String[] args)
{
Farbverlauf fenster = new Farbverlauf();
}
public Farbverlauf()
{
super("Farbverlauf");
this.setSize(350,130);
this.setLocation(100,100);
this.setVisible(true);
this.setLayout(null);
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent we)
{
System.exit(0);
}
});
}
public void paint(Graphics g)
{
int rot = 255;
//
int blau = 255;
// rgb-Kombination für Magenta
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 162 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
int gruen = 0;
//
int breite = getWidth()- getInsets().left - getInsets().right;
int hoehe = getHeight() - getInsets().bottom;
int i = 0;
while (i < breite)
{
while (gruen < 256 - n)
{
i = i + 1;
gruen = gruen + n;
blau = blau - n;
// rot bleibt konstant
g.setColor(new Color(rot, gruen, blau)); //magenta
g.drawLine(i, 0, i, hoehe);
}
while (blau < 256 - n)
{
i = i + 1;
blau = blau + n;
rot = rot - n;
// gruen bleibt konstant
g.setColor(new Color(rot, gruen, blau));
g.drawLine(i, 0, i, hoehe);
}
while (rot < 256 - n)
{
i = i + 1;
gruen = gruen - n;
rot = rot + n;
// blau bleibt konstant
g.setColor(new Color(rot, gruen, blau));
g.drawLine(i, 0, i, hoehe);
}
}
}
}
12.6 Weitere Methoden zum Zeichnen aus der Klasse Graphics
Die Klasse Graphics stellt Methoden zum Zeichnen einfacher geometrischer Figuren bereit. Dazu gehören Linien,
Rechtecke, Polygone, Kreise und Kreisbögen.
Linien:
Mit einer Pixelstärke von einem Pixel.
Dicke Linien entweder durch Zusammensetzung mehrerer 1-Pixel-Linien oder aber durch ein "schmales"
Rechteck.
Die Linie wird als Verbindung zwischen zwei Punkt-Koordinaten gezogen.
Liegt ein Punkt ausserhalb des Fensters, wird die Linie am Fensterrand abgeschnitten, ohne dass dies ein Fehler
wäre.
Nur durchgezogene Linien.
Eine einfache Möglichkeit für das Zeichnen breiter Linien oder Linien mit einem anderen Muster (gepunktet, usw.)
existiert nicht.
public abstact void drawLine(int x1, int y1, int x2, int y2)
x1, y1 steht für den Startpunkt und x2, y2 steht für den Endpunkt.
Ungefüllte Rechtecke:
Es lassen sich Rechtecke ohne und mit runden Ecken darstellen. x und y kennzeichnen den linken oberen
Startpunkt. width stellt die Breite und height die Höhe des Rechtecks dar.
Bei Rechtecken mit runden Ecken stellen die letzten beiden Parameter den horizontalen und vertikalen
Durchmesser es Eckbogens dar.
public void drawRect(int x, int y, int width, int height)
public abstract void drawRoundRect(int x, int y, int width, int heigt, int arcWidth,
int arcHeight)
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 163 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Gefüllte Rechtecke:
Rechtecke ohne und mit runden Ecken lassen sich auch als gefüllte Rechtecke darstellen:
public abstract void fillRect(int x, int y, int width, int height)
public abstract void fillRoundRect(int x, int y, int width, int height, int
arcWidth,
int arcHeight)
3D Rechtecke:
3D-Rechtecke erheben sich positive nach vorne (Parameter raised = true) oder netagiv nach hinten (Parameter
raised = false) aus der Ebene.
public void draw3DRect(int x, int y, int width, int height, Boolean raised)
raised == true = nach aussen
public void fill3DRect(int x, int y, int width, int height, Boolean raised)
raised == false= nach innen
Polygone:
Polygone sind Figuren aus mehreren Linien. Wird ein nicht geschlossenes Polygon angegeben, so wird automatisch
der letzte Punkt mit dem ersten verbunden.
public
public
public
public
abstract void drawPolygon(int[] xPoints, int[] yPoints, int nPoints)
void drawPolygon(Polygon p)
abstract void fillPolygon(int[] xPoints, int[] yPoints, int nPoints)
void fillPolygon(Polygon p)
Polygon(int[] xPoints, int[] yPoints, int nPoints)
PolyLine:
Offener Polygonzug. Der Anfangs- und Endpunkt wird nicht miteinander verbunden.
public abstract void drawPolyline(int[] xPoints, int[] yPoints, int nPoints)
Kreise, Ellipsen:
In AWT werden Kreise und Ellipsen über die gleichen Methoden gezeichnet, da Kreise spezielle Ellipsen sind.
public abstract void drawOval(int x, int y, int width, int height)
public abstract void fillOval(int x, int y, int width, int height)
Die x und y-Koordinaten geben den linken oberen Eckpunkt des Rechtecks an, das den
Kreis aufnimmt. Der Parameter width spezifiert die horizontale Ausdehnung und der
Parameter height die vertikale Dimension.
Bogen:
Als Bogen wird ein Teilstück eines Kreises bzw. einer Ellipse verstanden. Beschrieben wird der Bogen wie der Kreis
(auf dem er liegt) mit zusätzlicher Angabe von Start- und Endwinkel.
public abstract void drawArc(int x, int y, int width, int height, int startAngle,
int endAngle)
public abstract void fillArc(int x, int y, int width, int height, int startAngle,
int endAngle)
Beispiel – Nochmals: die Klasse Graphics und der Umgang mit Zeichenmethoden
//Eclipse-Projekt: kap12_awt_graphics_case_study_
import java.awt.*;
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 164 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
import java.awt.event.*;
public class ZeichenFenster6 extends Frame
{
public static void main(String[] args)
{
ZeichenFenster6 fenster = new ZeichenFenster6();
}
public ZeichenFenster6()
{
super("Zeichenfenster 6");
this.setSize(450,140);
this.setLocation(100,100);
this.setVisible(true);
this.setLayout(null);
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent we)
{
System.exit(0);
}
});
}
public void paint(Graphics g)
{
g.setColor(Color.yellow);
g.fillRect (45, 55, 100, 70);
g.setColor(Color.black);
g.drawOval (45, 55, 100, 70);
g.setColor(Color.yellow);
g.fillRect (175, 55, 100, 70);
g.setColor(Color.black);
g.drawArc (175, 55, 100, 70, 0, 140);
}
}
12.7 Bitmaps anzeigen: (nur gif und jpeg-Format)/ Die Klasse
Image
Bitmaps werden in Java durch die Klasse Image repräsentiert. Bitmaps müssen
zuerst geladen werden und
können dann angezeigt werden.
Unterstützt werden die Grafikformate gif und jpeg. Über das JAI Java Advanced Imaging API stehen weiter
Grafikformate zur Verfügung (siehe: http://java.sun.com/products/java-media/jai/index.html)
Das Laden einer Bitmap kann mit Hilfe der Methode getImage() durchgeführt werden.
public abstract Image getImage(String filename) // Name der Bilddatei
public abstract Image getImage(URL url)
// URL der Bilddatei
1. Laden:
URL url = this.getClass().getResource("smile.gif")
Image img = this.getToolkit().getImage(url)
oder
Image icon = this.getToolkit().getImage(“./smile.gif”);
oder alternative:
Image icon = Toolkit.getDefaultToolkit().getImage(“./smile.gif”);
(Hinweis 1: die Methode getTookit() (aus der Klasse Window) liefert ein Toolkit-Objekt.
Hinweis 2: Klasse Toolkit: This class is the abstract superclass of all actual implementations of the Abstract
Window Toolkit. Subclasses of Toolkit are used to bind the various components to particular native toolkit
implementations. … Some methods defined by Toolkit query the native operating system directly.)
2. Anzeigen:
Angezeigt wird ein Bild mit der Methode drawImage() aus der Klasse
Graphics.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 165 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
public abstract boolean drawImage(Image img, int x, int y, ImageObserver observer)
public abstract boolean drawImage(Image img, int x, int y, int width, int height
ImageObserver observer)
public abstract boolean drawImage(Image img, int x, int y, Color bgcolor,
ImageObserver observer)
drawImage() ist sehr polymorph: es gibt sechs verschiedene Parameterlisten.
In jedem Fall werden den Methoden das Image-Objekt und die linke obere Ecke als Startposition für das Bild
übergeben.
Als letzter Parameter kann ein ImageObserver-Objekt übergeben werden, das die Informationen zum Bild
enthält, z.B. den Ladezustand. Dieser Parameter kann null sein.
werden Breite und Höhe angegeben, so wird das Bild in diese Ausmaße hinein abgebildet. Dabei kann es
vergrößert, verkleinert, verzerrt werden.
Für transparente Bilder kann der Parameter bgcolor zur Bestimmung der Hintergrundfarbe angegeben
werden.
In bestimmten Anwendungen kann es erforderlich sein, den Ladevorgang des Bildes zu beenden, bevor im
Programm weitergemacht werden kann. Dies ist z.B. notwendig, wenn die Größe des Bildes für die weiteren
Positionierung benötigt wird. Diese Aufgabe übernimmt die
Klasse MediaTracker .
Für die Umsetzung sind die folgenden drei Schritte notwendig (siehe Beispiel class JavaTasse):
1. Über den Konstruktor wird ein Objekt der Klasse MediaTracker erzeugt.
2. Durch die Methode addImage(Image image, int id) wir das betreffende Image-Objekt dem
MediaTracker bekannt gemacht. Dabei bekommt es die angegebene ID-Nummer zugewiesen. Diese ID ist
frei wählbar.
3. Nach dem Aufruf der Methode getImage() wird die Methode waitForID(int id) der Klasse
MediaTracker aufgerufen. Damit wird solange gewartet, bis der Ladevorgang des Images mit der
angegebenen ID abgeschlossen ist. (Mit der Methode waitForAll() können mehrere Bilder auf einmal
überwacht werden.)
Beispiel – Bitmaps anzeigen
//Eclipse-Projekt: kap12_awt_graphics_case_study_
import java.awt.*;
import java.awt.event.*;
import java.net.*;
public class JavaTasse extends Frame
{
public static void main(String[] args)
{
JavaTasse fenster = new JavaTasse();
}
public JavaTasse()
{
super("Heisse Tasse");
this.setSize(330,200);
this.setLocation(100,100);
this.setVisible(true);
this.setLayout(null);
this.setBackground(Color.yellow);
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent we)
{
System.exit(0);
}
});
}
public void paint(Graphics g)
{
URL url = null;
// 1 Objekt der Klasse MediaTracker wird erzeugt <<<<-----------------------------
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 166 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
}
MediaTracker tracker = new MediaTracker(this) ;
try
{
url = this.getClass().getResource("HeisseTasse.gif");
}
catch(Exception ex)
{
System.out.println(ex.getMessage());
}
Image img = getToolkit().getImage(url);
// 2 Image dem Mediatracker bekannt machen <<<<---------------------------tracker. addImage(img, 1);
try
{ // 3 warten
tracker.waitForID(1 ); <<<<<----------------------------------------}catch(InterruptedException ex){}
// 4 Zeichnen
g.drawImage(img, 20, 30, this);
g.drawImage(img, 120, 30, 60, 140, this);
g.drawImage(img, 230, 30, Color.green, this);
}
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 167 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Bearbeiten Sie die Case Studies (kap10_awt_graphics_case_studies) und lösen
Sie die Übungsaufgeben zu diesem Kapitel! (Siehe Unterlagen hierzu auf dem
Materialserver.)
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 168 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
13. AWT-Komponenten:
Was Sie lernen:
welche AWT-Komponenten es gibt
wie AWT-Komponenten definiert und verwendet werden
wie (Mensch-Maschinen-) Dialoge erstellt werden
wie Menüs erzeugt werden
Voraussetzungen:
Fensterprogrammierung
Grafikprogrammierung
Eventhandling
13.1 Grundlagen
Für die Gestaltung vonFenstern stellt AWT verschiedene Komponenten (Oberflächenelemente) bereit.
java.lang.Object
java.util.EventObject
java.awt.AWTEvent
java.awt.Component
java.awt.Container
...Choice
...Label
...Button
...List
…Canvas
java.awt.Dialog
java.awt.Window
javax.awt.Panel
java.awt.Frame
javax.applet.Applet
…Scrollbar
…Checkbox
…TextComponet
nt
javax.swing.JDialog
javax.swing.JFrame
javax.swing.JComponent
javax.swing.Label
java.applet.Applet
javax.swing.JApplet
Die Eigenschaften und Funktionsweisen einer Komponente werden jeweils in einer Klasse gekapselt. Die Klassen
sind alle direkt oder indirekt von der Klasse Component abeleitet. Dadurch haben sie gemeinsame
Eigenschaften, wie z.B. Größe, Position und Farbe. Die Komponenten besitzen Methoden, um Listener zu
registrieren; damit können Sie Events verarbeiten. (Semantische Events.)
Komponenten können direkt auf dem Fenster (i.d. R. Frame) angeordnet werden. Wenn eine Gruppierung von
Komponenten gewünscht wird, können diese zusammengehörigen Komponenten in einem Panel angeordnet
werden. In einem Fenster (i.d.R. Frame) lassen sich dann mehrer Panels (mit jeweils mehreren Komponenten)
anordnen.
Diese Vorgehensweise hat den Vorteil, dass sich alle Komponenten eines Panels mit einer Anweisung ein- und
ausblenden lassen.
Auch bei der Arbeit mit Layout-Managern, die eine automatische Anordnung der Komponente im Container
bewirken, werden die Komponenten häufig auf Panels platziert.
Container sind Fenster (Window, Frame, Dialog), Panels und Applets (siehe Bild oben).
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 169 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
13.2 Überblick über AWT-Komponenten:
Menüleiste (Menu, MenuBar, MenuItem)
Text (Label) mehzeiliges Listenfeld (List)
mehrzeiliges Listenfeld (TextArea)
Titelleiste
Leinwand
Bildlaufleiste
(scrollbar)
(Canvas)
einzeiliges Textfeld
(TextField)
Container
(Panel)
Schaltfläche (Button)
Kontrollfeld (Checkbox) einzeiliges Listenfeld (choice)
Überblick über die AWT-Komponenten, ihre Verwendung und die relevanten Listener:
AWTKomponente
Button
Canvas
Label
Dialog,
FileDialog
TextField
Choice
Verwendung
Listener 2 1
Über Schaltflächen kann der Benutzer das
Programm steuern. Durch das Betätigen der
Schaltfläche wird das Programm angewiesen,
bestimmte Aktionen auszuführen.
API-DOCU: A Canvas component represents
a blank rectangular area of the screen onto
which the application can draw or from which
the application can trap input events from the
user.
Ein Label dient der Anzeige von Text, den er
Benutzer nicht ändern kann. Labels werden
als Beschriftung von Textfeldern verwendet.
Dialog und FileDialog werden als modale
Fenster kreiert. Ausgestaltung des Fenster
(mit Komponenten und den zugehörigen
Listenern) obliegt beim Dialog dem User.
Beim FileDialog ist die Ausgestaltung dagegen
per default komplett, inclusive der zugehörigen
Ereignisbehandlung.
Über Textfelder werden Benutzereingaben
und Benutzerausgaben realisiert, die nur
eine Zeile benötigen.
Ein Listenfeld dient der Auflistung und
Auswahl vorgegebener Werte (EinfachSelektion). Die Werte können vom Benutzer
ActionListener
MouseListener
KeyListener, MouseListener,
MouseMotionListener
u.v.a.m: siehe API-Doku „add“Methoden
KeyListener
MouseListener
WindowListener
WindowFocusListener
TextListener
ActionListener
ComponentListener,ItemListener
FocusListener
21
Hier sind nicht immer alle Listener aufgelistet! Im Einzelfall also in der API-Docu der AWT-Komponentenklasse
nachsehen!!!! An den „add“-Methoden ist erkennbar, welche Listener zugeodnet werden können.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 170 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
weder verändert werden, noch können neue
Werte hinzugefügt werden.
List
Mehrzeilige Listenfelder dienen ebenfalls der
ItemListener
Auflistung und Auswahl vorgegebener Werte.
ActionListener
Eine Mehrfachselektion ist möglich.
TextArea
Mehrzeilige Textfelder dienen der Eingabe
TextListener
und Anzeige längerer Texte.
Checkbox
Kontrollfelder (Checkboxen) eignen sich für
ItemListener
die Eingabe bzw. die Anzeige von Ja/NeinWerten durch das Setzen oder Entfernen der
Markierung.
CheckboxGroup
Optionsfelder sind mehrere Kontrollfelder
ItemListener
(Checkbox), die zu einer Gruppe
(CheckboxGroup) zusammengefasst sind. Von
den Optionsfeldern einer Gruppe kann nur
jeweils eins aktiv sein, die Optionen
schließen sich gegenseitig aus.
Scrollbar
AdjustmentListener
Bildlaufleisten dienen zum Blättern
(Scrollen), z.B. wenn der Inhalt eines Fensters
nicht vollständig angezeigt werden kann. Der
eigenliche Scrollvorgang muss jedoch
manuell durchgeführt werden.
Panel
ContainerListener
Panel sind Container, die andere
PropertyChangeListener
Komponenten in sich aufnehmen können.
Sie dienen der Gruppierung von
Komponenten und haben eine besondere
Bedeutung im Zusammenhang mit
LayoutManagern.
Scrollpane
ContainerListener
Diese Komponente dient er Aufnahme einer
anderen Komponente, die durch ihre Größe
auf dem verfügbaren Platz nicht vollständig
angezeigt werden kann. Mit den beiden
Bildlaufleisten lassen sich die verdeckten
Bereiche der Komponente sichtbar machen.
MenuBar mit
beim Menüeintrag wird
Eine Menüleiste wird direkt unter der
Menu und
Titelleiste in ein Fenster eingefügt. Sie kann
ActionListener
MenuItem
registriert
mehrere Menüs besitzen, die wiederum
Menüeinträge und Untermenüs beinhalten.
Die Menüeinträge dienen der Auswahl
bestimmter Aktivitäten es Programms.
PopupMenu
beimMenüeintrag wird
Ein Popup-Menü (auch als Kontext-Menü
bezeichnet) wird durch Klicken mit der
ActionListener
registriert
rechten Maustaste angezeigt. Allen
Komponenten und dem Fenster selbst kann
ein Popup-Menü hinzugefügt werden.
xListener = Low Level Ereignis; yListener = Semantisches Ereignis; gelb markierte Felder: Muss-Komponenten im
Adreli-Projekt.
13.3 Anwendung / Umgang mit den Komponenten:
Die typische Verwendung von AWT-Komponenten vollzieht sich in drei Schritten:
1. Objekt der Komponenten-Klasse erzeugen
2. Eigenschaften der Komponenten festlegen
3. Komponenten-Objekt zum Container (Fenster, Panel,…) hinzufügen. Dazu wird die Methode
add() des Containers verwendet.
Hinweis: in den folgenden Beispielen wird für die Anordnung der Komponenten kein LayoutManager verwendet.
Die Komponenten werden mit der Methode setLocation() direkt an einer bestimmten Position fixiert.
Es sollten Namenskonventionen beachtet werden, damit eine eindeutige Komponentenzuordnung die
Lesbarkeit erleichtert.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 171 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Klasse
Button
Label
Textfield
Choice
List
Präfix
btn
lbl
txt
choice
lst
Klasse
Textarea
Checkbox
Scrollbar
Panel
Menue
Präfix
ta
cb
sb
pnl
mnu
13.3.1 Die Klasse Label
Ein Label dient der Anzeige von Text, den er Benutzer nicht ändern kann.
Ein Label wird für die Beschriftung von Text-, Options- und Kontrollfeldern benutzt werden.
Ein Label wird über einen der folgenden drei Konstruktoren erzeugt:
public Label()
public Label(String text)
public Label(String text, int alignment)
Label ohne Beschriftung
Label mit Beschriftung
Label mit Beschriftung und Ausrichtung
Für das Alignement stehen symbolischen Konstanten bereit:
o Label.LEFT
o Label.RIGHT
o Label.CENTER
Text und Alignment des Labels lassen sich auch dynamisch zur Laufzeit mit folgenden Methoden ändern, bzw.
abfragen:
o void setText(String text)
o void setAlignment(int alignment)
o String getText()
o int getAlignment()
13.3.2 Die Klasse Button
Objekte der Klasse Button repräsentieren Schaltflächen. Über Schaltflächen kann der Benutzer das Programm
steuern. Durch das Betätigen der Schaltfläche wird das Programm angewiesen, bestimmte Aktionen auszuführen.
Zwei Konstruktoren stehen zur Verfügung:
public Button()
public Button(String text)
Schaltfläche ohne Beschriftung
Schaltfläche mitBeschriftung
Schaltflächen werden dem Benutzer angeboten, um bestimmte Aktionen auszuführen. Die Betätigung der
Schaltfläche löst ein ActionEvent-Ereignis aus.
Um das Ereignis auszuwerten wird wie folgt vorgegangen:
1. Implementieren Sie das Interface ActionListener.
2. Überlagern Sie die Methode actionPerformed() in der betreffenden Klasse (es ist die einzige Methode
des Interfaces).
3. In der Methode actionPerformed() wird die Aktion für die jeweilige Schaltfläche implementiert.
Hinweis: sind mehrer Schaltflächen in einem Container enthalten, so kann mit der Methode
getActionCommand() ermittelt werden, welche der Schaltflächen das Ereignis ausgelöst hat.
public String getActionCommand()
void setActionCommand(String command)
Liefert Zeichenkette, mit der die Schaltfläche beschriftet
ist (sofern nicht mit setActionCommand() ein anderer
Text als Action-Kommando gesetzt wurde).
Setzt den Kommandonamen, der von
getActionCommand() zurückgeliefert wird.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 172 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Beispiel – Buttons und Labels nutzen (UND: nur semantischer Event ActionListner-Meth. im Einsatz)
//Eclipse-Projekt: kap13_awt_components_case_study_
import java.awt.*;
import java.awt.event.*;
public class ButtonFenster extends Frame implements ActionListener
{
Button btnOk, btnAbbrechen;
Label lblUeberschrift, lblMeldung;
public static void main(String[] args)
{
ButtonFenster fenster = new ButtonFenster();
}
public ButtonFenster()
{
super("Betätigen Sie eine Schaltfläche");
this.setSize(250, 120);
this.setLocation(100, 100);
this.setBackground(Color.cyan);
this.setLayout(null);
lblUeberschrift = new Label("Schaltflächen-Test"); ////////// Label
lblUeberschrift.setFont(new Font("Serif", Font.BOLD+Font.ITALIC, 20));
lblUeberschrift.setSize(200, 30);
lblUeberschrift.setLocation(40, 25);
this.add(lblUeberschrift);
btnOk = new Button("OK");
////////////////////////////// Button
btnOk.setSize(80, 23);
btnOk.setLocation(40, 60);
this.add(btnOk);
btnOk.addActionListener(this);
// besitzt Methode actionPerformed()
btnAbbrechen = new Button("Abbrechen"); ///////////////////// Button
btnAbbrechen.setSize(80, 23);
btnAbbrechen.setLocation(130, 60);
this.add(btnAbbrechen);
btnAbbrechen.addActionListener(this);
lblMeldung = new Label("keine Schaltfläche betätigt", Label.CENTER);
////
lblMeldung.setSize(170, 23);
lblMeldung.setLocation(40, 85);
this.add(lblMeldung);
this.setVisible(true);
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent we)
{
System.exit(0);
}
});
}
public void actionPerformed (ActionEvent ae)
{
if(ae.getActionCommand().equals("OK"))
lblMeldung.setText("OK wurde betätigt");
else
lblMeldung.setText("Abbrechen wurde betätigt");
}
}
Aufgabe: Schauen Sie in der API-Doku für die Klasse Button nach. Neben den hier vorgestellten ButtonMethoden gibt es zahlreiche weitere! Wieviel?
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 173 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
13.3.3 Die Klasse Textfeld (abgeleitet von TextComponent)
Über Textfelder werden Benutzereingaben und Benutzerausgaben realisiert, die nur eine Zeile benötigen. Im
Gegensatz zum Label kann er Benutzer die Zeichenketten ändern bzw. neue Zeichenketten eingeben.
Textfelder besitzen Methoden
um Zeichenketten zu setzen und abzufragen
um die Editierbarkeit der Zeichenkette einzustellen (zulassen, verbieten)
um die Editierbarkeit der Zeichenkette abzufragen
um die Curserposition in der Zeichenkette abzufragen und zu setzen
um Teile in der Zeichenkette zu markieren, bzw. die Position der Markierung abzufragen
um verdeckte Eingaben zu ermöglichen (z.B. für die Passworteingabe)
String getText()
void setText(String t)
void setEditable(boolean b)
boolean isEditable()
int getCaretPosition()
void setCaretPosition(int position)
void select(int selectionStart, int
selectionEnd)
void selectAll()
void setSelectionStart(int
selectionStart)
int getSelectionEnd()
void setEchoChar(char c)
char getEchoChar()
boolean echoCharIsSet()
Zeichenkette abfragen
Zeichenkette setzen
Editierbarkeit erlauben, bzw. verbieten
Editierbarkeit abfragen
Cursorposition in der Zeichenkette abfragen
Cursorposition in der Zeichenkette ändern
Textmarkierung setzen
gesamte Zeichenkette markieren
setzt den Markierungsanfang auf die Startposition
liefert die Entposition des markierten Texts
setzt den für das Echo zu nutzenden Buchstaben
liefert den für das Echo zu nutzenden Buchstaben
testet, ob ein Echo-Zeichen gesetzt ist
Wird vom Benutzer ein Zeichen in das Textfeld eingegeben, so wird ein TextEvent-Ereignis ausgelöst.
o Um ein solches Ereignis abzufangen, muss ein TextListener-Interface für das Textfeld mit der
Methode addTextListener() registriert werden.
o Tritt das Ereignis ein, so wird die Methode textValueChanged(TextEvent event)
aufgerufen
Betätigt der Benutzer die Enter-Taste, so wird ein ActionEvent-Ereignis ausgelöst.
Beispiel:
TextField tf1, tf2, tf3, tf4;
// a blank text field
tf1 = new TextField();
// blank field of 20 columns
tf2 = new TextField("", 20);
// predefined text displayed
tf3 = new TextField("Hello!");
// predefined text in 30 columns
tf4 = new TextField("Hello", 30);
Beispiel – TextFields nutzen
//Eclipse-Projekt: kap13_awt_components_case_study_
import java.awt.*;
import java.awt.event.*;
public class EchoFenster extends Frame implements ActionListener,
TextListener
{
TextField txtEingabe, txtEcho;
Button btnLoeschen;
Label lblMldg;
public static void main(String[] args)
{
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 174 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
EchoFenster fenster = new EchoFenster();
}
public EchoFenster()
{
super("EchoFenster");
this.setSize(480, 140);
this.setLocation(100, 100);
this.setBackground(Color.cyan);
this.setLayout(null);
txtEingabe = new TextField(); ///////////////// TextField 1
txtEingabe.setSize(300, 23);
txtEingabe.setLocation(40, 50);
this.add(txtEingabe);
txtEingabe.addTextListener(this);
// für Reaktion auf Zeicheneingabe
txtEingabe.addActionListener(this);
// für Reaktion auf ENTER-Taste
txtEcho = new TextField();
///////////////// TextField 2
txtEcho.setSize(300, 23);
txtEcho.setLocation(40, 100);
txtEcho.setEditable(false);
// damit erübrigen sich Listener...
this.add(txtEcho);
btnLoeschen = new Button("loeschen"); ///////// Button
btnLoeschen.setSize(100, 23);
btnLoeschen.setLocation(360, 75);
this.add(btnLoeschen);
btnLoeschen.addActionListener(this);
lblMldg = new Label();
////////////////// Label
lblMldg.setSize(200,23);
lblMldg.setLocation(40, 75);
this.add(lblMldg);
this.setVisible(true);
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent we)
{
System.exit(0);
}
});
}
public void actionPerformed(ActionEvent ae) // wird vom ActionListener gerufen
{
Object object = ae.getSource();
if(object.equals(btnLoeschen))
{
txtEingabe.setText("");
txtEingabe.requestFocus(); //Cursor geht in’s Eingabetextfeld
}
else if(object.equals(txtEingabe))
{
txtEingabe.selectAll();
}
}
public void textValueChanged(TextEvent te) // wird vom TextListener gerufen
{
txtEcho.setText(txtEingabe.getText());
lblMldg.setText("aktuelle Cursorposition " + txtEingabe.getCaretPosition());
}
}
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 175 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
13.3.4 Die Klasse TextArea (abgeleitet von der Klasse TextComponent)
Mehrzeilige Textfelder dienen der Eingabe und Anzeige längerer Texte. Mehrzeilige Textfelder sind
standardmäßig mit Bildlaufleisten (wenn der Text breiter oder länger als die Textarea ist) ausgerüstet.
Da die Klasse TextArea wie die Klasse TextField von der Klasse TextComponent abeleitet ist,
besitzen beide Klassen gemeinsame Methoden, wie z. B. getText(), setText(),
getCaretPosition(), getSelectedText() und setEditable().
TextAreas besitzen zusätzliche Methoden
um Texte einzufügen und anzuhängen
um Teile von Texten zu ersetzen
um die Anzahl von Zeilen und Spalten zu ermitteln bzw. zu ändern
um Größeninformationen zu liefern
um zu ermitteln, welche Rollbalken genutzt werden
Methode
void insert(String str, int pos)
void append(String str)
void replaceRange(String str, int start,
int end)
int getRows(e)
void setRows(int rows)
int getColumns()
void setColumns(int columns)
Dimension getMinimumSize()
Dimension getPreferredSize()
int getScrollbarVisibility()
Wirkung
Zeichenkette str an der Position pos einfügen
Zeichenkette str anhängen
Zeichenkette von start bis end durch str ersetzten
liefert die aktuelle Zeilenanzahl
setzt die Zeilenzahl auf rows
liefert die aktuelle Spaltenanzahl
setzt die Spaltenzahl auf columns
liefert minimale Größe
liefert bevorzugte Größe
ermittlet, welcher Rollbalken genutzt wird
Wird vom Benutzer ein Zeichen in das Textfeld eingegeben, so wird auch hier ein TextEvent-Ereignis
ausgelöst. (Durch die Betätigung der Enter-Taste wird jedoch kein ActionEvent ausgelöst wie beim
TextField.)
13.3.5 Die Klasse Choice und die Klasse List:
Die Klassen Choice und List repräsentieren einzeilige, bzw. mehrzeilige Listenfelder. Ein Listenfeld dient
der Auflistung und Auswahl vorgegebener Werte. Die Werte können vom Benutzer weder verändert werden, noch
kann der Benutzer neue Werte hinzugefügen.
Der Programmierer kann Werte über die add()-Methode zur Laufzeit den Listenfeldern hinzufügen. Die
Einträge werden dann in der Reihenfolge, in der sie hinzugefügt wurden, angezeigt. Eine Möglichkeit die
Anzeigenreihenfolge nachträglich zu ändern gibt es nicht.
Für Choice und List stehen u.a. folgende Methoden zur Verfügung:
Methode
Wirkung
String getSelectedItem()
liefert den ausgewählten Entrag
int getSelectedIndex()
liefert den Indes des ausgewählten Eintrags
void select(int pos)
programmgesteuerte Auswahl eines Eintrags
void select(String str)
programmgesteuerte Auswahl eines Eintrags
Wird vom Benutzer ein Eintrag aus dem Listfeld ausgewählt, so wird ein ItemEvent-Ereignis ausgelöst.
Um ein solches Ereignis abzufangen, muss ein ItemListener-Interface implementiert werden.
Beim Eintreten des Ereignisses wird die Methode itemStateChanged() aufgerufen.
Mehrzeilige Listenfelder (Klasse List) bieten mehr Funktionalität als einzeilige Listen (Klasse choice):
Wahlweise kann eine Mehrfachselektion zugelassen werden (2. Parameter des Konstruktors).
Elemente können dynamisch entfernt oder geändert werden (Methoden deItem(), remove(), replaceItem()).
Ein ActionEvent-Ereignis wird ausgelöst, wenn ein Doppelklick auf den Eintrag erfolgt.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 176 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Beispiel – TextField-, List- und Choice-Komponenten
//Eclipse-Projekt: kap13_awt_components_case_study_
import java.awt.*;
import java.awt.event.*;
public class ListenFenster extends Frame implements ActionListener,
ItemListener
{
Button btnUebernehmen;
TextField txtEingabe;
List lstEingaben;
Choice choiceEingaben;
Label lbl;
public static void main(String[] args)
{
ListenFenster fenster = new
ListenFenster();
}
public ListenFenster()
{
super("Funktionsweise von Listenfeldern");
this.setSize(400, 160);
this.setLocation(100, 100);
this.setBackground(Color.cyan);
this.setLayout(null);
txtEingabe = new TextField("");
txtEingabe.setSize(100, 23);
txtEingabe.setLocation(40, 50);
add(txtEingabe);
btnUebernehmen = new Button("übernehmen");
btnUebernehmen.setSize(100, 23);
btnUebernehmen.setLocation(40, 90);
add(btnUebernehmen);
btnUebernehmen.addActionListener(this);
lstEingaben = new List();
lstEingaben.setSize(100, 65);
lstEingaben.setLocation(160, 50);
lstEingaben.addItemListener(this);
lstEingaben.addActionListener(this);
add(lstEingaben);
choiceEingaben = new Choice();
choiceEingaben.setSize(100, 23);
choiceEingaben.setLocation(280, 50);
choiceEingaben.addItemListener(this);
add(choiceEingaben);
lbl = new Label("");
lbl.setSize(400, 23);
lbl.setLocation(40, 120);
this.add(lbl);
this.setVisible(true);
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent we)
{
System.exit(0);
}
});
}
public void actionPerformed(ActionEvent ae) //wird vom ActionListener
gerufen
{
Object object = ae.getSource();
if (object.equals(btnUebernehmen))
{
lstEingaben.add(txtEingabe.getText());
choiceEingaben.add(txtEingabe.getText());
txtEingabe.setText("");
txtEingabe.requestFocus();
}
else if(object.equals(lstEingaben))
{
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 177 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
lbl.setText("Doppelklick auf: " + lstEingaben.getSelectedItem());
}
}
public void itemStateChanged(ItemEvent ie) // wird vom ItemListener
gerufen
{
Object object = ie.getSource();
if (object.equals(choiceEingaben))
{
lstEingaben.select(choiceEingaben.getSelectedIndex());
lbl.setText("aus Choice wurde ausgewählt: " + choiceEingaben.getSelectedItem());
}
else if(object.equals(lstEingaben))
{
choiceEingaben.select(lstEingaben.getSelectedIndex());
lbl.setText("aus List wurde ausgewählt: " + lstEingaben.getSelectedItem());
}
}
}
13.3.6 Die Klasse Checkbox:
Kontrollfelder (Checkboxen) eignen sich für die Eingabe bzw. die Anzeige von Ja/Nein-Werten durch das
Setzen oder Entfernen der Markierung. Die Darstellung OS-abhängig: z.B. ist der True-Wert unter Windows ein
Haken im Kontrollfeld und unter Linux ist es ein hervorgehobenes Quadrat.
Beschriftung neben dem Kontrollfeld erfolgt mit setLabel() oder wird dem Konstruktor mitgegeben:
cbBuch = new Checkbox();
cbBuch.setLabel("Buch");
cbCD = new Checkbox("CD", true);
Den aktuellen Zustand eines Kontrollfelds liefert die Methode boolean getState()
Kontrollfelder werden einzeln ausgewertet, d.h. das Setzen ihrer Werte hat keinen Einfluss auf die Werte anderer
Kontrollfelder.
Durch Ändern des Wertes wird ItemEvent-Ereignis wie beim Listenfeld ausgelöst.
13.3.7 Die Klasse CheckboxGroup:
Zur Realisierung von Optionsfeldern, bei denen von mehreren Werten jeweils nur einer ausgewählt werden
kann. Ein Optionsfeld wird durch eine Checkbox dargestellt, die zu einer CheckBoxGroup gehört.
Vorgehensweise:
o Vor der Definition der Optionsfelder (Checkboxen) wird die CheckBoxGroup deklariert.
o Im Konstruktor der CheckBox wird als zweiter, bzw. dritter Parameter der Name der zugehörigen
CheckBoxGroup übergeben.
o Der Status der einzelnen Checkboxen muss in diesem Fall angegeben werden.
Beispiel:
cbg = new CheckboxGroup();
cbOne = new Checkbox("one", cgb, true);
cbTwo = new Checkbox("two", cbg, false);
cbThree = new Checkbox("three", cbg, false);
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 178 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
13.3.8 Die Klasse Scrollbar:
Bildlaufleisten dienen zum Blättern (Scrollen), z.B. wenn der Inhalt eines Fensters nicht vollständig angezeigt
werden kann. Der eigenliche Scrollvorgang muss jedoch manuell durchgeführt werden.
Standardmäßig wird eine vertikale Bildlaufleiste erzeugt. Die Ausrichtung
der Bildlaufleiste kann aber im Konstruktor durch die symbolischen
Konstanten Scrollbar.HORIZONTAL, bzw.
Scrollbar.VERTICAL geändert werden.
Die Beziehung zwischen der Position des Bildlauffeldes und dem Verschieben des Fensterinhalts muss vom
Programm aus gesteuert werden. Festgelegt werden muss dazu
der maximale Wert es Bildlauffeldes (Methode void setMaximum(int newMax))
der minimale Wert des Bildlauffeldes (Methode void setMinimum(int newMin))
Bildlauffeld
die Anfangsposition des Bildlauffeldes (Methode void setValue(int newVal)) Schaltflächen scrollbar,
bubble
(Die hier skizzierten Werte können auch dem Konstruktor mitgegeben werden.)
Klickt der Benutzer auf einen beliebigen Teil der Scrollbar, so wird ein AdjustmentEvent-Ereignis ausgelöst.
Der AdjustmentListener muss implementiert werden, wenn dieses Ereignis empfangen werden soll. Die
Klasse Scrollbar stellt die Methode addAdjustmentListener zur Registrierung es Listeners bereit.
Beim Auftreten des Ereignisses wird die Methode void
adjustmentValueChanged(adjustmentEvent e) aufgerufen. Darin wird die Anpassung des
Bildausschnitts odes des Wertes durchgeführt, für den die entsprechende Bildlaufleiste zuständig ist.
Über verschiedene Methoden lässt sich herausfinden, welcher Bereich der Bildlaufleiste angeklickt wurde:
eine der beiden Schaltflächen an den Enden der Bildlaufleiste oder
der Bereich zwischen Schaltfläche und Bildlauffeld oder
das Bildlauffeld
hier weiter 09.12.2015
Beispiel – Anwendung der Klasse Scrollbar
//Eclipse-Projekt: kap13_awt_components_case_study_
import java.awt.*;
import java.awt.event.*;
public class ScrollFenster extends Frame implements AdjustmentListener
{
Scrollbar sbHor, sbVert;
Panel1 pnl;
int x = 0;
int y = 0;
public static void main(String[] args)
{
ScrollFenster fenster = new ScrollFenster();
}
public ScrollFenster()
{
super("ScrollFenster");
this.setSize(200, 150);
this.setLocation(100, 100);
this.setBackground(Color.cyan);
this.setLayout(new BorderLayout());
pnl = new Panel1();
pnl.setSize(300, 400); // Panel größer als Fenster (soll scrollable sein)
pnl.setBackground(Color.pink);
add(pnl);
sbHor = new Scrollbar(Scrollbar.HORIZONTAL, 1, 25, 1, pnl.getWidth());
// s. u.
sbVert = new Scrollbar(Scrollbar.VERTICAL, 1, 70, 1, pnl.getHeight());
// s. u.
sbHor.addAdjustmentListener(this);
sbVert.addAdjustmentListener(this);
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 179 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
add("South", sbHor); // Gegenüber: North
add("East", sbVert); // Gegenüber: West
this.setVisible(true);
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent we)
{
System.exit(0);
}
});
}
public void adjustmentValueChanged(AdjustmentEvent ae) // wird vom
AdjstmentListener
{
// gerufen
int i = 1;
Object a = ae.getSource();
if(a.equals(sbHor))
{
i = sbHor.getValue() - x;
pnl.setSize((int) (pnl.getWidth() + i), pnl.getSize().height);
pnl.setLocation((int) - sbHor.getValue() +
this.getInsets().left,
(int) pnl.getLocation().getY());
x = sbHor.getValue();
}
else
{
i = sbVert.getValue() - y;
pnl.setSize(pnl.getSize().width, (int) (pnl.getHeight() + i));
pnl.setLocation((int) pnl.getLocation().getX(),
(int) - sbVert.getValue() +
this.getInsets().top);
y = sbVert.getValue();
}
}
}
class Panel1 extends Panel
{
public Panel1(LayoutManager layout)
{
super(layout);
}
public Panel1()
{
super();
}
}
public void paint(Graphics g)
{
g.setColor(Color.yellow);
g.fillRect(25,35,200,300);
g.setColor(Color.blue);
g.fillOval(70,70,130,200);
}
Erläuterung zum Konstruktor:
public Scrollbar(int orientation,
int value,
int visible,
int minimum,
int maximum)
throws HeadlessException
Parameters:
orientation - indicates the orientation of the scroll bar. (Orientierung)
value - the initial value of the scroll bar (Startposition)
visible - the visible amount of the scroll bar, typically represented by the size of the bubble (Größe)
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 180 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
minimum
maximum
- the minimum value of the scroll bar
- the maximum value of the scroll bar
13.3.9 Die Klasse Scrollpane
Die Klasse Scrollpane ist einem Panel ähnlich, kann aber nur eine Komponente aufnehmen.
Besitzt standardmäßig zwei Bildlaufleisten.
Diese Komponente wird eingesetzt, wenn eine andere Komponente auf Grund ihrer Größe auf dem verfügbaren
Platz nicht vollständig angezeigt werden kann.
13.4 Dialoge
Ein "Dialog" ist ein Top-Level-Fenster
Liefert Meldung oder Frage an den User und wartet auf dessen Reaktion
13.4.1 Die Klasse
Dialog:
Ein Dialog ist i.d.R. ein "modales" Fenster: d.h. ein anderes Fenster der Anwendung kann nicht aktiv werden,
bis das Dialogfenster wieder geschlossen wird. Wird gesteuert mit dem 3. Konstruktor-Parameter (true/false)
Siehe unten „MeinDialog.java“
Dialog-Fenster ist einem Owner-Fenster/Vater-Fenster (hierarchisch übergeordnetes Fenster) zugeordnet
o Dialog(Frame owner, boolean modal)
o Dialog(Frame owner, String title, boolean modal)
Die Klasse Dialog wird i.d.R. für Meldungen oder zum Einstellen von Optionen eingesetzt. Üblicherweise
können diese Dialoge nicht in ihrer Größe verändert werden. Über die Methode void
setResizable(boolean resizable) kann die Größenänderung unterbunden werden.
Um ein Dialogfenster zu erzeugen, muss eine neue Klasse erstellt weren, die von der Klasse Dialog abgeleitet
wird. Die Komponenten, die in dem Dialogfenster angezeigt werden sollen, müssen vom Programm selbst
positioniert werden. Das gleiche gilt für die Reaktionen auf die Betätigung dieser Komponenten.
Beispiel – Anwendung der Klasse Dialog
//Eclipse-Projekt: kap13_awt_components_case_study_
import java.awt.*;
import java.awt.event.*;
public class DialogFenster extends Frame
implements ActionListener
{
Button btnDlg;
public static void main(String[] args)
{
DialogFenster fenster = new DialogFenster();
}
public DialogFenster()
{
super("DialogFenster");
this.setSize(200, 130);
this.setLocation(100, 100);
this.setBackground(Color.lightGray);
this.setLayout(null);
btnDlg = new Button("Beenden?");
btnDlg.setSize(100, 23);
btnDlg.setLocation(50, 60);
this.add(btnDlg);
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 181 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
btnDlg.addActionListener(this);
this.setVisible(true);
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent we)
{
System.exit(0);
}
});
}
}
public void actionPerformed (ActionEvent ae) // ActionListener callback
{
MeinDialog dlg = new MeinDialog(this,
"Ende-Dialog",
"Wollen Sie die Anwendung wirklich beenden?");
if(dlg.getAntwort())
System.exit(0);
}
import java.awt.*;
import java.awt.event.*;
public class MeinDialog extends Dialog implements ActionListener
{
Button btnOK, btnAbbrechen;
private boolean antwort = false;
public MeinDialog(Frame owner, String titel, String mldg)
{
super(owner, titel, true); // Dialog-Fenster als modales Fenster!
this.setSize(300, 130);
this.setLocation(200, 150);
this.setResizable(false);
this.setLayout(null);
Label lblMldg = new Label(mldg);//”Wollen Sie die Anwendung wirklich…”
lblMldg.setSize(260, 25);
lblMldg.setLocation(20, 40);
add(lblMldg);
btnOK = new Button("OK");
btnOK.setSize(70, 25);
btnOK.setLocation(70, 80);
this.add(btnOK);
btnOK.addActionListener(this);
btnAbbrechen = new Button("Abbrechen");
btnAbbrechen.setSize(70, 25);
btnAbbrechen.setLocation(160, 80);
this.add(btnAbbrechen);
btnAbbrechen.addActionListener(this);
this.setVisible(true);
}
public void actionPerformed (ActionEvent ae) // ActionListener callback
{
Object object = ae.getSource(); // geerbt von Class EventObject
if(object.equals(btnOK))
antwort = true;
setVisible(false);
dispose();
}
public boolean getAntwort()
{
return antwort;
}
}
13.4.2 Der Dateidialog (Klasse
FileDialog )
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 182 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Im Package java.awt steht ein vordefinierter Dialog zur Verfügung, der die Auswahl von Dateinamen und
Verzeichnissen ermöglicht, um Dateien zu lesen oder zu schreiben.
Die Klasse
FileDialog ist von der Klasse Dialog abgeleitet.
Erzeugt wird der Dataiedialog mit einem von drei zur Verfügung stehenden Konstruktoren.
FileDialog (Frame parent)
Dialogfenster zum Dateiladen
FileDialog(Frame parent, String title)
Dialogfenster zum Dateiladen mit Titel
FileDialog(Frame parent, String title, int mode) Dialogfenster mit Titel zum Laden oder
Speichern (FileDialog.LOAD,
FileDialog.SAVE)
Durch show() setVisible() wird der Dateidialog angzeigt:
1. FileDialog dlg = new FileDialog(this,"Öffnen",FileDialog.LOAD);
2. dlg.show(); deprecated -> dlg.setVisible(true);
oder
3. FileDialog dlg = new FileDialog(this,"Speichern",FileDialog.SAVE);
4. dlg.show(); deprecated -> dlg.setVisible (true);
Über die folgenden Methoden kann auf den Namen der Datei, bzw. des Verzeichnisses zugegriffen werden.
(Achtung! Die Dateiverwaltung ist stark OS-spezifisch: Aufbau von Dateinamen, Aufbau der Verzeichnisstruktur,
u.a.m.)
public String getDirectory()
Liefert das ausgewählte Verzeichnis als String zurück.
public void setDirectroy(String dir)
Setzt das Verzeichnis auf den als String mitgegebenen
Namen.
public String getFile()
Liefert den ausgewählten Dateinamen als String zurück.
public void setFile(String file)
Setzt den Dateinamen auf den als String mitgegebenen
Namen.
Vor Aufruf des Dialogs kann über die set-Methoden ein Verzeichnis-Name und ein File-Name voreingestellt
werden.
Die get-Methoden werden nach Beendigung des Dialogs aufgerufen und liefern den ausgewählten
Verzeichnisnamen und den Dateinamen.
Das tatsächliche Öffnen, Lesen und Schreiben muss dann im Programm noch vorgenommen werden.
Beispiel: Siehe weiter unten MenueFenster.java
Beispiel – Anwendung der Klasse FileDialog
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 183 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
//Eclipse-Projekt: kap13_awt_components_case_study_
import java.awt.*;
import java.awt.event.*;
public class OpenDialogFenster extends Frame implements ActionListener
{
Button btnOeffnen;
Label lbl;
public static void main(String[] args)
{
OpenDialogFenster fenster = new OpenDialogFenster();
}
public OpenDialogFenster()
{
super("DialogFenster");
this.setSize(400, 130);
this.setLocation(100, 100);
this.setBackground(Color.lightGray);
this.setLayout(null);
btnOeffnen = new Button("Öffnen");
btnOeffnen.setSize(100, 23);
btnOeffnen.setLocation(40, 60);
this.add(btnOeffnen);
btnOeffnen.addActionListener(this);
lbl = new Label();ƒ
lbl.setSize(360, 23);
lbl.setLocation(20, 90);
this.add(lbl);
this.setVisible(true);
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent we)
{
System.exit(0);
}
});
}
public void actionPerformed (ActionEvent ae)
{
FileDialog dlg = new FileDialog(this, "Öffnen", FileDialog.LOAD);
dlg.setVisible(true);
lbl.setText("Datei " + dlg.getDirectory() + dlg.getFile() + " ausgewählt");
}
}
13.5 Menüs:
Menüs sollen die Funktionalität der Anwendung übersichtlich und sinnvoll gruppieren.
Menüs bestehen aus einer Menüleiste, die bestimmte Menüpunkte (Menüs) enthält.
Menüs werden durch Anklicken geöffnet und enthalten wiederum Menüeinträge und Untermenüs.
Einzelne Bestandteile von Menüs sind Klassen
Menü (Menu)
Menüleiste (MenuBar)
Menüeintrag (MenuItem)
(CheckboxMenuItem)
Trennstrich (Separator)
(Menu.addSeparator())
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 184 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Tastenkombinationen (MenuShortcut)
1.
2.
3.
4.
5.
Die Erstellung eines Menüs erfolgt in folgenden Schritten
Menüleiste erstellen und dem Fenster hinzufügen
Dann ein Menü erstellen und der Menüleiste hinzufügen
Dann Menüeintrag erzeugen un dem Menü hinzufügen
Schritt 3 so lange wiederholen, bis alle Menüeinträge für ein Menü erstellt sind
ab Schritt 2 so lange wiederholen, bis alle Menüs (Sub- und Popup-Menüs) erstellt sind
13.5.1 Die Menüleisten (Klasse
MenuBar )
Die Menüleiste wird über den parameterlosen Konstruktor der Klasse MenuBar erzeugt:
Konstruktor:
MenuBar mnuBar = new MenuBar();
this.setMenuBar(mnuBar); // Methode der Klasse Frame
Wird automatisch unter der Titelleiste positioniert (OS-abhängig!)
Die Menüleiste ist zunächst leer
Menüs hinzufügen à add()
Menüs wegnehmen à remove()
Anzeige in der Reihenfolge des Hinzufügens
13.5.2 Die Menüs (Klasse
Menu )
Ein Menü gruppiert ein oder mehrere Menüeinträge.
Dem Konstruktor wird die Beschriftung des Menüs als Parameter mitgegeben:
Menu mnu = new Menu("Datei");
mnuBar.add(mnu);
Durch die Methode add() der Klasse MenuBar wird das Menü zur Menüleiste hinzugefügt.
Das Menü ist zunächst leer.
Menüeinträge hinzufügen à add(), oder à insert()
Menüeinträge wegnehmen à remove()
Trennstriche zwischen den Menüpunkten werden mit addSeparator() oder insertSeparator()
eingefügt.
Anmerkung: durch add() wird die Reihenfolge der Menüeinträge definiert. Durch insert() kann ein
Menüeintrag an bestimmter Indexposition eingefügt werden. (Erster Menüeintrag hat den Index 0.)
13.5.3 Die Menü-Einträge (Klasse
MenuItem )
Menüeinträge erzeugen
Die Beschriftung eines Menüeitrags wird dem Konstruktor als Parameter übergeben:
MenuItem mi = new MenuItem("Öffnen");
mnu.add(mi);
Als zweiter Parameter im Konstruktor kann eine Tastenkombination (als Shortcut zum Aktiviern des
Menüpunkts) übergeben werden.
o Diese Tastenkombination wird in Form eines Objekts der Klasse MenuShortcut definiert.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 185 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
o
o
o
Erzeugt wird ein solches Objekt durch den Aufruf des Konstruktors der Klasse MenuShortcut.
Als Parameter wird der Code der Taste übergeben, die in Verbindung mit der STRG-Taste den Shortcut
ergibt.
Für den Tastencode können die symbolischen Konstanten der Klasse KeyEvent verwendet werden
(„virtual key code“). (Java lässt für Shortcats nur alphanumerische Tasten zu.)
Beispiel: MenuItem mi=new MenuItem(„Öffnen“,new MenuShortcut(KeyEvent.VK_O));
Wählt er Benutzer einen Menüeintrag aus, so wird ein ActionEvent-Ereignis ausgelöst. Soll das Ereignis für
den Menüeintrag verarbeitet werden, so muss der ActionListener registriert werden.
o mi.addActionListener(this);
o In der Methode actionPerformed() erfolgt die Verarbeitung aller ActionEvent-Ereignisse,
die auch durch eine Schaltfläche oder ein Listenfeld ausgelöst werden können.
o Es gilt zunächst herauszufinden, welches Objekt das Ereignis ausgelöst hat.
o Die Methode getLabel() liefert die Beschriftung des Menüeintrags.
o Besser: alternativ liefert die Methode getSource() die Ereignisquelle.
Beispiel:
public void actionPerformed(ActionEvent ae)
{
Object o = actionevent.getSource();
// geerbt von Class EventObject
if (((MenuItem)o).getLabel() == “Öffnen”)
. . .
Menüeinträge besitzen die Eigenschaft enabled, diese Eigenschaft gibt an, ob der Menüeintrag ausgewählt
werden darf oder nicht.
Inaktive Menüeinträge werden grau dargestellt und lassen sich nicht aktivieren.
Über die Methoden setEnabled() bzw. isEnabled() kann der Wert gesetzt bzw. abgefragt werden.
o mi.setEnabled(false);
Menüeinträge markieren
Markierungen in Menüeinträgen werden für das Setzen von Optionen verwendet.
Ist die Menüoption gesetzt,so ist der Menüeintrag links neben der Beschriftung durch eine (OS-abhänige)
Markierung gekennzeichnet (Häckchen, erhabenes Quadrat, …)
Zur Erzeugung optionaler Menüeinträge wird die Klase checkboxMenuItem verwendet (abgeleitet von
MenuItem)
Die Klasse hat zusätzliche Methoden, um die Statusinformationen zu verarbeiten:
void setState(boolean b)
Setzt den Status der Option auf den übergebenen Wert b.
boolean getState()
Liefert den aktuellen Status zurück.
Wird ein optonaler Menüeintrag ausgewählt, so wird ein ItemEvent-Ereignis ausgelöst (wie bei Checkboxen
oder Listenfeldern).
o Ist die Verarbeitung des Ereignisses notwendig, so muss der ItemEvent-Listener beim
Menüeintrag registriert werden.
Untermenüs
Für die Erzeugung des Untermenüs wird dem Menü statt eines Menüeintags ein weiteres Menü hinzugefügt.
An diese Menü werden dann die Menüeinträge angehängt. (Siehe Beispiel unten.)
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 186 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
mnuBar = new MenuBar();
this.setMenuBar(mnuBar);
// Menüleiste erzeugen
// und zum Fenster hinzufügen
mnu = new Menu("Grafik"); // Menü „Graphik“ erzeugen
mnuBar.add(mnu);
// und zur Menüleiste hinzufügen
mnu2 = new Menu("Farbe"); // Unter-Menü “Farbe” erzeugen
mnu.add(mnu2);
// und zum Menü
mi = new MenuItem("rot"); // 1. Menüeintrag für das Unter-Menü “Farbe” erzeugen
mnu2.add(mi);
// und zum Unter-Menü hinzufügen
Beispiel – Anwendung von Menüs
//Eclipse-Projekt: kap13_awt_components_case_study_
import java.awt.*;
import java.awt.event.*;
import java.io.*;
public class MenueFenster extends Frame implements ActionListener, ItemListener
{
Label lbl;
boolean autosave = true;
public static void main(String[] args)
{
MenueFenster fenster = new MenueFenster();
}
public MenueFenster()
{
super("Fenster mit Menü");
this.setSize(400, 130);
this.setLocation(100, 100);
this.setLayout(null);
MenuBar mnuBar = new MenuBar();
this.setMenuBar(mnuBar);
Menu mnu = new Menu("Datei"); //----- erstes Meneue ------------------mnuBar.add(mnu);
MenuItem mnuItem = new MenuItem("Öffnen", new MenuShortcut(KeyEvent.VK_O));
mnuItem.addActionListener (this);
mnu.add(mnuItem);
mnuItem = new MenuItem("Speichern", new MenuShortcut(KeyEvent.VK_S));
mnuItem.addActionListener (this);
mnu.add(mnuItem);
mnu.addSeparator();
CheckboxMenuItem cbmnuItem = new CheckboxMenuItem("automatisch Speichern",
autosave);
cbmnuItem.addItemListener(this);
mnu.add(cbmnuItem);
mnu.addSeparator();
mnuItem = new MenuItem("Beenden");
mnuItem.addActionListener (this);
mnu.add(mnuItem);
mnu = new Menu("Info"); //---------- zweites Menue ------------------mnuBar.add(mnu);
mnuItem = new MenuItem("Info", new MenuShortcut(KeyEvent.VK_I));
mnuItem.addActionListener (this);
mnu.add(mnuItem);
cbmnuItem = new CheckboxMenuItem("Hintergrund weiß", true);
cbmnuItem.addItemListener (this);
mnu.add(cbmnuItem);
lbl = new Label();
lbl.setSize(360, 23);
lbl.setLocation(20, 90);
this.add(lbl);
this.setVisible(true);
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent we)
{
System.exit(0);
}
});
}
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 187 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
public void actionPerformed(ActionEvent ae) //ActionListener cb
{
Object o = ae.getSource();
if(((MenuItem)o).getLabel().equals("Öffnen"))
{
FileDialog dlg = new FileDialog(this, "Datei öffnen", FileDialog.LOAD);
dlg.setVisible(true);
lbl.setText("Datei " + dlg.getDirectory() + dlg.getFile() + " ausgewählt");
}
else if(((MenuItem)o).getLabel().equals("Speichern"))
{
FileDialog dlg = new FileDialog(this, "Datei speichern unter", FileDialog.SAVE);
dlg.show();
lbl.setText("speichen unter " + dlg.getDirectory() + dlg.getFile());
}
else if(((MenuItem)o).getLabel ().equals("Beenden"))
{
MeinDialog dlg = new MeinDialog(this, "Ende-Dialog",
"Wollen Sie die Anwendung wirklich beenden?");
if(dlg.getAntwort())
System.exit(0);
}
else if(((MenuItem)o).getLabel ().equals("Info"))
{
MeinDialog dlg = new MeinDialog(this, "Info",
"Dieses Programm enthält ein Menü");
}
}
}
public void itemStateChanged (ItemEvent ie) // ItemListener CB
{
CheckboxMenuItem cmi = (CheckboxMenuItem) ie.getSource();
if(cmi.getLabel().equals("Hintergrund weiß"))
if (cmi.getState())
setBackground(Color.white);
else
setBackground(Color.lightGray);
else if(cmi.getLabel().equals("automatisch Speichern"))
autosave = cmi.getState();
}
Zugehörige Klasse MeinDialog (für den Beenden-Dialog)
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 188 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
import java.awt.*;
import java.awt.event.*;
public class MeinDialog extends Dialog implements ActionListener
{
Button btnOK, btnAbbrechen;
private boolean antwort = false;
public MeinDialog(Frame owner, String titel, String mldg)
{
super(owner, titel, true);
this.setSize(300, 130);
this.setLocation(200, 150);
this.setResizable(false);
this.setLayout(null);
Label lblMldg = new Label(mldg);
lblMldg.setSize(260, 25);
lblMldg.setLocation(20, 40);
add(lblMldg);
btnOK = new Button("OK");
btnOK.setSize(70, 25);
btnOK.setLocation(70, 80);
this.add(btnOK);
btnOK.addActionListener(this);
btnAbbrechen = new Button("Abbrechen");
btnAbbrechen.setSize(70, 25);
btnAbbrechen.setLocation(160, 80);
this.add(btnAbbrechen);
btnAbbrechen.addActionListener(this);
this.setVisible(true);
}
public void actionPerformed(ActionEvent ae)
{
Object object = ae.getSource();
if(object.equals(btnOK))
antwort = true;
setVisible(false);
dispose();
}
public boolean getAntwort()
{
return antwort;
}
}
13.5.4 Kontextmenüs (Klasse
PopupMenu)
Kontextmenüs erscheinen, wenn mit der rechten Maustaste auf eine Komponente bzw. ein Fenster geklickt
wird. (Achtung: in Abhängigkeit vom
Betriebssystem kann der „popUpTrigger“
auch die mittlere Maustaste sein!)
Erzeugt wird ein Kontextmenü durch die
Klasse PopupMenu, die von der Klasse
Menu abgeleitet ist.
Es sind für die Kontext-Menüerstellung die
gleichen Schritte notwendig, wie für die
Erstellung eines Menüs (ohne Menüleiste).
Die Erstellung eines Kontextmenüs erfolgt in folgenden Schritten
1. Die Komponente, an die das Kontextmenü durch die Methode add() gebunden ist, muss
Mausereignisse empfangen können. Dazu wird die Methode enableEvents() aufgerufen (von
Class Component geerbt). Siehe unten 1) (dafür ist KEIN addMouseListener() notwendig!)
2. Die Component-Methode processMouseEvent() muss überschrieben werden (von Class
Component geerbt). Siehe unten 2)
3. Durch die MouseEvent-Methode isPopUpTrigger() muss abgefragt werden, ob es sich um
das Ereignis handelt, welches zum Anzeigen des Kontextmenüs führen soll. Siehe unten 3)
4. Das Kontextmenü wird durch den Aufruf der Methode show() angezeigt. Siehe unten 4)
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 189 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Beispiel – Anwendung von Kontextmenüs
import java.awt.*;
import java.awt.event.*;
import java.io.*;
public class KontextmenueFenster extends Frame implements ActionListener,
ItemListener
{
PopupMenu pmnu;
boolean autosave = true;
Label lbl;
public static void main(String[] args)
{
KontextmenueFenster fenster = new KontextmenueFenster();
}
public KontextmenueFenster()
{
super("Fenster mit Kontextmenü");
this.setSize(400, 130);
this.setLocation(100, 100);
this.setLayout(null);
pmnu = new PopupMenu();
this.add(pmnu);
MenuItem mnuItem = new MenuItem("Datei Öffnen");
mnuItem.addActionListener(this);
pmnu.add(mnuItem);
mnuItem = new MenuItem("Datei Speichern");
mnuItem.addActionListener(this);
pmnu.add(mnuItem);
pmnu.addSeparator();
CheckboxMenuItem cbmnuItem = new CheckboxMenuItem("automatisch Speichern",
autosave);
cbmnuItem.addItemListener(this);
pmnu.add(cbmnuItem);
pmnu.addSeparator();
mnuItem = new MenuItem("Beenden");
mnuItem.addActionListener(this);
pmnu.add(mnuItem);
lbl = new Label();
lbl.setSize(360, 23);
lbl.setLocation(20, 90);
add(lbl);
this.setVisible(true);
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent we)
{
System.exit(0);
}
});
// die folgenden 4 Schritte 1) – 4) konfigurieren das Popup-Menue
// “Problem”: Kontextmenue ist an das Frame-Innere gebunden, nicht
// an eine konkrete Component, wie z.B. an ein Menuitem...
this.enableEvents(AWTEvent.MOUSE_EVENT_MASK); //1)Siehe o. die 4 Schritte
}
// von Class Component geerbt
public void processMouseEvent(MouseEvent me) // 2)“alternative Mouse-Event-Behandlung
{
// von Class Component geerbt
if(me.isPopupTrigger())
// 3) aus MouseEvent
pmnu.show(me.getComponent(), me.getX(), me.getY());
// 4) aus PopupMenue
}
public void actionPerformed (ActionEvent ae) //ActionListener CB
{
Object o = ae.getSource();
if(((MenuItem)o).getLabel().equals("Datei Öffnen"))
{
FileDialog dlg = new FileDialog(this, "Datei öffnen", FileDialog.LOAD);
dlg.setVisible(true);
lbl.setText("Datei " + dlg.getDirectory() + dlg.getFile() + " ausgewählt");
}
else if(((MenuItem)o).getLabel().equals("Datei Speichern"))
{
FileDialog dlg = new FileDialog(this, "Datei speichern unter", FileDialog.SAVE);
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 190 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
dlg.setVisible(true);
lbl.setText("speichen unter " + dlg.getDirectory() + dlg.getFile());
}
else if(((MenuItem)o).getLabel().equals("Beenden"))
{
MeinDialog dlg = new MeinDialog(this, "Ende-Dialog",
"Wollen Sie die Anwendung wirklich beenden?");
if(dlg.getAntwort())
System.exit(0);
}
else if(((MenuItem)o).getLabel().equals("Info"))
{
MeinDialog dlg = new MeinDialog(this, "Info",
"Dieses Programm enthält ein Menü");
}
}
public void itemStateChanged (ItemEvent ie) // ItermListener CB
{
CheckboxMenuItem cmi = (CheckboxMenuItem) ie.getSource();
if(cmi.getLabel().equals("automatisch Speichern"))
autosave = cmi.getState();
}
}
Bearbeiten Sie die Case Studies und stellen Sie die Benutzeroberflächen
Für den Server und für den Client auf eine AWT-GUI um.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 191 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
14. LayoutManager (ein Überblick)
ggf. erst in ADRELI_5_JDBC einbauen
Was Sie lernen:
welche Aufgaben LayoutManager erfüllen
welche LayoutManager es gibt
wie LayoutManager eingesetzt werden
Voraussetzungen:
Fensterprogrammierung
Umgang mit AWT-Komponenten
14.1 Grundlagen
Die Komponenten von AWT bzw. von Swing können durch direkte Angabe der Position (x,y) und Größe im
Fenster platziert warden (so genanntes Null-Layout).
Das Null-Layout ist sinnvoll, wenn das Programm nur unter einem Betriebssystem laufen soll oder eine exakte
Positionierung erforderlich ist.
Bei einem Einsatz unter anderem Betriebssystem können sich Verschiebungen und Größenänderungen
ergeben. Aus diesem Grund wurde in Java für die Anordnung der Komponenten in einem Fenster bzw. Container
ein besonderes Konzept entwickelt: die LayoutManager.
LayoutManager haben folgende Eigenschaften:
o Man kann jeder Container-Komponente einen LayoutManager zuordnen.
o Die Positionierung und Festlegung der Größe der Komponente erfolgt über den LayoutManager
auf allen Plattformen in der gleichen Art und Weise.
§ Die Größe der Komponenten wird vom LayoutManager festgelegt. Einige LayoutManager
greifen auf Eigenschaften der Komponenten zurück (über die Component-Methoden
getPreferedSize(), getMinimumSize(), getMaximumSize()).
Die Position und Größe der Komponenten wird ggf. an die Fenstergröße angepasst, wenn diese verändert
wird.
Container können mehrfach verschachtelt werden. Da jeder Container einen eigenen LayoutManager besitzt,
ist somit eine vielfältige Oberflächengestaltung möglich.
Um LayoutManager einzusetzten, muss das Packet java.awt.* importiert werden.
Hinweis: In manchen Programmierumgebungen (z. B. JBuilder) stehen noch weitere LayoutManager zur Verfügung.
Hierbei handelt es sich um proprietäre LayoutManger, die nicht zum Sprachumfang gehören.
Layout-Manager
FlowLayout
(Für Panels und der davon
abgeleiteten Klasse Applet ist das
FlowLayout voreingestellt.)
BorderLayout
(Für Fenster (Frame, Window) ist
das BorderLayout voreingestellt.)
Erläuterung
Ordnet die hinzugefügten
Komponenten nebeneinander
in ihrer bevorzugten Größe
(getPreferedSize()) an. Passt
eine neue Komponente nicht
mehr in eine Zeile, sow wird
eine neue Zeile begonnen.
Ordnet die hinzugefügte
Komponente an den
Außenseiten und in der Mitte
des Containers an. Dadurch
können maximal fünf
Komponenten platziert werden.
Beispiel
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 192 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
GridLayout
Ordnet die hinzugefügten
Komponenten in einem Gitter
mit vorgegebener Anzahl von
Spalten und Zeilen an.
GridBagLayout
Ordnet die hinzugefügten
Komponenten in einem Gitter
an. Eine Komponente kann
mehrere Zellen eines Gitters
einnehmen. Das Gitter passt
sich ggf. an.
CardLayout
BoxLayout
Die eigentliche Ausrichtigung
der Komponenten erfolgt über
das GridBagConstraints
Objekt, welches beim
Hinzufügen zum Container
anzugeben ist.
Ordnet die hinzugefügten
Komponenten übereinander in
Form eines Stapels an. Alle
unteren Komponenten werden
durch die oberste verdeckt.
Über Methoden der Klasse kann
jeweils die nächste oder
vorherige Komponente
angezeigt werden.
Ordnet die hinzugefügten
Komponenten wahlweise in
einer Zeile oder Spalte im
Container an. Die
voreingestellte maximale Größe
der Komponentewird nicht
geändert.
OverlayLayout
Dieser Manager wurde für
Swing entwickelt, kann aber
auch im AWT eingesetzt
werden. Dazu muss das
Package javax.swing.*
importiert werden.
Nur für Swing.
SpringLayout
Nur für Swing.
Damit können mehrere Componenten
übereinander angeordnet werden.
Durchscheinend.
… added in JDK version 1.4 to support layout
in GUI builders….very low-level and as such
you really should only use it with a GUI
builder….
Siehe auch: http://java.sun.com/docs/books/tutorial/uiswing/layout/
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 193 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Beispiel – Das FlowLayout
import java.awt.*;
import java.awt.event.*;
public class FlowLayoutFenster extends Frame
{
Button btn1, btn2, btn3;
public static void main(String[] args)
{
FlowLayoutFenster fenster = new FlowLayoutFenster();
}
public FlowLayoutFenster()
{
super("Anordnung im FlowLayout");
this.setSize(250, 80);
this.setLocation(100, 100);
this.setBackground(Color.cyan);
this.setLayout(new FlowLayout(FlowLayout.CENTER, 10, 10));
btn1 = new Button("Nr. 1");
this.add(btn1);
btn2 = new Button("Nr. 2");
this.add(btn2);
btn3 = new Button("Nr. 3");
this.add(btn3);
pack();
this.setVisible(true);
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent we)
{
System.exit(0);
}
});
}
}
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 194 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Beispiel – Das BorderLayout
import java.awt.*;
import java.awt.event.*;
public class BorderLayoutFenster extends Frame
{
Button btn1, btn2, btn3, btn4, btn5;
public static void main(String[] args)
{
BorderLayoutFenster fenster = new BorderLayoutFenster();
}
public BorderLayoutFenster()
{
super("Anordnung im BorderLayout");
this.setSize(250, 100);
this.setLocation(100, 100);
this.setBackground(Color.cyan);
this.setLayout(new BorderLayout());
btn1 = new Button("Norden");
this.add("North", btn1);
// zu add() geerbt von class Container
btn2 = new Button("Westen");
this.add("West", btn2);
btn3 = new Button("Osten");
this.add(btn3, BorderLayout.EAST);
btn4 = new Button("Süden");
this.add(btn4, BorderLayout.SOUTH);
btn5 = new Button("Zentrum");
this.add("Center", btn5);
pack();
this.setVisible(true);
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent we)
{
System.exit(0);
}
});
}
}
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 195 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Beispiel – Das GridLayout
import java.awt.*;
import java.awt.event.*;
public class GridLayoutFenster extends Frame
{
Button btn1, btn2, btn3, btn4, btn5, btn6;
public static void main(String[] args)
{
GridLayoutFenster fenster = new GridLayoutFenster();
}
public GridLayoutFenster()
{
super("Anordnung im GridLayout");
this.setSize(250, 100);
this.setLocation(100, 100);
this.setBackground(Color.cyan);
GridLayout gl = new GridLayout(0,
this.setLayout(gl);
btn1 = new Button("Nr. 1");
this.add(btn1);
btn2 = new Button("Nr. 2");
this.add(btn2);
btn3 = new Button("Nr. 3");
this.add(btn3);
btn4 = new Button("Nr. 4");
this.add(btn4);
btn5 = new Button("Nr. 5");
this.add(btn5);
btn6 = new Button("Nr. 6");
this.add(btn6);
3);
pack();
this.setVisible(true);
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent we)
{
System.exit(0);
}
});
gl.setColumns(2);
pack();
}
}
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 196 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Beispiel – Das GridBagLayout
import java.awt.*;
import java.awt.event.*;
public class GridBagLayoutFenster extends Frame
{
Button btn1, btn2, btn3, btn4, btn5, btn6;
public static void main(String[] args)
{
GridBagLayoutFenster fenster = new GridBagLayoutFenster();
}
public GridBagLayoutFenster()
{
super("Anordnung im GridBagLayout");
this.setSize(250, 100);
this.setLocation(100, 100);
this.setBackground(Color.cyan);
GridBagLayout gbl = new
GridBagLayout();
this.setLayout(gbl);
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(1, 1, 1, 1);
gbc.fill = GridBagConstraints.BOTH;
gbc.weightx = 1;
gbc.weighty = 1;
btn1 = new Button("Nr. 1");
gbc.gridwidth = 1;
gbc.gridheight = 1;
gbc.gridx = 0;
gbc.gridy = 0;
gbl.setConstraints(btn1, gbc);
this.add(btn1);
btn2 = new Button("Nr. 2");
gbc.gridwidth = 1;
gbc.gridheight = 1;
gbc.gridx = 0;
gbc.gridy = 1;
gbl.setConstraints(btn2, gbc);
this.add(btn2);
btn3 = new Button("Nr. 3");
gbc.gridwidth = 1;
gbc.gridheight = 2;
gbc.gridx = 1;
gbc.gridy = 0;
gbc.weightx = 2;
gbl.setConstraints(btn3, gbc);
this.add(btn3);
btn4 = new Button("Nr. 4");
gbc.gridwidth = 2;
gbc.gridheight = 1;
gbc.gridx = 0;
gbc.gridy = 2;
gbc.weightx = 1;
gbl.setConstraints(btn4, gbc);
this.add(btn4);
btn5 = new Button("Nr. 5");
gbc.gridwidth = 1;
gbc.gridheight = 1;
gbc.gridx = 2;
gbc.gridy = 0;
gbl.setConstraints(btn5, gbc);
this.add(btn5);
btn6 = new Button("Nr. 6");
gbc.gridwidth = 1;
gbc.gridheight = 2;
gbc.gridx = 2;
gbc.gridy = 1;
gbl.setConstraints(btn6, gbc);
this.add(btn6);
//pack();
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 197 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
this.setVisible(true);
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent we)
{
System.exit(0);
}
});
}
}
Beispiel – Das CardLayout
import java.awt.*;
import java.awt.event.*;
public class CardLayoutFenster extends Frame implements ActionListener
{
Button btn1, btn2, btn3, btn4;
CardLayout cl = new CardLayout();
public static void main(String[] args)
{
CardLayoutFenster fenster = new CardLayoutFenster();
}
public CardLayoutFenster()
{
super("Anordnung im CardLayout");
this.setSize(250, 100);
this.setLocation(100, 100);
this.setBackground(Color.cyan);
this.setLayout(cl);
btn1 = new Button("Nr. 1");
this.add("First", btn1);
btn1.addActionListener(this);
btn2 = new Button("Nr. 2");
this.add("Second", btn2);
btn2.addActionListener(this);
btn3 = new Button("Nr. 3");
this.add("Third", btn3);
btn3.addActionListener(this);
btn4 = new Button("Nr. 4");
this.add("Fourth",btn4);
btn4.addActionListener(this);
this.setVisible(true);
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent we)
{
System.exit(0);
}
});
}
}
public void actionPerformed(ActionEvent ae)
{
cl.next(this);
}
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 198 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Beispiel – Das BoxLayout
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class BoxLayoutFenster extends JFrame
{
JButton btn1, btn2, btn3;
public static void main(String[] args)
{
BoxLayoutFenster fenster = new BoxLayoutFenster();
}
public BoxLayoutFenster()
{
super("Anordnung im BoxLayout");
this.getContentPane().setLayout(new BorderLayout());
this.setSize(280, 90);
this.setLocation(100, 100);
this.setBackground(Color.cyan);
JPanel p = new JPanel();
p.setBackground(Color.yellow);
this.getContentPane().add(p);
BoxLayout bl = new BoxLayout(p, BoxLayout.X_AXIS);
p.setLayout(bl);
btn1 = new JButton("Nr. 1");
btn1.setAlignmentY(1.0f);
p.add(btn1);
p.add(Box.createHorizontalGlue());
btn2 = new JButton("Nummer 2");
btn2.setAlignmentY(0.5f);
p.add(btn2);
p.add(Box.createHorizontalGlue());
btn3 = new JButton("Nr. 3");
btn3.setAlignmentY(0.0f);
p.add(btn3);
this.setVisible(true);
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent we)
{
System.exit(0);
}
});
}
}
Bearbeiten Sie die Case Studies und lösen Sie die Übungsaufgeben zu
diesem Kapitel! (Siehe Unterlagen hierzu auf dem Materialserver.)
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 199 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
(Sem-Projekt 5. JDBC)
15. Elementare Dienste für JEE: JDBC
Was Sie lernen:
wie die Architektur von JDBC aussieht
wie Sie über Java auf Datenbanken zugreifen
wie Anfragen an eine Datenbank gerichtet und Ergebnisse ausgewertet werden
Voraussetzungen:
Java Fundamentals
Datenbank-Grundlagen
Literatur: Siehe Literaturverzeichnis
Goll/Weiß/Müller: “Tigerbuch”
Wutka: J2EE Developer’s Guide
http://knol.google.com/k/j-anton-illik/jdbc-ein-elementarer-dienst-fr-jee-how/2rmlvpldf2pey/2#
15.1 Hinführung
Die Notwendigkeit, die SQL-Sprache in eine algorithmische Programmiersprache einzubetten, ergibt sich daraus,
dass es sich bei SQL um eine rudimentäre (non computational complete) Programmiersprache handelt, die
allein nicht ausreicht, um komplexe Datenbankanwendungen zu schreiben.
Eine Möglichkeit, Datenbankanwendungen zu schreiben, bietet die Einbettung der SQL-Sprache in
prozedurale und objektorientierte Programmiersprachen wie C/C++/C# und Java. Man spricht dann von
Embedded SQL. Dabei wird ein Vorübersetzer/Präprozessor verwendet, der jede SQL-Anweisung übersetzt.
Nach der Vorübersetzerphase wird ein Programm mit einem einheitlichen Code (Java Code pur) erzeugt, das
anschließend übersetzt werden kann. (Die Programmiersprache, in die SQL eingebettet wird, in unserem Fall
Java, wird Host-Sprache genannt.)
Eine andere Möglichkeit der SQL-Nutzung in Java läuft über eine Aufrufschnittstelle (Call Library
Interface): hier werden native Klassen und Methoden der Sprache (Java) speziell für den Umgang mit einer
Datenbank zur Verfügung gestellt.
Für die Nutzen von Datenbanken aus einer Programmiersprache wie Java gibt es also zwei verschiedene Ansätze:
den Embedded SQL Ansatz SQLJ und
den CLI (Call Library Interface) Ansatz von JDBC.
Wir betrachten zunächst den CLI-Ansatz von JDBC.
15.2 Grundlagen
Einer (von mehreren) elementaren Java-Dienst für JavaEE ist JDBC. Im JavaEE-Umfeld wird JDBC an
verschiedenen Stellen genutzt: z.B. in JSP (Java Server Pages)/Servlets und insbesondere in EJBs (Enterprise
Java Beans).
JDBC, Java Database Connectivity, ist ist eine Java API und Teil des Java JDK. Es wird dazu benutzt Daten
aus einer beliebigen Tabellenstruktur herauszulesen. Dabei sind nicht nur Datenbanktabellen gemeint,
sondern auch auch normale Dateien oder Datenquellen, die Tabellenstrukturen aufweisen. In erster Linie
bietet es aber die Möglichkeit, insbesondere Datenbankapplikationen komplett in Java zu schreiben. JDBC wird
dazu benutzt, SQL Befehle an die Datenbank weiterzuleiten. Der große Vorteil dabei ist, dass es genügt, ein
(einziges) Programm für alle Arten von Datenbanken zu schreiben. Mit JDBC kann also ein einzelnes
Programm z.B. auf eine Oracle-, eine IBM DB2- oder eine MySQL-Datenbank (und weitere) zugreifen, ohne
dass etwas im Code dafür geändert werden muss. Zusammen mit den Vorteilen der Programmiersprache Java ist
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 200 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
ein JDBC basiertes Java Programm also in der Lage, Clienten auf einer beliebigen Plattform (egal ob
beispielsweise Windows, Macintosh oder eine UNIX Maschine) mit einer beliebigen Datenbank zu verbinden.
Die JDBC API besteht eigentlich aus zwei Teilen. Das Kern-JDBC (java.sql.*) wird mit dem Java Standard
Development Kit (kurz: JDK) geliefert. JavaEE entält zusätzlich das optionale JDBC-Paket javax.sql.*, das
einige zusätzliche serverseitige Features enthält, die in der JavaEE-Entwicklung häufiger gebraucht werden,
vor allem im Bereiche der Enterprise JavaBeans. Schlagworte hierfür:
o
o
o
o
DataSource,
Connection-Pooling,
verteilte Transaktionen,
RowSets (erweitert das ResultSet-Interface)
Moderne Software-Architekturen bestehen aus mindestens drei Ebenen:
o
Der Hardware am nächsten ist die Datenebene, („Model“)
o darüber angesiedelt ist die Geschäftslogik-Ebene („Controller“)
o und dann folgt die Präsentatinsebene. („View“)
Datenebene
i.d.R
relationalen Datenbanken
Die
wird
. durch den Einsatz von
unterschiedlicher Hersteller (MySQL (-> SUN ->) Oracle, IBM, SyBase, …) realisiert (wenn strukturierte
Daten vorliegen).
Zur Anbindung von Java-Anwendungen und Java-Applets an relationale Datenbanksysteme wird über das
JDBC-API eine (betriebssystem-)plattform- und datenbankunabhängige Schnittstelle bereitgestellt. Die
Datenbankhersteller können über JDBC-Treiber eine Anbindung an ihr DBMS anbieten.
Eine Liste der verfügbaren JDBC-Treibern finden Sie unter http://servlet.java.sun.com/product/jdbc/drivers oder
http://developers.sun.com/product/jdbc/drivers http://devapp.sun.com/product/jdbc/drivers
http://www.oracle.com/technetwork/java/javase/jdbc/index.html (oder nach „jdbc driver“ googeln). Zum Zeitpunkt
der Skripterstellung gab es 221 verfügbare Treiber.
Um Daten einer Datenbank abzulegen, abzufragen und zu ändern wird die strukturierte Abfragesprache SQL
(Structured Query Language) eingesetzt. Für die Abfragesprach SQL wurden mehrere Standards und
Funktionsumfänge definiert. Der SQL ANSI 92 ENTRY LEVEL wurde als Grundlage für JDBC festgelegt. Damit
ist sichergestellt, dass ein standardisierter Teil von SQL-Anweisungen für alle JDBC-Treiber verwendet werden
kann.
Die Java-Aufrufschnittstelle von SQL basiert auf dem Standard X/Open SQL CLI (Call Level Interface). Das
heißt, Sie verwenden in Ihrem Programm SQL-Anweisungen in Form von Java-Zeichenketten (Strings), die an die
betreffenden Methoden der Datenbanktreiber weitergegeben werden. Werden Daten zurückgegeben
(Ergebnismenge), können Sie auf diese über andere Methoden zugriffen.
Im Fall von EJBs wird JDBC auf zwei Arten genutzt: Zum einen können direkte JDBC-Aufrufe in Session Beans
und Bean Managed Persistance Entity Beans benutzt werden, zum anderen kann der EJB-Container intern
JDBC zur Speicherung von Daten aus Container Managed Persitance Entity Beans verwenden. JDBC ist zwar
ein typischer Weg, Daten aus EJBs heraus persistent abzulegen, aber es ist nicht der einzige Weg.
Beispielsweise sind auch indirekte Zugriffe auf Datenhaltungssysteme über CORBA, JMS (Java Messaging
Service) oder JCA (Java Connector Architecture) möglich.
15.3 JDBC als Basis für andere APIs: SQLJ und JDO
Die JDBC API kann zum einen ohne andere APIs zur Erstellung von Datenbank-Applikationen benutzt werden.
Andererseits ist JDBC zugleich Grundlage für eine Reihe von anderen APIs, die JDBC erweitern und
spezialisieren. Diesbezüglich stellen wir kurz SQLJ und JDO vor.
15.3.1 SQLJ
Insbesondere im Bereich SQL und JDBC wurde der Standard SQLJ aus einem Konsortium von Oracle, IBM, Sun
und anderen Firmen geschaffen, um Embedded SQL in Java zu integrieren.
Während der Kompilierung lassen sich (direkte) SQL Anweisungen mit Java Anweisungen kombinieren.
(Mit Hilfe von Tags für SQL.)
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 201 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Beispielsweise kann eine Java Variable in einem SQL Befehl verwendet werden, um die Ergebnisse des SQL
Befehls zu erhalten und weiter zu verarbeiten.
15.3.2 SQLJ im Vergleich zu JDBC
Der wichtigste Unterschied zwischen den beiden Sprachen ist, dass SQLJ eine statische und JDBC eine
dynamische Sprache ist.
Eine Sprache wird dynamisch genannt, falls der Datenbankprogrammierer damit Datenbankobjekte abfragen
und ändern kann, die erst zur Ablaufzeit (runtime) bekannt sind.
Das gilt nicht nur für Datenbankobjekte, sondern auch für ganze SQL-Anweisungen.
Im Unterschied zu JDBC, kann SQLJ nur statisch verwendet werden, d.h. alle SQL-Anweisungen und alle in
ihnen verwendeten Datenbankobjekte (bis eventuell auf die Werte der Host-Variablen) sind zur Übersetzungszeit
bekannt und damit auch ab dann fixiert (und zur Laufzeit nicht mehr änderbar.)
Obwohl eine dynamische Sprache flexibler als eine statische ist, hat SQLJ auch Vorteile im Vergleich zu JDBC.
Diese sind:
SQLJ befindet sich auf einem höherem logischen Niveau/hat eine grössere Aggregationsdichte
als JDBC. Damit wird ein Programm kompakter/kürzer (mit weniger Notation wird mehr erreicht).
SQLJ hat eine einfachere Syntax als JDBC
SQLJ kann die Syntax- und Semantikanalyse zur Übersetzungszeit durchführen.
Im Unterschied zu JDBC, wo alle SQL-Anweisungen, die in einem JDBC-Programm in Methodenaufrufen versteckt
sind, ist jede SQL-Anweisung in einem SQLJ-Programm in ihrer ursprünglichen Form dargestellt und dadurch
gleich erkennbar. Aus diesem Grund bezeichnet man SQLJ als logisch höhere Programmiersprache (als JDBC).
Das folgende Beispiel verdeutlicht diesen Sachverhalt.
Zunächst geben wir kurz einen Ausschnitt aus einem JDBC-Programm wieder, in dem eine SELECT-Anweisung mit
Hilfe der preparedStatement()-Methode vorbereitet wird und danach die Spaltenwerte den entsprechenden
Variablen zugewiesen werden.
java.sql.PreparedStatement ps =
con.preparedStatement(„SELECT address FROM emp WHERE name=?“);
....
ps.setString(1,name);
java.sql.ResultSet names = ps.executeQuery();
names.next();
addr = names.getString(1);
....
names.close();
Um dieselbe Funktionalität mit Hilfe von SQLJ zu implementieren, reicht eine einzige Programmzeile aus:
#sql (con) {SELECT address INTO :addr FROM emp WHERE name= :name};
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 202 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
15.3.3 SQLJ und der Übersetzungprozess
SQLJ Quelleprogramm: Java-Programm mit eingebettetem SQL
SQLJ – Übersetzer (auch Präprozessor oder Vorübersetzer genannt)
Java-Programm mit JDBC-Aufrufen
Java-Compiler
Java-Byte-Code mit JDBC-Methodenaufrufen
JDBC-Teiber
DB
Bild: Übersetzung eines SQLJ-Programms
15.3.4 Persistente Java Objekte: JDO Java Data Objects
22
23
Mit der Java Data Objects (JDO) API und Enterprise Java Beans (EJB) ist es möglich, Daten aus einem
Datenspeicher mit Java Objekten zu verknüpfen und Java Objekte dauerhaft (persistent) in einem Datenspeicher
aufzubewahren. Im Falle von JDBC können beispielsweise ganze Reihen oder Spalten von Datenbanken, die mit
JDBC ausgelesen wurden, als Objekte gespeichert werden. Mit JDO ist es ausserdem möglich, auch Datenbanken
anzusprechen, die nicht auf SQL basieren. Dadurch kann der Java Code unabhängig von der
Datenbanksprache geschrieben werden. JDO kann also mit JDBC relationelle Datenbanken erreichen. Mit der
eigenen Abfragesprache JDOQL kann JDO bei relationellen Datenbanken diese Befehle direkt mithilfe von JDBC in
SQL Abfragen umwandeln. JDO lässt generelle Java Objekte fortbestehen, wohingegen EJB Entity-Beans verwendet,
um Abfragen und dergleichen zu speichern.
15.4 Architektur von JDBC
Eine Java-Anwendung oder ein Java-Applet lädt im ersten Schritt mit Hilfe eines Treibermanagers einen JDBCTreiber.
Über diesen wird dann eine Verbindung zu einer Datenbank hergestellt.
22
23
Siehe http://java.sun.com/products/jdo
Siehe http://java.sun.com/products/ejb
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 203 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Je nach dem Typ des verwendeten JDBC-Treibers können zur Kommunikation mit der Datenbank noch weitere
Komponenten benötigt werden, z.B. ein ODBC-Treiber.
Diese JDBC-Treiberkombination kommuniziert (u.U. über das verwendete Netzwerk) mit dem Datenbankserver
(DBMS)
Der DBMS-Server reguliert den eigentlichen Zugriff auf die Datenbank.Dabei kann ein Datenbankserver auch
mehrere Datenbanken verwalten.
15.5 Treibertypen
Eine zentrale Rolle spielen die Datenbanktreiber. Auch wenn nicht direkt mit JDBC programmiert wird, sondern
SQLJ oder eine zugekaufte Zurgriffsschicht oder ein EJB-Applikationsserver verwendet wird, ist immer eine
JDBC-Treiber erforderlich: den auch SQLJ, die Zugriffschichten oder EJB-Applikationsserver setzen irgendwann auf
der JDBC-Schnittstelle auf. Die Tabelle unten beschreibt die verschiedenen Treibertypen.
Die Treibertypen im Überblick:
1. Typ 1: JDBC / ODBC Brücke
2. Typ 2: Native API Treiber
3. Typ 3: JDBC Middelware Treiber
4. Typ 4: Native Protocol Treiber
Treibertyp
Class-1-Teiber
JDBC-ODBC-Bridge
Eigenschaften
24
Über die JDBC-ODBC-Brücke werden Datenbankverbindungen über die ODBC Schnittstelle aufgebaut.
Dazu werden die Anweisungen von JDBC an die betreffende Funktion des ODBCTreibers weitergegeben, der im nativen Code des Betriebssystems auf dem Client
vorliegt und die eigentliche Verbindung zur Datenbank oder dem Datenbankserver
herstellt. Der ODBC-Treiber nutzt das CLI des Herstellers.
D.h. die nativen (ODBC-) Bibliotheken des Datenbanksystems für den
Datenbankzugriff müssen auf dem Cient installiert werde.
24
ODBC Open Database Connectivity wurde ursprünglich von Microsoft auf Basis des Call Level Interface von
X/Open und ISO/IEC entwickelt, ist aber inzwischen auch von anderen Softwareherstellern übernommen worden. Mit
ODBC kann auf jede lokale oder ferne Datenquelle zugegriffen werden.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 204 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Die Bridge ist nur einmal vorhanden. Für jedes Datenbanksystem ist jedoch ein ODBCTreiber erforderlich. ODBC-Treiber sind für die meisten Datenbanksysteme verfügbar
und werden hauptsächlich beim Zugriff auf Datenressourcen unter Windows eingesetzt.
Nachteilig ist die zusätzliche Installation und Konfiguration des ODBC-Treibers, die
auf dem Client erfolgen muss. Applets können diese Schnittstelle nur begrenzt
nutzen (über das Security API/SecurityManager), da sie standardmäßig keinen
Zugriff auf die lokalen Ressourcen des Clients haben.
Class-2-Treiber
JDBC-Native-API-partialJava-Treiber (CLI des
Herstellers)
Class-3-Treiber
JDBC-net-pure-JavaTreiber
Dieser Treibertyp ist sinnvoll für den Test, nicht aber für den operativen Einsatz.
JDBC-Aufrufe werden hier auf native Anweisungen des Datenbank-Clients abgebildet,
der die Kommunikation zum Datenbankserver herstellt.
Dazu müssen zusätzlich die nativen Bibliotheken des Datenbanksystems für
den Datenbankzugriff auf dem Client installiert werden.
Der Zugriff ist hier sehr schnell. Der JDBC-Treiber verwendet jedoch nativen Code (den
des DBMS-Client-Treibers) und ist damit selbst nicht plattformunabhängig.
Hierbei handelt es sich um einen plattformunabhängigen Java-Treiber. Die
Anweisungen werden von Java über das verwendete Netzwerkprotokoll
(datenbankunabhängig) an einen auf dem Datenbankserver vorhandenen JDBCServer übergeben und von diesem in Datenbankanweisungen übersetzt.
Der JDBC-Treiber enthält zusätzlich die Client-Funktionalität des DBMS. Es sind
keine zusätzlichen nativen Bibliotheken auf dem Client erforderlich, sodass er
einfach gewartet werden kann
Class-4-Treiber
Pure-JavaProtokolltreiber
Im Falle eines Applets wird der Datenbanktreiber gegebenenfalls erst auf den Client
übertragen.
In dieser Treibervariante erfolgt eine direkte Kommunikation (datenbankherstellerspezifisches Protokoll) des JDBC-Treibers mit dem DBMS. Dabei wird auf dem
DBMS die gleiche Struktur wie bei Class-1- und Class-2-Treibern verwendet.
Es sind keine zusätzlichen nativen Bibliotheken auf dem Client zu installieren. Wg.
Java-Implementierung -> plattformunabhängig.
Eine Liste der momentan verfügbaren 221 JDBC-Treiber findet sich auf der folgenden Web-Site
http://developers.sun.com/product/jdbc/drivers (ggf. nach „jdbc driver“ googlen). Stand: 19.06.2010; nach der
Übernahme durch Oracle wurde die Liste registrierter Treiben nicht mehr aktualisiert...(prüfen Sie das!)
Die meisten Datenbankhersteller bieten entweder einen Typ-3- oder Typ-4-Treiber mit ihren Datenbanken an.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 205 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Hier ein Überblick
JDBC/ODBC-Bridge
eigene Serverschicht
CLI des Herstellers
15.6 Die Datenbank
MySQL / Die DBMS Cloudscape / PointBase
Empfehlung: in MySQL einarbeiten! Wenn Zeit/Muse/Intersse ggf. in Cloudscape/PointBase
hineinschnuppern. Cloudscape/PointBase sind Datenbanken, die von Sun mit dem SUN-JEEServer zur Verfügung gestellt werden/wurden. Die Beispiele in der Sun-Doku beziehen sich
(teilweise) darauf.
Download und Installation
Sonstige Hinweise
è Siehe Literatur und Internet
15.7 Aufbau einer Datenbankverbindung
Der Verbindungsaufbau zu einer Datenbank ist der erste Schritt in einer JDBC Datenbank
Anwendung. Dies geschieht üblicherweise mit der Klasse DriverManager (Siehe API-Doku:
„The basic service for managing a set of JDBC drivers“). Es sind drei Schritte nötig, um mit
DriverManager eine Verbindung zu einer Datenquelle herzustellen:
Das Downloaden und Installieren des Treibers
Im Programm: das dynamische Laden des Treibers zur Laufzeit und
Im Programm: der eigentliche Verbindungsaufbau.
•
Im Folgenden benutzen wir den Treiber Connector/J von MySQL.
•
Dieser Treiber ist ein Typ-4-Treiber. Als reine Java-Implementierung des MySQLProtokolls hängt der Treiber nicht ab von den MySQL-Client-Libraries und ist als
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 206 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Java-Implementierung auch plattformunabhängig. Eine eigene Serverschicht ist auf
Seiten des MySQL-Servers auch nicht notwendig.
•
Die zur Zeit aktuelle Version ist unter
http://dev.mysql.com/downloads/connector/j/5.1.html
zu finden.
•
Als Java-Implementierung ist der Treiber sowohl für MAC, Linux und auch für
Windows nutzbar (eben plattformübergreifend). Geliefert wird ein zip-File oder ein
tar.gz-File. In beiden Fällen sind die Sources und die Binaries enthalten. Nach dem
Entpacken findet man den Treiber unter dem Namen mysql-connector-java-5.1.16bin.jar
•
Der Treiber muss im CLASSPATH des nutzenden Programmes liegen und auch zur
Laufzeit verfügbar sein. Zur Laufzeit wird der Treiber wie folgt geladen:
Class.forName(“com.mysql.jdbc.Driver“); //Lädt die Treiber-Klassen-Lib
•
Für den Test unter Eclipse kopieren Sie den DB-Treiber mysql-connector-java-5.1.7bin.jar mit in das Eclipse-Verzeichnis „src“.
Andere potenzielle Ablageorte für den DB-Treiber
•
•
Für lokale Java-Applikationen: Das dynamische Laden eines Treibers zeigt JDBC, zu welcher Datenbank oder
Datenquelle es sich verbinden soll: Treiber also in die JRE-Lib (z.B. C:\Program Files\Java\jre6\lib\ext) und in die JDK25
Lib (z.B. C:\Program Files\Java\jdk1.6.0_05\lib) kopieren.
•
Besser ist es allerdings jar-Files „lokal“ zu lassen und über die Compiler-Optionen –classpath (durch „:“ separiert)
oder über Tool-/IDE-Optionen einzubinden. Oder:
•
Den kompletten Pfad inclusive jar-File in die Umgebungsvariable CLASSPATH aufnehmen. (Eclipse: „Project“ ->
„Properties“ -> „Build Path“ -> „Libraries“ -> „Add External Libraries“ -> zum jar-File navigieren -> aufnehmen.)
Für Tomcat-Programme: (Servlets/JSP) wird der
$CATALINA_HOME/common/lib (Tomcat 5) hinterlegt.
Treiber
unter
$CATALINA_HOME/lib
(Tomcat
6)
bzw.
Der Pfad des Connector/J Treibers in der JAR-Lib lautet „com.mysql.jdbc.Driver“.
Jeder Treiber für eine Datenbank hat einen eigenen Pfad, mit dem dieser aufgerufen
werden muss. Eine Instanziierung des Treibers oder eine direkte Verknüpfung oder
Registrierung mit der Klasse DriverManager ist nicht nötig, da dies dies automatisch
geschieht. Nach der obigen Zeile Programmcode ist der Treiber einsatzbereit und eine
Verbindung kann hergestellt werden.
•
Der Verbindungsaufbau geschieht genauso einfach wie Schritt eins:
Connection con = DriverManager.getConnection(String url,
String username,
String passwort);
Die Methode getConnection() liefert eine ein Connection Objekt zurück, mit
welchem man später SQL Befehle ausführen kann. Die URL (englisch: Uniform
Resource Locators) ist ein String, der zur Bestimmung der Datenbank dient. Eine typische
URL sieht folgendermaßen aus:
String url = “jdbc:mysql:promod2“;
25
Auspacken mit dem Linux-Kommando tar –zxvf <myfile>.tar.gz
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 207 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Dabei wird auf eine lokale MySQL Datenbank mit dem Namen „promod2“ zugegriffen.
Will man eine Verbindung zu einer externen Datenbank herstellen, so sähe die URL
aus:
String url = “jdbc:mysql://host:port/promod2“;
‚host’ und ‚port’ müssen dabei ersetzt werden. Der Benutzername und Passwort in der
getConnection() Methode sind selbst erklärend.
Es gibt noch zwei weitere Varianten dieser Methode:
DriverManager.getConnection(String url);
Hierbei wird kein Benutzername und Passwort benötigt, wenn man eine Verbindung zu
einer Datenbank aufbaut.
DriverManager.getConnection(String url, Properties info);
‚Properties’ ist eine Erweiterung von java.util.HashTable, welche eine Reihe von
Einstellungen für die Datenbankverbindung enthalten. In der unteren Tabelle werden
einige der möglichen Werte26 vorgestellt.
Name
Username
Passwort
autoReconnect
useSSL
requireSSL
allowMultiQuerries
Beschreibung
Der Benutzername für die Datenbank
Das zugehörige Passwort für den
Benutzernamen
Soll die Verbindung wiederhergestellt
werden, falls sie getrennt wurde?
Soll eine sichere Verbindung mit SSL
verwendet werden?
Soll eine SSL gesicherte Verbindung
vorausgesetzt sein, falls useSSL = true?
Erlaubt das Versenden mehrerer SQLBefehle in einem Befehlsobjekt, die durch ‚;’
getrennt werden.
Defaultwert
false
false
false
false
Tabelle 1 Mögliche Einträge in Properties
Beim Verbindungsaufbau können natürlich auch Fehler auftreten. Diese werden durch die
Ausnahmebehandlung (englisch: exception handling)‚ SQLException abgefangen‚ SQLException
wird bei allen Fehlern ausgelöst, die im Zusammenhang mit JDBC und SQL stehen.
Mit dem DriverManager können mehrere Connections zu Datenbanken betrieben werden.
Innerhalb einer Connection können mehrere Statements zur (sukzessiven) Ausführung einer oder
mehrerer SQL-Anweisungen verwendet werden. Die Ergebnisse solcher SQL-Anweisungen
werden als Instanzen der Klasse ResultSet zurückgegeben.
26
Eine komplette Liste findet sich unter http://dev.mysql.com/doc/connector/j/en/cj-configuration-properties.html
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 208 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Am Ende einer Datenbankanwendung sollte immer die Verbindung wieder geschlossen
werden, damit keine offenen Verbindungen bei der Datenbank zurückbleiben. Dies geschieht mit
einer einfachen close-Methode:
con.close();
Zusammenfassung Aufbau einer Datenbankverbindung:
Zur Herstellung einer Datenbankverbindung über JDBC sind folgende Schritte zu erledigen:
1. Installation eines Datenbankservers (lokal oder im LAN)
2. Installation eines JDBC-Treibers
3. Laden eines Treibermanagers mit dem ensprechenden JDBC-Treiber
4. Herstellen einer Verbindung zur Datenbank
5. SQL-Anweisungen an die Datenbank richten
o … Zum Bearbeiten der Struktur der Datenbank (Tabellen anlegen,…)
o … Zum Abfragen / Selektieren von Daten. Es wird eine Ergebnismenge geliefert.
o … Zum Bearbeiten von Daten (ändern, löschen, einfügen). Es wird keine Ereignismenge
geliefert.
6. Gegebenenfalls von der SQL-Anweisung zurückgegebene Ergebnismenge bearbeiten.
Treiber laden
DriverManager
Connection
PreparedStatement
Statement
CallableStatement
ResultSet
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 209 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Die Abbildung visualisiert das Zusammenspiel der einzelnen Interfaces und Klassen. Der JDBC-Treiber wird
unabhäng als Klasse geladen. Wenn über den Treibermanager eine Datenbankverbindung hergestellt wird (Interface
Connection), wird auf den bereits geladenen Treiber zurückgegriffen.
Über eine Datenbankverbindung werden Anweisungen an diese gesendet (Interfaces CallableStatement,
PreparedStatement, Statement). Zwei dieser Anweisungen liefern eine Ergebnismenge (Interface
Result) zurück.
15.8 SQL-Anweisungen ausführen
Nachdem eine Verbindung zu einer Datenbank hergestellt wurde, können nun SQL Anweisungen
(englisch: statements) verschickt werden. Diese können auf verschiedene Arten erstellt werden. In
den folgenden drei Unterkapiteln werden diese jeweils gesondert behandelt.
15.9 Die Klasse Statement
Dies ist die Standard Klasse für SQL Anweisungen. Ein solches Statement wird von dem
Verbindungsobjekt erstellt:
Statement st = con.createStatement();
Es gibt drei verschiedene Methoden, um ein Statement Objekt zu kreieren.
Methodenname
createStatement()
createStatement(int resultSetType,
int resultSetConcurrency)
createStatement(int resultSetType,
int resultSetConcurrency,
int resultSetHoldability)
Beschreibung
Die Standardmethode ohne jegliche
Parameter
Die Integerparameter sind für das
ResultSet Objekt, welches die
Ergebnisse eines Queries liefert
(siehe „Ereignisse anzeigen“).
Drei Parameter bestimmen die
Einstellungen für das
Rückgabeobjekt. Siehe Kapitel
15.12.2 ResultSet
Tabelle 2 Statement Objekt erzeugen
Mit diesem Objekt ‚st’ lassen sich nun Anweisungen abschicken. Es ist nicht nötig, bei jedem SQL
Befehl ein neues Objekt zu kreieren. Die Anweisungen werden mit ‚execute’ Methoden an die
Datenbank gesendet. Drei Methoden-Typen werden unterschieden:
Erstens:
boolean execute(String sql);
‚sql’ ist die SQL Anweisung, die an die Datenbank geschickt werden soll und mehrere
Rückgabeobjekte in Form von ResultSet entstehen lassen kann. Als Rückgabewert liefert
die Methode ein boolean zurück, welches ‚true’ ist, wenn das erste Rückgabeobjekt vom
Typ ResultSet ist.
Zweitens:
int executeUpdate(String sql);
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 210 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Im Gegensatz zur ersten Methode werden hier nur bestimmte SQL Befehle ausgeführt.
‚INSERT’, ‚UPDATE’, ‚DELETE’ oder eine SQL Anweisung, die keinen Rückgabewert
besitzt, können hier ausgeführt werden. Trotzdem liefert diese Methode etwas zurück. Bei
‚INSERT’, ‚UPDATE’ oder ‚DELETE’ Befehlen wird Reihenanzahl angegeben oder der
Wert ist 0, wenn eine andere Anweisung ausgeführt wurde.
Drittens:
ResultSet executeQuery(String sql);
Typischerweise werden ‚SELECT’ Anweisungen mit dieser Methode verschickt. Das
Rückgabeobjekt ist vom Typ ResultSet, welches in einem späteren Kapitel näher erläutert
wird.
Beispielanwendung:
Hat man die Datenbank ‚Test’ und man möchte die Tabelle ‚Obst’ anlegen, so kann man dies
folgendermaßen machen. Eine Verbindung zu der Datenbank besteht bereits.
Statement stmt = con.createStatement();
stmt.executeUpdate(“CREATE TABLE OBST (NAME VARCHAR(32),
PREIS FLOAT)“);
Die Tabelle ‚Obst’ hat zwei Attribute, Name und Preis. Name ist vom Type VARCHAR und kann
32 Zeichen lang sein. Preis hingegen ist ein Fliesskommawert. Natürlich lassen sich die Befehle
vorher in einer String-Variablen speichern und dann in das Statement Objekt einzufügen:
String createObstTable = “CREATE TABLE OBST (NAME
VARCHAR(32), PREIS FLOAT)”;
stmt.executeUpdate(createObstTable);
Um die Datenbank zu füllen, verwendet man die folgenden Befehl:
stmt.executeUpdate(“INSERT INTO OBST VALUES (’Banane’, 2.50)”);
stmt.executeUpdate(“INSERT INTO OBST VALUES (’Apfel’, 1.00)”);
stmt.executeUpdate(“INSERT INTO OBST VALUES (’Birne’, 2.00)”);
Wie man bisher sehen kann, sind die Befehle aufgebaut wie „normale“ SQL Anweisungen für eine
Datenbank.
In einem letzten Schritt kann man sich die Ergebnisse noch anzeigen lassen. Das wird so
realisiert:
String getDataFromTable = “SELECT * FROM OBST“;
System.out.println(stmt.executeQuery(getDataFromTable));
Das Ergebnis hierzu sähe auf der Konsole wie folgt aus:
NAME
----------Apfel
Banane
PREIS
---------1.00
2.50
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 211 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Birne
2.00
Falls keine spezielle Sortierung angegeben wurde, wird nach der ersten Spalte sortiert.
15.10 Klasse PreparedStatement27
PreparedStatements sind abgeleitet vom Interface Statement. Sie werden oftmals dann
eingesetzt, wenn ein Statement Objekt mehrmals verwendet werden soll. Wie der Name es
vermuten lässt, werden die Statements vorpräpariert, das heißt vorkompiliert. SQL Anweisungen, die Variablen verwenden, sind das bevorzugte Einsatzgebiet. Es können natürlich
auch SQL Befehle ohne Variablen benutzt werden. Der große Vorteil von einem PreparedStatement kommt erst dann zur Geltung, wenn der gleiche SQL Befehl mit unterschiedlichen
Werten ausgeführt wird. Ein PreparedStatemsent Objekt kann auf mehrere Arten erzeugt werden.
Dabei ist auch wieder ein Verbindungsobjekt vonnöten.
Methodenname
prepareStatement(String sql)
prepareStatement(String sql,
int autoGeneratedKeys)
prepareStatement(String sql,
int[] columnIndexes)
prepareStatement(String sql,
String[] columnNames)
prepareStatement(String sql,
int resultSetType,
Beschreibung
‚sql’ ist der SQL Befehle, der ein oder mehrere
‚?’ als Parameter enthalten kann.
Sollen automatisch generierte Schlüssel
zurückgegeben werden oder nicht:
Statement.RETURN_GENERATED_KEYS = ja
Statement.NO_GENERATED_KEYS = nein
Werden Reihen hinzugefügt, bestimmt das Array
‚columnIndex’, welche Spalten zurückgegeben
werden.
Das Prinzip der Methode ist dasselbe wie bei der
vorherigen, nur dass die Spaltennamen als
Strings übergeben werden
Beide Integervariablen setzen Parameter des
zurückgegebenen ResultSet (siehe Kapitel
15.12.2) Objektes.
int resultSetConcurrency)
prepareStatement(String sql,
int resultSetType,
Wie bei der obigen Methode werden
Einstellungen für das Rückgabeobjekt getätigt.
int resultSetConcurrency,
int resultSetHoldability)
Tabelle 3 PreparedStatement Objekt erzeugen
Ein typisches PreparedStatement kann folgendermaßen aussehen. Es wird dabei das obige
Beispiel weitergeführt:
PreparedStatement p_st = con.prepareStatement(“INSERT INTO
OBST VALUES(?, ?)“);
Dabei sind die ‚?’ Parameter, die beliebig gesetzt werden können. In den Beispielen für Statement
könnte man so schreiben:
27
Das Statement wird auf Programmiersprach-Ebene vorbereitet und gespeichert.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 212 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
p_st.setString(1,“Banane“);
p_st.setFloat(2, 2.50);
p_st.executeUpdate();
Die ‚set’-Methoden gibt es für alle Arten von Parametertypen28. Es werden immer zwei Parameter
übergeben. Der erste sagt aus, welches ‚?’ ersetzt werden soll. Der zweite beinhaltet den zu
ersetzenden Wert. Schließlich wird der Befehl mit einer ‚execute’-Methode ausgeführt.
15.11 Klasse CallableStatement / StoredProcedure29
Die dritte Möglichkeit in JDBC, einen SQL Befehl an eine Datenbank zu geben, ist das
CallableStatement. Mit diesem kann man (in der DB) vorgespeicherte Prozeduren und
Anweisungen ausführen. Solche vorgefertigten Skripte können eine Reihe von SQL Befehlen
enthalten, die dann in der Datenbank gespeichert werden, um sie jederzeit aufrufen zu können.
Eine vorgefertigte Prozedur kann man auch mit JDBC erstellen:
String storedProcedure = “CREATE PROCEDURE ZEIGE_OBST AS
“+“ SELECT * FROM OBST WHERE
PRICE > 1.50“;
Statement st = con.createStatement();
st.executeUpdate(storedProcedure);
Dieser SQL Befehl ist nun in der Datenbank unter dem Namen „ZEIGE_OBST“ gespeichert.
Aufgerufen wird dieser mit einem CallableStatement:
CallableStatement c_st = con.prepareCall(“{ ZEIGE_OBST }“);
ResultSet rs = c_st.executeUpdate();
Der Treiber übersetzt diese Syntax in ein für die Datenbank verständliches SQL, welche dann den
die gespeicherte Prozedur ausführt. Das Ergebnis dieser vorgefertigten Prozedur wird in unserem
Beispiel wie folgt aussehen:
NAME
----------Banane
Birne
PREIS
---------2.50
2.00
15.12 Auswertung der Ergebnismengen
Bei jeder SQL Anweisung gibt es Rückgabewerte. Es ist egal, ob man einen Befehl
wie UPDATE oder INSERT, die nur Daten in der Datenbank verändern und nicht abrufen
oder
ein SELECT Statement ausführt, welches eine ganze Reihe von Rückgabewerten
beinhalten kann.
In den nächsten Unterkapiteln werden die verschiedenen Arten von Rückgabewerten gesondert
behandelt.
28
Nachzulesen in http://java.sun.com/j2se/1.5.0/docs/api/java/sql/PreparedStatement.html
Im Gegensatz zum Prepared Statement wird die Stored Procedure in der DB gespeichert – nicht auf Ebene der
Programmiersprache.
29
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 213 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
15.12.1 Boolean und Integer als Rückgabewerte
Ein boolean oder ein Integer werden von Methoden zurückgegeben, wenn entweder die Methode
execute() oder executeUpdate() ausgeführt wurde. Typisch dafür sind die drei Anweisungen
UPDATE, INSERT oder DELETE.
Bei solchen Statements ist es nur wichtig zu wissen, ob der Befehl erfolgreich durchgeführt wurde
oder fehlgeschlagen ist. Ist er fehlgeschlagen, wird eine SQLException ausgelöst. Bei einem
erfolgreichen Durchführen wird je nach Methode ein bool`scher Wert oder ein Integer
zurückgegeben.
Bei jeder der drei Möglichkeiten Statement, PreparedStatement und CallableStatement gibt es
Methoden, die nur einen solchen Rückgabewert besitzen:
boolean execute();
int executeUpdate();
Dies sind zwei solche Methoden. Bei der oberen wird ein ‚true’ zurückgegeben, falls der SQL
Befehl ein RowSet Objekt zurückgibt. Bei einer Update Methode wird die Anzahl der Reihen als
Rückgabewert gesetzt. Löscht man mit einem DELETE Statement drei Reihen, so erhält man von
der Methode ebenfalls den Wert drei.
15.12.2 ResultSet
Ein Objekt vom Interface ResultSet wird üblicherweise bei SQL Anweisungen erstellt, die eine
Datenbank Abfrage sind. Der SELECT Befehl ist die wohl am häufigsten verwendete Anweisung.
Ein ResultSet kann auf verschiedene Weise konfiguriert werden. In der Standardeinstellung kann
das ResultSet reihenweise ausgelesen werden, beginnend mit der ersten Reihe.
Mit der Erstellung eines Statements, egal welcher Art, kann das ResultSet gestaltet werden. In der
folgenden Tabelle werden die einzelnen Einstellungsmöglichkeiten beschrieben. Als
Wiederholung wird eine Methode nun aufgezeigt, welche die verschiedenen Parameter für das
ResultSet beinhaltet:
Statement createStatement(int resultSetType,
int resultSetConcurrency,
int resultSetHoldability);
Diese drei Übergabewerte können mit verschiedenen Konstanten aus dem Interface ResultSet
besetzt werden:
Parametertyp
resultSetType
Konstante in ResultSet
ResultSet.TYPE_FORWARD_ONLY
ResultSet.TYPE_SCROLL_INSENSITIVE
Beschreibung
Das ResultSet darf nur vorwärts
gelesen werden.
Während ein ResultSet Objekt
geöffnet ist, reagiert es nicht auf
Veränderungen, die an diesem
vollzogen werden.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 214 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
ResultSet.TYPE_SCROLL_SENSITIVE
resultSetConcurrency
resultSetHoldability
Während ein ResultSet Objekt
geöffnet ist, reagiert es auf
Veränderungen.
ResultSet.CONCUR_READ_ONLY
Das ResultSet darf nicht
verändert werden.
ResultSet.CONCUR_UPDATABLE
Das ResultSet darf verändert
werden.
ResultSet.HOLD_CURSORS_OVER_COMM Das ResultSet Objekt wird nicht
IT
bei einem Commit-Befehl
geschlossen, sondern ist für
weitere Anweisungen verfügbar.
ResultSet.CLOSE_CURSORS_AT_COMMIT Das ResultSet wird bei einem
Commit-Befehl geschlossen.
Tabelle 4: ResultSet Konstanten
Nun gibt es mehrere Möglichkeiten, sich die Ergebnisse mit einem ResultSet anzeigen zu lassen.
Will man den kompletten Rückgabewert auf einen Schlag, so kann man einfach das Objekt als
Ganzes ausgeben. Beispielsweise könnte dies so aussehen:
ResultSet rs = stmt.executeQuery(“SELECT Name,
Preis FROM Obst“);
System.out.println(rs);
Das Ergebnis zeigt alle Daten der Tabelle Obst an:
NAME
----------Apfel
Banane
Birne
PREIS
---------1.00
2.50
2.00
Es ist aber auch möglich, die Werte einzeln abzurufen. Statt der Ausgabe des gesamten
Objektes auf einmal, kann man jede Reihe einzelnen auswählen:
while (rs.next()) {
String name = rs.getString("Name");
float preis = rs.getFloat("Preis");
System.out.println("Ein(e) " + name + " kostet " + preis);
}
Die while-Schleife wird solange durchlaufen, wie es Reihen in dem ResultSet Objekt gibt. Das
Ergebnis sieht wiederum ähnlich zu dem oberen aus:
Ein(e) Apfel kostet 1.00
Ein(e) Banane kostet 2.50
Ein(e) Birne kostet 2.00
Ähnlich wie bei PreparedStatement gibt es eine Reihe von Datentypen, die aufgerufen werden
können. Eine aktuelle Liste dieser ‚getter’-Methoden gibt es in der Java JDK API30. In einem
ResultSet kann man auch zu einer bestimmten Reihe springen. Es gibt verschiedene Methoden
dafür, die in der nächsten Tabelle vorgestellt werden.
30
siehe http://java.sun.com/j2se/1.5.0/docs/api/java/sql/ResultSet.html
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 215 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Methode
boolean absolute(int row);
Beschreibung
boolean first();
Springt zu der angegebenen Reihe. Ist diese
außerhalb des Objektes, wird ‚false’ zurückgegeben.
Damit kann man Reihen vor- und zurückgehen.
Auch hier wird ‚false’ zurückgegeben, falls die Reihe
nicht in dem Objekt liegt.
Springt eine Reihe zurück. Der Rückgabewert ist
‚true
, wenn die Reihe noch innerhalb des Objektes liegt.
Springt eine Reihe vor. Die Art des
Rückgabewertes ist dieselbe wie in der vorherigen
Methode.
Der Zeiger geht zurück zur ersten Reihe.
boolean last();
Das Objekt ist nun in der letzten Reihe.
void afterLast();
Mit dieser Methode springt man zu der Stelle nach
der letzten Reihe.
Mit dieser Methode springt man zu der Stelle vor der
esten Reihe.
boolean relative(int row);
boolean previous();
boolean next();
void beforeFirst();
Tabelle 5 Methoden, um eine Reihe in einem ResultSet auszuwählen
Ein ResultSet Objekt lässt sich auch bearbeiten. Dafür gibt es eine Reihe von ‚update’-Methoden.
(Siehe API Beschreibung). Natürlich wird dabei nur der Wert innerhalb des Objektes geändert,
nicht aber der zugrundeliegende Wert in der Datenbank. Will man beispielsweise den Preis des
Apfels verändern, so kann man dies folgendermaßen machen:
while (rs.next()) {
String name = rs.getString("Name");
float preis = rs.getFloat("Preis");
if(name.equals("Apfel") {
System.out.println("Ein " + name + " kostet " + preis);
Rs.updateFloat(1,1.75);
System.out.println("Ein " + name + " kostet nun" + preis);
}
}
Das Ergebnis sieht wie folgt aus:
Ein Apfel kostet 1.00
Ein Apfel kostet nun 1.75
Die Möglichkeit, den Datenbankeintrag doch mit diesem neuen Wert zu versehen, besteht mit
dieser Methode:
void updateRow();
Ein vorzeitiges Schließen des ResultSet Objektes, um keine weiteren Daten in das Objekt
einfließen zu lassen, ist mit einer simplen close()-Methode möglich.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 216 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Siehe Literatur und zu folgenden Themen:
15.12.3 Konfigurieren der Ergebnismenge
15.12.4 Nachladen von Datensätzen konfigurieren
15.12.5 Navigieren in der Ergebnismenge
15.12.6 Werte der Ergebnismenge ermitteln
15.12.7 Update der Ergebnismenge
15.12.8 Nullwerte
<<<< end of adreli >>>>>
15.13 Auslesen von Metadaten
Für Werkzeuge, die mit beliebigen Datenbankstrukturen arbeiten, sind Metadaten unerlässlich.
Metadaten, auch Metainformationen genannt, sind Daten, die Datenbankstrukturen und deren
Eigenschaften beschreiben. Eine Metainformation ist beispeilsweise die Auskunft, welche
Tabellen sich in einer Datenbank befinden oder welche Attribute in einem ResultSet enthalten
sind.
JDBC stellt zwei Klassen bereit, mit denen Metadaten abgefragt werden können:
ResultSetMetaData liefert Informationen über ein bestimmtes ResultSet.
DataBaseMetaData liefert Informationen zu Datenbanken.
15.13.1 Datenbankinformationen ermitteln
15.13.2 Informationen über eine Ergebnismenge ermittlen
15.14 Transaktionen
15.14.1 Manuelles und automatischs Commit
15.14.2 Isolationsebenen
15.14.3 Dirty Read
15.14.4 Repeatabel Read
15.14.5 Phantom Read
15.15 JDBC Option Package
15.15.1 DataSource
Ein DataSource-Objekt repräsentiert eine Datenbank oder eine beliebige andere Datenquelle und
beinhaltet die wichtigsten Informationen darüber. Verschiedene Datenquellen können so im
gleichen Programm abstrakt gehandabt werden. Für den Client sehen diese Datenquellen gleich
aus.
Ein DataSource-Objekt wird in der Regel von einem separaten Programm erzeugt und beim
Naming-Service registriert. Clients können dann im Netzwerk auf diese Datenquelle über ihren
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 217 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
logischen Namen zugreifen. Es ist nicht mehr notwendig die genau URL der Datenbank zu
kennen.
15.15.2 Connection-Pooling
Der Aufbau von Datenbankverbindungen kostet Performance. Durch das Verwalten von
geöffneten Verbindungen in einem Pool können diese Kosten reduziert werden.
15.15.3 Verteilte Transaktionen
Das XA-Protokoll (ein X/Open Standard) wird verwendet, wenn mehrere Datenbanken oder
allgemeiner Datenquellen innerhalb einer Transaktion angesprochen werden sollen. Denkbar ist
beispielsweise die Anbindung eines Altsystems, das parallel weiter betrieben wird und damit auch
die Änderungen erhalten muss. Für die Implementierung verteilter Transaktionen ist die XASchnittstelle mit dem dem spezifizierten Zwei-Phasen-Commit notwendige Voraussetzung.
15.16 Datenbankzugriff über Applets
15.17 Debuggen von JDBC-Anwendungen
15.18 CaseStudy Aufgabe (Aufgabe 1 von 030_JDBC)
Wenn Sie mit Eclipse arbeiten und falls noch nicht geschehen, erstellen sie ein neues Eclipse
Projekt. Achten sie darauf, dass der JDBC Treiber für die MySQL Datenbank richtig in das Projekt
eingebunden ist. Erstellen sie eine neue Java Datei und folgen sie den Anweisungen:
1. Laden sie den Datenbanktreiber und erstellen sie eine Verbindung zur Datenbank
‚promod2’ her. Achten sie gegebenenfalls darauf, dass die Datenbank auf einem externen
Server liegen könnte und sich somit die URL ändern wird.
2. Sie sollen mithilfe eines Statement Objektes eine neue Tabelle ‚semester’ erstellen.
Folgende Attribute soll die Tabelle haben:
•
Die Matrikelnummer als INTEGER Typ
•
Vorname als VARCHAR Typ mit der Länge 32
•
Dasselbe für den Nachnamen
•
Die Semesterstufe als INTEGER Typ
•
Die Note als DOUBLE Typ
3. Überprüfen sie mit einer Ausgabe auf die Konsole, ob die Tabelle korrekt erstellt wurde.
Erklären sie, warum der Rückgabewert entweder false oder 0 sein muss.
4. Füllen sie die Tabelle mit Daten. Verwenden sie dazu einmal ein normales Statement und
für die restlichen PreparedStatements. Setzen sie dabei die Vorzüge eines
PreparedStatement ein. Diese Daten sollten in der Tabelle zu finden sein:
Matrikelnummer
111222
123456
987654
Vorname
Max
Philipp
Maria
Nachname
Muster
Mueller
Durchschnitt
Semester
4
5
4
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 218 / 229
Note
1.3
2.0
3.0
15.03.2016
PROMOD-2 / Ausgabe 2016
135797
Klara
Stein
4
4.7
5. Wenn sie die Methode executeUpdate() verwenden, zeigen sie den Unterschied zu Punkt 3
auf und erklären sie ihn.
6. Schließen sie die Verbindung korrekt.
15.19 CaseStudy Lösung
import
import
import
import
import
java.sql.Connection;
java.sql.DriverManager;
java.sql.PreparedStatement;
java.sql.SQLException;
java.sql.Statement;
public class Loesung_jdbc_1 {
public static void main(String[] args) {
try {
// Laden des Treibers
Class.forName("com.mysql.jdbc.Driver").newInstance();
} catch (Exception e) {
System.err.println("Treiber konnte nicht geladen werden: " + e);
}
try {
String url = "jdbc:mysql://127.0.0.1/promod2"; /////////////<<<<-------Connection con = DriverManager.getConnection(url, "benutzer","passwort");
Statement stmnt = con.createStatement();
String createTable =
+
+
+
"CREATE TABLE semester"
"(matrikelnummer INTEGER, vorname VARCHAR(32), "
"nachname VARCHAR(32), semester INTEGER, "
"note DOUBLE)";
if (stmnt.executeUpdate(createTable) == 0) {
System.out.println("Tabelle semester wurde erfolgreich erstellt."
+ " Der Rueckgabewert ist 0, weil ein CREATE "
+ "Statement nichts zurueckgibt.");
}
String insertData = "INSERT INTO semester(matrikelnummer, vorname, "
+ "nachname, semester, note) "
+ "VALUES(111222, 'Max', 'Muster', 4, 1.3)";
}
if(stmnt.executeUpdate(insertData) > 0 ){
System.out.println("Daten wurden korrekt in der DB gespeichert."
+ " Der Unterschied zum CREATE Statement liegt darin, "
+ "das 1 Zeile der Tabelle hinzugefuegt"
+ " und dieser Wert zurueckgegeben wurde.");
String preparedSt = "INSERT INTO semester(matrikelnummer, vorname, "
+ "nachname, semester, note) "
+ "VALUES(?, ?, ?, ?, ?)";
PreparedStatement p_stmnt = con.prepareStatement(preparedSt);
p_stmnt.setInt(1,123456);
p_stmnt.setString(2,"Philipp");
p_stmnt.setString(3,"Mueller");
p_stmnt.setInt(4,5);
p_stmnt.setDouble(5,2.0);
if(p_stmnt.executeUpdate()> 0){
System.out.println("Daten wurden korrekt in der DB gespeichert.");
}
p_stmnt.setInt(1,987654);
p_stmnt.setString(2,"Maria");
p_stmnt.setString(3,"Durchschnitt");
p_stmnt.setInt(4,4);
p_stmnt.setDouble(5,3.0);
if(p_stmnt.executeUpdate()> 0){
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 219 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
System.out.println("Daten wurden korrekt in der DB gespeichert.");
}
p_stmnt.setInt(1,135797);
p_stmnt.setString(2,"Klara");
p_stmnt.setString(3,"Stein");
p_stmnt.setInt(4,4);
p_stmnt.setDouble(5,4.7);
if(p_stmnt.executeUpdate()> 0){
System.out.println("Daten wurden korrekt in der DB gespeichert.");
}
con.close();
} catch (SQLException e) {
System.err.println("Ein Fehler ist aufgetreten: " + e);
}
}
} //end
15.20 Die Datenbank Derby
Seit Java 6 (Jahr 2005) ist die Datenbank „Derby“ Bestandteil von Java. Derby wurde
ursprünglich unter dem Namen Cloudscape von IBM entwickelt. Der Computerkonzern überließ
den Quellcode der Apache Foundation, die die Datenbank unter dem neuen Namen als OpenSource-Projekt weiterentwickelt.
WORKSHOP: Bearbeiten Sie die 2. Aufgabe der Case Studie 030_JDBC.
Nachdem die „Inbetriebnahme“ Ihrer JDBC-Lösung geklappt hat, entwickeln Sie
diese weiter.
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 220 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
1. Literatur (nehmen Sie jeweils die jüngste Ausgabe)
Downloads: jdk-docs; langspec-3.0 (Sie finden die Dokumente auf der Oracle / Sun Microsystem Site)
Pflichtlektüre (Kolloquium):
Goll/Weiß/Müller:
„Java als erste Programmiersprache – Vom Einsteiger zum Profi“,
Verlag Teubner, Stuttgart/Leipzig/Wiesbaden, [u.U. jüngere Auflage]
Empfohlene Lektüre (Kolloquium /Übungen):
Christian Ullenboom:
„Java ist auch eine Insel“,
, geb., mit CD, 49,90 Euro,
Galileo Computing, Bonn,
[u.U. jüngere Auflage]
Cay S. Horstmann, Gary Cornell
„Core Java. Band 1 – Grundlagen“
Markt+Technik Verlag, München,
Die englische Version ist “revised and updated vor Java
SE 6, 7” [u.U. jüngere Auflage]
Weitere empfohlene Literatur (Übungen/Projekte )
Verteilte Systeme:
J. Anton Illik
“Verteilte Systeme. Architekturen und Software-Technologien“
Expert Verlag, Renningen, 2007
ISBN 978-3-8169-2730-3
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 225 / 229
15.03.2016
PROMOD-2 / Ausgabe 2016
Cay S. Horstmann, Gary Cornell
„Core Java. Band 2– Expertenwissen“
Markt+Technik Verlag, München,
Die englische Version ist “revised and updated vor Java
SE 6, 7” ” [u.U. jüngere Auflage]
Weitere Projekt-Literatur
Balzert, Helmut: Lehrbuch der Softwaretechnik – Entwurf, Implementierung, Installation und
Betrieb
Spektrum Akademischer Verlag, Heidelberg, 2011 [u.U. jüngere Auflage]
Udo Wiegärtner, “Agile Softwareentwicklung mit SCRUM”, Video2Brain, 2013
-
siehe auch Empfehlung aus den Kolloquium –
*** Ende Skript ***
Prof. Illik _PROMOD2_illik_02a_Java_SE_part02_v13_160113_students2print.docx 226 / 229
15.03.2016
Herunterladen