Folien - Fakultät Informatik Uni Stuttgart

Werbung
© Holger Röder
Winter 2008/2009
Programmentwicklung
se
Java: Kapitel 5
Die Java-Standardbibliotheken
Programmentwicklung WS 2008/2009
Holger Röder
[email protected]
© Holger Röder
Winter 2008/2009
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 2008/2009
Überblick über die Java SE
se
Quelle: http://java.sun.com/javase/6/docs/
3
© Holger Röder
Winter 2008/2009
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 2008/2009
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 2008/2009
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 2008/2009
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 2008/2009
Die Klasse System
se
8
© Holger Röder
Winter 2008/2009
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 2008/2009
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 2008/2009
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 2008/2009
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 2008/2009
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 2008/2009
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 2008/2009
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 2008/2009
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
© Holger Röder
Character-Ausgabe: Writer
Winter 2008/2009
(from io)
Programmentwicklung
CharArrayWriter
(f ro m i o)
lock : Object
PrintWriter
se
Writer
(from io)
PrintWriter(arg0 : OutputStream, arg1 : boolean)
PrintWriter(arg0 : OutputStream)
PrintWriter(arg0 : Writer, arg1 : boolean)
PrintWriter(arg0 : Writer)
flush() : void
close() : void
checkError() : boolean
setError() : void
write(arg0 : int) : void
write(arg0 : char[], arg1 : int, arg2 : int) : void
write(arg0 : char[]) : void
write(arg0 : String, arg1 : int, arg2 : int) : void
write(arg0 : String) : void
print(arg0 : boolean) : void
print(arg0 : char) : void
print(arg0 : int) : void
print(arg0 : long) : void
print(arg0 : float) : void
print(arg0 : double) : void
print(arg0 : char[]) : void
print(arg0 : String) : void
print(arg0 : Object) : void
println() : void
println(arg0 : boolean) : void
println(arg0 : char) : void
println(arg0 : int) : void
println(arg0 : long) : void
println(arg0 : float) : void
println(arg0 : double) : void
println(arg0 : char[]) : void
println(arg0 : String) : void
println(arg0 : Object) : void
Writer(arg0 : Object)
Writer()
write(arg0 : int) : void
write(arg0 : char[]) : void
write(arg0 : char[], arg1 : int, arg2 : int) : void
write(arg0 : String) : void
write(arg0 : String, arg1 : int, arg2 : int) : void
flush() : void
close() : void
FilterWrit er
(from io)
PipedWriter
(from io)
St ringWriter
(f ro m i o)
OutputStreamWriter
BufferedWriter
(f ro m i o)
(f ro m i o)
OutputSt reamWriter(arg0 : Output Stream, arg1 : CharsetEncoder)
OutputSt reamWriter(arg0 : Output Stream, arg1 : Charset)
OutputSt reamWriter(arg0 : Output Stream)
OutputSt reamWriter(arg0 : Output Stream, arg1 : String)
getEncoding() : String
flus hBuffer() : void
writ e(arg0 : int) : void
writ e(arg0 : char[], arg1 : int, arg2 : int) : void
writ e(arg0 : String, arg1 : int, arg2 : int ) : void
flus h() : void
close() : void
BufferedWriter(arg0 : Writer, arg1 : int)
BufferedWriter(arg0 : Writer)
flushBuffer() : void
write(arg0 : int) : void
write(arg0 : char[], arg1 : int, arg2 : int) : void
write(arg0 : String, arg1 : int, arg2 : int) : void
newLine() : void
flush() : void
close() : void
FileWriter
(from io)
FileWriter(arg0 :
FileWriter(arg0 :
FileWriter(arg0 :
FileWriter(arg0 :
FileWriter(arg0 :
FileDescriptor)
File, arg1 : boolean)
File)
String, arg1 : boolean)
String)
17
© Holger Röder
Winter 2008/2009
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
© Holger Röder
Character-Eingabe: Reader
Reader
(from io)
StringReader
lock : Object
(from io)
Reader(arg0 : Object)
Reader()
read() : int
read(arg0 : c har[]) : int
read(arg0 : c har[], arg1 : int, arg2 : int) : int
skip(arg0 : long) : long
ready() : boolean
markSupported() : boolean
mark(arg0 : int) : void
res et() : void
close() : void
Winter 2008/2009
FilterReader
(from io)
CharArrayReader
(from io)
PipedReader
(from io)
InputStreamReader
Programmentwicklung
(from io)
se
InputStreamReader(arg0 : InputStream, arg1 : CharsetDecoder)
InputStreamReader(arg0 : InputStream, arg1 : Charset)
InputStreamReader(arg0 : InputStream, arg1 : String)
InputStreamReader(arg0 : InputStream)
getEncoding() : String
read() : int
read(arg0 : char[], arg1 : int, arg2 : int) : int
ready() : boolean
close() : void
FileReader
(f rom io )
FileReader(arg0 : FileDescriptor)
FileReader(arg0 : File)
FileReader(arg0 : String)
BufferedReader
(f rom i o)
BufferedReader(arg0 : Reader)
BufferedReader(arg0 : Reader, arg1 : int)
read() : int
read(arg0 : char[], arg1 : int, arg2 : int) : int
readLine(arg0 : boolean) : String
readLine() : String
skip(arg0 : long) : long
ready() : boolean
markSupported() : boolean
mark(arg0 : int) : void
reset() : void
close() : void
19
© Holger Röder
Winter 2008/2009
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
© Holger Röder
Byte-Ausgabe: OutputStream
(from io)
(f rom io )
(from io)
Programmentwicklung
ByteArray Out putSt ream
se
#out
OutputStream()
write(arg0 : int) : void
write(arg0 : byte[]) : void
write(arg0 : byte[], arg1 : int, arg2 : int) : void
flush() : void
close() : void
PipedOutputStream
Winter 2008/2009
FilterOutput Stream
OutputStream
(f ro m i o)
PrintStream
(f rom i o)
ObjectOutput Stream
(f rom i o)
FileOutputStream
(from io )
FileOutputStream(arg0 : FileDescriptor)
FileOutputStream(arg0 : File, arg1 : boolean)
FileOutputStream(arg0 : File)
FileOutputStream(arg0 : String, arg1 : boolean)
FileOutputStream(arg0 : String)
write(arg0 : int) : void
write(arg0 : byte[]) : void
write(arg0 : byte[], arg1 : int, arg2 : int) : void
close() : void
getFD() : FileDescriptor
getChannel() : FileChannel
finalize() : void
+$out
+$err
SocketOutputStream
System
(from net)
(f rom l ang)
PrintStream(arg0 : OutputStream, arg1 : boolean, arg2 : String)
PrintStream(arg0 : OutputStream, arg1 : boolean)
PrintStream(arg0 : OutputStream)
flush() : void
close() : void
checkError() : boolean
setError() : void
write(arg0 : int) : void
write(arg0 : byte[], arg1 : int, arg2 : int) : void
print(arg0 : boolean) : void
print(arg0 : char) : void
print(arg0 : int) : void
print(arg0 : long) : void
print(arg0 : float) : void
print(arg0 : double) : void
print(arg0 : char[]) : void
print(arg0 : String) : void
print(arg0 : Object) : void
println() : void
println(arg0 : boolean) : void
println(arg0 : char) : void
println(arg0 : int) : void
println(arg0 : long) : void
println(arg0 : float) : void
println(arg0 : double) : void
println(arg0 : char[]) : void
println(arg0 : String) : void
println(arg0 : Object) : void
21
© Holger Röder
Byte-Eingabe: InputStream
InputStream
(from io)
System
(from lang)
Winter 2008/2009
+$in
StringBufferInputStream
(from io)
InputStream()
read() : int
read(arg0 : byte[]) : int
read(arg0 : byte[], arg1 : int, arg2 : int) : int
skip(arg0 : long) : long
available() : int
close() : void
mark(arg0 : int) : void
reset() : void
markSupported() : boolean
Byt eArrayInputStream
(from io)
FileInputStream
Programmentwicklung
(from io)
se
ObjectInputSt ream
(from io)
FileInputSt ream(arg0 : FileDescriptor)
FileInputSt ream(arg0 : File)
FileInputSt ream(arg0 : String)
read() : int
read(arg0 : byte[]) : int
read(arg0 : byte[], arg1 : int, arg2 : int) : int
sk ip(arg0 : long) : long
available() : int
close() : void
getFD() : FileDescript or
getChannel() : FileChannel
finalize() : void
22
© Holger Röder
Winter 2008/2009
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 2008/2009
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 2008/2009
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 2008/2009
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
Winter 2008/2009
Programmentwicklung
se
Objekte speichern
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
27
© Holger Röder
Winter 2008/2009
Programmentwicklung
se
Objekte laden
try {
ObjectInputStream in = new ObjectInputStream(
new FileInputStream("C:/pe/pers.ser"));
Stream für Deserialisierung
Person[] personen = (Person[]) in.readObject();
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 2008/2009
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 2008/2009
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 2008/2009
Programmentwicklung
se
File im Überblick, Beispiel
File
Liefert alle untergeordneten
Dateien und Verzeichnisse
(from io)
File(arg0 : URI)
File(arg0 : File, arg1 : String)
File(arg0 : String, arg1 : String)
File(arg0 : String)
getPrefixLength() : int
getName() : String
getParent() : String
getParentFile() : File
getPath() : String
isAbsolute() : boolean
getAbsolutePath() : String
getAbsoluteFile() : File
getCanonicalPath() : String
getCanonicalFile() : File
toURL() : URL
toURI() : URI
canRead() : boolean
canWrite() : boolean
exists() : boolean
isDirectory() : boolean
isFile() : boolean
isHidden() : boolean
lastModified() : long
length() : long
createNewFile() : boolean
delete() : boolean
deleteOnExit() : void
list() : Logical View::java::lang::String[]
list(arg0 : FilenameFilter) : Logical View::java::lang::String[]
listFiles() : File[]
listFiles(arg0 : FilenameFilter) : File[]
listFiles(arg0 : FileFilter) : File[]
mkdir() : boolean
mkdirs() : boolean
renameTo(arg0 : File) : boolean
setLastModified(arg0 : long) : boolean
setReadOnly() : boolean
listRoots() : File[]
createTempFile(arg0 : String, arg1 : String, arg2 : File) : File
createTempFile(arg0 : String, arg1 : String) : File
compareTo(arg0 : File) : int
compareTo(arg0 : Object) : int
equals(arg0 : Object) : boolean
hashCode() : int
toString() : String
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 2008/2009
Threads
se
32
© Holger Röder
Winter 2008/2009
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 2008/2009
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 2008/2009
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 2008/2009
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 2008/2009
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 2008/2009
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 2008/2009
Ü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 2008/2009
Ü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 2008/2009
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 2008/2009
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
Winter 2008/2009
Programmentwicklung
se
Iterieren über eine Datenstruktur: Iterator
„ 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!
43
© Holger Röder
Winter 2008/2009
Programmentwicklung
se
Collection, Iterator und Iterable
„ Die Schnittstelle Iterable kennzeichnet eine Datenstruktur als
iterierbar: über die definierte Methode iterator() kann ein
passendes Iterator-Objekt 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.
„ Das Iterieren über die Elemente kann mit einer erweiterten forSchleife erfolgen.
44
© Holger Röder
Winter 2008/2009
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 2008/2009
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 2008/2009
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 2008/2009
© Holger Röder
Übersicht: Map
48
© Holger Röder
Winter 2008/2009
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 2008/2009
Sortierung
se
50
© Holger Röder
Winter 2008/2009
Programmentwicklung
se
Schnittstelle Comparable
„ Die Schnittstelle Comparable definiert eine Methode:
public int compareTo(Object o)
Rückgabewert Bedeutung
< 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()-Methode
public int compareTo(Person p2) {
der Klasse String
Person p1 = this;
return p1.getName().compareTo(p2.getName());
}
}
51
© Holger Röder
Schnittstelle Comparator
„ Analog zu Comparable definiert Comparator nur eine Methode:
public int compare(Object o1, Object o2)
Programmentwicklung
Winter 2008/2009
„ Auch hier ist eine Typisierung möglich.
se
„ Beispiel: PersonComparator sortiert absteigend nach Namen
public class PersonComparator implements Comparator<Person> {
public int compare(Person o1, Person o2) {
return o2.getName().compareTo(o1.getName());
// Alternativ: Sortierung aufsteigend nach Alter
// return p1.getAlter() – p2.getAlter();
}
}
52
© Holger Röder
Winter 2008/2009
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:
Ü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 = 2800.0 53
Herunterladen