Java: Kapitel 5 Die Java

Werbung
© Holger Röder
Java: Kapitel 5
Programmentwicklung
Winter 2009/2010
Die Java-Standardbibliotheken
se
Programmentwicklung WS 2009/2010
Holger Röder
[email protected]
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
Überblick über Kapitel 5
 
 
 
 
 
 
 
 
 
 
Mathematik
Strings
Die Klasse System
Datum und Zeit
Internationalisierung/Lokalisierung
  Datums- und Zahlenformatierung
  Formatierung à la printf
Eingabe und Ausgabe
  Character- und Byte-Streams, Wahlfreier Zugriff
Dateien und Verzeichnisse
Serialisierung
Threads
Datenstrukturen
  Collection und Map
  Sortierung
2
© Holger Röder
Programmentwicklung
Winter 2009/2010
Überblick über die Java SE
se
Quelle: http://java.sun.com/javase/6/docs/
3
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
Mathematik mit java.lang.Math
  Die Klasse Math enthält statische mathematische Funktionen, vor allem
zur Gleitkommarechnung:
  Winkelberechnung: sin(), cos(), tan() etc.
  Exponentialrechnung, Logarithmus: exp(), pow(), log(), log10
(), sqrt()
  Minimum, Maximum: min(), max()
  Runden, Abschneiden: ceil(), floor(), round(), abs()
double
double
double
double
double
a = -17.51;
x1 = Math.ceil(a);
x2 = Math.floor(a);
x3 = Math.round(a);
x4 = Math.abs(a);
// -17.0
// -18.0
// -18.0
// 17.51
4
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
Strings
  Die Klasse String repräsentiert Zeichenketten in Java. Sie bietet eine
Vielzahl von Methoden zur Manipulation und zur Ermittlung bestimmter
Eigenschaften von Zeichenketten.
  String-Zeichenketten sind – einmal festgelegt – unveränderlich: Länge
und Inhalt bleiben konstant.
String s = "Hallo Welt"; // -> String-Objekt erzeugt
s = s.substring(6); // "Welt" -> neues, zweites String-Objekt erzeugt
  Dynamische Zeichenketten werden von der Klasse StringBuilder
implementiert. Diese bietet Methoden zum
  Einfügen: append(), insert()
  Löschen: deleteCharAt(), delete() und
  Verändern: setCharAt(), replace()
von StringBuilder-Zeichenketten.
  Die Methoden erzeugen keine neuen Objekte, sondern manipulieren eine
einzige Instanz der Zeichenkette im Speicher.
5
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
String vs. StringBuilder
  Zum Beispiel bei der vielfachen Konkatenation von Zeichenketten
machen sich die Unterschiede zwischen String und StringBuilder
bemerkbar.
String s = new String();
Date t1 = new Date();
for (int i = 0; i < 20000; i++) {
s += 'x'; // String-Konkatenation
}
Date t2 = new Date();
System.out.println("Laufzeit String: "
+ (t2.getTime() - t1.getTime()) + "ms");
StringBuilder sb = new StringBuilder();
Date t3 = new Date();
for (int i = 0; i < 20000; i++) {
sb.append('x'); // StringBuilder-Konkatenation
}
Laufzeit String: 547ms
Date t4 = new Date();
Laufzeit StringBuilder: 0ms
System.out.println("Laufzeit StringBuilder: "
+ (t4.getTime() - t3.getTime()) + "ms");
6
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
String zerlegen
  Mit der Methode String.split() kann ein String in Teil-Strings zerlegt
werden.
  Die Trennzeichen sind frei wählbar und werden als regulärer Ausdruck
angegeben.
  Einfaches Beispiel: Komma-Trennung:
String laenderliste = "Frankreich,Schweiz,Deutschland,Österreich";
String[] laender = laenderliste.split(",");
Frankreich
for (String land: laender) {
Schweiz
System.out.println(land);
Deutschland
}
Österreich
  Beispiel mit regulärem Ausdruck: mehrere Trennzeichen
String laenderliste = "Frankreich,
Schweiz;Deutschland; Österreich";
String[] laender = laenderliste.split("(,|;) *");
Frankreich
...
Schweiz
Deutschland
Österreich
„Komma oder Semikolon, jeweils gefolgt
von beliebig vielen Leerzeichen.“
7
© Holger Röder
  Die Klasse System bietet einige nützliche statische Methoden, z. B. für
den Zugriff auf Properties (vom Laufzeitsystem zur Verfügung gestellte
Eigenschaften):
  System.getProperty("os.name"); // bspw. Windows XP
  Weitere Schlüssel: java.version, java.class.path,
os.version, user.name, user.home, file.separator, ...
  System.exit(int status) beendet das Programm mit dem
angegebenen Rückgabewert.
  System.gc() startet die Garbage Collection.
Programmentwicklung
Winter 2009/2010
Die Klasse System
se
8
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
Datum und Zeit
  Zur Behandlung von Datumswerten wird die abstrakte Klasse Calendar
bzw. die konkrete Klasse GregorianCalendar aus dem Paket
java.util verwendet.
  Die Klasse Date aus dem gleichen Paket sollte in den meisten Fällen
wegen fehlender Unterstützung für unterschiedliche Zeitzonen etc. nicht
mehr verwendet werden (deprecated).
  Nützliche „Ausnahme“: Die Methode getTime() liefert die Anzahl
Millisekunden seit dem 01.01.1970 00:00:00h GMT zurück.
Date t1 = new Date(); // vor Methodenaufruf
irgendeineSehrLangeDauerndeMethode();
Date t2 = new Date(); // nach Methodenaufruf
long laufzeit = t2.getTime() – t1.getTime();
/* "Laufzeit" in ms - Vorsicht: Garbage Collector, Multithreading ... */
9
© Holger Röder
Winter 2009/2010
Klasse GregorianCalendar
  Die Klasse GregorianCalendar implementiert den gregorianischen
Kalender. Sie bietet Methoden zur Erzeugung von Datumsobjekten und zur
Datumsarithmetik. Zeitzonen werden unterstützt.
  Überladenene Konstruktoren zur Erzeugung eines Datumsobjekts, z. B.
GregorianCalendar() // "jetzt"
GregorianCalendar(int jahr, int monat, int tag,
int stunde, int minute, int sekunde) // Datumsangabe
  Für Wochentage, Monate etc. sind Konstanten definiert:
GregorianCalendar.NOVEMBER, GregorianCalendar.SUNDAY
Programmentwicklung
GregorianCalendar d = new GregorianCalendar(2006, 4, 12); // 12.05.2006
se
String s1 = "Jahr: " + d.get(Calendar.YEAR);
String s2 = "Monat:" + d.get(Calendar.MONTH); // Achtung: 0 - 11
String s3 = "Tag: " + d.get(Calendar.DATE);
int wtag = d.get(Calendar.DAY_OF_WEEK); // So = 0, Mo = 1 ...
/* Abweichung von GMT */
int zeitunterschied = d.get(Calendar.ZONE_OFFSET)/(1000*60*60);
10
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
Datumsvergleiche und -arithmetik
  Die Methoden equals(), before() und after() erlauben den
Vergleich zweier Datumswerte:
GregorianCalendar d1 = new GregorianCalendar(2006, 4, 12); // 12. Mai
GregorianCalendar d2 = new GregorianCalendar(2006, 4, 14); // 14. Mai
if (d1.before(d2)) {
... // d1 liegt vor d2
}
  Über add() können Zeitspannen addiert oder subtrahiert werden. Die
Einheit der Zeitspanne wird über den ersten Parameter festgelegt (YEAR,
MONTH, WEEK, DATE etc.):
d1.add(Calendar.MONTH, 3);
// + 3 Monate -> 12. August 2006
d1.add(Calendar.DATE, -17); // - 17 Tage
-> 26. Juli 2006
11
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
Internationalisierung/Lokalisierung
  Die Standardbibliotheken bieten Unterstützung bei der
Internationalisierung von Java-Programmen, z. B.
  Zeitzonen
  Datums- und Zahlenformatierung
  Unterstützung länderspezifischer Zeichensätze bei Ein- und
Ausgabe
  lokalisierte Ressourcen (Beschriftungen, Meldungstexte etc.)
  Die Klasse java.util.Locale ist Ausgangspunkt für die
Internationalisierung. Ein Objekt dieser Klasse identifiziert eine Region der
Erde (und damit beispielsweise Sprache und Formatierungsregeln).
  Über die statische Methode getDefault() kann zur Laufzeit
ermittelt werden, in welchem Land das Programm läuft
  Vorgegebene Instanzen: Locale.GERMANY, Locale.US usw.
  Locale-Objekte können bei der Formatierung angegeben werden.
12
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
Datumsformatierung
  Die Formatierung eines Datums in „lesbarer“ und lokalisierter Form erfolgt
über die Klasse DateFormat bzw. SimpleDateFormat aus dem Paket
java.text.
Calendar jetzt = Calendar.getInstance();
String s1 = DateFormat.getTimeInstance().format(jetzt.getTime());
// s1 = 17:35:24
String s2 = DateFormat.getDateInstance().format(jetzt.getTime());
// s2 = 24.11.2006
String s3 = DateFormat.getDateTimeInstance().format(jetzt.getTime());
// s3 = 24.11.2006 17:35:24
String s4 = DateFormat.getDateTimeInstance(DateFormat.LONG,
DateFormat.LONG, Locale.US).format(jetzt.getTime());
// Internationalisiert (US): November 24, 2006 5:35:24 PM CET
SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd ** HH:mm");
String s5 = sd.format(jetzt.getTime());
// s5 = 2006-11-24 ** 17:35
13
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
Formatierung von Zahlen
  Neben Datumswerten können auch Zahlen (lokalisiert) formatiert werden.
Hierfür existieren die Klassen NumberFormat und DecimalFormat aus
dem Paket java.text.
DecimalFormat df1 = new DecimalFormat("0.00;(0.00)");
DecimalFormat df2 = new DecimalFormat("000E00");
String s1, s2, s3, s4, s5;
double x1 = 1234.567;
s1 = df1.format(x1); // 1234,57
s2 = df2.format(x1); // 123E01
double x2 = -.9876;
s3 = df1.format(x2); // (0,99)
s4 = df2.format(x2); // -988E-03
NumberFormat nf_US =
NumberFormat.getNumberInstance(Locale.US);
s5 = nf_US.format(x1); // US-Format: 1,234.567
  Der Formatstring kann aus vorgegebenen
Symbolen zusammengesetzt werden.
Sym.
Bedeutung
0
Einzelne Ziffer
#
Einzelne Ziffer (keine
führende Nullen)
.
Dezimaltrennzeichen
,
Tausendertrennzeichen
%
Prozentdarstellung
E
Exponentialdarstellung
14
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
Formatierung à la printf
  Seit Java 5 steht eine der aus C/C++ bekannten printf()-Funktion
ähnliche Möglichkeit zur formatierten Ausgabe von Text und Zahlen zur
Verfügung: die Klasse Formatter aus dem Paket java.util.
  Die Funktionalität dieser Klasse steht u. a. bequem über die format()Methoden in den Klassen String und PrintStream zur Verfügung.
for (int i = 1; i < 7; i++) {
System.out.format("Rechtsbündig [%04d] ", i*i*i*i);
System.out.format("Linksbündig [%-04d]%n", i*i*i*i);
}
Rechtsbündig
Rechtsbündig
Rechtsbündig
Rechtsbündig
Rechtsbündig
Rechtsbündig
[
1]
[ 16]
[ 81]
[ 256]
[ 625]
[1296]
Linksbündig
Linksbündig
Linksbündig
Linksbündig
Linksbündig
Linksbündig
[1
]
[16 ]
[81 ]
[256 ]
[625 ]
[1296]
Linksbündig (-)
Breite 4 (4)
Ganzzahl (d)
  Die sehr umfangreichen Formatierungsmöglichkeiten sind in der APIDokumentation beschrieben: http://java.sun.com/javase/6/docs/api/java/
util/Formatter.html
15
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
Ein- und Ausgabe
  Die sequentielle Ein- und Ausgabe wird in Java über sogenannte
Datenströme (Streams) realisiert. Dabei wird unterschieden zwischen
  Byte-Streams zur Ein- und Ausgabe von Bytes (8 Bit) und
  Character-Streams zur Ein- und Ausgabe von Unicode-Zeichen
(16 Bit), insbesondere zur Ein- und Ausgabe von „normalem“ Text.
  Für die verschiedenen Ein- und Ausgabemöglichkeiten (z. B. UnicodeZeichen in eine Datei schreiben etc.) stehen verschiedene konkrete
Implementierungen abstrakter Stream-Klassen zur Verfügung.
  Weiterhin ist auch wahlfreier Zugriff (random access) zur Ein- und Ausgabe
möglich.
  Die Ein- und Ausgabeklassen gehören zum Paket java.io.
16
se
Programmentwicklung
Winter 2009/2010
© Holger Röder
Character-Ausgabe: Writer
17
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
Beispiel: In Textdatei schreiben
Person belegschaft[] = new Person[3];
/* Person(name, alter, gehalt) */
belegschaft[0] = new Person("Alex", 37, 2800.0);
belegschaft[1] = new Person("Bastian", 22, 2400.0);
belegschaft[2] = new Person("Carlos", 41, 4350.0);
try {
PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(
"c:/pe/belegschaftDaten.txt")));
for (Person p : belegschaft) {
Ausgabe-Stream öffnen,
Datei ggf. neu erzeugen
out.println(p.getName() + "\t"
+ p.getAlter()
+ "\t"
+ p.getGehalt());
}
Ausgabe in Datei (\t = Tabulator)
out.close();
} catch (IOException e) {
Datei schließen
}
18
se
Programmentwicklung
Winter 2009/2010
© Holger Röder
Character-Eingabe: Reader
19
© Holger Röder
Winter 2009/2010
Beispiel: Aus Textdatei lesen
try {
BufferedReader in = new BufferedReader(
new FileReader("c:/pe/belegschaftDaten.txt"));
String zeile;
while ((zeile = in.readLine()) != null) {
String[] tokens = zeile.split("\t");
String name = tokens[0];
int alter = Integer.parseInt(tokens[1]);
double gehalt = Double.parseDouble(tokens[2]);
Datei für
Eingabe öffnen
Zeilen einlesen,
Tabulator als
Trennzeichen
Programmentwicklung
Person p = new Person(name, alter, gehalt);
p.out(); // Hilfsmethode d. Klasse Person
se
}
in.close();
} catch (FileNotFoundException e) {
} catch (IOException e) {
}
Objekte erzeugen,
Daten ausgeben
Person: Name = Alex, Alter = 37, Gehalt = 2800,0
Person: Name = Bastian, Alter = 22, Gehalt = 2400,0
Person: Name = Carlos, Alter = 41, Gehalt = 4350,0
20
se
Programmentwicklung
Winter 2009/2010
© Holger Röder
Byte-Ausgabe: OutputStream
21
se
Programmentwicklung
Winter 2009/2010
© Holger Röder
Byte-Eingabe: InputStream
22
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
Wahlfreier Zugriff (Random Access)
  Wahlfreier Zugriff ermöglicht das Lesen und Schreiben von/an einer
beliebigen Stelle in der Datei.
  Hierfür steht die Klasse RandomAccessFile zur Verfügung.
RandomAccessFile f1 = new RandomAccessFile("C:/pe/binaer.dat", "rw");
for (int i = 0; i < 20; i++)
Datei für
f1.write((byte) (Math.random() * 255));
Schreiben öffnen
f1.close();
RandomAccessFile f2 = new RandomAccessFile("C:/pe/binaer.dat", "r");
System.out.println("Dateigröße: " + f2.length() + " Bytes");
f2.seek(3);
Satzzeiger
int b = f2.read();
positionieren
System.out.println("Byte an Position 3 hat den Wert " + b);
f2.close();
  Binär-Dump der Datei:
0000000:
0000004:
0000008:
000000c:
0000010:
10100101
01010111
10110011
01001000
11110110
00000010
11111100
11001111
10111010
01001111
00100111
11011011
11110101
11111011
00011001
00001100
11110000
00100110
10000010
10000001
23
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
Serialisierung
  Wenn Objekte mit ihren Objektbeziehungen gespeichert werden sollen,
müssen diese zunächst in ein geeignetes Format konvertiert werden.
  Dieser Vorgang wird als Serialisierung bezeichnet. Im umgekehrten Fall
spricht man von Deserialisierung.
Objekte werden serialisiert
(d.h. „speichern sich“)
(komplexe)
Objekte mit
gegenseitigen
Beziehungen
z. B. Datei
Objekte werden deserialisiert (d.h.
werden instanziert und „laden sich“)
24
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
Serialisierung in Java
  Java bietet über die Klassen ObjectOutputStream und
ObjectInputStream sowie die Schnittstelle Serializable eine
einheitliche Möglichkeit zur Serialisierung und Deserialisierung von
Objekten.
  Die vorhandene generische Implementierung funktioniert für primitive
Datentypen, viele Klassen des JDK (bspw. Collection-Klassen, Arrays)
und für eigene (zusammengesetzte) Klassen.
  Die „richtige“ Instanziierung als Objekt der entsprechenden Klasse
erfolgt automatisch beim Laden.
  Attribute, die selbst Objekte sind, werden ebenfalls (transitiv)
serialisiert.
  Klassen, die (de-)serialisiert werden sollen, müssen die Schnittstelle
Serializable implementieren.
25
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
Versionskonflikte bei der Serialisierung
  Jede serialisierbare Klasse erhält eine interne „SerialisierungsVersionsnummer“ (serialVersionUID). Anhand dieser Versionsnummer
wird geprüft, ob ein serialisiertes Objekt mit der zur Laufzeit verfügbaren
(gleichnamigen) Klasse kompatibel ist.
  Die Versionsnummer sollte in der serialisierbaren Klasse explizit definiert
werden, um die Kompatibilität zu gewährleisten:
static final long serialVersionUID = 42L; // Long-Zahl
  Wird für eine Klasse keine Versionsnummer angegeben, wird diese von
der Java-Runtime dynamisch aus der Klassenstruktur berechnet. Schon
kleine Änderungen der Klasse führen zu unterschiedlichen
Versionsnummern und damit zu Inkompatibilitäten: Serialisierte Objekte
einer „früheren“ Version der Klasse können nicht mehr deserialisiert
werden.
26
© Holger Röder
Person belegschaft[] = new Person[3];
/* Person(name, alter, gehalt) */
belegschaft[0] = new Person("Alexander", 37, 2800.0);
belegschaft[1] = new Person("Bastian", 22, 2400.0);
belegschaft[2] = new Person("Carlos", 41, 4350.0);
try {
ObjectOutputStream out = new ObjectOutputStream(
new FileOutputStream("C:/pe/pers.ser"));
Stream für Serialisierung
out.writeObject(belegschaft);
in Datei öffnen
out.close();
} catch (FileNotFoundException e) {
} catch (IOException e) {
Komplexes Objekt
}
(Person-Array) serialisieren
Programmentwicklung
Winter 2009/2010
Objekte speichern
se
27
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
Objekte laden
try {
ObjectInputStream in = new ObjectInputStream(
new FileInputStream("C:/pe/pers.ser"));
Person[] personen = (Person[]) in.readObject();
Stream für Deserialisierung
aus Datei öffnen
for (Person p : personen) {
p.out();
}
Komplexes Objekt lesen;
Typecast notwendig, da readObject()
} catch (FileNotFoundException e) {
formal Object zurückliefert
} catch (IOException e) {
} catch (ClassNotFoundException e) {
}
Person: Name = Alex, Alter = 37, Gehalt = 2800.0
Person: Name = Bastian, Alter = 22, Gehalt = 2400.0
Person: Name = Carlos, Alter = 41, Gehalt = 4350.0
28
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
Spezielles Verhalten bei der (De-)
Serialisierung
  Falls bei der Serialisierung oder Deserialisierung zusätzliche
klassenspezifische Schritte notwendig sind, können die Methoden
readObject und writeObject selbst implementiert werden.
  Diese Methoden werden beim Serialisieren bzw. Deserialisieren von
Objekten dieser Klasse aufgerufen.
private void writeObject(ObjectOutputStream s)
Standard-Serialisierung aufrufen
throws IOException {
s.defaultWriteObject();
System.out.println("DEBUG: Objekt serialisiert: " + getName());
}
Zusätzliche Schritte bei
der Serialisierung
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException {
s.defaultReadObject();
System.out.println("DEBUG: Objekt deserialisiert: " + getName());
}
29
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
Dateien und Verzeichnisse
  Die Hilfsklasse File aus dem Paket java.io dient zur Verwaltung von
Dateien und Verzeichnissen.
  Ein File-Objekt kann eine Datei oder ein Verzeichnis repräsentieren.
  Wichtige Methoden:
  exists() – existiert die Datei/das Verzeichnis?
  isFile() und isDirectory()
  length() – liefert die Dateigröße (bei Verzeichnissen: 0)
  getName(), getPath(), getParent() – Namens- und
Pfadinformationen
  delete(), renameTo() – Datei/Verzeichnis löschen,
verschieben/umbenennen
  mkdir() – Verzeichnis anlegen
30
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
File im Überblick, Beispiel
Liefert alle untergeordneten
Dateien und Verzeichnisse
File root = new File("C:/");
for (File child : root.listFiles()) {
if (child.isFile()) {
System.out.format("%-15s [%d Byte(s)]%n",
child.getName(), child.length());
}
}
AUTOEXEC.BAT
BOOT.INI
bootfont.bin
BOOTLOG.PRV
BOOTLOG.TXT
...
[0 Byte(s)]
[194 Byte(s)]
[4952 Byte(s)]
[0 Byte(s)]
[0 Byte(s)]
31
© Holger Röder
  Ein Thread ist ein leichtgewichtiger Prozess ohne eigenen Speicherraum.
Java-Programme können mehrere parallel laufende Threads besitzen.
  In Java werden Threads über die Klasse Thread und die Schnittstelle
Runnable realisiert.
Programmentwicklung
Winter 2009/2010
Threads
se
32
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
Die Klasse Thread
  Eigene Threads können durch Ableitung von der Klasse Thread aus dem
Paket java.lang erstellt werden.
  Gestartet wird ein Thread über die Methode start(), die ihrerseits die
run()-Methode des Threads aufruft.
  In der überlagerten Methode run() wird der vom Thread auszuführende
Code angegeben. Der Thread läuft, bis das Ende von run() erreicht ist.
  Jeder Thread hat eine Priorität, die in der Regel seine
Ausführungsgeschwindigkeit beeinflusst. Diese kann mit setPriority()
verändert werden.
  Die statische Methode Thread.sleep() pausiert den aktuellen Thread
für die angegebene Anzahl Millisekunden.
33
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
Threads – Beispiel
public static void main(String[] args) {
Unterklasse von Thread
class PersonThread extends Thread {
private String threadName;
private Person person;
public PersonThread(String threadName, Person person) {
this.threadName = threadName;
this.person = person;
run-Methode implementiert die
}
Funktionalität des Threads
@Override
public void run() {
for (int i = 0; i < 50; i++) {
person.erhoeheAlter();
System.out.println("(" + threadName + ") "
+ person.getAlter());
}
}
Instanziieren der Threads
}
Person p = new Person("Mona Maurer", 23, 2500.0);
PersonThread t1 = new PersonThread("Thread 1", p);
PersonThread t2 = new PersonThread("Thread 2", p);
t1.start();
t2.start();
Starten der Threads
}
34
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
Threads und Synchronisation
  Unsynchronisiert
  Kurzzeitig
  Unsynchronisiert
  Langdauernd
  Synchronisiert
  Langdauernd
public void erhoeheAlter() {
int a = getAlter();
setAlter(a + 1);
}
(Thread
(Thread
(Thread
(Thread
(Thread
(Thread
1)
2)
1)
2)
1)
2)
24
25
26
27
28
29
public void erhoeheAlter() {
int a = getAlter();
try {
Thread.sleep(200);
} catch (InterruptedException e) { }
setAlter(a + 1);
}
(Thread
(Thread
(Thread
(Thread
(Thread
(Thread
1)
2)
2)
1)
2)
1)
24
24
25
25
26
26
synchronized public void erhoeheAlter() {
int a = getAlter();
try {
Thread.sleep(200);
} catch (InterruptedException e) { }
setAlter(a + 1);
}
(Thread
(Thread
(Thread
(Thread
(Thread
(Thread
1)
2)
1)
2)
1)
2)
24
25
26
27
28
29
35
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
Synchronisation
  In Java kommunizieren Threads über gemeinsame Daten. Führen
mehrere Threads Änderungen an diesen Daten durch, kann es zu
Synchronisationsproblemen kommen.
  Java nutzt das Monitor-Konzept zur Synchronisation nebenläufiger
Prozesse: kritische Bereiche werden mit einer automatischen Sperre
versehen, die dafür sorgt, dass jeweils nur ein Prozess gleichzeitig diesen
Programmteil durchläuft.
  Zur Definition solcher kritischen Bereiche dient das Schlüsselwort
synchronized.
  Es können komplette Methoden und einzelne Blöcke geschützt werden:
  Bei Methoden wird der this-Zeiger als Sperre verwendet.
  Bei synchronized-Blöcken muss das als Sperre zu verwendende
Objekt (beliebigen Typs) explizit angegeben werden:
synchronized (sperrObjekt) { ... }
36
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
Die Schnittstelle Runnable
  Soll eine Klasse als Thread laufen, aber nicht von Thread abgeleitet
werden, kann sie die Schnittstelle Runnable implementieren.
  Ein Thread-Hilfsobjekt, dem ein Objekt dieser Klasse im Konstruktor
übergeben wurde, sorgt dann für die Ausführung als Thread.
class Sterne implements Runnable {
public void run() {
for (int i = 0; i < 100; i++) {
System.out.print("*");
}
}
}
Sterne sterne = new Sterne();
Thread t = new Thread(sterne);
t.start();
Klasse implementiert die
Schnittstelle Runnable
Spezieller Thread-Konstruktor
37
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
Datenstrukturen
  Das JDK stellt im Paket java.util Implementierungen verschiedener
komplexer Datenstrukturen bereit.
  Zentrale Schnittstellen sind Collection und Map.
  Assoziative Speicher mit Schlüssel-Wert-Zuordnungen (z. B. HashTabellen) implementieren die Schnittstelle Map.
  Alle anderen Datenstrukturen implementieren die Schnittstelle
Collection. Diese definiert die gemeinsamen Zugriffsmethoden
(Auswählen, Einfügen, Löschen, Suchen etc.).
  Collection-Datenstrukturen können weiter unterteilt werden in
  Listen (Schnittstelle List) und
  Mengen (Schnittstelle Set).
38
© Holger Röder
Programmentwicklung
Winter 2009/2010
Übersicht: Collection, List, Set
se
Ein Objekt darf höchstens einmal
enthalten sein (Mengensemantik)
Ein Objekt kann mehrfach enthalten sein
39
© Holger Röder
Programmentwicklung
Winter 2009/2010
Übersicht: Collection und
Implementierungen
se
Ein Objekt darf höchstens einmal
enthalten sein (Mengensemantik)
Ein Objekt kann mehrfach enthalten sein
40
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
Schnittstelle List und Implementierungen
  Collections vom Typ List repräsentieren geordnete Listen von
Elementen:
  Elemente können mehrfach in der Liste enthalten sein.
  Auf die Elemente kann entweder sequentiell oder wahlfrei über einen
Index zugegriffen werden.
  Implementierungen der Schnittstelle List im Paket java.util:
  LinkedList: interne Realisierung als doppelt verkettete Liste
  ArrayList: interne Realisierung als Array
  Vector: interne Realisierung ebenfalls als Array, synchronisierte
Zugriffsmethoden
41
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
Die Klasse Vector
  Vector ist eine konkrete Implementierung der List-Schnittstelle und
repräsentiert dynamische Arrays. Im Unterschied zu normalen Arrays
können Vector-Objekte „wachsen“, d.h. die Anzahl enthaltener Objekte
ist auch zur Laufzeit variabel.
  Einfügen, Entfernen und Auslesen von Elementen sind leicht möglich.
  Vector ist eine generische Klasse und sollte typisiert werden.
Person p1 = new Person("Alexander", 37, 2800.0);
Person p2 = new Person("Bastian", 22, 2400.0);
Person p3 = new Person("Carlos", 41, 4350.0);
Elemente
einfügen,
auslesen,
entfernen
List<Person> v = new Vector<Person>();
Objekt erzeugen
v.add(p1);
v.add(p2);
Person p = v.get(0); // liefert "Alexander"
v.remove(0);
v.add(0, p3);
p = v.get(0); // liefert "Carlos"
42
© Holger Röder
  Iteratoren sind Objekte, die zum Durchlaufen von Datenstrukturen
dienen. In Java existiert hierzu die Schnittstelle Iterator.
  Iterator definiert drei Methoden:
  hasNext() liefert true zurück, wenn die Datenstruktur, zu der der
Iterator gehört, ein weiteres Element enthält.
  next() liefert dieses nächste Element.
  remove() entfernt das zuletzt geholte Element.
Wichtig: alle anderen Änderungen (Einfügen, Sortieren etc.) an der
Datenstruktur führen zu einem undefinierten Zustand des Iterators!
Programmentwicklung
Winter 2009/2010
Iterieren über eine Datenstruktur: Iterator
se
43
© Holger Röder
Winter 2009/2010
Collection, Iterator und Iterable
  Die Schnittstelle Iterable kennzeichnet eine Datenstruktur als iterierbar:
über die definierte Methode iterator() kann ein passendes IteratorObjekt geholt werden.
  Die Schnittstelle Collection ist von der Schnittstelle Iterable
abgeleitet. Über Collection-Datenstrukturen können somit iteriert
werden. Über die Methode iterator() kann für jedes Objekt, das
Collection implementiert, ein Iterator-Objekt geholt werden.
Programmentwicklung
  Das Iterieren über die Elemente kann mit einer erweiterten for-Schleife
erfolgen.
se
44
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
Beispiel: Vector und Iterator
  Vector implementiert Collection, also kann die Datenstruktur mit
einem Iterator durchlaufen werden.
Person p1 = new Person("Alexander", 37, 2800.0);
Person p2 = new Person("Bastian", 22, 2400.0);
Person p3 = new Person("Carlos", 41, 4350.0);
Vector<Person> v = new Vector<Person>();
v.add(p1);
v.add(p2);
v.add(p3);
Iterator-Objekt
holen
Iterator<Person> iter = v.iterator();
while (iter.hasNext()) {
Person p = iter.next();
p.out();
}
/* Alternativ: */
for (Person p: v) {
p.out();
}
true, wenn noch ein Element
enthalten ist
nächstes Element
“elegantere” Lösung mit erweiterter for-Schleife
45
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
Schnittstelle Set und Implementierungen
  Collections vom Typ Set repräsentieren Mengen:
  Kein Element darf mehrfach enthalten sein. Die Prüfung auf Gleichheit
erfolgt beim Einfügen über equals().
  Die Elemente haben keine definierte Reihenfolge.
  Implementierungen der Schnittstelle Set im Paket java.util:
  HashSet: interne Speicherung als Hash-Tabelle, generische Klasse
Set<String> menge = new HashSet<String>();
String s1 = "Hallo Welt";
String s2 = "Hallo" + " Welt";
boolean erfolg;
erfolg = menge.add(s1); // true
erfolg = menge.add(s2); // false, schon enthalten!
  Für sortierte Mengen steht die Schnittstelle SortedSet und die
Implementierung TreeSet zur Verfügung.
46
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
Schnittstelle Map und Implementierungen
  Datenstrukturen vom Typ Map repräsentieren assoziative Speicher, in
denen Schlüssel auf Werte (also Elemente) abgebildet werden.
  Für jeden Schlüssel gibt es entweder kein oder genau ein
zugeordnetes Wert-Element.
  Der Zugriff auf die gespeicherten Elemente erfolgt über den
Schlüssel.
  Zugriffsmethoden (vereinfacht): put(key, value) und get(key)
  Implementierungen der Schnittstelle Map im Paket java.util:
  HashMap: interne Realisierung als Hash-Tabelle
  Hashtable: interne Realisierung ebenfalls als Hash-Tabelle,
synchronisierte Zugriffsmethoden
47
se
Programmentwicklung
Winter 2009/2010
© Holger Röder
Übersicht: Map
48
© Holger Röder
Winter 2009/2010
Programmentwicklung
se
Die Klasse HashMap
  Die Klasse HashMap implementiert die Schnittstelle Map und repräsentiert
eine Hash-Tabelle.
  HashMap sollte zweifach typisiert werden (Schlüssel und Werte).
Person p1 = new Person("Alexander", 37, 2800.0);
Person p2 = new Person("Bastian", 22, 2400.0);
Person p3 = new Person("Carlos", 41, 4350.0);
Map<String, Person> map = new HashMap<String, Person>();
map.put(p1.getName(), p1);
HashMap anlegen:
map.put(p2.getName(), p2);
Schlüssel: String, Wert: Person
map.put(p3.getName(), p3);
Person p = map.get("Bastian");
p.out();
for (String key: map.keySet()) {
System.out.println(key);
}
for (Person value: map.values()) {
value.out();
}
Einfügen
und
Auslesen
Iterieren über alle Schlüssel
bzw. alle Werte
49
© Holger Röder
  Um die Elemente einer Datenstruktur zu sortieren, muss eine Ordnung
auf den Elementen definiert werden. Hierzu existieren zwei Möglichkeiten:
  Die Elemente implementieren die Schnittstelle Comparable (Paket
java.lang).
  Ein explizites Vergleichsobjekt, das die Schnittstelle Comparator
implementiert, übernimmt die Sortierung.
Programmentwicklung
Winter 2009/2010
Sortierung
se
50
© Holger Röder
Schnittstelle Comparable
  Die Schnittstelle Comparable definiert eine Methode:
public int compareTo(Object o)
Programmentwicklung
Winter 2009/2010
Rückgabewert Bedeutung
se
< 0
Aktuelles Objekt liegt vor dem zu vergleichenden Objekt.
= 0
Aktuelles Objekt und zu vergleichendes Objekt sind gleich.
> 0
Aktuelles Objekt liegt hinter dem zu vergleichenden Objekt.
  Wenn möglich sollte die Implementierung typisiert werden: die
compareTo()-Methode erhält dann keine Object-Parameter, sondern
entsprechend typisierte Parameter.
  Beispiel: Sortierung von Person-Objekten anhand des Namens:
public class Person implements Comparable<Person> {
...
Aufruf der compareTo()public int compareTo(Person p2) {
Methode der Klasse String
Person p1 = this;
return p1.getName().compareTo(p2.getName());
}
}
51
© Holger Röder
Winter 2009/2010
Schnittstelle Comparator
  Analog zu Comparable definiert Comparator nur eine Methode:
public int compare(Object o1, Object o2)
  Auch hier ist eine Typisierung möglich.
  Beispiel: PersonComparator sortiert absteigend nach Namen
public class PersonComparator implements Comparator<Person> {
Programmentwicklung
public int compare(Person o1, Person o2) {
return o2.getName().compareTo(o1.getName());
se
// Alternativ: Sortierung aufsteigend nach Alter
// return p1.getAlter() – p2.getAlter();
}
}
52
© Holger Röder
Winter 2009/2010
Sortierung der Datenstrukturen
  Sortierte Datenstrukturen im Paket java.util:
  Sortierte Mengen: Schnittstelle SortedSet und Implementierung
TreeSet
  Sortierte assoziative Speicher (Sortierung der Schlüssel):
Schnittstelle SortedMap und Implementierung TreeMap
  Beispiel:
Person p1 = new Person("Alexander", 37, 2800.0);
Person p2 = new Person("Bastian", 22, 2400.0);
Person p3 = new Person("Carlos", 41, 4350.0);
Programmentwicklung
SortedSet<Person> menge = new TreeSet<Person>(new PersonComparator());
se
menge.add(p2);
menge.add(p3);
menge.add(p1);
for (Person p: menge)
p.out();
Person:
}
Person:
Person:
2800.0
Übergabe des ComparatorObjekts an den Konstruktor
{
Name = Carlos, Alter = 41, Gehalt = 4350.0
Name = Bastian, Alter = 22, Gehalt = 2400.0
Name = Alexander, Alter = 37, Gehalt =
53
Herunterladen